Skip to content

Commit d9b57b4

Browse files
docs(openspec): add-datamatrix-generation change
Proposal, design, specs, and task checklist for adding Data Matrix and GS1 Data Matrix generation to the barcode generator widget. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 27a8cbb commit d9b57b4

5 files changed

Lines changed: 222 additions & 0 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-07-09
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
## Context
2+
3+
`@mendix/barcode-generator-web` renders three-way today: 1D barcodes via `jsbarcode` (isolated in `src/utils/barcodeRenderer-utils.ts`) and QR via `qrcode.react` (isolated in `src/components/QRCode.tsx`). `src/config/Barcode.config.ts` maps Mendix props into a lib-agnostic discriminated union `BarcodeConfig = BarcodeTypeConfig | QRCodeTypeConfig`, and `src/BarcodeGenerator.tsx:25` dispatches on `config.type`. Rendering is SVG-first; download converts SVG→PNG via `src/utils/download-code.ts` + `download-utils.ts`.
4+
5+
Neither current lib can produce DataMatrix. Pharma requires **GS1 DataMatrix** (FNC1 + GS1 Application Identifiers), not just plain DataMatrix.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
11+
- Add DataMatrix and GS1 DataMatrix generation as a third render path, reusing the existing config-union + dispatch + SVG→PNG download architecture.
12+
- Keep the new library isolated behind a single seam module, mirroring the QR split.
13+
- No regression to existing barcode/QR behavior.
14+
15+
**Non-Goals:**
16+
17+
- Replacing jsbarcode or qrcode.react.
18+
- Building a GS1 AI parser/validator beyond loose syntax checks (bwip-js validates the encoding).
19+
- Adding non-DataMatrix 2D symbologies (Aztec, PDF417) — out of scope.
20+
21+
## Decisions
22+
23+
**Library: `@bwip-js/browser`.** Only maintained (v4.11.x, ~676k dl/wk, MIT) library with native GS1 DataMatrix (`bcid: "gs1datamatrix"`, accepts human-readable AI syntax) and rectangular support. Import the named `datamatrix` + `drawingSVG` exports so bundlers tree-shake — do NOT use `toSVG()`, which links all ~100 BWIPP encoders.
24+
25+
- _Alternatives:_ `datamatrix-svg` (no GS1, unmaintained since 2020) rejected; `@zxing/library` (used by scanner) is decode-only, cannot encode.
26+
27+
**Third render path, not a `customCodeFormat` sub-option.** DataMatrix is 2D with its own options (GS1 toggle, shape), like QR. Add `DataMatrix` to the top-level `codeFormat` enum and a `DataMatrixTypeConfig` (`type: "datamatrix"`) to the union. `barcodeConfig()` branches on `format === "DataMatrix"` before the QR check.
28+
29+
- _Alternative:_ nesting under `customCodeFormat` (the 1D list) rejected — that list is jsbarcode-specific and 1D-shaped.
30+
31+
**GS1 as a boolean toggle under Data Matrix, not a separate top-level format.** Matches the pharma mental model ("same symbology, GS1-encoded") and keeps the top-level enum small. `dmGs1Mode` selects `gs1datamatrix` vs `datamatrix`.
32+
33+
**New seam `src/components/DataMatrix.tsx`.** Mirrors `QRCode.tsx`: builds bwip-js options, renders SVG, exposes an `SVGSVGElement` ref so the existing `DownloadButton` + `downloadCode(ref, config, ...)` pipeline works unchanged. Validation + try/catch error state live in this component (the `useRenderBarcode` hook is 1D/jsbarcode-specific).
34+
35+
**Validation.** Extend `validateBarcodeValue` in `src/config/validation.ts` with a `DataMatrix` case (the `format` param already unions `CodeFormatEnum`): plain mode = charset/length sanity; GS1 mode = loose balanced-`(nn)` AI-syntax check. Encoder errors caught at render.
36+
37+
## Risks / Trade-offs
38+
39+
- **Bundle size** → bwip-js is large if fully linked. Mitigation: import only `datamatrix`/`drawingSVG` named exports; verify bundle delta at build time.
40+
- **Download ref shape** → bwip-js `drawingSVG()` returns SVG markup, not a React-managed `<svg>`. Mitigation: inject markup into a container and target the real `SVGSVGElement` for the ref so SVG→PNG works.
41+
- **GS1 AI validation drift** → hand-rolled loose check may diverge from bwip-js. Mitigation: keep it minimal (structure only), let bwip-js be the source of truth and catch its errors.
42+
- **Two enums to keep in sync**`codeFormat` value must round-trip through config + validation + preview. Mitigation: covered by unit tests.
43+
44+
## Migration Plan
45+
46+
Additive, no data migration. New dependency added to `package.json`; `typings/BarcodeGeneratorProps.d.ts` regenerated by build. Rollback = revert the change and drop the dependency; existing barcode/QR configs unaffected.
47+
48+
## Open Questions
49+
50+
- Exact bwip-js option for rectangular DataMatrix (shape flag vs explicit `rows`/`columns`) — confirm at implementation.
51+
- Whether to reuse `codeMargin`/`qrSize`-style sizing or add dedicated `dmSize`/`dmMargin` props — lean toward dedicated to avoid overloading 1D "bar width" semantics.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## Why
2+
3+
The Barcode Generator widget (`@mendix/barcode-generator-web`) can only produce 1D linear barcodes (jsbarcode) and QR codes (qrcode.react); it cannot generate DataMatrix. DataMatrix — specifically **GS1 DataMatrix** (FNC1 + GS1 Application Identifiers) — is mandatory for pharma serialization (EU FMD, US DSCSA). The Barcode Scanner widget already decodes DataMatrix, so generation closes the round-trip gap for pharma and logistics apps.
4+
5+
## What Changes
6+
7+
- Add **Data Matrix** as a top-level `codeFormat` option in the Barcode Generator.
8+
- Support two encodings: plain DataMatrix and **GS1 DataMatrix** (pharma), selectable via a boolean toggle.
9+
- Support square and rectangular symbol shapes.
10+
- Add `@bwip-js/browser` as a dependency (tree-shakeable DataMatrix encoder) — the only maintained library with native GS1 DataMatrix support. jsbarcode and qrcode.react remain for their existing formats.
11+
- Render DataMatrix as inline SVG (consistent with existing render + SVG→PNG download pipeline).
12+
- Add runtime/design-time validation for DataMatrix values (plain charset/length; GS1 AI syntax).
13+
- Add Studio Pro editor preview for the DataMatrix format.
14+
15+
No breaking changes — existing barcode/QR behavior is untouched; DataMatrix is additive.
16+
17+
## Capabilities
18+
19+
### New Capabilities
20+
21+
- `barcode-generation`: Generation of barcodes, QR codes, and (new) DataMatrix / GS1 DataMatrix from a string input in the Barcode Generator widget, including format selection, encoding options, validation, rendering, and download.
22+
23+
### Modified Capabilities
24+
25+
<!-- None: no prior spec exists for this widget. -->
26+
27+
## Impact
28+
29+
- **Package**: `packages/pluggableWidgets/barcode-generator-web`
30+
- **New dependency**: `@bwip-js/browser` (MIT, tree-shakeable)
31+
- **Widget config**: `src/BarcodeGenerator.xml` (new enum value + Data Matrix property group); regenerated `typings/BarcodeGeneratorProps.d.ts`
32+
- **Source**: new `src/components/DataMatrix.tsx` (bwip-js seam), extended `src/config/Barcode.config.ts` (discriminated union), `src/BarcodeGenerator.tsx` (dispatch), `src/config/validation.ts`, editor-preview files
33+
- **Reuses** existing `DownloadButton` + `src/utils/download-code.ts` SVG→PNG pipeline unchanged
34+
- **Tests**: Jest unit tests + Playwright E2E; `CHANGELOG.md` entry
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
## ADDED Requirements
2+
3+
### Requirement: DataMatrix format selection
4+
5+
The Barcode Generator widget SHALL offer "Data Matrix" as a top-level barcode format alongside the existing Barcode (1D) and QR Code options.
6+
7+
#### Scenario: Data Matrix appears in format list
8+
9+
- **WHEN** a developer configures the Barcode Generator in Studio Pro and opens the Barcode Format property
10+
- **THEN** "Data Matrix" is available as a selectable format value
11+
12+
#### Scenario: Selecting Data Matrix routes to the DataMatrix renderer
13+
14+
- **WHEN** the Barcode Format is set to "Data Matrix" and a non-empty value is provided
15+
- **THEN** the widget renders a DataMatrix symbol as inline SVG, and does not invoke the 1D barcode or QR code renderers
16+
17+
### Requirement: Plain DataMatrix generation
18+
19+
The widget SHALL encode the provided string value as a standard (non-GS1) DataMatrix symbol when GS1 mode is off.
20+
21+
#### Scenario: Encode a plain string
22+
23+
- **WHEN** the format is "Data Matrix", GS1 mode is off, and the value is `ABC-12345`
24+
- **THEN** the widget renders a scannable DataMatrix symbol encoding exactly that string
25+
26+
#### Scenario: Round-trip with the scanner
27+
28+
- **WHEN** a plain DataMatrix generated by the widget is scanned by the Barcode Scanner widget
29+
- **THEN** the decoded value equals the original input string
30+
31+
### Requirement: GS1 DataMatrix generation
32+
33+
The widget SHALL support GS1 DataMatrix encoding (FNC1 leading character, GS1 Application Identifier data) when GS1 mode is enabled, so pharma serialization data (GTIN, expiry, batch, serial) can be encoded.
34+
35+
#### Scenario: Encode GS1 Application Identifier data
36+
37+
- **WHEN** GS1 mode is enabled and the value is `(01)09501101020917(17)261231(10)ABC123`
38+
- **THEN** the widget renders a GS1 DataMatrix symbol with the FNC1 indicator and the AI-structured data intact
39+
40+
#### Scenario: GS1 mode toggles the encoder
41+
42+
- **WHEN** the same value is rendered first with GS1 mode off and then on
43+
- **THEN** the off result is a plain DataMatrix and the on result is a GS1 DataMatrix (distinct symbols)
44+
45+
### Requirement: DataMatrix symbol shape
46+
47+
The widget SHALL allow choosing between square and rectangular DataMatrix symbol shapes.
48+
49+
#### Scenario: Rectangular shape
50+
51+
- **WHEN** the DataMatrix shape option is set to "rectangle"
52+
- **THEN** the rendered symbol uses a rectangular DataMatrix layout
53+
54+
#### Scenario: Square shape is the default
55+
56+
- **WHEN** no shape is explicitly chosen
57+
- **THEN** the widget renders a square DataMatrix symbol
58+
59+
### Requirement: DataMatrix value validation
60+
61+
The widget SHALL validate the DataMatrix value and surface errors consistently with existing formats, honoring the configured log level.
62+
63+
#### Scenario: Empty value at design time
64+
65+
- **WHEN** no value is present (design time, before dynamic binding resolves)
66+
- **THEN** validation passes and no error is shown, matching existing barcode behavior
67+
68+
#### Scenario: Malformed GS1 AI syntax
69+
70+
- **WHEN** GS1 mode is on and the value has unbalanced or malformed Application Identifier syntax
71+
- **THEN** the widget reports a validation error and, when the log level permits, displays the generic "unable to generate" message and logs detail to the console
72+
73+
#### Scenario: Encoding failure
74+
75+
- **WHEN** the DataMatrix encoder throws for an invalid input
76+
- **THEN** the error is caught, the error UI is shown per log level, and the widget does not crash
77+
78+
### Requirement: DataMatrix download
79+
80+
The widget SHALL allow downloading a generated DataMatrix as a PNG using the existing download control, when downloads are enabled.
81+
82+
#### Scenario: Download a DataMatrix as PNG
83+
84+
- **WHEN** downloads are enabled and the user activates the download button on a DataMatrix
85+
- **THEN** a PNG file of the rendered DataMatrix is downloaded, using the configured or an auto-generated filename
86+
87+
### Requirement: DataMatrix editor preview
88+
89+
The widget SHALL show a representative DataMatrix preview in the Studio Pro editor when the Data Matrix format is selected.
90+
91+
#### Scenario: Preview in Studio Pro
92+
93+
- **WHEN** the Data Matrix format is selected in the Studio Pro page editor
94+
- **THEN** the widget preview displays a DataMatrix-style glyph rather than a 1D barcode or QR preview
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## 1. Dependency & Widget Config
2+
3+
- [x] 1.1 Add `@bwip-js/browser` to `packages/pluggableWidgets/barcode-generator-web/package.json` dependencies
4+
- [x] 1.2 Add `DataMatrix` enum value to top-level `codeFormat` in `src/BarcodeGenerator.xml`
5+
- [x] 1.3 Add "Advanced Data Matrix Settings" property group in XML: `dmGs1Mode` (boolean, default false), `dmShape` (enum square/rectangle, default square), and sizing property (`dmSize` / reuse margin)
6+
- [x] 1.4 Build to regenerate `typings/BarcodeGeneratorProps.d.ts` and confirm `CodeFormatEnum` includes `DataMatrix`
7+
8+
## 2. Config Model
9+
10+
- [x] 2.1 Add `DataMatrixTypeConfig` (`type: "datamatrix"`, `gs1Mode`, `shape`, `size`, `margin`) to the `BarcodeConfig` union in `src/config/Barcode.config.ts`
11+
- [x] 2.2 In `barcodeConfig()`, branch `format === "DataMatrix"` before the QR check, returning the new config
12+
- [x] 2.3 Extend `src/utils/download-code.ts` filename-prefix logic to handle `config.type === "datamatrix"`
13+
14+
## 3. Rendering Seam
15+
16+
- [x] 3.1 Create `src/components/DataMatrix.tsx` with `DataMatrixRenderer({ config })`, importing `{ datamatrix, drawingSVG }` from `@bwip-js/browser` (tree-shakeable; do NOT use `toSVG()`)
17+
- [x] 3.2 Build bwip-js options: `bcid` = `gs1datamatrix` when `gs1Mode` else `datamatrix`; apply rectangular option when `shape === "rectangle"`; set `text`, size, margin
18+
- [x] 3.3 Render SVG and expose a real `SVGSVGElement` ref so `DownloadButton` + `downloadCode(ref, config, ...)` work unchanged
19+
- [x] 3.4 Wrap encoding in try/catch → error state; render the existing error-alert markup honoring `logLevel` (mirror `BarcodeRenderer`)
20+
- [x] 3.5 Extend dispatch in `src/BarcodeGenerator.tsx:25` to route `config.type === "datamatrix"` to `DataMatrixRenderer`
21+
22+
## 4. Validation
23+
24+
- [x] 4.1 Add `DataMatrix` case to `validateBarcodeValue` in `src/config/validation.ts` (plain: charset/length sanity; GS1: loose balanced-`(nn)` AI syntax)
25+
- [x] 4.2 Wire GS1-mode-aware validation into the DataMatrix renderer before encoding
26+
27+
## 5. Editor Preview
28+
29+
- [x] 5.1 Add a DataMatrix preview asset/branch alongside `src/hooks/useBarcodePreviewSvg.ts`, `src/assets/barcodes/*.svg`, `src/components/preview/*` so Studio Pro shows a DataMatrix glyph for the format
30+
31+
## 6. Tests & Changelog
32+
33+
- [x] 6.1 Unit tests: config mapping for DataMatrix, GS1 vs plain `bcid` selection, shape option, validation (valid GS1 AI, malformed AI, plain string, empty value)
34+
- [ ] 6.2 Playwright E2E: render + download a DataMatrix per `docs/requirements/e2e-test-guidelines.md` _(blocked: existing e2e spec is a placeholder; needs the external Mendix testProjects page configured with a DataMatrix widget instance)_
35+
- [x] 6.3 Add user-facing `CHANGELOG.md` entry ("Added Data Matrix and GS1 Data Matrix generation support")
36+
37+
## 7. Verification
38+
39+
- [x] 7.1 `cd packages/pluggableWidgets/barcode-generator-web && pnpm run test` passes
40+
- [ ] 7.2 `pnpm run build` succeeds; confirm bundle delta small (bwip-js tree-shaken to DataMatrix encoder only) _(blocked: local build env has a broken rollup binary — stale `.bin/rollup` shim points to uninstalled rollup@3.29.5; unrelated to this change. Code verified via `tsc --noEmit` + jest instead.)_
41+
- [ ] 7.3 Live Studio Pro test: plain DataMatrix scans via Barcode Scanner (round-trip); GS1 value `(01)09501101020917(17)261231(10)ABC123` renders + decodes; PNG download works; rectangular shape renders _(requires human: live Studio Pro session with `MX_PROJECT_PATH` set)_

0 commit comments

Comments
 (0)