Table toolbar, columns & grid navigation
The two opt-in behaviors that sit on top of a data table: a toolbar with column visibility and export, and the keyboard grid navigation model. Both are deliberate opt-ins. Do not add either to a read-mostly list — a reference table someone scans a few times a day gains nothing from column toggles (its columns were chosen server-side for a reason), and keyboard grid navigation actively costs: the whole table collapses to one Tab stop, and every control inside it — here the select-all and each row checkbox — is pulled out of the Tab sequence, reachable only by arrowing to its cell and pressing Enter. A table a screen-reader user could browse natively becomes a single widget they must learn to operate. The plain data table already ships selection, sort and filter free; earn these two with a real need — many optional columns, or genuinely cell-level work.
Toolbar — column visibility & export opt-in
| Invoice # | Vendor | Cost center | Amount | Status |
|---|---|---|---|---|
| INV-10234 | Acme Supply Co. | CC-4021 | $4,208.00 | Pending |
| INV-10235 | Globex Industrial | CC-4021 | $18,940.50 | Approved |
| INV-10236 | Initech GmbH | CC-1180 | $730.25 | Rejected |
Columns is the same multi-select
dropdown pattern as elsewhere (data-multiselect, real checkboxes),
so it needs initDropdowns() alongside
initTableToolbar() — the toolbar behavior owns show/hide and the
export intent, the dropdown behavior owns the menu (anchoring it to its
button, staying open across selections, and the count on the trigger label)
— checking/unchecking a data-col-toggle box shows/hides every
<th>/<td> with the matching
data-col, scoped to this table only. If you put column toggles
on a data-grid-nav table — these two demos are separate tables,
so you cannot try it here — a hidden column is dropped from the grid model
too: the cell cursor skips it, and hiding the column the cursor is parked in
hands the grid's single tab stop to a visible cell rather than stranding it
somewhere Tab cannot reach.
Export
dispatches bo:table-export with a format — this
behavior only tracks the intent, generating and downloading the actual file
is your code (fetch a server export endpoint, or build a client-side CSV/PDF)
— same split as bo:row-save and bo:scan. Try it:
uncheck a column, then click Export.
Advanced — keyboard grid navigation opt-in
The second tier: data-grid-nav + initDataGrid() turns
the table into a WAI-ARIA APG "Grid": role="grid" (cells get their
gridcell/columnheader roles implicitly — no per-cell
markup needed), aria-selected synced from the row checkboxes, and
two-axis roving tabindex — Tab enters the grid at ONE cell, arrow keys move the
cursor (clamped, no wrap), Home/End jump to the row's
first/last cell and Ctrl+Home/Ctrl+End
to the grid's, Enter focuses a cell's single widget (the
checkbox), Esc hands focus back to the cell. Reach for this when a
screen is genuinely spreadsheet-like (dense data entry, heavy keyboard use) —
the Simple tier above is enough for most tables, including ones with sort and
bulk-select. Click into the table below, then try arrow keys.
| Invoice # | Amount | |
|---|---|---|
| INV-10234 | $4,208.00 | |
| INV-10235 | $18,940.50 | |
| INV-10236 | $730.25 |
<table class="bo-data-table" data-grid-nav>
<thead><tr>
<th scope="col"><input type="checkbox" class="bo-checkbox bo-data-table__select-all" aria-label="Select all" /></th>
<th scope="col">Invoice #</th>
<th scope="col" class="bo-data-table__col--numeric">Amount</th>
</tr></thead>
<tbody><tr>
<td><input type="checkbox" class="bo-checkbox bo-data-table__row-select" aria-label="Select INV-1" /></td>
<td class="bo-data-table__col--code">INV-1</td>
<td class="bo-data-table__col--numeric">$4,208.00</td>
</tr></tbody>
</table>
<script type="module">
import { initDataGrid } from "@busy-office/ui/js";
initDataGrid(); // role=grid, two-axis roving tabindex, Enter/Escape into-and-out-of a cell's widget
</script>