docs(data-table): propose reworked Toolbar layout API - #451
Conversation
Documents a rewritten `DataTable.Toolbar` surface as though it ships today, so the team can review the API by reading it rather than by reading a diff: - `showFilters` / `showColumnSettings` for default placement, with `DataTable.Filters` / `DataTable.ColumnSettings` as the placement escape hatches, under one rule: boolean prop = default, sub-component = custom. - `DataTable.ToolbarRow` for horizontal rows and multi-row toolbars, with an `endSection` for right-aligned content, plus `DataTable.Separator`. - `columnSettings` deprecated in favour of `showColumnSettings`. - Wrapping bare children in an implicit row — the flex-col to flex-row default flip — is deferred to the next major and called out inline. Open questions are kept in the doc for review and come out before any code lands. No implementation yet. Refs: tailor-inc/platform-planning#1699 Refs: tailor-professional-service/knowledge#350
| </DataTable.Root> | ||
| ``` | ||
|
|
||
| Add your own content as children. It sits alongside the built-in controls: |
There was a problem hiding this comment.
Should specify that children are left-aligned
|
|
||
| ## Rows | ||
|
|
||
| `DataTable.ToolbarRow` lays its children out horizontally with a gap sized for comfortable tap targets. Use it when the toolbar needs more than one line: |
There was a problem hiding this comment.
more than one line" +
"or to lay out children in one row horizontally with default spacing" (or something)
| | -------------------- | ----------- | ------- | --------------------------------------------------------------------------------------------------------------- | | ||
| | `children` | `ReactNode` | — | Toolbar content. Use `DataTable.ToolbarRow` for multi-row layouts. | | ||
| | `showFilters` | `boolean` | `false` | Render the **Add filter** trigger and active chips in their default position. Requires `control`. | | ||
| | `showColumnSettings` | `boolean` | `false` | Render the **Columns** control (show/hide + reorder + pin) anchored top-right. Persists per-user via `tableId`. | |
There was a problem hiding this comment.
Make minor reference to columnSettings prop here, and refer to deprecations below
|
|
||
| `DataTable.ToolbarRow` is the only element that sets a direction: it is a horizontal flex row with `gap` spacing and vertically centred items. | ||
|
|
||
| Children passed directly to `DataTable.Toolbar` — without a `ToolbarRow` — currently stack vertically and stretch to full width. This is why `DataTable.Filters` renders on its own line in the split-slot recipe: |
There was a problem hiding this comment.
Let's add row: boolean for ergonomics, and this would wrap children in ToolbarRow, plus add col: boolean for when we make row (and wrapping) the default: col would then be the opt-out of the default wrapping
|
|
||
| | Prop | Type | Default | Description | | ||
| | ----------- | -------- | ------- | ------------------------------------- | | ||
| | `className` | `string` | — | Additional CSS class for the control. | |
There was a problem hiding this comment.
Passing in these classNames, I think we don't want to mix the internal aswt:* prefixed classNames with classes that consumers pass in, because they don't merge well and end up unreliably resolving based on the stylesheet import order... I think
So, maybe if we want this, the implementation must mean that these classNames get dropped onto a wrapping div element (that is always there and wrapping) to allow this (it would have no astw classes on it)
- State that toolbar children are left-aligned. - Broaden ToolbarRow's stated purpose to cover single-row horizontal layout, not just multi-row toolbars. - Point `showColumnSettings` at the `columnSettings` deprecation inline. - Add `row` / `col` on `DataTable.Toolbar`: `row` wraps children in a single ToolbarRow today, `col` is the no-op-today opt-out that survives the flip when row becomes the default. Both give a forward-compatible migration. - Document the `className` contract: classes land on a wrapper carrying no app-shell classes, so they never compete with `astw:` internals or depend on stylesheet order. Internal layout stays prop-driven, since a wrapper can't reach inside. - Rework the styling open question around that contract. Refs: tailor-inc/platform-planning#1699
- Say explicitly that `row` is skipped when any child is a ToolbarRow, including the all-children-are-rows case (the ordinary multi-row toolbar). - Make `row` / `col` mutually exclusive in the type via a discriminated union, with a runtime warning as the backstop for spreads and JS callers. - Spell out which CSS a wrapper can and cannot reach: box and inherited properties yes, the inner element's flex-direction / gap / align-items no. - Note that `data-slot` stays on the inner elements, so CSS already written against [data-slot="data-table-toolbar"] keeps matching. Refs: tailor-inc/platform-planning#1699
Replace the `row` / `col` booleans with `direction?: "row" | "col"`. One enum is mutually exclusive by construction — no `row col` to diagnose, and no union whose "no overload matches" error says nothing about the actual mistake. Scope the className wrapper by job rather than applying it everywhere: - Placed components (`Filters`, `ColumnSettings`, `Separator`) get a wrapper. They are dropped into a layout the consumer wrote, so positioning and sizing is the need, and a bare wrapper serves it deterministically. It also makes `Separator className="h-6"` work, since the rule itself is `h-full`. - Containers (`Toolbar`, `ToolbarRow`) merge onto the container. The consumer already owns the element these sit in, so a wrapper adds a DOM node without adding capability. - Toolbar's own padding / border-bottom stay honestly documented as unreliable to override, rather than papered over. Also correct the reasoning in the open question: configuring a twMerge prefix cannot fix cross-boundary conflicts. `cn()` dedupes within one class string, so an unprefixed `gap-4` and `astw:gap-2` are unrelated classes that both survive and are resolved by source order either way. Refs: tailor-inc/platform-planning#1699
Issue
tailor-inc/platform-planning#1699 — from tailor-professional-service/knowledge#350
What
Docs only. No implementation, no behaviour change. This proposes a reworked
DataTable.ToolbarAPI by writing it as though it already ships, so it can be reviewed by reading the documentation rather than a design bullet list.docs/components/data-table-toolbar.md.docs/components/data-table.mdupdated so the two don't contradict — sub-components table, Toolbar props section replaced by a pointer,columnSettings→showColumnSettingsin the two places it appeared.The proposed shape
showFilters,showColumnSettingsDataTable.Filters,DataTable.ColumnSettingsDataTable.ToolbarRow(+endSection,gap)DataTable.SeparatorOne rule ties it together: boolean prop = default placement, sub-component = custom placement, never both for the same control.
columnSettingsis deprecated in favour ofshowColumnSettingsand removed at the next major.Why
DataTable.Toolbarrenders a row at its root but wrapschildrenin a hardcodedastw:flex-coldiv, so multiple children stack vertically and stretch full width.classNamecan't fix it — it lands on the root, not the wrapper — andcn()is baretwMerge(clsx(...))with noastw:prefix configured, so prefixed utilities don't resolve conflicts either way. Consumers currently reach for global CSS against[data-slot="data-table-toolbar"].Reported against 1.8.0; still reproduces on 1.11.0.
The default flip is deferred
Wrapping bare children in an implicit
ToolbarRow— the flex-col → flex-row change — is held for the next major and called out inline in the doc, with a forward-compatible migration consumers can do today.Worth knowing for review: the current column default is not purely legacy. The
slot="add"/slot="chips"recipe indata-table.mddeliberately relies on stacking so filter chips wrap onto their own line. 7 of 8DataTable.Toolbarusages in this repo pass a single child and are unaffected by a flip; that recipe is the exception, and it wants column. Column has to stay expressible.Open questions
The doc ends with an Open questions section — sub-component naming, gap props vs. configuring the twMerge prefix,
endSectionprop vs. a nested section sub-component,DataTable.Separatorvs. exporting the existingSeparatorprimitive, and the timing of the implicit-row flip. That section comes out before any code lands.Notes for reviewers
check-catalogue-links.shvalidates those and there's no toolbar slug yet.astw:flex-rowoverride, rendering identically) exists locally and can be added as an example page if that would help review.🤖 Generated with Claude Code