Handoff: Logs table with divider (recipe)
What and why
A recipe, not a component: a story and Usage page that compose the published PDS Table (the Logs Table pattern) with a LabeledDivider as one row. In a runtime or edge log it marks a point in time (a day change) and lets the reader jump to another one, through the divider's time menu and date picker. PDS has the Logs Table pattern but no way to put a labeled break in it. The recipe has no API of its own, so there is nothing to port beyond the components it uses (below); the engineer copies the composition. The pattern's CSS is copied from the reference checkout because the published package does not ship it.
- Figma / Jira: none. Source: Erika, 2026-10-16, "pull in the Logs Table PDS and add a divider there as an example".
- Context:
context.md
Exploration and decisions
- Recipe, with the pattern copied in. The Logs Table is a pattern in the PDS source, not a published component, so
logs-table.cssis copied unchanged frompatterns/tables/logs-table/and the story is a trimmed copy of the pattern's Default story (8 entries, a shorter detail panel) plus one divider row. - The divider is a row, stretched across the cells.
Tableis data-driven and cannot span columns. Two tables would lose the sticky header, the scroll area and Prev/Next in the detail panel. So the divider is a row whose first cell holds theLabeledDividerand CSS stretches it across the row. - Newest first, as in the pattern, so entries above the divider are newer. The label shows the day the entries below belong to.
- The divider is the time-jump selector, not the agentic divider (Erika, 2026-10-16). "Zappy engaged" belongs in the Event log with agent recipe. The agentic divider would work the same way in a row.
Full list and reasons: decisions.md.
What it composes and how
No API. The composition (story: LogsTableWithDivider.stories.tsx):
Table(published) inside adiv.pds-logs-table, withhasPanel,hasDesktopDetailPanel,aria-label,columns(Time, Level, Environment, Message),detailPanelTitle,maxHeightandminHeightof30rem,getRowId,prevDetailPanelRowLabel,nextDetailPanelRowLabel,renderDetailPanelandrowData.getRowClassNamegives status rowspds-table__row--criticalorpds-table__row--warning, and the divider rowlogs-table-example__divider-row.- The divider row is one more entry in
rowData, placed between the newer and older entries. Itstimestampcell holds aLabeledDividerwithsize='xs',label(the day),menuItems(a "Jump to..." heading, then Today, Yesterday, Last week, Last month, The very beginning) anddatePicker(maxDate,menuLabel,modalTitle,onChange,pickerLabel). The other cells are empty strings. renderDetailPanelreturnsnullfor the divider row, and adl(Timestamp, Level, Source) for entries.- Event handling: the divider gets
onClickandonKeyDownhandlers that callstopPropagation, so using the menu or the date picker modal does not open the row's detail panel. The menu and modal render in portals, but their events still bubble through the React tree. - CSS (
logs-table-divider.css, story-only): the divider row getsposition: relativeandpointer-events: none; its first cell getspadding-block: var(--pds-spacing-xl); the divider in it isposition: absolute, full row width (inset-inline: 0), vertically centered, withpointer-events: auto.
What the screen owns: the entries and their statuses, the divider's position (which day it marks), its label, the menu items and what each does (the callbacks and onChange are empty in the story), the column set, the detail panel content, and any filtering or loading that follows a jump.
Tokens used
Story-only CSS; nothing here ships in the package. From npm run check:tokens -- --report LogsTableWithDivider:
--pds-border-width-default--pds-color-badge-critical-muted-background--pds-color-badge-critical-muted-foreground--pds-color-border-default--pds-color-foreground-default--pds-color-foreground-default-secondary--pds-color-status-critical-foreground--pds-color-status-warning-foreground--pds-spacing-2xs--pds-spacing-3xs--pds-spacing-4xs--pds-spacing-m--pds-spacing-xl--pds-spacing-xs--pds-typography-fw-medium--pds-typography-fw-semibold--pds-typography-size-s--pds-typography-size-xs
No custom CSS variables and no raw values kept. npm run check:tokens reports 0 problems.
Impact
From impact.md: nothing reaches existing components. Everything it adds is scoped to its own classes, it is opt-in, and it redefines no --pds-* token. No other proposal imports or uses it. It depends on the proposals below, and changes to them change what the story shows.
Reused vs net-new
- Reused: the published Table (and its
hasPanel, row status classes and detail panel), and the LabeledDivider proposal (../LabeledDivider/). Through LabeledDivider it also depends on the Button, Icon Button, Menu Button, Icon and Spinner proposals and Agent Avatar (fromnode scripts/port-order.mjs --needs=LogsTableWithDivider). - Net-new: no components, props or custom variables. Classes copied from the PDS Logs Table pattern (not in the published package):
.pds-logs-table(with__footer,__footer-controls) and.pds-logs-detail__section(with__badge,__label,__value,__value--critical,__value--warning). Story-only:.logs-table-example__divider-row. Details incustom.md.
Proposals it composes and their readiness: LabeledDivider is ready for dev, so port it with this recipe (it needs the Agentic Menu Button and the Menu Button, see docs/PORT-ORDER.md). Check the others (Agent Avatar, Button, Icon, Icon Button, Menu Button, Spinner) against their own HANDOFF.md and the ready tags before porting; this handoff does not assert their status.
Accessibility
- a11y panel: not run. Light and dark: not checked by eye.
- What the code does: the
Tablehas anaria-label("Application logs"), and the Prev/Next detail panel buttons carry labels. The divider's menu and date picker have their own labels (menuLabel,modalTitle,pickerLabel). Keyboard and focus come fromTableandLabeledDivider. - Known gap: the divider row is still in the table's row list, so it has
tabIndex=0. It can take keyboard focus, Enter or Space opens an empty detail panel, and Prev/Next steps onto it. The divider stops its own clicks and keys from reaching the row, but the row itself is not skipped. Mobile cards are not handled.
Decisions and open questions
- Does
Tableneed a first-class separator row (spans the columns, not inspectable, skipped by Prev/Next, sort and selection)? Owner: Table maintainers. The row workaround above is only fit for the recipe; raise this with the Table owners before porting, since it is also the fix for the focus gap. - Should the divider show a time (for example the day)? The label is the screen's.
- LabeledDivider has to land with or before this recipe (its own handoff is part of this release).
Port notes
- Nothing to generate and nothing to copy into
pds-toolkit-reactfor the recipe itself. Port the components it uses first (LabeledDivider and its dependencies); the engineer then copies the composition, and the story and Usage page stay as documentation. logs-table.cssalready lives in PDS as the Logs Table pattern;logs-table-divider.cssis the only recipe-specific CSS and exists to stretch the divider across the row. A separator row inTablewould replace it.
Preview
npm run storybook, then Recipes/Logs table with divider (story Default). Figma: none.