Table
Storybook
Browse the props, controls and all the stories of Table in Storybook.
The Table component renders a data grid with sorting, column filters, pagination and a column of operation buttons on each row. It is built on top of TanStack Table (@tanstack/react-table v8) and accepts its table options. It is the base of the FormsTable, SubmissionsTable and ActionsTable components.
Usage
The Table resolves its cells, filters, header and pagination from the components registry. Import the all entry point once to register the default implementations, then import the component itself:
import "@tsed/react-formio/molecules/table/all";
import { Table } from "@tsed/react-formio/molecules/table/Table";
import { mapFormToColumns } from "@tsed/react-formio/molecules/table/utils/mapFormToColumns";
const columns = mapFormToColumns({ form });
export function ProductsTable({ submissions }) {
return (
<Table
data={submissions}
columns={columns}
operations={[
{ title: "Edit", action: "edit", alias: "row", icon: "edit" },
{ action: "delete", icon: "trash", buttonType: "danger" }
]}
onClick={(submission, operation) => console.log(operation.action, submission)}
/>
);
}Props
Table accepts the TanStack table options (initialState, state, manualPagination, manualSorting, manualFiltering, pageCount, getRowId...), except getCoreRowModel which is provided by the component, plus the following props:
| Name | Type | Default | Description |
|---|---|---|---|
data | Data[] | Rows to display. | |
columns | ColumnDef<Data>[] | TanStack column definitions. Use mapFormToColumns to build them from a form. | |
operations | Operation<Data>[] | Buttons displayed in an extra "Operations" column. Pass an empty array to hide the column. | |
onClick | (data: Data, operation: Operation) => void | Called when an operation button is clicked, or when a cell is clicked (with { action: "row" }). | |
onChange | (state: TableState) => void | Called with the table state when the filters, sorting, pagination, column order, pinning, sizing or visibility change. | |
metadata | Record<string, unknown> | Extra data passed to the operations permissionsResolver. | |
enablePagination | boolean | true | Displays the pagination below the table (only when data is not empty). |
pageSizes | number[] | Page sizes proposed by the pagination ([10, 25, 50, 100] by default). | |
rowCount | number | Total number of items displayed by the pagination. | |
enableFooter | boolean | Renders a tfoot with the footer of each column. | |
manualFaceted | boolean | Disables the client-side faceting (unique values and min/max used by the filters). | |
i18n | FormOptions["i18n"] | Translations of the labels (Operations, operation titles, pagination labels...). | |
className | string | Additional CSS classes on the table group. | |
children | ReactNode | Content rendered in the table footer, next to the pagination. |
Sorting, filtering and pagination are performed client-side unless manualSorting, manualFiltering or manualPagination is set. In that case, use onChange to query your server with the new state.
Columns
Columns are standard TanStack column definitions. The library extends the column meta with the following options:
| Name | Type | Description |
|---|---|---|
type | "string" | "number" | "boolean" | "date" | string | Type of the value, used to resolve the cell component (Cell.<type>). |
format | string | Date format used by the date cell (moment format, L by default). |
labels | Record<string, string> | Labels used by the boolean cell (yes / no, Yes / No by default). |
filter | FilterOptions | Filter configuration of the column. See Filters. |
cellProps | TdHTMLAttributes | Attributes added to each td of the column (colSpan, className...). |
hidden | boolean | Hides the column cells. |
Build columns from a form
mapFormToColumns creates a column for each component of a form that has tableView enabled (components nested in a tabs component are included). The accessor is data.<component key>, the header is the component label and the column type and filter variant are deduced from the component type (number and currency use a range filter, checkbox a boolean filter, other components a text filter).
import { mapFormToColumns } from "@tsed/react-formio/molecules/table/utils/mapFormToColumns";
const columns = mapFormToColumns({
form,
// optional: column definitions merged with the generated ones
columns: [
{
accessorKey: "data.is_open",
header: "Is open",
meta: {
type: "boolean",
filter: { variant: "boolean", layout: "react" },
labels: { yes: "Open", no: "Closed" }
}
}
],
// optional: accessor prefix, "data." by default
prefix: "data."
});Options:
form: the form definition.columns: custom column definitions. A column whoseidoraccessorKeymatches a component key (with or without the prefix) overrides the generated column, the other ones are appended.prefix: prefix of the generated accessors (data.by default).
Columns are sorted by meta.order (by default the column position multiplied by 10), so you can insert a custom column between generated ones by setting its meta.order.
Filters
A filter is displayed in the header of each filterable column. Its type is set with meta.filter.variant:
| Variant | Options | Description |
|---|---|---|
text | disableDatalist?: boolean, disabled?: boolean, InputText props | Text input. Suggests the unique values of the column through a datalist unless disableDatalist is set. |
select | options?: SelectOptionProps[] | (props) => SelectOptionProps[], Select props (layout...) | Select filled with the unique values of the column, or with the given options. |
boolean | options?: SelectOptionProps[], Select props | Select filtering on true / false. |
range | min?: number, max?: number, step?: number | Two number inputs (min / max). Bounds default to the min / max values of the column. |
const columns = mapFormToColumns({
form,
columns: [
{
accessorKey: "data.id",
meta: { filter: { variant: "select", layout: "react" } }
},
{
accessorKey: "data.description",
meta: { filter: { variant: "text", disableDatalist: true } }
}
]
});Set meta.filter.disabled to hide the filter of a column, or use the TanStack enableColumnFilter: false option to disable filtering on it.
Pagination
The pagination is rendered with the Pagination component. Configure the initial page size with the TanStack initialState, the proposed sizes with pageSizes and the total of items with rowCount:
import "@tsed/react-formio/molecules/table/all";
import { Table } from "@tsed/react-formio/molecules/table/Table";
<Table
data={submissions}
columns={columns}
operations={[]}
enablePagination
pageSizes={[5, 10, 25]}
rowCount={submissions.length}
initialState={{
pagination: { pageIndex: 0, pageSize: 5 }
}}
/>;Custom cells
For the columns built by mapFormToColumns, the cell is resolved from the registry with the keys Cell.<id>, Cell.<meta.type> then Cell. The library provides cells for strings, booleans (boolean, checkbox) and dates (date, datetime). You can also give a cell renderer to a column, and use meta.cellProps and meta.hidden to merge columns:
const columns = mapFormToColumns<ProductSubmission>({
form,
columns: [
{
accessorKey: "data.id",
meta: { cellProps: { colSpan: 2 } },
cell: (context) => (
<div>
<div className="text-lg text-primary">{context.row.original.data.label}</div>
<div className="text-xs">{context.getValue()}</div>
</div>
)
},
{
accessorKey: "data.label",
meta: { hidden: true }
},
{
accessorKey: "data.price",
cell: (context) => {
const value = context.getValue();
return value === undefined ? "-" : new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR" }).format(value);
}
}
]
});Operations
Each operation adds a button in the "Operations" column. Clicking it calls onClick(row, operation).
| Name | Type | Default | Description |
|---|---|---|---|
action | string | Operation identifier. | |
title | string | Button label (translated with i18n). | |
icon | string | Icon displayed in the button. | |
alias | string | Alias of the operation, available in the onClick handler. | |
path | string | Path associated with the operation, available in the onClick handler. | |
buttonType | string | "primary" | Button variant (btn-<buttonType>). |
buttonOutline | boolean | Uses the outline variant (btn-outline-<buttonType>). | |
buttonSize | string | "xs" | Button size (btn-<buttonSize>). |
className | string | "btn" | CSS class of the button. |
permissionsResolver | (data, metadata, operation) => boolean | Displays the button only for the rows where it returns a truthy value. |
import type { Operation } from "@tsed/react-formio";
const operations: Operation[] = [
{ title: "Edit", action: "edit", alias: "row", icon: "edit" },
{
action: "delete",
icon: "trash",
buttonType: "danger",
permissionsResolver: (data, metadata) => metadata.canDelete === true
}
];Override
The sub-components of the Table are resolved from the components registry and can be replaced with registerComponent. See Registering react components.
| Registry name | Default component | Role |
|---|---|---|
CellHeader | DefaultCellHeader | Header cell (label, sort arrow and filter). |
CellFooter | DefaultCellFooter | Footer cell (when enableFooter is set). |
ArrowSort | DefaultArrowSort | Sort indicator of the header. |
Filter | DefaultFilter | Resolves and renders the column filter. |
Filter.text, Filter.range | TextFieldFilter, RangeFilter | Filter variants. |
Filter.select, Filter.boolean | SelectFilter | Filter variants. |
Cell, Cell.string | DefaultCell | Default cell. |
Cell.boolean, Cell.checkbox | DefaultCellBoolean | Boolean cell. |
Cell.date, Cell.datetime | DefaultDateCell | Date cell. |
CellOperations | DefaultCellOperations | Operations cell. |
OperationButton | DefaultOperationButton | Operation button. |
Pagination | Pagination | Table pagination. |
You can also register a filter for a single column with Filter.<column id>, and a cell with Cell.<id> when the column definition passed to mapFormToColumns has an explicit id. Note that TanStack derives the column id from the accessorKey by replacing dots with underscores (data.price becomes data_price):
import { registerComponent } from "@tsed/react-formio/registries/components";
registerComponent("Filter.data_price", PriceFilter);
registerComponent("Cell.price", PriceCell); // for a column defined with { id: "price", accessorKey: "data.price" }