feat: add release baseline and workspace scene
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
# RFC: DataTable
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Summary
|
||||
|
||||
`DataTable` is the next high-value advanced pattern for `@ai-ui/ui`, but it should not ship as a giant "kitchen sink" component.
|
||||
|
||||
The recommended path is:
|
||||
|
||||
1. adopt a headless row-model dependency for the hard table state problems
|
||||
2. keep the public API source-owned and design-system specific
|
||||
3. ship a narrow core table first
|
||||
4. layer richer behaviors only after the base contract stabilizes
|
||||
|
||||
The recommended dependency choice is `@tanstack/react-table`, but only as an internal implementation detail. Consumers should build against `@ai-ui/ui`, not against TanStack APIs directly.
|
||||
|
||||
## Why now
|
||||
|
||||
The repo has already completed most of the preconditions that the roadmap called out before advanced patterns:
|
||||
|
||||
- token system is in place
|
||||
- core component layer is in place
|
||||
- Storybook coverage exists across the current primitives
|
||||
- interaction and smoke coverage are present
|
||||
- `Sheet` and `EmptyState` are now available as adjacent workflow patterns
|
||||
|
||||
Relevant current building blocks already exist in `packages/ui`:
|
||||
|
||||
- input and form controls: `Input`, `Select`, `Checkbox`, `RadioGroup`, `Switch`, `Form`
|
||||
- structure and feedback: `Card`, `Badge`, `Alert`, `Separator`, `Skeleton`, `Progress`
|
||||
- overflow and contextual actions: `DropdownMenu`, `Popover`, `Tooltip`, `Sheet`, `Dialog`, `Toast`
|
||||
- empty and first-run states: `EmptyState`
|
||||
|
||||
That means the main missing piece for list-heavy product surfaces is not another primitive. It is a stable data presentation pattern.
|
||||
|
||||
## Problem statement
|
||||
|
||||
Today the design system can express forms, overlays, command/search, and empty states, but it cannot yet express a reusable operational list surface such as:
|
||||
|
||||
- release queues
|
||||
- approval backlogs
|
||||
- rollout logs
|
||||
- audit result lists
|
||||
- environment health tables
|
||||
- reviewer assignments
|
||||
|
||||
Without a `DataTable` pattern, application code will drift into one-off table wrappers with inconsistent:
|
||||
|
||||
- toolbar layout
|
||||
- filter/search interactions
|
||||
- row selection behavior
|
||||
- loading and empty states
|
||||
- keyboard behavior
|
||||
- sticky headers and scrolling decisions
|
||||
- bulk action affordances
|
||||
- pagination treatment
|
||||
|
||||
This is exactly the kind of "parallel wrapper" problem the roadmap warns against.
|
||||
|
||||
## Non-goals
|
||||
|
||||
The first implementation should **not** attempt to solve every table problem.
|
||||
|
||||
Explicit non-goals for the first slice:
|
||||
|
||||
- virtualization
|
||||
- column resizing
|
||||
- column reordering
|
||||
- nested tree rows
|
||||
- grouped/aggregated rows
|
||||
- drag and drop
|
||||
- inline cell editing
|
||||
- Excel-style spreadsheet behavior
|
||||
- server transport concerns
|
||||
- generic charting or pivot-table behavior
|
||||
|
||||
Those can come later if real product surfaces prove they are needed.
|
||||
|
||||
## Current repo constraints
|
||||
|
||||
This RFC is anchored in the current repo, not an idealized future state.
|
||||
|
||||
### Architectural constraints
|
||||
|
||||
- Components are source-owned and exported from `packages/ui/src/index.ts`
|
||||
- Styling is token-first and stateful styling flows through stable `data-*` attributes
|
||||
- Slot naming is already standardized in `packages/ui/src/lib/contracts.ts`
|
||||
- Motion should be purposeful and reduced-motion safe
|
||||
- Stories are expected to explain anatomy and behavior, not just render a demo
|
||||
- QA already uses `Vitest`, Storybook interaction coverage, and Playwright smoke
|
||||
|
||||
### Dependency constraints
|
||||
|
||||
Current `packages/ui/package.json` has no table model dependency, no date library, and no grid engine.
|
||||
|
||||
That means the first `DataTable` implementation must either:
|
||||
|
||||
- hand-roll row modeling, sorting, selection, and visibility management, or
|
||||
- adopt a focused headless dependency
|
||||
|
||||
Hand-rolling is not recommended for this repo stage.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Use `@tanstack/react-table` internally for the first `DataTable` implementation.
|
||||
|
||||
### Why TanStack Table
|
||||
|
||||
It solves the complex headless data problems we do not want to rediscover:
|
||||
|
||||
- row modeling
|
||||
- sorting state
|
||||
- filtering state
|
||||
- pagination state
|
||||
- selection state
|
||||
- column visibility
|
||||
- stable cell/header modeling
|
||||
|
||||
This matches the repo's principle of using a strong interaction/state base under a source-owned UI layer, the same way Radix underpins overlays and controls.
|
||||
|
||||
### Why not build it from scratch
|
||||
|
||||
Building even a "simple" table pattern from scratch will immediately force the repo to own:
|
||||
|
||||
- sorting semantics
|
||||
- accessor APIs
|
||||
- row identity rules
|
||||
- selection bookkeeping
|
||||
- controlled vs uncontrolled state design
|
||||
- column metadata normalization
|
||||
|
||||
That is too much API design surface for a first table release.
|
||||
|
||||
### Why not expose TanStack directly
|
||||
|
||||
Exposing TanStack types and concepts as the public API would weaken the repo's current direction:
|
||||
|
||||
- it would leak a third-party mental model into consumer code
|
||||
- it would make docs look like a wrapper around someone else's library
|
||||
- it would make future refactors harder
|
||||
|
||||
The public contract should stay `@ai-ui/ui` first. TanStack should remain an implementation detail wherever possible, with any unavoidable type exposure deliberately minimized.
|
||||
|
||||
## Proposed scope: Stage 1 core
|
||||
|
||||
The first shipping slice should cover the operational table use case, not the spreadsheet use case.
|
||||
|
||||
### Must-have behaviors
|
||||
|
||||
- typed columns
|
||||
- sortable columns
|
||||
- optional row selection
|
||||
- optional client-side text filter
|
||||
- empty state rendering
|
||||
- loading state rendering
|
||||
- pagination controls
|
||||
- column-level cell formatting
|
||||
- row-level actions slot
|
||||
|
||||
### Must-have supporting surfaces
|
||||
|
||||
- toolbar for search, filters, and view actions
|
||||
- sticky or visually distinct header treatment
|
||||
- bulk selection affordance when selection is enabled
|
||||
- responsive overflow strategy
|
||||
|
||||
## Proposed public API shape
|
||||
|
||||
The API should be compositional and stable, following the repo's current component contract.
|
||||
|
||||
### Root component
|
||||
|
||||
```tsx
|
||||
<DataTable
|
||||
columns={columns}
|
||||
rows={rows}
|
||||
getRowId={(row) => row.id}
|
||||
loading={loading}
|
||||
empty={<DataTableEmpty ... />}
|
||||
searchValue={search}
|
||||
onSearchValueChange={setSearch}
|
||||
selection={selection}
|
||||
onSelectionChange={setSelection}
|
||||
sorting={sorting}
|
||||
onSortingChange={setSorting}
|
||||
/>
|
||||
```
|
||||
|
||||
### Suggested slot family
|
||||
|
||||
- `DataTable`
|
||||
- `DataTableToolbar`
|
||||
- `DataTableSearch`
|
||||
- `DataTableFilters`
|
||||
- `DataTableContent`
|
||||
- `DataTableTable`
|
||||
- `DataTableHeader`
|
||||
- `DataTableHeaderCell`
|
||||
- `DataTableBody`
|
||||
- `DataTableRow`
|
||||
- `DataTableCell`
|
||||
- `DataTableEmpty`
|
||||
- `DataTableLoading`
|
||||
- `DataTablePagination`
|
||||
- `DataTableSelectionBar`
|
||||
|
||||
This is intentionally a pattern family, not a single monolith.
|
||||
|
||||
### Suggested stable slots
|
||||
|
||||
The following `data-slot` names should exist in the first implementation:
|
||||
|
||||
- `root`
|
||||
- `toolbar`
|
||||
- `input`
|
||||
- `content`
|
||||
- `table`
|
||||
- `header`
|
||||
- `row`
|
||||
- `cell`
|
||||
- `empty`
|
||||
- `pagination`
|
||||
- `actions`
|
||||
|
||||
Additional slot names should only be added when they create a durable styling or testing hook.
|
||||
|
||||
### Suggested stable state surface
|
||||
|
||||
The following public state hooks are likely worth exposing:
|
||||
|
||||
- `data-loading`
|
||||
- `data-empty`
|
||||
- `data-selected`
|
||||
- `data-sort`
|
||||
- `data-density`
|
||||
|
||||
`data-state` should still be used for finite row/header states where that is the clearest representation.
|
||||
|
||||
## Proposed column model
|
||||
|
||||
The public API should accept a narrow column definition owned by this repo, even if it is internally adapted to TanStack.
|
||||
|
||||
Example direction:
|
||||
|
||||
```ts
|
||||
type DataTableColumn<TData> = {
|
||||
id: string;
|
||||
header: ReactNode | ((column: DataTableColumnContext<TData>) => ReactNode);
|
||||
cell: (row: TData) => ReactNode;
|
||||
accessor?: keyof TData | ((row: TData) => unknown);
|
||||
sortable?: boolean;
|
||||
align?: "start" | "center" | "end";
|
||||
width?: number | string;
|
||||
priority?: "primary" | "supporting" | "low";
|
||||
};
|
||||
```
|
||||
|
||||
Key point:
|
||||
|
||||
- keep the first public column shape small
|
||||
- do not surface every TanStack option
|
||||
- do not make users learn a second full configuration language on day one
|
||||
|
||||
## Composition model
|
||||
|
||||
The table should integrate naturally with the patterns the repo already ships.
|
||||
|
||||
### DataTable should compose with
|
||||
|
||||
- `Input` for search
|
||||
- `Select` and `DropdownMenu` for filters and view options
|
||||
- `Checkbox` for row selection
|
||||
- `Button` for primary and secondary actions
|
||||
- `Badge` for row metadata
|
||||
- `Tooltip` for truncated or status-heavy cells
|
||||
- `Popover` for compact detail reveal
|
||||
- `Sheet` for row inspection or bulk edit side panels
|
||||
- `EmptyState` for no-results and first-run moments
|
||||
- `Skeleton` for loading rows
|
||||
|
||||
### DataTable should not try to replace
|
||||
|
||||
- `Sheet` detail workflows
|
||||
- `Dialog` destructive confirmations
|
||||
- `EmptyState` standalone onboarding/empty moments
|
||||
- `Form` validation and edit flows
|
||||
|
||||
The table is the list surface. Other patterns handle adjacent work.
|
||||
|
||||
## Accessibility requirements
|
||||
|
||||
The first release should treat accessibility as a primary contract, not a follow-up.
|
||||
|
||||
### Baseline requirements
|
||||
|
||||
- semantic table structure for the standard grid use case
|
||||
- keyboard reachable sort controls
|
||||
- keyboard reachable row selection controls
|
||||
- visible focus styling on row actions and interactive header controls
|
||||
- accessible labeling for search, filters, and pagination controls
|
||||
- empty and loading states that remain understandable to screen readers
|
||||
- no motion dependency for critical state changes
|
||||
|
||||
### First-release accessibility decisions
|
||||
|
||||
- Prefer semantic HTML table markup for the default implementation
|
||||
- Do not start with `role="grid"` unless we truly need spreadsheet-like keyboard control
|
||||
- Treat sorting controls as buttons inside header cells
|
||||
- Keep row selection explicit with `Checkbox`, not hidden click-to-select rows
|
||||
|
||||
This fits the repo's current accessibility posture better than jumping straight to a complex grid interaction model.
|
||||
|
||||
## QA expectations
|
||||
|
||||
The table should meet the repo's current QA bar from the first release.
|
||||
|
||||
### Unit and interaction coverage
|
||||
|
||||
Minimum expected coverage:
|
||||
|
||||
- renders header, rows, and cells with stable slots
|
||||
- sortable columns update controlled and uncontrolled sort state
|
||||
- selection toggles update row state and bulk action surface
|
||||
- loading state renders skeleton or loading rows
|
||||
- empty state renders when no rows remain after filtering
|
||||
- search/filter plumbing updates the rendered row set
|
||||
- pagination changes page and respects bounds
|
||||
|
||||
### Storybook coverage
|
||||
|
||||
Minimum story recipe:
|
||||
|
||||
- `Playground`
|
||||
- `States`
|
||||
- `Anatomy`
|
||||
- `Accessibility`
|
||||
|
||||
Likely additional stories:
|
||||
|
||||
- `Selection`
|
||||
- `Empty and Loading`
|
||||
|
||||
### Playwright smoke
|
||||
|
||||
At least one smoke scenario should cover:
|
||||
|
||||
- table renders
|
||||
- search narrows rows
|
||||
- sorting changes row order
|
||||
- selecting rows reveals a bulk-action affordance
|
||||
- opening row detail in `Sheet` still works
|
||||
|
||||
## Release plan
|
||||
|
||||
### Stage 0: RFC and composition rehearsal
|
||||
|
||||
- finalize API direction in this RFC
|
||||
- build one realistic docs composition page that uses a table-like operational surface
|
||||
- identify missing supporting primitives before table code starts
|
||||
|
||||
### Stage 1: Headless core
|
||||
|
||||
Ship a narrow `DataTable` that supports:
|
||||
|
||||
- typed columns
|
||||
- sorting
|
||||
- selection
|
||||
- search
|
||||
- pagination
|
||||
- loading and empty states
|
||||
|
||||
No virtualization, resizing, pinning, or editing.
|
||||
|
||||
### Stage 2: Workflow integration
|
||||
|
||||
Add only after Stage 1 stabilizes:
|
||||
|
||||
- row action menu patterns
|
||||
- bulk action bar
|
||||
- column visibility menu
|
||||
- row details opening in `Sheet`
|
||||
|
||||
### Stage 3: Scale features
|
||||
|
||||
Only if justified by real product usage:
|
||||
|
||||
- server-driven pagination hooks
|
||||
- column pinning
|
||||
- virtualization
|
||||
- advanced filters
|
||||
|
||||
## Likely file layout
|
||||
|
||||
When implementation begins, the initial table slice should probably live in:
|
||||
|
||||
```txt
|
||||
packages/ui/src/components/data-table.tsx
|
||||
packages/ui/src/components/data-table.variants.ts
|
||||
packages/ui/src/components/data-table.test.tsx
|
||||
apps/docs/src/components/data-table.stories.tsx
|
||||
```
|
||||
|
||||
If the implementation grows into a larger family, the repo may eventually want a dedicated component folder, but the first slice should stay consistent with the current flat component layout.
|
||||
|
||||
## Dependency recommendation
|
||||
|
||||
### Recommended
|
||||
|
||||
- add `@tanstack/react-table` to `packages/ui`
|
||||
|
||||
### Not recommended for the first slice
|
||||
|
||||
- virtualization package
|
||||
- drag and drop package
|
||||
- date package for table filters
|
||||
- full export/import package support
|
||||
|
||||
The first implementation should minimize dependency expansion and keep the complexity budget focused on a single new capability.
|
||||
|
||||
## Open questions
|
||||
|
||||
1. Should the public column type stay fully source-owned, or should the first release expose a thin alias around TanStack column defs?
|
||||
2. Should the first release include client-side search/filter state inside `DataTable`, or should filtering remain externally controlled?
|
||||
3. Do we want a density variant in the first release, or should row height stay fixed until real product usage appears?
|
||||
4. Should pagination UI be part of the root component family, or remain a separate companion component?
|
||||
5. Should row selection be opt-in only, or should the root always reserve structure for it?
|
||||
|
||||
## Decision
|
||||
|
||||
The next `DataTable` implementation should:
|
||||
|
||||
- be built as an advanced pattern, not a primitive
|
||||
- be source-owned at the public API layer
|
||||
- use TanStack Table internally from the first implementation
|
||||
- ship as a narrow, headless/core-first operational table
|
||||
- defer scale features until real usage proves they are necessary
|
||||
Reference in New Issue
Block a user