feat: Added QR code component with encoding and rendering capabilities - #2242
Conversation
Some of the key changes include: - Added new properties to the QR code component for logo source, size, and margin. - Updated the QR code generation logic to account for the logo area and error correction level. - Refactored the corner rendering logic to ensure the finder patterns are not obscured by the logo. - Added new tests to cover the logo functionality and ensure the QR code is still scannable with the logo present.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new igc-qr-code web component that can encode input data into a QR matrix and render it as an SVG, with customization options (error correction, sizing, module/corner styles, optional centered logo). It also adds Storybook coverage and a fairly comprehensive unit test suite for both the component and the underlying QR encoding/model logic.
Changes:
- Added
IgcQrCodeComponentwith SVG rendering, styling knobs, and optional logo masking. - Implemented a QR encoder/model pipeline (encoding, ECC, masking, matrix generation) with unit tests.
- Added Storybook stories and exported the new component from the package entrypoint.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| stories/qr-code.stories.ts | Adds Storybook documentation and visual test cases for QR code variants (styles, colors, sizes, logos). |
| src/index.ts | Exports IgcQrCodeComponent from the package entrypoint. |
| src/components/qr-code/types.ts | Introduces public type unions for QR styles, error correction level, and encoding mode. |
| src/components/qr-code/renderer/constants.ts | Defines renderer constants (CSS var-based colors) and safe-area defaults for logo sizing. |
| src/components/qr-code/renderer/helpers.ts | Implements SVG path generation for modules and finder patterns plus helper geometry. |
| src/components/qr-code/renderer/dots.ts | Renders data modules as a single SVG path from the boolean matrix. |
| src/components/qr-code/renderer/corner.ts | Renders finder patterns separately to allow style customization. |
| src/components/qr-code/renderer/image.ts | Renders the logo <image> and optional mask to preserve scannability. |
| src/components/qr-code/qr-code.ts | Implements the Lit component, properties/attributes, SVG output, and logo handling. |
| src/components/qr-code/qr-code.spec.ts | Adds component-level tests including a11y checks, rendering behavior, and logo validation/structure. |
| src/components/qr-code/model/qr-model.spec.ts | Adds model-level tests for encoding mode detection, ECC behavior, masking, and matrix structure. |
| src/components/qr-code/model/matrix.ts | Implements matrix construction (finder/timing/alignment/data placement + format/version info + masking). |
| src/components/qr-code/model/mask.ts | Implements mask application and penalty scoring to select the best mask pattern. |
| src/components/qr-code/model/error-correction.ts | Implements Reed–Solomon ECC and QR block interleaving with the EC blocks table. |
| src/components/qr-code/model/encode.ts | Implements data encoding to codewords, including version selection and padding. |
| src/components/common/util.ts | Adds documentation to nanoid() used for stable SVG mask IDs. |
… fit in any version
…iteui-webcomponents into rkaraivanov/qr-code
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/components/qr-code/qr-code.ts:119
- The
logoSizedocumentation describes it as a ratio of the QR code size (and implies1would fill the whole code), but the implementation treats it as a multiplier ofMAX_SAFE_AREA(targetArea = logoSize * MAX_SAFE_AREA). This mismatch will confuse consumers and makes the default description (“40% of the QR code size”) inaccurate.
/**
* The size of the logo as a ratio of the QR code size. This determines how large the logo will appear within the QR code.
* The value should be a number between 0 and 1, where 0 means no logo and 1 means the logo will take up the entire QR code (which is not recommended).
* The default value is 0.4, meaning the logo will take up 40% of the QR code size.
*
src/components/qr-code/renderer/constants.ts:26
- The comment for
DEFAULT_SIZE_RATIOstates it represents “40% of the QR code area”, butDEFAULT_SIZE_RATIOis just the defaultlogoSizefactor and is later converted/capped by safe-area logic. As written, the comment is misleading.
/**
* Default size ratio for the logo relative to the QR code. This means that by default, the logo will occupy 40% of the QR code area.
* This is a conservative default that allows for a reasonably sized logo while maintaining good scannability.
*/
src/components/qr-code/qr-code.ts:26
- The component JSDoc starts with an empty summary line (
*on its own), which is currently propagating into generated docs/Storybook as a leading newline. This is just formatting noise in the public docs.
This issue also appears on line 115 of the same file.
/**
*
* Generates a QR code based on the provided value and options.
* The component renders an SVG representation of the QR code, which can be customized using various properties.
*
src/components/qr-code/renderer/constants.ts:4
- The QR code uses
--igc-qr-*for the main colors, but the corner colors are exposed as--qr-corner-*(no--igc-prefix). Since these CSS custom properties are part of the public styling API, the inconsistent naming is likely to be a long-term footgun for consumers and makes them harder to discover alongside other--igc-*variables used across the codebase (e.g.var(--igc-chat-height)insrc/components/chat/themes/chat.base.scss).
This issue also appears on line 23 of the same file.
const DOT_COLOR = 'var(--igc-qr-dark, #000)';
const DOT_BACKGROUND = 'var(--igc-qr-background, #fff)';
const CORNER_SQUARE_COLOR = 'var(--qr-corner-square-fill, #000)';
const CORNER_DOT_COLOR = 'var(--qr-corner-dot-fill, #000)';
* feat(qr-code): adopt igniteui-theming for component theming Depends on IgniteUI/igniteui-theming#593 * deps(theming): bump to latest version
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 32 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/components/qr-code/qr-code.ts:121
- The
logoSizedocs describe it as a ratio of the full QR code size (including claiming 1 = the entire QR code), but the implementation useslogoSizeas a multiplier ofMAX_SAFE_AREA(i.e. a ratio of the maximum safe area). The docs should match the actual behavior so consumers can predict sizing.
* The size of the logo as a ratio of the QR code size. This determines how large the logo will appear within the QR code.
* The value should be a number between 0 and 1, where 0 means no logo and 1 means the logo will take up the entire QR code (which is not recommended).
* The default value is 0.4, meaning the logo will take up 40% of the QR code size.
src/components/qr-code/renderer/constants.ts:21
DEFAULT_SIZE_RATIOis documented as "40% of the QR code area", butlogoSizeis multiplied byMAX_SAFE_AREA, so the default corresponds to 40% of the maximum safe area (not 40% of the full QR code area).
* Default size ratio for the logo relative to the QR code. This means that by default, the logo will occupy 40% of the QR code area.
* This is a conservative default that allows for a reasonably sized logo while maintaining good scannability.
*/
src/components/qr-code/qr-code.ts:139
dotStyleis documented as applying to the data modules only, but it is also passed as the finder-pattern corner dot style (see renderQrFinders call). Either document that it affects the corner dots too, or introduce a separate property for corner-dot styling.
* The style of the data modules (dots) in the QR code. This can be 'square', 'circle', or 'rounded'.
src/components/qr-code/qr-code.ts:36
- The component exposes several CSS custom properties (e.g. --ig-qr-code-background, --ig-qr-code-dark-color) that are used in the story/tests, but they are not documented in the component JSDoc. This means the generated API docs will miss these theming hooks.
* @csspart background - The background rect of the QR code.
* @csspart dots - The data modules (dots) of the QR code.
* @csspart corner-square - The outer corner (finder-pattern) squares of the QR code.
* @csspart corner-dot - The inner corner (finder-pattern) dots of the QR code.
*/
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/components/qr-code/model/matrix.ts:373
- Mask pattern selection is computed before format information bits (and the fixed dark module at (size-8,8)) are written into the candidate matrices. QR mask penalty evaluation is defined over the final symbol, so excluding format info can pick a different “best” mask than intended and reduce scan reliability.
// Select best mask
const bestMask = selectBestMask(matrix, functionModules);
// Apply the best mask to the data modules
const maskedMatrix = applyMask(matrix, functionModules, bestMask);
src/components/qr-code/qr-code.ts:252
errorLevelis initialized to'M', so_getErrorLevelAndArea()always treats it as user-specified and never runs the auto-pick branch. This contradicts the documented behavior (“when error-level is not explicitly set… chosen automatically”) and prevents auto-raising the EC level to accommodate a logo.
private _getErrorLevelAndArea(hasLogo: boolean) {
const userErrorLevel = this.errorLevel;
const size = this.logoSize;
const sizeRatio = hasLogo ? clamp(size ?? DEFAULT_SIZE_RATIO, 0, 1) : 0;
const targetArea = sizeRatio * MAX_SAFE_AREA;
Description
Added a new component able to encode and render data as a QR code SVG
Type of Change
Checklist