Skip to main content
@stralos-co-nz/ui exports RowMenu, the kebab that carries a row’s actions in DataTable and the scheduler’s resource grid. Every row action surface in the suite — rowActions, sectionActions, and the scheduler’s per-resource menu — is built from it.

Props

Each RowMenuItem accepts: Use instead of the seven row-action vocabularies in the estate: a View link, an Edit button, Open, Open plus a separate kebab, two inline buttons, or nothing at all.

How activation works (from 0.34.0)

Each item’s onSelect handler is wired to the item’s own onClick event. This replaces the previous wiring through Ark’s Menu.Root onSelect, which dropped activations silently when the menu machine had no highlighted item. The old path failed in three cases that look unrelated in tests and in production:
  • Assistive technology — an activate gesture (Space, Enter, or a pointer click) with no prior hover over the item left the machine’s highlightedValue as null. The menu closed; the handler was not called.
  • Programmatic activation — any click() or fireEvent.click() that arrived without a preceding pointermove at distinct coordinates hit the same gap.
  • Later selections in a test file — @zag-js/focus-visible stores lastPointerPosition at module scope. In happy-dom, where every pointer event reports (0, 0), only the first pointermove in a module updated the interaction modality. Every later pointer event was treated as stationary, so the modality reverted to "virtual" when the menu opened and highlighted nothing.
From 0.34.0, RowMenu and the scheduler’s resource menu wire each handler directly to the item’s onClick. The keyboard path is unchanged — Ark activates the highlighted item by calling element.click() on it, which fires onClick. A disabled item is now stopped by RowMenu’s own guard rather than relying on Ark refusing to emit, so the disabled prop is load-bearing.

Testing a RowMenu (from 0.34.0)

Open the trigger, then click the item. fireEvent is sufficient — @testing-library/user-event is not required, and the pattern works any number of times in one test file.
userEvent.click also works. The keyboard path is {ArrowDown}{Enter} after opening the trigger. Assert toHaveBeenCalledTimes(1), not toHaveBeenCalled(). The failure mode this replaced was a silent zero; a future rewiring could produce a two. Both are invisible to toHaveBeenCalled. Add a positive control alongside any “handler did not fire” assertion. A click that reaches nothing satisfies every negative assertion. Without a positive control you cannot distinguish a correctly blocked action from a broken gesture.

Migrating older tests

Tests written against 0.33.0 and earlier used workarounds: keyboard-only activation, a single selection per test file, or a full pointerDown/pointerUp/click sequence. Those workarounds continue to pass — the fix does not change what the workaround gestures do. Remove them when you are already editing the test for an unrelated reason; do not leave them as the sole form of coverage.

Accessible name

DataTable passes a label derived from the row. The label comes from getRowLabel when supplied. When getRowLabel is not set, the label comes from the first column’s rendered text if that column renders a plain string, and falls back to getRowId otherwise. Pass getRowLabel whenever the first column renders a link, a badge, or anything that is not plain text.

Use in DataTable

DataTable creates the kebab column automatically when rowActions is set. The column is shared with the columnPicker slot; DataTable creates it even when only one of the two is present.
Section actions work the same way through sectionActions. The section kebab is named after the section label, falling back to section <id>.