Tables
The Table component is an all inclusive and accessible table based on correct HTML semantics.
Please use the properties instead of overwriting the styles. And if you miss a feature, get in touch with us.
NB: If you have more than three (3) columns, please consider to use border
property in order to enhance accessibility.
Accessibility
Tables do both serve as a way of navigation for screen readers and other assertive technologies. But they also help to give data an ordered structure.
Use the documentation from MDN – The Table element for more information on making semantic correct tables, including scope
, align
, colSpan
and rowSpan
.
Here is a list of things you may follow along in order to ensure your coded tables still are accessible:
- Keep a semantic correct structure.
- Let tables align the column width, when possible.
- Do not use CSS
display
property on any table element. - Do not overwrite styles in general, but rather get in touch with DNB UX.
- Never put a table inside a table.
- Text inside tables do not need to be wrapped inside a paragraph as well. They give screen readers no additional useful information.
Table header components
<Th.SortButton />
to be used for additional sorting functionality.<Th.HelpButton />
to be used for help related content.
Alignment
Use e.g. align="right"
on a <Th>
, <Td>
or <Tr>
to align a table header or a table data element.
Fixed layout
You may consider using table-layout: fixed;
. You can use the modifier property fixed
for doing so and combine it with CSS e.g. width: 40%
on specific table headers.
Scrollable
Depending on your situation, you may want to wrap your Table within Table.ScrollView
:
import { Table } from '@dnb/eufemia'render(<Table.ScrollView><Table /></Table.ScrollView>,)
Sticky header
You have two options (both have their downsides):
-
use
sticky={true}
. It works even when using aTable.ScrollView
or aoverflow: hidden;
is used on any parent elements. And it works inside a Drawer as well. The downside is, that it uses JavaScript and the browser may drop some frames, which results in a potential flickering during scrolling. -
use
sticky="css-position"
for using the CSSposition: sticky;
method. It is super smooth. But then you can not use aoverflow: hidden;
oroverflow: auto;
on any parent elements. This is a know issue happening on every modern browser.
Method no. 2 should be used when a max-height
is set to the wrapping Table.ScrollView
e.g.:
<Table.ScrollView style={{ maxHeight: '20rem' }}><Table sticky="css-position" /></Table.ScrollView>
Have a look at this example.
Sortable table
Optionally, make use of the following React Hook to handle the Th.SortButton
directions.
It can be used as a "controller" for your own sorting logic of your data.
By default, it will cycle trough three stages ['asc', 'desc', 'off']
.
Show how to use the useHandleSortState React Hook.
import useHandleSortState from '@dnb/eufemia/components/table/useHandleSortState'// You can also provide a default that will be used as the fallback e.g.const defaultOptions = { direction: 'asc', modes: ['asc', 'desc', 'off'] }export const YourComponent = () => {const { sortState, sortHandler, activeSortName } = useHandleSortState({// Define your column names with options (optional)column1: { active: true }, //column2: { direction: 'desc', modes: ['asc', 'desc'] }, // overwrite the defaultOptionscolumn3: { modes: ['asc', 'off'] }, // will only allow one directioncolumn4: {}, // etc.},defaultOptions,)// Use these properties for your custom sorting logicconsole.log(sortState.column1.direction) // returns either "asc", "desc" or "off"console.log(activeSortName) // returns the current active one: "column1" (returns null when nothing is active)// Handle your logicuseEffect(() => {switch (sortState.column1.direction) {default:case 'asc':setYourLocalState(mockData.sort(compareFunctionAsc))breakcase 'desc':setYourLocalState(mockData.sort(compareFunctionsDesc))breakcase 'off':setYourLocalState(mockData)break}}, [sortState.column1.direction])return (<Table><thead><Tr><Thsortableactive={sortState.column1.active}reversed={sortState.column1.reversed}><Th.SortButtontext="Column 1"title="Sort this column"on_click={sortHandler.column1}/></Th></Tr></thead></Table>)}
Demos
Basic table
NB: In this example, the sort buttons do react on your input. But will not change the table data.
Column | Help Button | ||
---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | Row 2 |
Row 3 with paragraph | Row 3 with code | Row 3 with medium paragraph | Row 3 with medium text |
Complex table
You can force a row to overwrite the automated odd/even counting by providing e.g. variant="even"
to a <Tr />
. You can use this in combination with rowSpan
.
NB: The table header in the first column needs to have scope="row"
!
Column 2 newline | Column 3 that spans | ||
---|---|---|---|
Row 1+2 Header | Row 1 that spans | Row 1 | Row 1 |
Row 2 | Row 2 | ||
Row 3 Header newline | Row 3 | noSpacing + align="right" | |
Row 4 Header | Row 4 | Row 4 |
Row scope headers only
This table has only scope="row"
and scope="rowgroup"
headers – without the default scope="col"
.
Header A | Row 1 | Row 1 |
---|---|---|
Header B | Row 2 | Row 2 |
Fixed table
Column 1 | Column 2 | Column 3 | Column 4 | Column 5 | Column 6 | Column 7 | Column 8 |
---|---|---|---|---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 | Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | Row 2 | Row 2 | Row 2 | Row 2 | Row 2 |
Row 3 | Row 3 | Row 3 | Row 3 | Row 3 | Row 3 | Row 3 | Row 3 |
Row 4 | Row 4 | Row 4 | Row 4 | Row 4 | Row 4 | Row 4 | Row 4 |
Medium and small sized
Column | Column | |
---|---|---|
Row 1 | Row 1 | Row 1 |
Row 2 with paragraph | Row 2 with medium paragraph | Row 2 with medium text |
A small
sized table is only for special circumstances, where a lot of data needs to be shown on the screen at the same time.
Column | Column | |
---|---|---|
Row 1 | Row 1 | Row 1 |
Row 2 with paragraph | Row 2 with medium paragraph | Row 2 with medium text |
Table with accordion
Expand a single container
The second table uses both a border
and an outline
.
Column A | Column B | Column C | Column D | |
---|---|---|---|---|
Row 1 | Row 1 | |||
Row 2 | Row 2 | |||
Row 3 | Row 3 |
Column A | Column B | Column C | Column D | |
---|---|---|---|---|
Row 1 | Row 1 | Row 1 | ||
Row 2 | Row 2 | Row 2 | ||
Row 3 | Row 3 | Row 3 |
Expand additional rows
It's also possible to use accordion to expand the table with more rows.
Column A | Column B | Column C | Column D | |
---|---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 | |
Row 2 | Row 2 | Row 2 | Row 2 |
Collapse all rows at once
You can collapse all expanded rows by sending a ref to the collapseAllHandleRef
prop and calling the .current()
function on your ref.
const myTableCollapseAll = React.useRef<() => void>()return (<button onClick={() => myTableCollapseAll.current()}>Close all rows</button><Table accordion collapseAllHandleRef={myTableCollapseAll}>{/* ... your table code */}</Table>)
Table with sticky header
Header | |||
---|---|---|---|
Row 1 with p | Row 1 with code | Row 1 with span | Row 1 |
Column which spans over two columns | Row 2 | Row 2 | |
Row 3 | Row 3 | Row 3 | Row 3 |
Footer | Sum |
Table with a max height
A sticky table header with sticky="css-position"
and max-height
on the Table.ScrollView
.
Column 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | Row 2 |
Row 3 | Row 3 | Row 3 | Row 3 |
Row 4 | Row 4 | Row 4 | Row 4 |
Row 5 | Row 5 | Row 5 | Row 5 |
Row 6 | Row 6 | Row 6 | Row 6 |
Row 7 | Row 7 | Row 7 | Row 7 |
Row 8 | Row 8 | Row 8 | Row 8 |
Row 9 | Row 9 | Row 9 | Row 9 |
Row 10 | Row 10 | Row 10 | Row 10 |
Row 11 | Row 11 | Row 11 | Row 11 |
Row 12 | Row 12 | Row 12 | Row 12 |
Several tables in one container
Show how the import and syntax is structured.
<TableContainer> <TableContainer.Head> <H2>Heading</H2> </TableContainer.Head> <TableContainer.Body> <Table>Content</Table> <Table>Content</Table> </TableContainer.Body> <TableContainer.Foot> <P>Footer</P> </TableContainer.Foot> </TableContainer>
Header
Text
I have a superscript 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | Row 2 |
Column 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | |
Row 3 | Row 3 | Row 3 |
Row Header Group | Row 1 | Row 1 |
---|---|---|
Row 2 | Row 2 |
Footer
With no (empty) head
and foot
content.
Column 1 | Column 2 | Column 3 | Column 4 |
---|---|---|---|
Row 1 | Row 1 | Row 1 | Row 1 |
Row 2 | Row 2 | Row 2 | Row 2 |
Table with long header text (wrapping)
Static long header senectus ornare convallis ut at erat imperdiet commodo |
---|