Skip to content

feat: add composable low-level table components (TableRoot, TableHead, TableRow, TableCell, and more) - #4977

Draft
gethinwebster wants to merge 2 commits into
mainfrom
dev-v3-gethinw-table-cell-extract
Draft

feat: add composable low-level table components (TableRoot, TableHead, TableRow, TableCell, and more)#4977
gethinwebster wants to merge 2 commits into
mainfrom
dev-v3-gethinw-table-cell-extract

Conversation

@gethinwebster

@gethinwebster gethinwebster commented Sep 7, 2026

Copy link
Copy Markdown
Member

What this adds

A set of low-level, composable table components — TableRoot, TableHead, TableHeaderRow, TableHeaderCell, TableBody, TableRow, and TableCell. They let a consumer assemble a table from primitives, as a lower-level alternative to the existing high-level Table component.

How they are built

Rather than reimplementing table cell and row markup and styling from scratch — which would risk drifting visually from the existing Table — these components are built on the same internal building blocks the existing Table already uses for its own cells and rows. The existing Table is refactored to compose those same building blocks.

The result is a single shared implementation of the cell/row box model and its visual states (selection, shading, dividers, spacing), instead of two implementations that could diverge over time.

Effect on the existing Table

The existing Table renders exactly as before. The refactor only changes which internal pieces it composes, not the markup or styles it produces. The new components' additional styling is keyed on markers the existing Table never sets, so the shared styles are reused (not duplicated) and stay inert for the existing Table.

Verification

  • The existing Table is confirmed visually unchanged across selection, striping, sticky columns, inline editing, loading/empty, footer, and grouped-header states.
  • The new components are checked for visual and behavioural parity with the equivalent existing-Table output — including selection-control alignment and the absence of any content shift when a row's selection is toggled.
  • Full build, linting, unit tests, and the generated API / test-utils / documentation artifacts all pass.

Notes for reviewers

  • This builds on the recently merged change that deduplicated table cell spacing and moved first/last-row edge borders to CSS, so shared spacing lives in one place.
  • The change is organized as two commits: one adds the new components (with demo pages and tests), and one refactors the existing Table to build on the shared internals.

@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.97959% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.67%. Comparing base (711f084) to head (8658af9).

Files with missing lines Patch % Lines
src/table-root/internal.tsx 92.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #4977    +/-   ##
========================================
  Coverage   97.66%   97.67%            
========================================
  Files         960      985    +25     
  Lines       31368    31551   +183     
  Branches    11592    11638    +46     
========================================
+ Hits        30637    30817   +180     
- Misses        724      727     +3     
  Partials        7        7            

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

ARIA counts and multi-sort semantics are incorrect, capped flex sizing drops weights, and the PR title violates the required format.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

src/table-root/internal.tsx:43

  • aria-rowcount counts every row in the accessibility tree, including the header. This API describes ariaRowcount as the number of data rows, and TableRow.ariaRowindex starts data rows at 2, so forwarding the value unchanged makes the last virtualized row's index exceed the declared count (the demo declares 10,000 but reaches row index 10,001). Add the header row when emitting the attribute, as the existing Table does in src/table/table-role/table-role-helper.ts:36-40, and update the assertions accordingly.
    src/table-root/use-table-root.ts:27
  • Supplying maxWidth silently discards a flexible column's size.flex: for example, flex weights 1 and 2 both compile to the same minmax(..., 300px) track. This contradicts the documented proportional size behavior while the public type permits the combination. Either preserve the weight in the capped-track implementation or make maxWidth and size.flex mutually exclusive in the type and documentation.
  • Files reviewed: 61/61 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread pages/table-root/sorting.page.tsx Outdated
Comment thread pages/table-root/selection.page.tsx Outdated
@gethinwebster gethinwebster changed the title feat(table): add composable low-level table components (TableRoot, TableHead, TableRow, TableCell, …) feat: add composable low-level table components (TableRoot, TableHead, TableRow, TableCell, and more) Sep 11, 2026
@gethinwebster
gethinwebster force-pushed the dev-v3-gethinw-table-cell-extract branch from 7a55898 to f9668ba Compare September 11, 2026 09:38
@gethinwebster
gethinwebster requested a balanced review from Copilot September 11, 2026 09:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shared context leaks into nested high-level tables, and auto-layout edge rows can shift when selection changes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 62/62 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment on lines +53 to +55
const { columnLayout } = useTableContext();
const variant = useRowVariant();
const isVisualRefresh = useVisualRefresh();
Comment thread src/table-cell/styles.scss
Comment on lines +30 to +33
const { columnLayout } = useTableContext();
const isVisualRefresh = useVisualRefresh();
const isGrid = columnLayout.type === 'grid';
const mergedNativeAttributes = isGrid ? { ...nativeAttributes, role: 'columnheader' as const } : nativeAttributes;

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The scroll viewport is not keyboard-accessible, and selected-row outlines are incorrect when grid tracks overflow.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 62/62 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/table-root/internal.tsx Outdated
Comment thread src/table-row/styles.scss

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Grid rows can lose column alignment, and reserved variant attributes can be overridden by consumer data attributes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 62/62 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread src/table-row/internal.tsx Outdated
Comment on lines +32 to +33
const selectedDataAttribute = variant === 'selected' ? { 'data-variant-selected': 'true' } : undefined;
const shadedDataAttribute = variant === 'shaded' ? { 'data-variant-shaded': 'true' } : undefined;
Comment thread src/table-row/styles.scss Outdated
Comment on lines +21 to +22
min-inline-size: 100%;
inline-size: max-content;
Comment on lines +51 to +55
const scrollRegionProps = isScrollable
? {
role: ariaLabel || ariaLabelledby ? ('region' as const) : undefined,
tabIndex: 0,
'aria-label': ariaLabel,
…aderCell/Body/Row/Cell)

Public atomic table components built on internal substrates extracted from the
existing Table, plus the shared cell-base geometry partial, demo pages, unit
tests, and generated test-utils/documenter snapshots.
…ell dedup

Delegate the existing Table's td/th elements to InternalTableCell/
InternalTableHeaderCell (atomic-default-on, with disableContentWrapper/
disableDivider/suppressBlock*Placeholder opt-outs for the classic path),
extract shared cell geometry into the cell-base partial, migrate first/last-row
edges to positional CSS, and fix atomic-grid selection-control centering. Also
regenerate the generated-artifact snapshots and fix the test-utils finder
pluralization for the new components.
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