Concepts
This package contains many low-level "primitive" components, which can be composed together to achieve the desired table interface.
Primitives
Primitives are "dumb" components that assume as little as possible about their usage, they don't own any state and have no side-effects, making them easier to understand and maintain for our team.
Primitives allow us to move quickly and build the best abstraction for each case. While the approach to composition may vary depending on requirements and constraints, primitives offer familiarity along with flexibility.
<Table> <TableHeader> <TableColumnHeader /> </TableHeader> <TableBody> <TableRow> <TableCell /> </TableRow> </TableBody> <TableFooter> <TableCell /> </TableFooter></Table>
Motivation
Monolithic components don't know your application's constraints and requirements. Assumptions must be made about how a component will be used, which often results in some less-than-ideal implementation artefacts:
- increased API surface-area i.e. endless prop configuration
- limited opportunity for performance improvements
- inability to support changes in design requirements
Appearance
Cell appearance
The TableCell
component accepts props to adust appearance:
align
— Sets the horizontal alignment of cell contents.maxWidth
— Sets the maximum width of the cell.minWidth
— Sets the minimum width of the cell.width
— Sets the width of the cell.showDivider
— Whether the cell should show a divider between it and the next cell. When using sticky cells dividers are shown by default.
Row appearance
The TableRow
component accepts props to adust appearance:
status
— Sets the interaction status of the row.tone
— Sets tone of the row, influences the background colour.disabled
- Makes the row as disabled. Can be used while dragging or selecting where some rows aren't meant to be interacted with.
Table appearance
The root Table
component accepts props to infuence the appearance of every cell:
alignY
— Sets the vertical alignment for content within each cell. Only relevant when allowing text content to wrap.density
— Sets the amount of vertical padding within each cell.overflowStrategy
— Sets the overflow strategy forText
content within each cell.
Sticky behaviour
The stickyOffset
property applies "sticky" positioning to the element, offset
relative to its nearest scrolling ancestor.
Sticky rows
The TableHeader
and TableFooter
may be "sticky" along the vertical axis:
- The
TableHeader
will be offset against the top. - The
TableFooter
will be offset against the bottom.
Sticky cells
The TableColumnHeader
and TableCell
may be "sticky" along the horizontal axis:
- Positive values e.g.
0, 240
will offset the cell against the left. - Negative values e.g.
-0, -240
will offset the cell against the right.
Providing a stickyOffset
will default the showDivider
property to true
.
Custom content
There's some optimisations provided out-of-the-box that are only supported with
default usage. When providing ReactText
(string
and number
) content to
cells TableColumnHeader
and TableCell
everything will behave as expected.
Text behaviour
Custom implementations, depending on design requirements, can adjust Text
appearance and behaviour to match the table defaults.
Memoization
Custom cell content should be memoized with useMemo for simple cases and memo for complex component cases.
Sorting
The TableColumnHeader
accepts an onClick
prop, which should be used for sorting. When a click handler is provided you must also provide aria-sort to the actively sorted column, which will render the appropriate arrow indicator and let users of assistive tech know how the data within the table is sorted.
Default Sort
The table package exposes a createDefaultSort
utility, which will handle most sorting cases.
type SortDescriptor = { column: string; direction: 'ascending' | 'descending';};
The utility takes a SortDescriptor
as its only argument and returns a compare function that should be provided to the sort method of your data array.
const rowData = [ { name: 'Brig Joderli', email: 'bjoderli0@nymag.com', }, { name: 'Jill Rowledge', email: 'jrowledge1@amazon.co.uk', },];rowData.sort(createDefaultSort({ column: 'name'; direction: 'descending';}));
Selection
Use the TableSelectionCell
to implement checkbox selection of rows—internally it composes a TableCell
of standard width and checkbox for consistent appearance and behaviour across products.
Indeterminate state
The indeterminate state of a checkbox input element is controlled by JavaScript, not HTML attribution. To make things more declarative and less confusing Balance checkboxes accept a "tri-state"; the checked value for a checkbox may be true | false | 'mixed'
, where "mixed" indicates an indeterminate state aligning with the aria-checked attribute.
Conditional elements
Some tables may have many columns and/or many rows. Loading a subset of columns or rows, via mechanisms like virtualization and pagination, can improve performance and user experience.
Low-level primitives
The TableHeader
and TableFooter
are provided as convenience components for the most common cases. Internally they compose the TableHead
and TableFoot
with TableRow
, which are exposed for cases like conditionally rendered elements.
<TableHead> <TableRow> ... </TableRow></TableHead>...<TableFoot> <TableRow> ... </TableRow></TableFoot>
Conditional rows
When only a subset of rows are loaded, you need to let all users know which rows are being displayed.
On the Table
, use the aria-rowcount attribute to let assistive technologies know how many rows the table would have if all rows were present. For paginated results, consider providing an aria-description
or aria-describedby
attribute to announced the current state.
On the TableRow
, use the aria-rowindex attribute to let assistive technologies know where each row is in relation to the total possible rows.
Conditional columns
When only a subset of rows are loaded, you need to let all users know which columns are being displayed.
On the Table
, use the aria-colcount attribute to let assistive technologies know how many columns the table would have if all columns were present.
On the TableCell
and TableColumnHeader
, use the aria-colindex attribute to let assistive technologies know where each column is in relation to the total possible columns.
Groups
Groups are non-standard table structure and should be used sparingly.
Tables that implement groups have no TableBody
, instead a number of TableGroup
components should be rendered as siblings of the TableHeader
.
The TableGroupHeader
must be given an aria-colspan matching the total number of columns.
Empty state
The TableEmptyState
component can be used instead of TableBody
when there is no data available.