Skip to content

feat(DataGrid): add headless compound shell over createDataGrid - #826

Draft
johnleider wants to merge 5 commits into
devfrom
cursor/datagrid-compound-shell-d904
Draft

feat(DataGrid): add headless compound shell over createDataGrid#826
johnleider wants to merge 5 commits into
devfrom
cursor/datagrid-compound-shell-d904

Conversation

@johnleider

@johnleider johnleider commented Aug 12, 2026

Copy link
Copy Markdown
Member

Summary

Implements the DataGrid compound shell for FR #513, providing structural wrappers over the existing createDataGrid composable.

Changes

Compound Components

  • DataGrid.Root — Context provider wrapping createDataGrid
  • DataGrid.Table — Semantic wrapper with role="grid"
  • DataGrid.Header / DataGrid.Body — Section containers with role="rowgroup"
  • DataGrid.Row — Row container with optional id for ordering/selection
  • DataGrid.Column — Header cell with sorting state and aria-sort
  • DataGrid.Cell — Data cell with editing state and row spanning

Column Resize via Splitter Composition

Column resize composes the shipped Splitter primitives rather than reimplementing drag mechanics:

<Splitter.Root
  orientation="horizontal"
  @layout="sizes => context.layout.distribute(sizes)"
>
  <Splitter.Panel :default-size="50" :min-size="20">
    <!-- Column header content -->
  </Splitter.Panel>
  <Splitter.Handle />
  <Splitter.Panel :default-size="50" :min-size="20">
    <!-- Column header content -->
  </Splitter.Panel>
</Splitter.Root>

Mapping:

Splitter DataGrid
Splitter.Root Wraps header row with orientation="horizontal"
Splitter.Panel One per column header — handles flex-basis sizing
Splitter.Handle Between panels — provides drag + WAI-ARIA role="separator"
@layout event Syncs sizes to context.layout.distribute(sizes)

Other Changes

  • Context injection: useDataGridRoot / provideDataGridRoot, useDataGridRow / provideDataGridRow
  • Maturity: draftpreview, since: null
  • Documentation page with basic and resizable examples
  • Browser tests for rendering, ARIA, slot props, layout API
  • Surface freeze updated
  • Changeset authored

Checklist

  • Directory + barrel (packages/0/src/components/DataGrid/)
  • Maturity updated (preview, since: null)
  • Docs page (apps/docs/src/pages/components/data/data-grid.md)
  • Examples (basic, resizable with Splitter composition)
  • Browser tests (index.browser.test.ts)
  • A11y fixture (fixtures/DataGrid.vue)
  • Surface freeze (surface.test.ts)
  • README sync
  • Changeset

Deferred

  • Row reordering UI (drag handles) — use context.rows.move() API directly
  • Cell editing UI — use context.editing API directly
  • Row spanning UI — use context.spans API directly
  • Pin regions with multiple Splitter roots — current example shows basic resize

Closes #513
Related: #507

Open in Web Open in Cursor 

Implements FR #513 (epic #507) - thin structural shells for building data grids.

Components:
- DataGrid.Root - context provider wrapping createDataGrid
- DataGrid.Table - semantic table with role="grid"
- DataGrid.Header / DataGrid.Body - section containers with role="rowgroup"
- DataGrid.Row - row container with optional id for ordering/selection
- DataGrid.Column - header cell with sorting state and aria-sort
- DataGrid.Cell - data cell with editing state and row spanning

Context injection via useDataGridRoot/provideDataGridRoot and
useDataGridRow/provideDataGridRow trinities.

Follows Avatar/Snackbar compound patterns. Registry surface uses
register/onboard for rows. Maturity promoted draft→preview.

Breaking: removes deprecated DataGridColumn type alias from
createDataGrid exports (use DataGridColumnTicketInput instead)
Adds DataGrid fixture to satisfy the shipped-component gate in
a11y.browser.test.ts. Fixture renders a basic grid with header
columns and data rows using role="grid" semantics.
Row now always provides row context, even when id is undefined.
Cell gracefully handles missing row.id in span/editing lookups.
Fixes browser test failures where Cell was mounted inside Row
without an id prop.
…eraction

Composes the Splitter drag interaction pattern into a new DataGrid.ResizeHandle
component that enables column resizing with real drag handles:

- DataGridResizeHandle.vue: Drag handle using pointer/keyboard interaction
  - Maps resize deltas to context.layout.resize(column, delta)
  - Keyboard support: Arrow keys (1%), Page Up/Down (10%), Home/End (min/max)
  - WAI-ARIA role=separator with valuenow/min/max for accessibility
  - Respects column resizable, minSize, maxSize constraints
  - Conditional rendering via v-if when column.resizable is false

- DataGridColumn.vue: Added layout-aware slot props
  - isResizable, size, minSize, maxSize, offset exposed via slot props
  - Style binding for width percentage

- Barrel exports: Added DataGridResizeHandle and related types
- Surface test: Added DataGridResizeHandle to freeze array

- Browser tests: Added tests for ResizeHandle
  - Rendering, ARIA attributes, slot props
  - Integration test for layout.resize API

- Docs: Updated with resizable columns section, example, and FAQ

Header/Column → Splitter mapping:
- Each DataGrid.Column becomes a logical panel (via layout percentage sizing)
- DataGrid.ResizeHandle is placed between columns like Splitter.Handle
- Horizontal orientation: columns side-by-side, vertical separator handles
- Pin regions use separate size pools (left/scrollable/right)

Closes #513, relates to #507
Removes parallel DataGridResizeHandle implementation and shows
proper composition of shipped Splitter primitives:

- Remove DataGridResizeHandle.vue (mirrored Splitter.Handle mechanics)
- Remove ResizeHandle from barrel exports and surface freeze
- Remove DataGrid.resizeHandle locale key
- Update resizable.vue example to compose Splitter.Root/Panel/Handle
- Update browser tests to test layout API (resize, distribute)
- Update docs to explain Splitter composition pattern

The composition pattern:
- Splitter.Root wraps header row with orientation="horizontal"
- Splitter.Panel for each column header (handles flex-basis sizing)
- Splitter.Handle between panels (provides drag + WAI-ARIA separator)
- @layout event syncs Splitter sizes to context.layout.distribute(sizes)
- Body cells read widths from context.layout.columns

Closes #513
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants