@dashforge/tw 0.8.1-beta → 0.9.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/COVERAGE.md +63 -0
  3. package/LICENSE +21 -0
  4. package/PERFORMANCE.md +59 -0
  5. package/REFINEMENT-NOTES.md +50 -0
  6. package/dist/index.esm.js +177 -67
  7. package/dist/src/components/DataGrid/DataGrid.d.ts.map +1 -1
  8. package/dist/src/components/DataGrid/filters/ColumnFilters.d.ts.map +1 -1
  9. package/dist/src/components/DataGrid/resize/useColumnResize.d.ts +7 -0
  10. package/dist/src/components/DataGrid/resize/useColumnResize.d.ts.map +1 -1
  11. package/dist/src/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts +6 -2
  12. package/dist/src/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts.map +1 -1
  13. package/dist/src/components/Dialog/Dialog.d.ts.map +1 -1
  14. package/dist/src/components/Table/Table.d.ts.map +1 -1
  15. package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -1
  16. package/dist/src/components/Table/table.types.d.ts +28 -3
  17. package/dist/src/components/Table/table.types.d.ts.map +1 -1
  18. package/dist/src/components/Typography/typography.variants.d.ts.map +1 -1
  19. package/eslint.config.mjs +16 -1
  20. package/package.json +9 -6
  21. package/src/components/Breadcrumbs/Breadcrumbs.perf.test.tsx +5 -2
  22. package/src/components/DataGrid/DataGrid.perf.test.tsx +114 -0
  23. package/src/components/DataGrid/DataGrid.test.tsx +240 -0
  24. package/src/components/DataGrid/DataGrid.tsx +104 -16
  25. package/src/components/DataGrid/filters/ColumnFilters.test.tsx +330 -0
  26. package/src/components/DataGrid/filters/ColumnFilters.tsx +26 -2
  27. package/src/components/DataGrid/reorder/useColumnReorder.test.ts +95 -3
  28. package/src/components/DataGrid/resize/useColumnResize.test.ts +144 -0
  29. package/src/components/DataGrid/resize/useColumnResize.ts +30 -1
  30. package/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx +42 -25
  31. package/src/components/Dialog/Dialog.tsx +9 -0
  32. package/src/components/Table/Table.perf.test.tsx +81 -0
  33. package/src/components/Table/Table.test.tsx +72 -1
  34. package/src/components/Table/Table.tsx +40 -4
  35. package/src/components/Table/cells/RenderChip.tsx +6 -9
  36. package/src/components/Table/cells/cells.test.tsx +247 -0
  37. package/src/components/Table/table.types.ts +28 -3
  38. package/src/components/Typography/Typography.test.tsx +15 -0
  39. package/src/components/Typography/typography.variants.ts +14 -1
  40. package/test-output/vitest/coverage/base.css +224 -0
  41. package/test-output/vitest/coverage/block-navigation.js +87 -0
  42. package/test-output/vitest/coverage/clover.xml +2418 -0
  43. package/test-output/vitest/coverage/coverage-final.json +89 -0
  44. package/test-output/vitest/coverage/favicon.png +0 -0
  45. package/test-output/vitest/coverage/index.html +746 -0
  46. package/test-output/vitest/coverage/prettify.css +1 -0
  47. package/test-output/vitest/coverage/prettify.js +2 -0
  48. package/test-output/vitest/coverage/sort-arrow-sprite.png +0 -0
  49. package/test-output/vitest/coverage/sorter.js +210 -0
  50. package/test-output/vitest/coverage/src/components/Accordion/Accordion.tsx.html +376 -0
  51. package/test-output/vitest/coverage/src/components/Accordion/accordion.variants.ts.html +181 -0
  52. package/test-output/vitest/coverage/src/components/Accordion/index.html +131 -0
  53. package/test-output/vitest/coverage/src/components/AppShell/AppShell.tsx.html +802 -0
  54. package/test-output/vitest/coverage/src/components/AppShell/appShell.variants.ts.html +247 -0
  55. package/test-output/vitest/coverage/src/components/AppShell/index.html +131 -0
  56. package/test-output/vitest/coverage/src/components/AspectRatio/AspectRatio.tsx.html +304 -0
  57. package/test-output/vitest/coverage/src/components/AspectRatio/index.html +116 -0
  58. package/test-output/vitest/coverage/src/components/Autocomplete/Autocomplete.tsx.html +3292 -0
  59. package/test-output/vitest/coverage/src/components/Autocomplete/autocomplete.variants.ts.html +508 -0
  60. package/test-output/vitest/coverage/src/components/Autocomplete/index.html +131 -0
  61. package/test-output/vitest/coverage/src/components/Box/Box.tsx.html +334 -0
  62. package/test-output/vitest/coverage/src/components/Box/box.variants.ts.html +775 -0
  63. package/test-output/vitest/coverage/src/components/Box/index.html +131 -0
  64. package/test-output/vitest/coverage/src/components/Breadcrumbs/Breadcrumbs.tsx.html +694 -0
  65. package/test-output/vitest/coverage/src/components/Breadcrumbs/breadcrumbs.variants.ts.html +247 -0
  66. package/test-output/vitest/coverage/src/components/Breadcrumbs/index.html +131 -0
  67. package/test-output/vitest/coverage/src/components/Button/Button.tsx.html +535 -0
  68. package/test-output/vitest/coverage/src/components/Button/button.variants.ts.html +562 -0
  69. package/test-output/vitest/coverage/src/components/Button/index.html +131 -0
  70. package/test-output/vitest/coverage/src/components/Checkbox/Checkbox.tsx.html +943 -0
  71. package/test-output/vitest/coverage/src/components/Checkbox/checkbox.variants.ts.html +289 -0
  72. package/test-output/vitest/coverage/src/components/Checkbox/index.html +131 -0
  73. package/test-output/vitest/coverage/src/components/ConfirmDialog/ConfirmDialog.tsx.html +841 -0
  74. package/test-output/vitest/coverage/src/components/ConfirmDialog/confirmDialog.variants.ts.html +313 -0
  75. package/test-output/vitest/coverage/src/components/ConfirmDialog/index.html +131 -0
  76. package/test-output/vitest/coverage/src/components/Container/Container.tsx.html +307 -0
  77. package/test-output/vitest/coverage/src/components/Container/container.variants.ts.html +328 -0
  78. package/test-output/vitest/coverage/src/components/Container/index.html +131 -0
  79. package/test-output/vitest/coverage/src/components/DataGrid/DataGrid.tsx.html +3937 -0
  80. package/test-output/vitest/coverage/src/components/DataGrid/dataGrid.variants.ts.html +541 -0
  81. package/test-output/vitest/coverage/src/components/DataGrid/filters/ColumnFilters.tsx.html +1453 -0
  82. package/test-output/vitest/coverage/src/components/DataGrid/filters/index.html +116 -0
  83. package/test-output/vitest/coverage/src/components/DataGrid/index.html +131 -0
  84. package/test-output/vitest/coverage/src/components/DataGrid/reorder/index.html +116 -0
  85. package/test-output/vitest/coverage/src/components/DataGrid/reorder/useColumnReorder.ts.html +517 -0
  86. package/test-output/vitest/coverage/src/components/DataGrid/resize/index.html +116 -0
  87. package/test-output/vitest/coverage/src/components/DataGrid/resize/useColumnResize.ts.html +490 -0
  88. package/test-output/vitest/coverage/src/components/DataGrid/visibility/ColumnVisibilityMenu.tsx.html +592 -0
  89. package/test-output/vitest/coverage/src/components/DataGrid/visibility/index.html +116 -0
  90. package/test-output/vitest/coverage/src/components/DateTimePicker/DateTimePicker.tsx.html +1042 -0
  91. package/test-output/vitest/coverage/src/components/DateTimePicker/dateTimePicker.variants.ts.html +337 -0
  92. package/test-output/vitest/coverage/src/components/DateTimePicker/index.html +131 -0
  93. package/test-output/vitest/coverage/src/components/Dialog/Dialog.tsx.html +391 -0
  94. package/test-output/vitest/coverage/src/components/Dialog/dialog.variants.ts.html +283 -0
  95. package/test-output/vitest/coverage/src/components/Dialog/index.html +131 -0
  96. package/test-output/vitest/coverage/src/components/Divider/Divider.tsx.html +505 -0
  97. package/test-output/vitest/coverage/src/components/Divider/divider.variants.ts.html +433 -0
  98. package/test-output/vitest/coverage/src/components/Divider/index.html +131 -0
  99. package/test-output/vitest/coverage/src/components/Grid/Grid.tsx.html +529 -0
  100. package/test-output/vitest/coverage/src/components/Grid/grid.variants.ts.html +484 -0
  101. package/test-output/vitest/coverage/src/components/Grid/index.html +131 -0
  102. package/test-output/vitest/coverage/src/components/LeftNav/LeftNav.tsx.html +1075 -0
  103. package/test-output/vitest/coverage/src/components/LeftNav/index.html +131 -0
  104. package/test-output/vitest/coverage/src/components/LeftNav/leftNav.variants.ts.html +406 -0
  105. package/test-output/vitest/coverage/src/components/NumberField/NumberField.tsx.html +1048 -0
  106. package/test-output/vitest/coverage/src/components/NumberField/index.html +131 -0
  107. package/test-output/vitest/coverage/src/components/NumberField/numberField.variants.ts.html +367 -0
  108. package/test-output/vitest/coverage/src/components/OTPField/OTPField.tsx.html +835 -0
  109. package/test-output/vitest/coverage/src/components/OTPField/index.html +131 -0
  110. package/test-output/vitest/coverage/src/components/OTPField/otpField.variants.ts.html +301 -0
  111. package/test-output/vitest/coverage/src/components/Pagination/Pagination.tsx.html +916 -0
  112. package/test-output/vitest/coverage/src/components/Pagination/index.html +146 -0
  113. package/test-output/vitest/coverage/src/components/Pagination/pagination.helpers.ts.html +391 -0
  114. package/test-output/vitest/coverage/src/components/Pagination/pagination.variants.ts.html +436 -0
  115. package/test-output/vitest/coverage/src/components/Popover/Popover.tsx.html +250 -0
  116. package/test-output/vitest/coverage/src/components/Popover/index.html +131 -0
  117. package/test-output/vitest/coverage/src/components/Popover/popover.variants.ts.html +157 -0
  118. package/test-output/vitest/coverage/src/components/RadioGroup/RadioGroup.tsx.html +934 -0
  119. package/test-output/vitest/coverage/src/components/RadioGroup/index.html +131 -0
  120. package/test-output/vitest/coverage/src/components/RadioGroup/radioGroup.variants.ts.html +346 -0
  121. package/test-output/vitest/coverage/src/components/Skeleton/Skeleton.tsx.html +232 -0
  122. package/test-output/vitest/coverage/src/components/Skeleton/index.html +131 -0
  123. package/test-output/vitest/coverage/src/components/Skeleton/skeleton.variants.ts.html +346 -0
  124. package/test-output/vitest/coverage/src/components/Snackbar/Snackbar.tsx.html +979 -0
  125. package/test-output/vitest/coverage/src/components/Snackbar/index.html +131 -0
  126. package/test-output/vitest/coverage/src/components/Snackbar/snackbar.variants.ts.html +307 -0
  127. package/test-output/vitest/coverage/src/components/Stack/Stack.tsx.html +430 -0
  128. package/test-output/vitest/coverage/src/components/Stack/index.html +131 -0
  129. package/test-output/vitest/coverage/src/components/Stack/stack.variants.ts.html +325 -0
  130. package/test-output/vitest/coverage/src/components/Switch/Switch.tsx.html +559 -0
  131. package/test-output/vitest/coverage/src/components/Switch/index.html +131 -0
  132. package/test-output/vitest/coverage/src/components/Switch/switch.variants.ts.html +295 -0
  133. package/test-output/vitest/coverage/src/components/Table/Table.tsx.html +2677 -0
  134. package/test-output/vitest/coverage/src/components/Table/cells/RenderButton.tsx.html +154 -0
  135. package/test-output/vitest/coverage/src/components/Table/cells/RenderChip.tsx.html +385 -0
  136. package/test-output/vitest/coverage/src/components/Table/cells/RenderText.tsx.html +196 -0
  137. package/test-output/vitest/coverage/src/components/Table/cells/RenderTwoLine.tsx.html +187 -0
  138. package/test-output/vitest/coverage/src/components/Table/cells/RowActionsMenu.tsx.html +526 -0
  139. package/test-output/vitest/coverage/src/components/Table/cells/index.html +191 -0
  140. package/test-output/vitest/coverage/src/components/Table/cells/index.ts.html +127 -0
  141. package/test-output/vitest/coverage/src/components/Table/index.html +131 -0
  142. package/test-output/vitest/coverage/src/components/Table/table.variants.ts.html +706 -0
  143. package/test-output/vitest/coverage/src/components/Tabs/Tabs.tsx.html +283 -0
  144. package/test-output/vitest/coverage/src/components/Tabs/index.html +131 -0
  145. package/test-output/vitest/coverage/src/components/Tabs/tabs.variants.ts.html +292 -0
  146. package/test-output/vitest/coverage/src/components/TextField/TextField.tsx.html +820 -0
  147. package/test-output/vitest/coverage/src/components/TextField/index.html +131 -0
  148. package/test-output/vitest/coverage/src/components/TextField/textField.variants.ts.html +400 -0
  149. package/test-output/vitest/coverage/src/components/Textarea/Textarea.tsx.html +655 -0
  150. package/test-output/vitest/coverage/src/components/Textarea/index.html +131 -0
  151. package/test-output/vitest/coverage/src/components/Textarea/textarea.variants.ts.html +340 -0
  152. package/test-output/vitest/coverage/src/components/Tooltip/Tooltip.tsx.html +295 -0
  153. package/test-output/vitest/coverage/src/components/Tooltip/index.html +131 -0
  154. package/test-output/vitest/coverage/src/components/Tooltip/tooltip.variants.ts.html +169 -0
  155. package/test-output/vitest/coverage/src/components/TopBar/TopBar.tsx.html +283 -0
  156. package/test-output/vitest/coverage/src/components/TopBar/index.html +131 -0
  157. package/test-output/vitest/coverage/src/components/TopBar/topBar.variants.ts.html +199 -0
  158. package/test-output/vitest/coverage/src/components/Typography/Typography.tsx.html +421 -0
  159. package/test-output/vitest/coverage/src/components/Typography/index.html +131 -0
  160. package/test-output/vitest/coverage/src/components/Typography/typography.variants.ts.html +436 -0
  161. package/test-output/vitest/coverage/src/components/VisuallyHidden/VisuallyHidden.tsx.html +262 -0
  162. package/test-output/vitest/coverage/src/components/VisuallyHidden/index.html +116 -0
  163. package/test-output/vitest/coverage/src/components/_shared/data/getNestedValue.ts.html +181 -0
  164. package/test-output/vitest/coverage/src/components/_shared/data/index.html +236 -0
  165. package/test-output/vitest/coverage/src/components/_shared/data/useColumnAutoDetect.ts.html +385 -0
  166. package/test-output/vitest/coverage/src/components/_shared/data/useControllableState.ts.html +229 -0
  167. package/test-output/vitest/coverage/src/components/_shared/data/useDebouncedValue.ts.html +157 -0
  168. package/test-output/vitest/coverage/src/components/_shared/data/useTableFilter.ts.html +439 -0
  169. package/test-output/vitest/coverage/src/components/_shared/data/useTableSearch.ts.html +283 -0
  170. package/test-output/vitest/coverage/src/components/_shared/data/useTableSelection.ts.html +385 -0
  171. package/test-output/vitest/coverage/src/components/_shared/data/useTableSort.ts.html +508 -0
  172. package/test-output/vitest/coverage/src/components/_shared/data/useVirtualizer.ts.html +682 -0
  173. package/test-output/vitest/coverage/src/components/_shared/index.html +116 -0
  174. package/test-output/vitest/coverage/src/components/_shared/resolveValidationState.ts.html +265 -0
  175. package/test-output/vitest/coverage/src/hooks/index.html +116 -0
  176. package/test-output/vitest/coverage/src/hooks/useAccessState.ts.html +328 -0
  177. package/test-output/vitest/coverage/src/index.html +116 -0
  178. package/test-output/vitest/coverage/src/index.ts.html +1153 -0
  179. package/test-output/vitest/coverage/src/utils/cn.ts.html +172 -0
  180. package/test-output/vitest/coverage/src/utils/index.html +116 -0
package/CHANGELOG.md CHANGED
@@ -12,6 +12,132 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
12
12
  > duplicated intentionally — no shared "lowest common denominator" headless
13
13
  > layer.
14
14
 
15
+ ## [0.9.0-beta] — 2026-05-20
16
+
17
+ **Sprint 6 — core hardening.** A refinement pass over the theme
18
+ core, `<Table>` + `<DataGrid>`, the validation gates, and test
19
+ coverage. Released together with `@dashforge/tw-tokens 0.2.0-beta`
20
+ and `@dashforge/tw-theme 0.2.0-beta` — **upgrade all three together.**
21
+
22
+ **Minor bump** for the additive API surface (`TableLabels.noResults`,
23
+ theme-controlled shadows, the `tailwindcss` peer requirement).
24
+ Strictly additive — no breaking changes on existing component APIs.
25
+
26
+ ### Added
27
+
28
+ - **Theme-controlled shadows.** `<Box elevation>` and every
29
+ `shadow-*` utility now resolve to the new `@dashforge/tw-tokens`
30
+ shadow token axis (CSS-var refs via `dashforgePreset()`), so
31
+ elevation is runtime-patchable via
32
+ `patchTheme({ shadow: { md: '…' } })`. Zero `<Box>` code change —
33
+ the existing classes pick up the var refs through the preset.
34
+ - **`DataGrid` accessibility — `aria-rowcount` / `aria-rowindex`.**
35
+ The virtualized grid now announces the TRUE dataset position to
36
+ assistive tech (`aria-rowcount` on the table, `aria-rowindex` on
37
+ the header + every data row). Before, a screen reader reported the
38
+ position within the ~visible window, not the full dataset.
39
+ - **`DataGrid` keyboard column resize.** The column resize handle is
40
+ now keyboard-operable: `tabIndex=0`, `ArrowLeft`/`ArrowRight` nudge
41
+ the width 16px (64px with `Shift`), `aria-valuemin/max/now` on the
42
+ separator. WCAG 2.1 keyboard operability — the drag is no longer
43
+ the only path.
44
+ - **`TableLabels.noResults`** — `<Table>` + `<DataGrid>` now show a
45
+ distinct "No matching results" message when an active search /
46
+ filter empties the view, vs the existing `noData` for a genuinely
47
+ empty dataset. New i18n key.
48
+ - **`Enter`-to-apply** in every `DataGrid` column-filter Popover
49
+ input (was: must click "Apply").
50
+
51
+ ### Changed
52
+
53
+ - **`DataGrid` column reorder** — the resize handle is now
54
+ `draggable={false}`, so grabbing it always resizes and never
55
+ starts a column-reorder drag. On coarse-pointer (touch) devices the
56
+ reorder `draggable` affordance is disabled — native HTML5
57
+ drag-and-drop does not fire touch events, so it was a dead
58
+ interaction. (A pointer-event reorder covering touch is tracked in
59
+ `REFINEMENT-NOTES.md`.)
60
+ - **`Table`** now emits a dev-only `console.warn` when `getRowId` is
61
+ omitted while sort / search / selection / expandable rows are
62
+ active — the positional-index fallback breaks row identity on
63
+ reorder. Prop JSDoc expanded. (`<DataGrid>` already requires
64
+ `getRowId`.)
65
+ - **`tailwind-merge`** bumped to `^3.0.0` (`tailwind-variants@3`
66
+ requires it). Full suite green on 3.6.0 — `cn()` + every TV recipe
67
+ unaffected.
68
+ - **`DataGrid` column-visibility menu** — the "Show all" / "Hide all"
69
+ text links are replaced by a single tri-state master checkbox at
70
+ the top of the column list (checked = all visible, indeterminate
71
+ = mixed, unchecked = all hidden), consistent with the row
72
+ select-all checkbox. Less chrome, no extra copy. The
73
+ `TableLabels` keys `columnsShowAll` + `columnsHideAll` are removed;
74
+ new key `columnsToggleAll` is the master checkbox `aria-label`.
75
+ The Popover title (`columnsTitle`) is unchanged and
76
+ consumer-customizable as before.
77
+
78
+ ### Fixed
79
+
80
+ - **`<Typography>` dark-mode colour consistency.** The default
81
+ `color` changed from `inherit` to `neutral`. A bare
82
+ `<Typography>` now emits `text-neutral-900` (auto-inverting via
83
+ the `dashforgePreset()` CSS-var swap) instead of inheriting the
84
+ consuming app's arbitrary root colour — which is typically driven
85
+ off the OS `prefers-color-scheme`, diverging from the Dashforge
86
+ `data-dash-tw-theme` mode. Before the fix, default-colour and
87
+ explicitly-coloured text rendered as two different greys in dark
88
+ mode. Pass `color="inherit"` explicitly on a `<Typography>` placed
89
+ inside a coloured surface (`<Box variant="solid">`). Paired with
90
+ the `@dashforge/tw-theme 0.2.0-beta` base-layer surface anchor.
91
+ - **Console hygiene** — `<Dialog>` now passes
92
+ `aria-describedby={undefined}` on the content when no `description`
93
+ is supplied, silencing Radix's 8× *"Missing Description"* dev
94
+ warning. Correct a11y semantics for description-less dialogs.
95
+ - **Packaging** — `vitest` + `@vitejs/plugin-react` moved from
96
+ `dependencies` to `devDependencies`; consumers no longer pull the
97
+ test runner into `node_modules`.
98
+
99
+ ### Dependencies
100
+
101
+ - New peer dependency: **`tailwindcss` `>=3.4.1`** — the
102
+ `dashforgePreset()` `darkMode: ['selector', …]` strategy requires
103
+ it, and the catalog's `dark:` brand-shift variants depend on that
104
+ config. Declared explicitly so a too-old Tailwind fails at install
105
+ rather than silently breaking dark mode.
106
+
107
+ ### Tests + tooling
108
+
109
+ - Render-perf suite: `Table.perf.test.tsx` + `DataGrid.perf.test.tsx`
110
+ pin the non-virtualized (500 rows) and virtualized (10k / 100k
111
+ rows, O(window)) budgets in CI.
112
+ - +62 unit tests — the cell renderer library, `ColumnFilters`,
113
+ `useColumnResize`, `useColumnReorder` are now directly covered
114
+ (line coverage 85.78%). See `COVERAGE.md` for the report + the
115
+ tracked pre-Sprint-6 branch-coverage debt.
116
+ - Strict-flag assessment (`exactOptionalPropertyTypes` /
117
+ `noUncheckedIndexedAccess`) — see `PERFORMANCE.md`. Result: zero
118
+ real bugs; flag enablement deferred to `1.0.0-rc`.
119
+
120
+ ### Migration
121
+
122
+ No code changes required:
123
+
124
+ ```bash
125
+ pnpm up @dashforge/tw@^0.9.0-beta \
126
+ @dashforge/tw-theme@^0.2.0-beta \
127
+ @dashforge/tw-tokens@^0.2.0-beta
128
+ ```
129
+
130
+ Ensure your project's `tailwindcss` is **`>=3.4.1`** (the new peer
131
+ requirement). If you customized `DataGrid` / `Table` labels via the
132
+ `labels` prop:
133
+
134
+ - the new optional `noResults` key defaults to
135
+ `"No matching results"` — translate it if you localize;
136
+ - the column-visibility keys `columnsShowAll` / `columnsHideAll`
137
+ are **removed** (the menu now uses a tri-state master checkbox) —
138
+ replace them with the single `columnsToggleAll` key
139
+ (default `"Toggle all columns"`, used as the checkbox `aria-label`).
140
+
15
141
  ## [0.8.1-beta] — 2026-05-19
16
142
 
17
143
  **UX swap on `DataGrid` column visibility.** The "Columns" toolbar
package/COVERAGE.md ADDED
@@ -0,0 +1,63 @@
1
+ # COVERAGE — `@dashforge/tw`
2
+
3
+ > Sprint 6 P4 deliverable (2026-05-20). Test-coverage audit + the
4
+ > gap-fill done this sprint, plus the tracked debt.
5
+
6
+ ## Current coverage (`vitest --coverage`, v8 provider)
7
+
8
+ | Metric | Value | Target |
9
+ |--------|-------|--------|
10
+ | Statements | 83.53% | — |
11
+ | **Branches** | **75.87%** | 85% (not met — see Deferred) |
12
+ | Functions | 89.06% | — |
13
+ | **Lines** | **85.78%** | 85% ✅ |
14
+
15
+ Suite: **1105 tests** across 55 files.
16
+
17
+ ## Filled in Sprint 6 P4 (+62 tests)
18
+
19
+ P4 targeted the Sprint 4.2 / 4.2-bis additions that shipped with
20
+ integration-only coverage. All are now directly unit-tested:
21
+
22
+ | Area | Before | After |
23
+ |------|--------|-------|
24
+ | Cell renderer library (`Table/cells/` — RenderText / RenderTwoLine / RenderChip / RenderButton / RowActionsMenu) | ~0% | high (30 tests) |
25
+ | `DataGrid/filters/ColumnFilters.tsx` | 22% branch | covered — every filter type + apply/clear/Enter (17 tests) |
26
+ | `DataGrid/resize/useColumnResize.ts` | 33% | **100%** (8 tests — `nudge` clamp paths + pointer-drag commit) |
27
+ | `DataGrid/reorder/useColumnReorder.ts` | 32% | 93% stmts / 98% lines (14 tests — `moveColumn` + drag handlers) |
28
+
29
+ ## Deferred — pre-Sprint-6 branch-coverage debt
30
+
31
+ Line coverage met the 85% target. **Branch coverage (75.87%) did
32
+ not.** The shortfall is concentrated in form-input components that
33
+ predate Sprint 6 and shipped with partial branch coverage — their
34
+ validation-state / RBAC / controlled-vs-uncontrolled branches are
35
+ under-exercised:
36
+
37
+ | Component | Branch % |
38
+ |-----------|----------|
39
+ | `NumberField` | 50.56% |
40
+ | `AppShell` | 55.81% |
41
+ | `Textarea` | 56% |
42
+ | `Switch` | 62.5% |
43
+ | `TextField` | 63.63% |
44
+ | `RadioGroup` | 64.06% |
45
+ | `Checkbox` | 67.5% |
46
+ | `Autocomplete` | 68% |
47
+ | `OTPField` | 69.56% |
48
+ | `Dialog` | 75% |
49
+ | `DataGrid` | 76.15% |
50
+
51
+ Closing this to 85% overall is a focused **test-debt paydown
52
+ sprint** (~100+ branch-targeted tests across the 11 components) —
53
+ deliberately scoped OUT of Sprint 6, which is a Table/DataGrid +
54
+ theme-core refinement pass. Recommended slot: alongside the
55
+ `1.0.0-rc` hardening sprint.
56
+
57
+ ## CI gate
58
+
59
+ The catalog-wide regression suite (`nx test @dashforge/tw`) is the
60
+ enforced gate. A hard per-file coverage threshold is **not** wired
61
+ into CI yet — doing so requires first closing the pre-Sprint-6 debt
62
+ above, otherwise the gate fails on day one. Wiring the threshold is
63
+ part of the same `1.0.0-rc` test-debt sprint.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dashforge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PERFORMANCE.md CHANGED
@@ -159,11 +159,70 @@ single Radix primitive's worth of code) without the policy
159
159
  triggering, but small enough that a careless dep addition gets
160
160
  flagged.
161
161
 
162
+ ## Render perf test suite (Sprint 6 P3)
163
+
164
+ CI-enforced render budgets, co-located as `*.perf.test.tsx`. Bounds
165
+ are deliberately generous — they catch **order-of-magnitude**
166
+ regressions (an accidental O(n²) in the pipeline, or a virtualization
167
+ break that mounts the full dataset), not micro-jitter.
168
+
169
+ ### `<Table>` — non-virtualized (`Table.perf.test.tsx`)
170
+
171
+ | Scenario | Budget | Asserts |
172
+ |---|---|---|
173
+ | Mount 500 rows (Table's row-count ceiling) | < 800 ms | render path stays linear |
174
+ | 500 rows in DOM | exactly 500 `<tbody tr>` | non-virtualized contract |
175
+ | Default sort over 500 rows | < 800 ms | sort pipeline is not O(n²) |
176
+
177
+ ### `<DataGrid>` — virtualized (`DataGrid.perf.test.tsx`)
178
+
179
+ | Scenario | Budget | Asserts |
180
+ |---|---|---|
181
+ | Mount 10 000 rows | < 500 ms | render is O(window) |
182
+ | 10 000-row DOM node count | < 100 `<tbody tr>` | only the window is mounted |
183
+ | Mount 100 000 rows | < 800 ms | cost does NOT scale with the dataset |
184
+
185
+ The 100 000-row test is the load-bearing one: if a regression makes
186
+ `renderedRows` slice the full array, the DOM-node-count assertion
187
+ fails immediately (100k `<tr>` ≫ 100).
188
+
189
+ ## Strict gate assessment (Sprint 6 P3)
190
+
191
+ A probe enabled `exactOptionalPropertyTypes` + `noUncheckedIndexedAccess`
192
+ + `noUnusedParameters` on `@dashforge/tw`. Result: **42 errors, zero
193
+ real bugs.** Every hit was either pedantic (`exactOptionalPropertyTypes`
194
+ flagging `fn | undefined` passed to an optional prop) or invariant-safe
195
+ but unprovable (`noUncheckedIndexedAccess` flagging array accesses
196
+ already guarded by a `.length` check — AppShell focus trap, RadioGroup
197
+ option access, Autocomplete highlight index).
198
+
199
+ **Decision**: enabling those flags is a workspace-wide change (the
200
+ `@dashforge/source` path mapping pulls the bridge layer's source into
201
+ the same program) for near-zero bug-catching value. Deferred to the
202
+ `1.0.0-rc` hardening sprint. The codebase is already solid under the
203
+ current `strict: true` baseline + `noImplicitOverride` /
204
+ `noImplicitReturns` / `noUnusedLocals` / `noFallthroughCasesInSwitch`.
205
+ ESLint is on `typescript-eslint recommended` + `react-hooks
206
+ recommended` + Nx boundary/dependency checks; promotion to
207
+ `strict-type-checked` (type-aware `no-unsafe-*` / `no-floating-promises`)
208
+ is the same workspace-wide 1.0-rc item.
209
+
210
+ ## Console hygiene (Sprint 6 P3)
211
+
212
+ The full test run is audited for stray `console.error` / `console.warn`.
213
+ Sprint 6 P3 fixed the one finding: Radix logged 8× *"Missing
214
+ `Description` or `aria-describedby` for DialogContent"* — `<Dialog>`
215
+ now passes `aria-describedby={undefined}` on the content when no
216
+ `description` is supplied (Radix's documented opt-out). Console is
217
+ clean apart from a single jsdom-only *"Not implemented: navigation"*
218
+ (an environment limitation, not a component defect).
219
+
162
220
  ## Reference
163
221
 
164
222
  - **Build command**: `nx build @dashforge/tw`
165
223
  - **Measurement script**: `gzip -c dist/index.esm.js | wc -c`
166
224
  - **Render perf setup**: see Sprint 2 P1
167
225
  (`/test-foundation` route in `dash` consumer)
226
+ - **Render perf suite**: `*.perf.test.tsx` (Sprint 6 P3 — Table + DataGrid)
168
227
  - **CHANGELOG entries**: every release records the bundle delta
169
228
  in the `Compatibility` section
@@ -0,0 +1,50 @@
1
+ # REFINEMENT-NOTES — `@dashforge/tw` Table + DataGrid
2
+
3
+ > Sprint 6 P2 deliverable (2026-05-20). Blind technical review of
4
+ > `<Table>` + `<DataGrid>` — what was fixed, and what was explicitly
5
+ > deferred.
6
+
7
+ ## Fixed in Sprint 6 P2
8
+
9
+ | ID | Area | Fix |
10
+ |----|------|-----|
11
+ | D1 | DataGrid a11y | `aria-rowcount` on the `<table>` (true dataset size, not the virtualized window) + `aria-rowindex` on the header row (1) and every data row (`absoluteIndex + 2`). A screen reader now announces the real position. |
12
+ | D3 | DataGrid a11y | Column resize is now keyboard-operable: the resize handle is `tabIndex=0`, `ArrowLeft`/`ArrowRight` nudge the width 16px (64px with Shift), `aria-valuemin/max/now` announce the bounds. New `nudge()` on `useColumnResize`. |
13
+ | D6 | DataGrid bug | The resize handle on a reorderable column could also start a column-reorder drag (the `<th>` is `draggable`). Handle now sets `draggable={false}` — grabbing it always resizes. |
14
+ | D2 | DataGrid touch | Column reorder rides native HTML5 drag-and-drop, whose events don't fire on touch. The grid now detects a coarse pointer (`matchMedia('(pointer: coarse)')`) and disables the `draggable` affordance there, so it isn't a dead interaction. (Full touch reorder — see Deferred below.) |
15
+ | D4 | DataGrid UX | `Enter` in any filter Popover input now applies the filter (was: must click "Apply"). |
16
+ | D7 | Table + DataGrid UX | Empty state distinguishes a genuinely empty dataset (`noData`) from one emptied by an active search / filter (`noResults`). New `TableLabels.noResults`. |
17
+ | D8 | Table | Verified non-issue — Table is auto-height by design; skeleton rows inherit cell density padding. Added a clarifying code comment. |
18
+ | M4 | Cell library | Cleaned up a self-contradictory comment in `RenderChip` (the `solid neutral` variant). |
19
+ | T1 | Table | `getRowId` stays optional (back-compat) but now emits a dev-only `console.warn` when omitted while sort / search / selection / expandable is active — the positional-index fallback breaks row identity on reorder. Expanded the prop JSDoc. |
20
+
21
+ ## Deferred — tracked follow-ups
22
+
23
+ ### T3 — Per-column filter UI on `<Table>`
24
+ `<DataGrid>` got the in-header filter Popover in 0.8.0-beta (`filterable: true`).
25
+ `<Table>` still only accepts a controlled `filterModel` with no UI to
26
+ produce it. Porting `ColumnFilterTrigger` + the filter pipeline to
27
+ Table is a feature, not a refinement — it belongs in a dedicated
28
+ feature sprint (or post-1.0). Until then, Table's `filterModel` is
29
+ consumer-driven only.
30
+
31
+ ### C2 — Cell renderer library additions
32
+ The library ships `RenderText`, `RenderTwoLine`, `RenderChip`,
33
+ `RenderButton`, `RowActionsMenu`. Common table-cell patterns still
34
+ missing:
35
+ - `RenderDate` — locale-aware date formatting (`Intl.DateTimeFormat`,
36
+ zero-dep)
37
+ - `RenderAvatar` — avatar image / initials + label, the canonical
38
+ "user" cell
39
+ - `RenderLink` — a styled anchor honoring the theme
40
+
41
+ Purely additive — slated for a feature sprint, not refinement.
42
+
43
+ ### D2-full — Pointer-events column reorder (touch support)
44
+ The D2 fix above is *graceful degradation* — reorder is cleanly
45
+ unavailable on touch rather than silently broken. A proper touch
46
+ implementation means replacing `useColumnReorder`'s HTML5
47
+ drag-and-drop with pointer events (à la `useColumnResize`): track
48
+ `pointerdown` → `pointermove` with hit-testing (`elementFromPoint`)
49
+ for the drop target → `pointerup` to commit. Meaningful rewrite —
50
+ deferred to a follow-up so P2 stays a refinement pass.