@dashforge/tw 0.4.0-beta → 0.6.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.
- package/CHANGELOG.md +391 -0
- package/dist/index.esm.js +2113 -2
- package/dist/src/components/Pagination/Pagination.d.ts +32 -0
- package/dist/src/components/Pagination/Pagination.d.ts.map +1 -0
- package/dist/src/components/Pagination/pagination.helpers.d.ts +23 -0
- package/dist/src/components/Pagination/pagination.helpers.d.ts.map +1 -0
- package/dist/src/components/Pagination/pagination.types.d.ts +120 -0
- package/dist/src/components/Pagination/pagination.types.d.ts.map +1 -0
- package/dist/src/components/Pagination/pagination.variants.d.ts +150 -0
- package/dist/src/components/Pagination/pagination.variants.d.ts.map +1 -0
- package/dist/src/components/Skeleton/Skeleton.d.ts +23 -0
- package/dist/src/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/src/components/Skeleton/skeleton.types.d.ts +56 -0
- package/dist/src/components/Skeleton/skeleton.types.d.ts.map +1 -0
- package/dist/src/components/Skeleton/skeleton.variants.d.ts +110 -0
- package/dist/src/components/Skeleton/skeleton.variants.d.ts.map +1 -0
- package/dist/src/components/Table/Table.d.ts +24 -0
- package/dist/src/components/Table/Table.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/getNestedValue.d.ts +20 -0
- package/dist/src/components/Table/_internal/getNestedValue.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts +45 -0
- package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useControllableState.d.ts +23 -0
- package/dist/src/components/Table/_internal/useControllableState.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useDebouncedValue.d.ts +10 -0
- package/dist/src/components/Table/_internal/useDebouncedValue.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useTableFilter.d.ts +13 -0
- package/dist/src/components/Table/_internal/useTableFilter.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useTableSearch.d.ts +23 -0
- package/dist/src/components/Table/_internal/useTableSearch.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useTableSelection.d.ts +25 -0
- package/dist/src/components/Table/_internal/useTableSelection.d.ts.map +1 -0
- package/dist/src/components/Table/_internal/useTableSort.d.ts +20 -0
- package/dist/src/components/Table/_internal/useTableSort.d.ts.map +1 -0
- package/dist/src/components/Table/cells/RenderButton.d.ts +13 -0
- package/dist/src/components/Table/cells/RenderButton.d.ts.map +1 -0
- package/dist/src/components/Table/cells/RenderChip.d.ts +80 -0
- package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -0
- package/dist/src/components/Table/cells/RenderText.d.ts +21 -0
- package/dist/src/components/Table/cells/RenderText.d.ts.map +1 -0
- package/dist/src/components/Table/cells/RenderTwoLine.d.ts +20 -0
- package/dist/src/components/Table/cells/RenderTwoLine.d.ts.map +1 -0
- package/dist/src/components/Table/cells/RowActionsMenu.d.ts +36 -0
- package/dist/src/components/Table/cells/RowActionsMenu.d.ts.map +1 -0
- package/dist/src/components/Table/cells/index.d.ts +11 -0
- package/dist/src/components/Table/cells/index.d.ts.map +1 -0
- package/dist/src/components/Table/table.types.d.ts +304 -0
- package/dist/src/components/Table/table.types.d.ts.map +1 -0
- package/dist/src/components/Table/table.variants.d.ts +285 -0
- package/dist/src/components/Table/table.variants.d.ts.map +1 -0
- package/dist/src/index.d.ts +13 -1
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Pagination/Pagination.test.tsx +314 -0
- package/src/components/Pagination/Pagination.tsx +275 -0
- package/src/components/Pagination/pagination.helpers.ts +102 -0
- package/src/components/Pagination/pagination.types.ts +117 -0
- package/src/components/Pagination/pagination.variants.ts +111 -0
- package/src/components/Skeleton/Skeleton.test.tsx +127 -0
- package/src/components/Skeleton/Skeleton.tsx +49 -0
- package/src/components/Skeleton/skeleton.types.ts +59 -0
- package/src/components/Skeleton/skeleton.variants.ts +86 -0
- package/src/components/Table/Table.test.tsx +603 -0
- package/src/components/Table/Table.tsx +816 -0
- package/src/components/Table/_internal/getNestedValue.test.ts +50 -0
- package/src/components/Table/_internal/getNestedValue.ts +32 -0
- package/src/components/Table/_internal/themeIdentity.test.ts +84 -0
- package/src/components/Table/_internal/useColumnAutoDetect.test.ts +123 -0
- package/src/components/Table/_internal/useColumnAutoDetect.ts +100 -0
- package/src/components/Table/_internal/useControllableState.ts +48 -0
- package/src/components/Table/_internal/useDebouncedValue.ts +24 -0
- package/src/components/Table/_internal/useTableFilter.ts +37 -0
- package/src/components/Table/_internal/useTableSearch.test.ts +115 -0
- package/src/components/Table/_internal/useTableSearch.ts +66 -0
- package/src/components/Table/_internal/useTableSelection.test.ts +100 -0
- package/src/components/Table/_internal/useTableSelection.ts +100 -0
- package/src/components/Table/_internal/useTableSort.test.ts +150 -0
- package/src/components/Table/_internal/useTableSort.ts +141 -0
- package/src/components/Table/cells/RenderButton.tsx +23 -0
- package/src/components/Table/cells/RenderChip.tsx +103 -0
- package/src/components/Table/cells/RenderText.tsx +37 -0
- package/src/components/Table/cells/RenderTwoLine.tsx +34 -0
- package/src/components/Table/cells/RowActionsMenu.tsx +147 -0
- package/src/components/Table/cells/index.ts +14 -0
- package/src/components/Table/table.types.ts +337 -0
- package/src/components/Table/table.variants.ts +207 -0
- package/src/index.ts +60 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,397 @@ 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.6.0-beta] — 2026-05-19
|
|
16
|
+
|
|
17
|
+
**Sprint 4.1 release.** Ships the central piece of the data layer
|
|
18
|
+
— a market-grounded `<Table>` built from scratch with NO new
|
|
19
|
+
runtime deps. The design references Stripe (visual style),
|
|
20
|
+
Atlassian / Jira (column UX), and Pencil & Paper UX research
|
|
21
|
+
(density tiers, hover-revealed row actions, selection patterns).
|
|
22
|
+
The existing MUI Table inherited from `hub-ws/admin` informed the
|
|
23
|
+
public API shape but not the implementation (we discarded the
|
|
24
|
+
`@mui/x-data-grid` backing — Table v1 is a hand-rolled native
|
|
25
|
+
`<table>`).
|
|
26
|
+
|
|
27
|
+
**Companion to Sprint 4.2** (DataGrid, virtualization, 10k+ rows)
|
|
28
|
+
— Table v1 covers the 80% admin-list use case up to ~500 rows.
|
|
29
|
+
|
|
30
|
+
**Minor bump** for the new public API surface — 15+ new exports.
|
|
31
|
+
Strictly additive — zero breaking changes. Drop-in upgrade from
|
|
32
|
+
`0.5.0-beta`.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`<Table>`** — declarative-first data table component.
|
|
37
|
+
- **Smart defaults**: column types auto-detected from the first
|
|
38
|
+
non-null value across visible rows. Number columns get
|
|
39
|
+
`text-right` + `tabular-nums` (digit grid alignment), date
|
|
40
|
+
columns get `tabular-nums` too, booleans get `text-center`,
|
|
41
|
+
strings get `text-left`. **The library never changes the font
|
|
42
|
+
family** — `tabular-nums` is a font-feature setting that
|
|
43
|
+
preserves the consumer's theme `font-sans`. Explicit
|
|
44
|
+
`align` / `tabularNums` / `monospace` per column wins.
|
|
45
|
+
- **Sort**: per-column `sortable: true` (default comparator
|
|
46
|
+
handles string / number / Date / boolean / bigint with
|
|
47
|
+
null-last invariant) or custom `(a, b) => number`. Click
|
|
48
|
+
cycles asc → desc → none. **Shift-click** adds the column to
|
|
49
|
+
a multi-sort model. Controlled via `sortModel` /
|
|
50
|
+
`onSortChange` or uncontrolled with internal state.
|
|
51
|
+
- **Search**: `enableSearch` renders a debounced input above
|
|
52
|
+
the table (default 200 ms). Matches any column flagged
|
|
53
|
+
`searchable: true`. **Nested keys supported** at runtime via
|
|
54
|
+
`getNestedValue(row, 'address.city')` — the type-level
|
|
55
|
+
autocomplete from `NestedKeyOf<T>` matches the runtime
|
|
56
|
+
behavior. Stringification covers string / number / boolean /
|
|
57
|
+
Date / array / object.
|
|
58
|
+
- **Selection**: `rowSelection: 'none' | 'single' | 'multiple'`,
|
|
59
|
+
optional select-all checkbox in the header (multiple mode),
|
|
60
|
+
`selectedRowIds` controlled state, **sticky bulk-action
|
|
61
|
+
footer** rendered only when at least one row is selected
|
|
62
|
+
(Pencil & Paper UX pattern: "appear only when selected").
|
|
63
|
+
- **Expandable rows**: `expandable={{ render, expandedRowIds?,
|
|
64
|
+
onExpandChange? }}`. Chevron toggle in a dedicated column;
|
|
65
|
+
`aria-expanded` updates; expanded detail renders as a
|
|
66
|
+
full-width sibling `<tr>` below the row.
|
|
67
|
+
- **Row actions revealed on hover**: `rowActions={(row) => …}`
|
|
68
|
+
slot — by default hidden via opacity, revealed on
|
|
69
|
+
`tr:hover` / `tr:focus-within` (Stripe pattern — reduces
|
|
70
|
+
visual density while keeping actions discoverable).
|
|
71
|
+
- **RBAC at 3 levels**: table-level `access` (hides / disables
|
|
72
|
+
the whole table), per-column `cols[i].access` (`hide` removes
|
|
73
|
+
the column entirely from header AND every row cell), and
|
|
74
|
+
per-action `actions[i].access` on `<RowActionsMenu>`.
|
|
75
|
+
- **A11Y**: semantic `<table>` + `<th scope="col">` + `aria-sort`
|
|
76
|
+
on sortable headers + `aria-selected` on selected rows +
|
|
77
|
+
`aria-expanded` on expand toggles + `<caption>` (optional,
|
|
78
|
+
`sr-only` by default) + keyboard nav via Tab + Enter/Space.
|
|
79
|
+
- **Loading state**: `loading={true}` renders N `<Skeleton>` rows
|
|
80
|
+
(the Sprint 4 component) with `aria-busy="true"`. Count
|
|
81
|
+
configurable via `loadingRowCount` (default 5).
|
|
82
|
+
- **i18n**: column `header` accepts plain strings (pass
|
|
83
|
+
`t('...')`); all internal default strings configurable via
|
|
84
|
+
`labels` prop with English defaults (search placeholder, a11y
|
|
85
|
+
announcements for sort / select / expand, selection counter
|
|
86
|
+
with `{count}` placeholder, density / filter labels). Same
|
|
87
|
+
pattern as `<Pagination>`.
|
|
88
|
+
- **5 variants** (`plain` · `lines` default — Stripe-style ·
|
|
89
|
+
`striped` · `bordered` · `card`) × **3 sizes** (`sm` · `md` ·
|
|
90
|
+
`lg`) × **3 densities** (`compact` 40px · `comfortable`
|
|
91
|
+
default 48px · `spacious` 56px — Pencil & Paper UX research).
|
|
92
|
+
- **Sticky header** by default (overridable).
|
|
93
|
+
- **`sx` + 15 slot props** for the standard customization
|
|
94
|
+
escape hatches.
|
|
95
|
+
|
|
96
|
+
- **Cell renderer library** — pre-built renderers for common
|
|
97
|
+
patterns, exported from `@dashforge/tw`:
|
|
98
|
+
- `RenderText` — one-line, optionally truncated / muted
|
|
99
|
+
- `RenderTwoLine` — bold primary + muted secondary
|
|
100
|
+
(name + email pattern)
|
|
101
|
+
- `RenderChip` — internal status badge, 7 intent colors × 3
|
|
102
|
+
variants (soft / solid / outline) × 2 sizes (sm / md)
|
|
103
|
+
- `RenderButton` — inline button wrapper (defaults `ghost` +
|
|
104
|
+
`sm`)
|
|
105
|
+
- `RowActionsMenu` — 3-dot Popover-backed menu with per-action
|
|
106
|
+
RBAC
|
|
107
|
+
|
|
108
|
+
- **Helpers exported for power use**:
|
|
109
|
+
- `getNestedValue(row, path)` — dotted-path lookup powering
|
|
110
|
+
cell rendering. Useful in consumer custom renderers.
|
|
111
|
+
|
|
112
|
+
- **Theme identity regression guard** —
|
|
113
|
+
`_internal/themeIdentity.test.ts` scans every Table source file
|
|
114
|
+
and fails if `dark:*-neutral-N` classes are introduced. The
|
|
115
|
+
dashforgePreset auto-inverts the neutral palette via CSS var
|
|
116
|
+
swap; adding `dark:` variants on neutral creates double
|
|
117
|
+
inversion and breaks dark mode. The Sprint 4.1 fix removes the
|
|
118
|
+
anti-pattern from Table; **Sprint 4.3 will sweep the rest of
|
|
119
|
+
the catalog** (Typography, Box, etc. carry the same latent bug).
|
|
120
|
+
|
|
121
|
+
- **Smoke-test page** in `dash` consumer at `/test-table` —
|
|
122
|
+
exercises every Table feature with 30 realistic users + nested
|
|
123
|
+
meta + chip-rendered status + RBAC per-column + i18n labels in
|
|
124
|
+
Italian + 5 variants × 3 sizes × 3 densities switcher.
|
|
125
|
+
|
|
126
|
+
- **Doc MDX** `/tw/docs/components/table` — full feature catalog,
|
|
127
|
+
i18n example with `react-i18next`, override matrix
|
|
128
|
+
(align / tabularNums / monospace), 15+ slot props reference,
|
|
129
|
+
cell renderer library, A11Y guarantees, roadmap.
|
|
130
|
+
|
|
131
|
+
### Internal
|
|
132
|
+
|
|
133
|
+
- **Theme identity rule** (now codified): the dashforgePreset
|
|
134
|
+
default IS the Dashforge visual identity. Consumer apps consume
|
|
135
|
+
it as-is and never override. Adding `dark:` variants on the
|
|
136
|
+
neutral palette = double inversion = breaks dark mode. Use
|
|
137
|
+
canonical patterns (LeftNav `itemActive` for selected,
|
|
138
|
+
auto-invert for `bg-neutral-*` and `text-neutral-*`).
|
|
139
|
+
- **Font family rule**: the library never picks a font family —
|
|
140
|
+
`tabular-nums` is safe to auto-apply (font-feature setting),
|
|
141
|
+
`font-mono` is consumer opt-in only via `col.monospace: true`.
|
|
142
|
+
The dashforgePreset does not own the `fontFamily` axis; the
|
|
143
|
+
consumer configures their mono stack in their own
|
|
144
|
+
`tailwind.config.ts` `theme.extend.fontFamily.mono`.
|
|
145
|
+
- **+147 new unit tests** for Table:
|
|
146
|
+
- `getNestedValue` (11) — nested keys, null-safety, zero / empty
|
|
147
|
+
string / false preservation
|
|
148
|
+
- `useTableSearch` (16) — stringification across primitive types
|
|
149
|
+
+ nested keys
|
|
150
|
+
- `useTableSort` (15) — null-last invariant (direction-independent),
|
|
151
|
+
multi-column tie-breaking, custom comparator
|
|
152
|
+
- `useTableSelection` (13) — single / multiple / none modes
|
|
153
|
+
- `useColumnAutoDetect` (14) — type inference + align /
|
|
154
|
+
tabularNums / monospace resolution
|
|
155
|
+
- `themeIdentity` (18 file scanner) — regression guard for
|
|
156
|
+
`dark:` on neutral palette
|
|
157
|
+
- `Table.test.tsx` (57) — rendering, smart defaults, sort,
|
|
158
|
+
search, selection, expandable, row actions, RBAC, variants,
|
|
159
|
+
densities, sizes, sx + slotProps, i18n
|
|
160
|
+
- Full TW suite at **828/828 passing** (46 files; +147 from
|
|
161
|
+
Table, +newer guard).
|
|
162
|
+
|
|
163
|
+
### Compatibility
|
|
164
|
+
|
|
165
|
+
| Axis | Pre-`0.6.0` | Post-`0.6.0` |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| Public API surface | 31 components | **+ 1 (`Table`)** + 5 cell renderers + `getNestedValue` helper + `Table*` types (`TableProps`, `TableColumn`, `TableSortModel`, `TableFilterModel`, `TableLabels`, `TableCellContext`, `NestedKeyOf`, `TableRowAction`, …) |
|
|
168
|
+
| Peer deps | `react ^18 \|\| ^19`, `tw-theme workspace`, `tw-tokens workspace` | unchanged |
|
|
169
|
+
| Bridge deps | `forms` / `rbac` / `ui-core` `workspace:*` | unchanged |
|
|
170
|
+
| New runtime deps | — | **none** (no `@tanstack/*`, no DnD libs — constraint honored) |
|
|
171
|
+
| Breaking changes | — | Zero |
|
|
172
|
+
| Bundle size | 336 KB raw / 73.9 KB gzipped | **402 KB raw / 91 KB gzipped** (+66 KB raw / +17 KB gz / **+23% gz**) |
|
|
173
|
+
| Migration | — | Drop-in. Zero code changes required on existing usages. |
|
|
174
|
+
|
|
175
|
+
> **Bundle regression note**: the +23% gz delta is **above the
|
|
176
|
+
> 10% reviewer-sign-off threshold** documented in
|
|
177
|
+
> `PERFORMANCE.md`. Justification:
|
|
178
|
+
> Table is the lib's central data-display primitive — it includes
|
|
179
|
+
> sort + search + selection + expandable + RBAC at 3 levels + 5
|
|
180
|
+
> cell renderers + RowActionsMenu (using Popover) + 5 variants ×
|
|
181
|
+
> 3 sizes × 3 densities. The bundle weight is proportional to
|
|
182
|
+
> the feature surface. Sign-off rationale: this is the
|
|
183
|
+
> "MUI X DataGrid Community alternative" component — the single
|
|
184
|
+
> highest-value addition before 1.0. Sprint 4.3 (theme identity
|
|
185
|
+
> sweep) will recover -1 to -3 KB gz by removing redundant
|
|
186
|
+
> `dark:` variants across the catalog.
|
|
187
|
+
|
|
188
|
+
### Migration
|
|
189
|
+
|
|
190
|
+
No code changes required:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pnpm up @dashforge/tw@^0.6.0-beta
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
To adopt the new Table:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import { Table, RenderTwoLine, RenderChip, RowActionsMenu } from '@dashforge/tw';
|
|
200
|
+
import { useTranslation } from 'react-i18next';
|
|
201
|
+
|
|
202
|
+
function UsersTable({ users }) {
|
|
203
|
+
const { t } = useTranslation();
|
|
204
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Table
|
|
208
|
+
rows={users}
|
|
209
|
+
cols={[
|
|
210
|
+
{
|
|
211
|
+
field: 'name',
|
|
212
|
+
header: t('users.fields.name'),
|
|
213
|
+
sortable: true,
|
|
214
|
+
searchable: true,
|
|
215
|
+
cellRenderer: ({ row }) => (
|
|
216
|
+
<RenderTwoLine primary={row.name} secondary={row.email} />
|
|
217
|
+
),
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
field: 'salary',
|
|
221
|
+
header: t('users.fields.salary'),
|
|
222
|
+
sortable: true,
|
|
223
|
+
// Right-aligned + tabular-nums automatically. Font family
|
|
224
|
+
// stays whatever the consumer's theme provides — opt into
|
|
225
|
+
// `monospace: true` if you want font-mono explicitly.
|
|
226
|
+
access: {
|
|
227
|
+
resource: 'employee.salary',
|
|
228
|
+
action: 'read',
|
|
229
|
+
onUnauthorized: 'hide',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
field: 'status',
|
|
234
|
+
header: t('users.fields.status'),
|
|
235
|
+
cellRenderer: ({ value }) => (
|
|
236
|
+
<RenderChip
|
|
237
|
+
color={value === 'active' ? 'success' : 'warning'}
|
|
238
|
+
>
|
|
239
|
+
{String(value)}
|
|
240
|
+
</RenderChip>
|
|
241
|
+
),
|
|
242
|
+
},
|
|
243
|
+
]}
|
|
244
|
+
getRowId={(r) => r.id}
|
|
245
|
+
enableSearch
|
|
246
|
+
rowSelection="multiple"
|
|
247
|
+
selectedRowIds={selected}
|
|
248
|
+
onSelectionChange={setSelected}
|
|
249
|
+
bulkActions={(rows) => (
|
|
250
|
+
<Button color="danger">
|
|
251
|
+
{t('actions.delete', { count: rows.length })}
|
|
252
|
+
</Button>
|
|
253
|
+
)}
|
|
254
|
+
rowActions={(row) => (
|
|
255
|
+
<RowActionsMenu
|
|
256
|
+
row={row}
|
|
257
|
+
actions={[
|
|
258
|
+
{ label: t('actions.edit'), onClick: edit },
|
|
259
|
+
{ label: t('actions.delete'), onClick: del, color: 'danger' },
|
|
260
|
+
]}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
labels={{
|
|
264
|
+
searchPlaceholder: t('table.search'),
|
|
265
|
+
selectedCount: t('table.selected', { defaultValue: '{count} selected' }),
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Heads-up — companion releases coming
|
|
273
|
+
|
|
274
|
+
- **Sprint 4.3 → `0.7.0-beta`**: theme identity audit across the
|
|
275
|
+
whole catalog (Typography, Box, etc. carry the same latent
|
|
276
|
+
`dark:` anti-pattern Table just fixed). Expected bundle delta:
|
|
277
|
+
**−1 to −3 KB gz** (removing redundant classes).
|
|
278
|
+
- **Sprint 4.2 → `0.8.0-beta`**: DataGrid with homemade
|
|
279
|
+
virtualization (`IntersectionObserver`-based, no new deps) for
|
|
280
|
+
10k+ row data sets, advanced filter model, sticky columns,
|
|
281
|
+
per-column RBAC.
|
|
282
|
+
|
|
283
|
+
## [0.5.0-beta] — 2026-05-19
|
|
284
|
+
|
|
285
|
+
**Sprint 4 release.** Two TW-only utility primitives — `<Skeleton>`
|
|
286
|
+
and `<Pagination>` — that were missing from the catalog and are
|
|
287
|
+
prerequisite visuals for the upcoming Sprint 4.1 (Table + DataGrid).
|
|
288
|
+
Both are pure-UI, no bridge integration, no RBAC sensible — they
|
|
289
|
+
exist to compose with the data layer arriving next sprint.
|
|
290
|
+
|
|
291
|
+
**MUI side note.** Neither component ships in `@dashforge/ui`:
|
|
292
|
+
`@mui/material/Skeleton` and `@mui/material/Pagination` already
|
|
293
|
+
cover the bridge-free use case fully. Per the Dashforge design rule
|
|
294
|
+
(`@dashforge/ui` wraps MUI only when adding bridge / RBAC /
|
|
295
|
+
validation / custom-behavior value), MUI consumers reach for the
|
|
296
|
+
upstream components directly.
|
|
297
|
+
|
|
298
|
+
**Minor bump** for 2 new public exports. Strictly additive — zero
|
|
299
|
+
breaking changes. Drop-in upgrade from `0.4.0-beta`.
|
|
300
|
+
|
|
301
|
+
### Added
|
|
302
|
+
|
|
303
|
+
- **`<Skeleton>`** — loading placeholder primitive.
|
|
304
|
+
Three shapes (`text` default · `rectangle` · `circle`), three
|
|
305
|
+
animations (`pulse` default · `wave` · `none`). Width / height as
|
|
306
|
+
inline CSS lengths; circle's height defaults to its width.
|
|
307
|
+
Rendered as `<span aria-hidden="true" role="presentation">` — screen
|
|
308
|
+
readers skip it; the surrounding container is responsible for
|
|
309
|
+
`aria-busy` / `aria-live` announcements. **WCAG 2.3.3**: both
|
|
310
|
+
animations gated on `prefers-reduced-motion: reduce`. Compose
|
|
311
|
+
multiple `<Skeleton>`s to mimic card / row shapes during fetch.
|
|
312
|
+
19 unit tests covering variants × animations × sizing × sx +
|
|
313
|
+
slotProps override + a11y attributes.
|
|
314
|
+
- **`<Pagination>`** — controlled pagination primitive. Three
|
|
315
|
+
variants:
|
|
316
|
+
- `default` — summary + page numbers + first/prev/next/last +
|
|
317
|
+
page-size selector + direct jump input
|
|
318
|
+
- `compact` — page numbers + nav buttons only
|
|
319
|
+
- `minimal` — "Page X of Y" + prev/next (mobile-friendly)
|
|
320
|
+
Three sizes (`sm` / `md` / `lg`). Configurable
|
|
321
|
+
`siblingCount` / `boundaryCount` for the ellipsis range
|
|
322
|
+
algorithm — small totals (≤ `2·boundary + 2·sibling + 3` pages)
|
|
323
|
+
short-circuit to the full range, no ellipsis. Full i18n via
|
|
324
|
+
`labels` prop (English defaults). A11Y: `<nav aria-label="Pagination">`
|
|
325
|
+
landmark, `aria-current="page"` on the active button, native
|
|
326
|
+
`<select>` for page size, native `<input type="number">` for
|
|
327
|
+
the jump input (commit on Enter / blur, clamped to range).
|
|
328
|
+
Page-size selector hidden when `onPageSizeChange` is omitted.
|
|
329
|
+
Jump input toggle via `showJumpInput`. 28 unit tests covering
|
|
330
|
+
the range helper (7) + rendering / interactions / variants /
|
|
331
|
+
i18n / disabled / overrides / edge cases (21).
|
|
332
|
+
|
|
333
|
+
### Internal
|
|
334
|
+
|
|
335
|
+
- **47 new unit tests** for the 2 components — full TW suite at
|
|
336
|
+
**681/681 passing** (39 files).
|
|
337
|
+
- **`TestUtilities.tsx`** added to the `dash` consumer
|
|
338
|
+
(`/test-utilities`) as the Sprint 4 smoke-test page: variant
|
|
339
|
+
matrix for both components + card-placeholder compose pattern +
|
|
340
|
+
Pagination i18n / size / variant / edge cases + a 50-instance
|
|
341
|
+
Skeleton stress test wrapped in `React.Profiler`. Validated
|
|
342
|
+
end-to-end before docs were written (per Dashforge workflow
|
|
343
|
+
policy: dash smoke test PRECEDES docs).
|
|
344
|
+
- **Sidebar entries** for the new `Skeleton` + `Pagination` doc
|
|
345
|
+
pages added to `dashforge-docs-lab/src/tw-docs/sidebar.model.ts`.
|
|
346
|
+
|
|
347
|
+
### Compatibility
|
|
348
|
+
|
|
349
|
+
| Axis | Pre-`0.5.0` | Post-`0.5.0` |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| Public API surface | 29 components | **+ 2 (`Skeleton`, `Pagination`)** + their `*Props` / `*SlotProps` types + `*Variants` recipes + `PaginationLabels` i18n type |
|
|
352
|
+
| Peer deps | `react ^18 \|\| ^19`, `tw-theme workspace`, `tw-tokens workspace` | unchanged |
|
|
353
|
+
| Bridge deps | `forms` / `rbac` / `ui-core` `workspace:*` | unchanged |
|
|
354
|
+
| New runtime deps | — | **none** (no new external libraries — constraint honored) |
|
|
355
|
+
| Breaking changes | — | Zero |
|
|
356
|
+
| Bundle size | 312 KB raw / 68.85 KB gzipped | **336 KB raw / 73.9 KB gzipped** (+24 KB raw / +5.05 KB gz / **+7.3% gz**) |
|
|
357
|
+
| Migration | — | Drop-in. Zero code changes required on existing usages. |
|
|
358
|
+
|
|
359
|
+
> **Bundle regression note**: the +7.3% gzipped delta is **above
|
|
360
|
+
> the 5% threshold** documented in `PERFORMANCE.md`. Justification:
|
|
361
|
+
> Pagination contributes ~4 KB gz (3 variants × 3 sizes × i18n
|
|
362
|
+
> surface + range-computation helper + jump-input commit logic);
|
|
363
|
+
> Skeleton contributes ~1 KB gz. The delta sits well under the
|
|
364
|
+
> 10% reviewer-sign-off threshold and is the necessary cost of
|
|
365
|
+
> adding two new public components. Filed as informational per
|
|
366
|
+
> the regression policy.
|
|
367
|
+
|
|
368
|
+
### Migration
|
|
369
|
+
|
|
370
|
+
No code changes required:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
pnpm up @dashforge/tw@^0.5.0-beta
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
To adopt the new components:
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
import { Skeleton, Pagination } from '@dashforge/tw';
|
|
380
|
+
|
|
381
|
+
// Loading placeholder while fetching
|
|
382
|
+
{isLoading ? (
|
|
383
|
+
<Skeleton variant="text" width="200px" />
|
|
384
|
+
) : (
|
|
385
|
+
<UserName>{user.name}</UserName>
|
|
386
|
+
)}
|
|
387
|
+
|
|
388
|
+
// Paginated list (controlled — wire to your data slice)
|
|
389
|
+
const [page, setPage] = useState(1);
|
|
390
|
+
const [pageSize, setPageSize] = useState(20);
|
|
391
|
+
|
|
392
|
+
<Pagination
|
|
393
|
+
page={page}
|
|
394
|
+
pageSize={pageSize}
|
|
395
|
+
totalCount={users.length}
|
|
396
|
+
onPageChange={setPage}
|
|
397
|
+
onPageSizeChange={setPageSize}
|
|
398
|
+
/>
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
MUI consumers: continue using `@mui/material/Skeleton` and
|
|
402
|
+
`@mui/material/Pagination` directly. `@dashforge/ui` will NOT ship
|
|
403
|
+
wrappers for these (per the design rule documented in
|
|
404
|
+
`PARITY.md` — no value-add wrappers).
|
|
405
|
+
|
|
15
406
|
## [0.4.0-beta] — 2026-05-19
|
|
16
407
|
|
|
17
408
|
**Sprint 3 release.** Five new Tier-4 overlay & disclosure
|