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 |