Initial Commit
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# React UI i18n — deferred tooling: stories, key extraction, registry ejection gate
|
||||
|
||||
**Packages**: `@suaveplan/react-ui`
|
||||
**Status**: PROPOSED
|
||||
**Type**: tooling + completion follow-up
|
||||
|
||||
## Why
|
||||
|
||||
The `react-i18n-plugin-registry-and-ui-primitives` change (archived 2026-05-10) shipped the registry substrate, the seven `<FormattedX/>` primitives, the `<ReactUiI18nBridge>`, the tanstack collator helpers, `<InputNumber>`, and the validation message bridge. Three items from that change's tasks list were intentionally deferred so the runtime surface could ship coherent without dragging tooling along:
|
||||
|
||||
1. **Storybook stories for the seven new primitives + `<InputNumber>` + `<ReactUiI18nBridge>`** — CLAUDE.md mandates every React component carry a `.stories.tsx` (default + playground + variants); these primitives shipped without stories so the gates would not slip on cosmetics. Reviewers and design integrators rely on stories to discover, browse, and visually verify components in isolation.
|
||||
2. **`bun run check:i18n-keys`** — an AST-walking script over `src/components/**/*.tsx` and `src/i18n/**/*.tsx` that diffs `t("react-ui.…")` calls and `<FormattedMessage i18nKey="react-ui.…" />` usages against the canonical `src/locales/en-GB.json`. The script catches missing keys (in code, not in bundle) AND orphan keys (in bundle, not in code). Specified in the new `react-ui-i18n` capability spec but not implemented yet.
|
||||
3. **`bun run check:registry-eject`** — a smoke test that resolves every shadcn `registry/r/*.json` entry, applies the registry transform, and type-checks the transformed source against a synthesized consumer `tsconfig.json` so an ejected component still compiles. Needed because the registry transform preserves `@suaveplan/react-i18n` imports in ejected sources, and the new primitives + `<ReactUiI18nBridge>` exercise that contract more aggressively than any prior component.
|
||||
|
||||
These are tooling, not runtime — but they are part of the surface the proposal committed to. Closing them is the difference between "shipped" and "shipped, gates intact." Reviewers picking up this branch downstream will trip over the missing stories first; the two scripts close the drift loop.
|
||||
|
||||
## What changes
|
||||
|
||||
### `@suaveplan/react-ui`
|
||||
|
||||
#### Stories for the new i18n primitives + InputNumber + Bridge
|
||||
|
||||
- ADDED: `src/i18n/formatted-number/formatted-number.stories.tsx` — default + playground + variants (Latin digits, Arabic-Indic via `numberingSystem: "arab"`, percent / scientific / unit notation, `as="strong" | "data" | "output"`).
|
||||
- ADDED: `src/i18n/formatted-currency/formatted-currency.stories.tsx` — default + playground + variants (USD / EUR / GBP / JPY across en-GB / fr-FR / zh-CN, display modes).
|
||||
- ADDED: `src/i18n/formatted-date/formatted-date.stories.tsx` — default + playground + variants (Date / ISO string / epoch ms inputs, preset names + raw options, invalid-input renders empty).
|
||||
- ADDED: `src/i18n/formatted-list/formatted-list.stories.tsx` — default + playground + variants (conjunction / disjunction / unit, long / short / narrow styles).
|
||||
- ADDED: `src/i18n/formatted-relative-time/formatted-relative-time.stories.tsx` — default + playground + variants (auto-mode interval, explicit unit, future / past).
|
||||
- ADDED: `src/i18n/plural/plural.stories.tsx` — default + playground + variants (en-GB collapses zero into other; ar-AE distinguishes zero / one / two / few / many).
|
||||
- ADDED: `src/i18n/formatted-message/formatted-message.stories.tsx` — default + playground + variants (rich-text tag interpolation, plural via `count`).
|
||||
- ADDED: `src/i18n/react-ui-i18n-bridge/react-ui-i18n-bridge.stories.tsx` — default + playground + variants showing the Radix `<DirectionProvider>` propagation (popover, dropdown-menu, dialog) under en-GB and ar-AE.
|
||||
- ADDED: `src/i18n/formatted-cell/formatted-cell.stories.tsx` — default + playground showing the helper inside a `<DataTable>` for each formatter kind.
|
||||
- ADDED: `src/i18n/validation-message-bridge/validation-message-bridge.stories.tsx` — default + playground showing the bridge inside an Alert and a Toast.
|
||||
- ADDED: `src/components/input/input-number/input-number.stories.tsx` — default + playground + variants (en-GB / de-DE / ar-AE locale, controlled vs uncontrolled, percent / currency display options).
|
||||
|
||||
Every story file follows the existing `.stories.tsx` convention (no `@storybook/react` dep — the meta shape is inlined). Each story renders inside `<TranslationProvider>` + `<RegistryProvider>` + `<ReactUiI18nBridge>` so the runtime path matches production.
|
||||
|
||||
#### `bun run check:i18n-keys` — translation-key drift gate
|
||||
|
||||
- ADDED: `scripts/check-i18n-keys.ts` — AST walks `src/components/**/*.{ts,tsx}` and `src/i18n/**/*.{ts,tsx}` for:
|
||||
- `t("react-ui.…")` calls in `useTranslation()` consumers
|
||||
- `<FormattedMessage i18nKey="react-ui.…" />` usages
|
||||
- `<Trans i18nKey="react-ui.…" />` usages (for already-shipped components)
|
||||
- `tDynamic("react-ui.…")` calls
|
||||
- Diffs the discovered keys against the canonical `src/locales/en-GB.json`.
|
||||
- Reports `MISSING: <key> at <path>:<line>` for keys referenced in code but absent from the bundle.
|
||||
- Reports `ORPHAN: <key>` for bundle keys not referenced anywhere in code.
|
||||
- Exits non-zero on any mismatch.
|
||||
- ADDED: `scripts/check-i18n-keys.test.ts` — fixtures-directory test exercising both the missing-key and orphan-key paths, plus a happy-path where source and bundle agree.
|
||||
- MODIFIED: `package.json::scripts` adds `"check:i18n-keys": "bun scripts/check-i18n-keys.ts"`.
|
||||
- MODIFIED: the package's `turbo.json` lint pipeline includes the new check so CI fails on drift.
|
||||
|
||||
#### `bun run check:registry-eject` — shadcn ejection compile gate
|
||||
|
||||
- ADDED: `scripts/check-registry-eject.ts` — walks every `registry/r/*.json`, copies the listed source files into a tmp directory, applies the registry transform (rewriting `@suaveplan/react-ui` imports to local relative paths, preserving `@suaveplan/react-i18n` imports), and runs `tsc --noEmit` against the transformed tree using a synthesized consumer `tsconfig.json` (extends `@suaveplan/typescript-config/library.json`, lists `@suaveplan/react-i18n` and the React peers in `compilerOptions.paths`).
|
||||
- Reports `EJECT FAILURE: <component>` with the underlying `tsc` diagnostics on any component whose ejected source fails to compile.
|
||||
- Reports `MISSING REGISTRY ENTRY: <path>` when a public component / primitive in `src/components/**` or `src/i18n/**` lacks a corresponding `registry/r/<name>.json`.
|
||||
- Exits non-zero on either failure.
|
||||
- ADDED: `scripts/check-registry-eject.test.ts` — happy-path test that ejects a known-good entry; failure-path test that introduces a missing import and asserts the diagnostic appears.
|
||||
- MODIFIED: `package.json::scripts` adds `"check:registry-eject": "bun scripts/check-registry-eject.ts"`.
|
||||
- MODIFIED: `turbo.json` lint pipeline includes the new check.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- **Adding stories to the 70 existing react-ui components.** Those already have stories from the chassis Wave 3 work. This change is scoped to the i18n surface added in `react-i18n-plugin-registry-and-ui-primitives`.
|
||||
- **Authoring a repo-wide `@suaveplan/i18n-extract` CLI.** The `check:i18n-keys` script is package-local — covers only `react-ui`'s `react-ui.*` namespace. A repo-wide CLI lives in a separate change.
|
||||
- **Extending the registry transform itself.** The transform exists and is correct today; the new gate verifies the transform's output against a real type-checker pass.
|
||||
- **Visual regression testing.** Stories enable visual review; automated visual regression is out of scope.
|
||||
|
||||
## Parity Checklist
|
||||
|
||||
Genesis-original; no upstream parent. The "Current genesis" column captures whether each artifact exists today (post-`react-i18n-plugin-registry-and-ui-primitives`).
|
||||
|
||||
| Capability | Current genesis | Post-change | Notes |
|
||||
|---|---|---|---|
|
||||
| Stories for `<FormattedNumber>` / `<FormattedCurrency>` / `<FormattedDate>` / `<FormattedList>` / `<FormattedRelativeTime>` / `<Plural>` / `<FormattedMessage>` | 🚫 | ✅ | One `.stories.tsx` per primitive. |
|
||||
| Stories for `<ReactUiI18nBridge>` | 🚫 | ✅ | Demonstrates Radix `<DirectionProvider>` propagation under RTL locales. |
|
||||
| Stories for `formattedCell` + `useValidationMessageBridge` | 🚫 | ✅ | Composed inside `<DataTable>`, `<Alert>`, `<Toast>` to show real-world placement. |
|
||||
| Stories for `<InputNumber>` | 🚫 | ✅ | Controlled / uncontrolled, locale variants. |
|
||||
| `bun run check:i18n-keys` | 🚫 | ✅ | AST-walking translation-key drift gate. |
|
||||
| `bun run check:registry-eject` | 🚫 | ✅ | shadcn-eject smoke compile gate. |
|
||||
| Both gates wired into `bunx turbo lint` | 🚫 | ✅ | CI fails on drift. |
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `bun run check:i18n-keys` exits 0 on the current source + bundle pair; exits non-zero when an obviously-missing key is introduced (test fixtures cover both paths).
|
||||
- `bun run check:registry-eject` exits 0 on every current `registry/r/*.json` entry; exits non-zero when a known-good entry is corrupted (test fixtures cover both paths).
|
||||
- Storybook smoke pass: every new story renders without console errors / warnings under en-GB and ar-AE.
|
||||
- Pre-publish gate: `bunx turbo lint typecheck test --filter=@suaveplan/react-ui` green.
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
- `@suaveplan/react-ui` — [x] tests run / [x] docs reviewed / [ ] no action needed (this change AUTHORS the new tooling)
|
||||
- Downstream consumers of `@suaveplan/react-ui` — [ ] tests run / [ ] docs reviewed / [x] no action needed (this change is purely additive: new scripts, new story files; no runtime surface changes)
|
||||
|
||||
`@suaveplan/react-ui` is on the foundation path of the React stack. The dependent sweep `bunx turbo test --filter=...@suaveplan/react-ui` SHALL be run as a sanity check, but no behavior change is expected.
|
||||
|
||||
## Migration impact
|
||||
|
||||
None — additive only. No runtime surface is touched.
|
||||
Reference in New Issue
Block a user