Components · Tables and data grid
BulkActionBar
Toolbar shown while items are selected: count, actions with an overflow menu, clear selection and a polite announcement.
With DataTable selection
Name | Team | Role | |
|---|---|---|---|
| Ana Silva | Support | Agent | |
| Ben Okafor | Support | Lead | |
| Chen Wei | Billing | Agent | |
| Dev Patel | Sales | Manager |
Pick an action.
import { useState } from 'react';
import { BulkActionBar, DataTable, type DataTableColumn } from '@rookcom/monarch-react/data';
interface Member {
id: string;
name: string;
team: string;
role: string;
}
const MEMBERS: Member[] = [
{ id: 'm1', name: 'Ana Silva', team: 'Support', role: 'Agent' },
{ id: 'm2', name: 'Ben Okafor', team: 'Support', role: 'Lead' },
{ id: 'm3', name: 'Chen Wei', team: 'Billing', role: 'Agent' },
{ id: 'm4', name: 'Dev Patel', team: 'Sales', role: 'Manager' },
];
const COLUMNS: DataTableColumn<Member>[] = [
{ id: 'name', header: 'Name' },
{ id: 'team', header: 'Team' },
{ id: 'role', header: 'Role' },
];
function WithTable() {
const [selected, setSelected] = useState<string[]>(['m1', 'm3']);
const [log, setLog] = useState('');
const act = (what: string) => () => setLog(`${what}: ${selected.length} members`);
return (
<div style={{ display: 'grid', gap: 8 }}>
<BulkActionBar
count={selected.length}
totalCount={MEMBERS.length}
onClear={() => setSelected([])}
actions={[
{ id: 'team', label: 'Move to team', onSelect: act('Moved') },
{ id: 'role', label: 'Change role', onSelect: act('Role changed') },
{ id: 'export', label: 'Export', onSelect: act('Exported') },
{ id: 'remove', label: 'Remove', tone: 'danger', onSelect: act('Removed') },
]}
/>
<DataTable
columns={COLUMNS}
rows={MEMBERS}
getRowId={(r) => r.id}
getRowLabel={(r) => r.name}
caption="Members"
selectable
selectedRowIds={selected}
onSelectedRowIdsChange={setSelected}
/>
<p style={{ margin: 0, fontSize: 13 }}>{log || 'Pick an action.'}</p>
</div>
);
}
<WithTable />Usage examples
Busy
import { BulkActionBar } from '@rookcom/monarch-react/data';
function Busy() {
return (
<BulkActionBar
count={12}
onClear={() => {}}
busy
actions={[
{ id: 'archive', label: 'Archive', onSelect: () => {} },
{ id: 'tag', label: 'Add tag', onSelect: () => {} },
]}
/>
);
}
<Busy />Purpose
Gives people the actions that apply to everything they selected in a table or list, says how many items are selected, and clears the selection. It appears with the first selection and disappears when the selection is empty.
Use when
- A DataTable or list supports multi-select and several actions apply to the selection.
- The selection count should be visible and announced.
Avoid when
- One action per row: use DataTable
rowActions. - Page-level actions unrelated to a selection: use PageHeader or Toolbar.
Design principles
- Selection-aware: hidden at 0, the count is always the first thing read.
- The app owns the selection and runs the actions; the bar renders and reports. Confirmations, previews and progress for long jobs stay in the app (ConfirmDialog, Toast).
- A promise returned by an action makes that button busy and blocks the others, so an action cannot run twice.
- One tab stop: arrow keys move between its controls (WAI-ARIA toolbar).
API reference
import { BulkActionBar, defaultBulkActionBarMessages } from '@rookcom/monarch-react/data';| Export | Kind | Rendering | Root class |
|---|---|---|---|
BulkActionBar | component | client | .mn-bulk-action-bar |
defaultBulkActionBarMessages | utility | client | – |
Imported from @rookcom/monarch-react/data. The ref is the outer div, which also receives focus after "Clear selection" unless returnFocusRef is given.
BulkAction: id, label, icon, onSelect (may return a promise), tone, disabled.
messages translates the count ("3 of 120 selected"), "Selection cleared", "Clear selection", "More" and the toolbar name.
Generated from the TypeScript declarations of packages/react/src/components/data-grid/bulk-action-bar.
Properties
BulkActionBar
Toolbar that appears while items are selected: the count, actions with an overflow menu, and "Clear selection". The count is announced politely. Arrow keys move between its controls (one tab stop).
| Prop | Type | Description |
|---|---|---|
count required | number | Number of selected items. The bar is shown while it is above 0. |
onClear required | () => void | Empties the selection (DataTable: set `selectedRowIds` to `[]`). |
actions | readonly BulkAction[] | Actions shown as buttons; those beyond `maxVisibleActions` go into a "More" menu. |
busy | boolean | An action is running: every action is blocked. |
children | ReactNode | Extra controls in the toolbar, e.g. a property picker. |
className | string | |
id | string | |
maxVisibleActions | number | Buttons shown before the overflow menu. Default 3. |
messages | Partial<BulkActionBarMessages> | |
placement | "sticky" | "inline" | `sticky` keeps the bar at the bottom of the scroll area. Default `inline`. |
returnFocusRef | RefObject<HTMLElement | null> | Where focus goes after "Clear selection". Default: the bar's container. |
totalCount | number | Total items that could be selected, for "3 of 120 selected". |
Variants
| Prop | Values | Description |
|---|---|---|
placement | 'inline''sticky' | sticky keeps the bar at the bottom of the scroll area with a stronger shadow (data-placement). |
maxVisibleActions | 'number' | Actions after this many (default 3) go into the "More" menu. |
tone (action) | 'default''danger' | Danger actions use the subtle danger button and danger menu item. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Closed | data-state="closed" | Nothing selected: only the live region renders. |
| Open | data-state="open" | Something is selected: the toolbar is shown. |
| Busy | aria-busy | busy, or an action whose promise has not settled: that button shows a spinner and the rest are disabled. |
Interaction behaviour
- Pressing an action calls its
onSelect; overflow actions are in a "More" menu. - "Clear selection" calls
onClearand moves focus toreturnFocusRef(or the bar container) so it is never lost. - Count changes are announced politely ("2 of 4 selected", then "Selection cleared").
- Extra controls (property pickers) can be passed as children.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves into the toolbar (one stop) and out again. |
| Arrow Left / Right | Moves between the toolbar’s controls, wrapping (RTL aware). |
| Home / End | First / last control. |
| Enter / Space | Runs the focused action or opens "More". |
Accessibility
- Role
- toolbar
- Accessible name
- "Bulk actions" (
messages.label).
- A persistent polite status region announces count changes, so the announcement works even when the bar first appears.
- The visible count is plain text inside the toolbar; icons are decorative.
- Busy actions keep focus (aria-disabled via Button
loading).
Responsive behaviour
- Wraps onto several lines; below 480px the actions take their own full-width row under the count and Clear.
Theme support
- Raised surface, border and shadow tokens, so it reads in every theme and contrast mode.
- The entrance animation is removed with
prefers-reduced-motion: reduce.
Do and don't
Do
- Confirm destructive bulk actions and report partial failures (Toast).
- Return a promise from long actions so the bar blocks double runs.
- Pass
totalCountwhen "select all matching" is possible.
Don't
- Do not show the bar without a way to clear the selection.
- Do not put more than a few actions in view; let the rest overflow.
Related components
- DataTable: Supplies the selection.
- Toolbar: Actions that do not depend on a selection.
- ConfirmDialog: Confirm destructive bulk actions.
- Toast: Report results and offer undo.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Button, DropdownMenu |
Testing coverage
- Hidden until DataTable rows are selected; announces "1 of 3 selected", then the new count.
- Clear empties the selection, announces it and keeps focus (container or returnFocusRef).
- Runs actions; overflow menu; a pending promise blocks other actions and sets aria-busy.
- Roving tabindex: one tab stop, arrows wrap, Home/End.
- Translated messages and sticky placement; server rendering open and closed.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | None yet |
Tests: packages/react/src/components/data-grid/bulk-action-bar