The bar above the table that holds its search, column menu and export, shows how many rows match, and carries the bulk actions while rows are ticked (also called a table header bar).
import { Toolbar } from "@umriss-ui/table";
It stands above the table wherever it stands in the JSX. On the left go the parts you put into it, then the conditions of the column filters; on the right the count of matches and “Reset” while something restricts, and the selection with its bulk actions while rows are ticked.
A table with a search or a column filter puts up a toolbar of its own when none stands. Outside the table, pass of to the toolbar and to each part in it, and place it before the table – after it, it registers only after the first paint.
Put the parts into a Toolbar inside the table; tick rows and the bulk actions and the selection count join it on the right.
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { ColumnMenu, Export, Search, Toolbar, useTable } from "@umriss-ui/table";
interface Incident {
id: string;
title: string;
service: string;
severity: "SEV1" | "SEV2" | "SEV3";
}
const INCIDENTS: Incident[] = [
{ id: "INC-1048", title: "Checkout slow, card payments time out", service: "Checkout", severity: "SEV1" },
{ id: "INC-1047", title: "Webhook deliveries delayed", service: "Webhooks", severity: "SEV3" },
{ id: "INC-1046", title: "Thumbnails missing for new uploads", service: "Image service", severity: "SEV2" },
{ id: "INC-1045", title: "Sign-in codes arrive late", service: "Sign-in", severity: "SEV2" },
];
export default function SearchArrangeAndExport() {
const [last, setLast] = useState("Nothing closed yet.");
const { Table, Column, RowActions, Action } = useTable(INCIDENTS, { rowKey: (i) => i.id });
return (
<Stack gap={3}>
<Table selectable ariaLabel="Incidents">
<Toolbar>
<Search placeholder="Search incidents" />
<ColumnMenu />
<Export filename="incidents.csv" />
</Toolbar>
<Column value="id" label="Incident" rowHeader />
<Column value="title" label="Title" />
<Column value="service" label="Service" />
<Column value="severity" label="Severity" />
<RowActions>
<Action bulk onSelect={(list) => setLast(`Closed: ${list.map((i) => i.id).join(", ")}`)}>
Close
</Action>
</RowActions>
</Table>
<Text size="sm" tone="secondary" role="status">
{last}
</Text>
</Stack>
);
}
Keep what useTable returns and pass it as of to the toolbar and to each part in it; put the toolbar before the table.
import { Stack } from "@umriss-ui/core";
import { Export, Search, Toolbar, useTable } from "@umriss-ui/table";
interface Incident {
id: string;
title: string;
}
const INCIDENTS: Incident[] = [
{ id: "INC-1048", title: "Checkout slow, card payments time out" },
{ id: "INC-1047", title: "Webhook deliveries delayed" },
{ id: "INC-1046", title: "Thumbnails missing for new uploads" },
];
export default function PlaceItOutside() {
const t = useTable(INCIDENTS, { rowKey: (i) => i.id });
const { Table, Column } = t;
return (
<Stack gap={3}>
<Toolbar of={t}>
<Search of={t} placeholder="Search incidents" />
<Export of={t} filename="incidents.csv" />
</Toolbar>
<Table ariaLabel="Incidents">
<Column value="id" label="Incident" rowHeader />
<Column value="title" label="Title" />
</Table>
</Stack>
);
}
Each part takes the table as of and stands where you put it: here in a card header at size="md", the card's height, with the pager above the rows.
import { Card, CardBody, CardHeader, Stack } from "@umriss-ui/core";
import { ColumnMenu, Export, Pagination, Search, useTable } from "@umriss-ui/table";
/* Outside a toolbar a part is `sm` unless it says otherwise. The pager stands
before the table: placed after it, it registers only after the first frame. */
interface Incident {
id: string;
title: string;
service: string;
}
const INCIDENTS: Incident[] = [
{ id: "INC-1048", title: "Checkout slow, card payments time out", service: "Checkout" },
{ id: "INC-1047", title: "Webhook deliveries delayed", service: "Webhooks" },
{ id: "INC-1046", title: "Thumbnails missing for new uploads", service: "Images" },
{ id: "INC-1045", title: "Search returns stale prices", service: "Search" },
{ id: "INC-1044", title: "Password reset mails late", service: "Identity" },
{ id: "INC-1043", title: "Nightly report missing a day", service: "Reporting" },
];
export default function PartsOutsideAToolbar() {
const t = useTable(INCIDENTS, { rowKey: (i) => i.id, pageSize: 4 });
const { Table, Column } = t;
return (
<Card>
<CardHeader
divider
title="Incidents"
actions={
<Stack direction="row" gap={2} align="center" wrap>
<Search of={t} size="md" placeholder="Search incidents" />
<ColumnMenu of={t} size="md" />
<Export of={t} size="md" filename="incidents.csv" />
</Stack>
}
/>
<CardBody>
<Stack gap={2}>
<Pagination of={t} pageSizes={[4, 10]} />
<Table ariaLabel="Incidents">
<Column value="id" label="Incident" rowHeader />
<Column value="title" label="Title" />
<Column value="service" label="Service" />
</Table>
</Stack>
</CardBody>
</Card>
);
}
The keys of First table, Input, ColumnMenu, RowActions, Button and Checkbox apply here.
The count of matches is a status region. It stays empty while nothing restricts the rows, and a screen reader hears “3 of 12” when a search or a filter narrows them, and the new count on every change after.
The parts the table puts in name themselves: the search field “Search table” unless its aria-label says otherwise, “Columns”, “Export”. A control of your own in the bar needs a label of its own, as anywhere.
ToolbarProps| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Search, column menu, export – on the left in the table toolbar, before the conditions. Shown in: Search, arrange and export, Place it outside the table, Set wording and formats once (Provider) and 37 more |
size | ToolbarSize"sm" | "md" | "sm" | The size of every control the table puts into it - search, column menu,
export, "reset", the bulk actions -: sm or md. A
control of one's own takes the same size by its own prop.Shown in: A larger toolbar (Toolbar controls), A dispatcher's bar (Toolbar controls) |
of | TableRef | — | The table, when the table toolbar does not stand inside it. If it then
stands behind the table in the tree, it registers only after the first
frame – until then a table with a search or a column filter shows its own
toolbar. Placed before the table, there is no such change. Shown in: Place it outside the table, Review cost centres against budget (Scenarios) |
| Name | Type | Default | Description |
|---|---|---|---|
className | string | — | Classes on the table toolbar. |
ToolbarSizeThe size of a toolbar's controls.
type ToolbarSize = "sm" | "md";TableRefWhat a part without a row type needs from a table – for of.
type TableRef = Omit<TableSnapshot<unknown>, "setFilter" | "conditionOf"> & {
readonly Table: unknown;
/** Without a row kind: any id, any condition. */
setFilter(column: string, condition: unknown): void;
};