Toolbar

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.

Examples

Search, arrange and export

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>
  );
}

Place it outside the table

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>
  );
}

Parts outside a toolbar

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>
  );
}

Keyboard

The keys of First table, Input, ColumnMenu, RowActions, Button and Checkbox apply here.

Accessibility

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.

API

ToolbarProps

NameTypeDefaultDescription
childrenReactNode—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

sizeToolbarSize
"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)

ofTableRef—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)

Styling

NameTypeDefaultDescription
classNamestring—Classes on the table toolbar.

Types on this page

ToolbarSize

The size of a toolbar's controls.

type ToolbarSize = "sm" | "md";

TableRef

What 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;
};

Every page of @umriss-ui/table

Getting started

Columns

Finding rows

Grouping

Rows

Around the table

Many rows

Editing

Limits and alarms

API index