Repository navigation
feat(data-table): add cell and header props customization - #631
Conversation
Code Metrics Report
Details | | main (eee5b85) | #631 (b84c9af) | +/- |
|---------------------|----------------|----------------|-------|
+ | Coverage | 89.5% | 89.5% | +0.0% |
| Files | 236 | 236 | 0 |
| Lines | 7512 | 7517 | +5 |
+ | Covered | 6727 | 6732 | +5 |
| Test Execution Time | 3m29s | 3m29s | 0s |Code coverage of files in pull request scope (93.7% → 93.8%, patch 100.0%)
Reported by octocov |
|
I would like to rebase this PR and cover |
|
@copilot resolve the merge conflicts in this pull request |
…eader-props # Conflicts: # docs-manifest.json # docs-src/components/data-table.docs.examples.tsx # docs-src/components/data-table.docs.outline.md # docs/components/data-table.md # packages/core/src/components/data-table/types.ts Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged |
|
/review |
|
🚀 Code Review has started processing this issue comment |
There was a problem hiding this comment.
🟢 Approval recommended
The implementation, tests, and documentation consistently support the callback APIs; only a non-blocking PR-description correction remains.
1 open issue
What changed in this PR
Adds DataTable column customization for surrounding body, header, and footer elements, allowing full-cell styling without replacing content renderers.
Changes:
- Adds
cellProps(row),headerProps(ctx), andfooterProps(ctx)callbacks supporting classes, inline styles, and titles. - Adds regression coverage for styling, property removal, pinning, width precedence, and existing interactions.
- Documents the APIs with runnable examples and includes a minor-release changeset.
| File | Description |
|---|---|
packages/core/src/components/data-table/types.ts |
Defines the three customization callbacks and their contracts. |
packages/core/src/components/data-table/data-table.tsx |
Applies custom props to body, header, and footer cells. |
packages/core/src/components/data-table/data-table.test.tsx |
Covers header sort context and preserved interactions. |
packages/core/src/components/data-table/data-table-styling.test.tsx |
Adds styling, pinning, and lifecycle regression coverage. |
docs/components/data-table.md |
Updates generated API documentation and examples. |
docs-src/components/data-table.docs.outline.md |
Documents customization boundaries and behavior. |
docs-src/components/data-table.docs.examples.tsx |
Adds full-cell styling and styled-footer examples. |
docs-manifest.json |
Updates documentation generation hashes. |
.changeset/bright-cells-shine.md |
Records the new APIs for a minor release. |
Review effort: Balanced
|
@copilot resolve the merge conflicts in this pull request |
…eader-props # Conflicts: # docs-manifest.json # docs-src/components/data-table.docs.examples.tsx # docs-src/components/data-table.docs.outline.md # docs/components/data-table.md Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged the latest |
|
@copilot resolve the merge conflicts in this pull request |
…eader-props # Conflicts: # docs-manifest.json Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Merged the latest |
interacsean
left a comment
There was a problem hiding this comment.
Left one loose thought comment, but I think let's go!
|
|
||
| Use `cellProps(row)` to set `className`, `style`, and `title` on the actual data-cell `<td>`, including its padding. Return `undefined` to keep the defaults. This works with built-in renderers, custom `render`, truncation tooltips, and editable cells. It does not customize headers, footers, loading placeholders, empty/error states, or the built-in selection, expansion, and action cells. Content stays owned by `render`; events, refs, and cell spanning are not supported through `cellProps`. | ||
|
|
||
| An explicit `style.backgroundColor` stays visible during row hover and selection, for both scrollable and pinned columns. Cells returning `undefined` retain the default hover/selection backgrounds; checkboxes and row selection semantics are unchanged. Classes are merged after built-in classes, but CSS specificity still applies: pinned cells have their own group-hover and group-aria-selected backgrounds, which a plain background class does not override. Use `style.backgroundColor` when an informational background must remain visible in every state. Keep the value or another non-color cue visible so color is not the only way to understand the cell. |
There was a problem hiding this comment.
Maybe something for the future: it seems there is no way to set the backgroundColor of a hover or selected state cell
There was a problem hiding this comment.
This is possible with the existing cellProps.className hook. For example, with Tailwind v4:
cellProps: () => ({
className: `
bg-orange-50!
[tr:not([aria-selected=true]):hover_&]:bg-orange-200!
[tr[aria-selected=true]_&]:bg-orange-300!
`,
}),This uses a light background normally, a darker one when hovering an unselected row, and another background while selected. Selection takes precedence even when hovering.
The arbitrary variants target the parent row’s state, and the ! modifiers override the built-in pinned-cell backgrounds.
The inline backgroundColor example in this doc is intended to preserve the same background across states. For state-specific backgrounds, they can be defined entirely in className instead—no separate CSS file or additional API is needed.
There was a problem hiding this comment.
Well my thoughts are while this is some handy CSS work, it is not ergonomic, it's not guided so if a consumer needed to do it, it would be on the dev/agent to work out how, and the aria-selected on the tr relies on something the appshell is doing internally.
Let's just leave it for now anyway

Motivation
DataTable custom renderers control a cell's content, but cannot style the surrounding cell element. Tables that use background color to communicate row-dependent information cannot reproduce that presentation with a colored span: the cell's padding remains unpainted. Header renderers have the same limitation when a column needs header-level styling.
Related: https://github.com/tailor-inc/platform-planning/issues/2185
Design Decision
Separate content rendering from element customization
Add three column-level callbacks: row-aware
cellProps(row)for data-cell<td>elements,headerProps(ctx)for the column's header<th>, andfooterProps(ctx)for its footer<td>. All three support onlyclassName,style, andtitle, and may returnundefinedto keep the defaults.Existing
render(row),header(ctx), andfootercontinue to own content.headerPropsreceives the sameHeaderRenderContextasheader, allowing sort-dependent styling.footerPropsreceives the sameFooterRenderContextas the footer renderer, including the current page's rows; it does not create a footer row by itself. Arbitrary events, refs, and cell spanning are deliberately not exposed, so consumers do not need to coordinate with DataTable's editing, selection, or context-menu behavior.Preserve table-owned layout and state behavior
Merge consumer classes after built-in classes, and merge inline styles before the declared column width and computed sticky positioning. Explicit inline cell backgrounds remain visible during hover and selection; cells that do not opt in retain their existing appearance and behavior. CSS specificity still applies to class-based backgrounds, and pinned body cells, headers, and footer cells require opaque backgrounds to hide scrolling content.
Alternatives considered
Allowing renderers to replace the entire cell element would transfer responsibility for pinning and interaction props to consumers. A style-only hook would solve the immediate background-color case but introduce a separate API for each additional presentation attribute. A narrowly scoped props object provides a single customization point without opening arbitrary DOM overrides.
Summary
cellProps(row), context-awareheaderProps(ctx), andfooterProps(ctx)across default and custom rendering paths, including truncated and editable body cells.