Table
A table allow to arrange large amounts of data into rows and columns.
| Install | yarn add @diallink-corp/convergo-react-table |
|---|---|
| Version | 4.1.2 |
| Usage | import {Table} from '@diallink-corp/convergo-react-table' |
Static Collection
The table component can either render static or dynamic collections of data. For simple table components that need to render a list of pre-defined data a static collection should be used. Static collections cannot be changed over time.
<Table aria-label='Users'>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label='Users'>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row>
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row>
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Dynamic Collection
For data that is non-static, such as data fetched from a backend, the table component supports dynamic collections. Dynamic collections support adding, updating and removing of the data that is being rendered.
function Example() {
const columns = [
{label: 'First Name', name: 'firstName'},
{label: 'Last Name', name: 'lastName'},
{label: 'Age', name: 'age'}
];
const rows = [
{id: 1, firstName: 'Alexander', lastName: 'Ehrenthal', age: 24},
{id: 2, firstName: 'John', lastName: 'Doe', age: 34},
{id: 3, firstName: 'Jeff', lastName: 'Bezos', age: 57},
{id: 4, firstName: 'Tony', lastName: 'Soprano', age: 47}
];
return (
<Table aria-label='Users'>
<TableHeader columns={columns}>
{(column) => <Column key={column.name}>{column.label}</Column>}
</TableHeader>
<TableBody items={rows}>
{(row) => <Row key={row.id}>{(key) => <Cell>{row[key]}</Cell>}</Row>}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{ label: 'First Name', name: 'firstName' },
{ label: 'Last Name', name: 'lastName' },
{ label: 'Age', name: 'age' }
];
const rows = [
{
id: 1,
firstName: 'Alexander',
lastName: 'Ehrenthal',
age: 24
},
{ id: 2, firstName: 'John', lastName: 'Doe', age: 34 },
{
id: 3,
firstName: 'Jeff',
lastName: 'Bezos',
age: 57
},
{
id: 4,
firstName: 'Tony',
lastName: 'Soprano',
age: 47
}
];
return (
<Table aria-label="Users">
<TableHeader columns={columns}>
{(column) => (
<Column key={column.name}>{column.label}</Column>
)}
</TableHeader>
<TableBody items={rows}>
{(row) => (
<Row key={row.id}>
{(key) => <Cell>{row[key]}</Cell>}
</Row>
)}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{
label:
'First Name',
name: 'firstName'
},
{
label: 'Last Name',
name: 'lastName'
},
{
label: 'Age',
name: 'age'
}
];
const rows = [
{
id: 1,
firstName:
'Alexander',
lastName:
'Ehrenthal',
age: 24
},
{
id: 2,
firstName: 'John',
lastName: 'Doe',
age: 34
},
{
id: 3,
firstName: 'Jeff',
lastName: 'Bezos',
age: 57
},
{
id: 4,
firstName: 'Tony',
lastName:
'Soprano',
age: 47
}
];
return (
<Table aria-label="Users">
<TableHeader
columns={columns}
>
{(column) => (
<Column
key={column
.name}
>
{column
.label}
</Column>
)}
</TableHeader>
<TableBody
items={rows}
>
{(row) => (
<Row
key={row.id}
>
{(key) => (
<Cell>
{row[
key
]}
</Cell>
)}
</Row>
)}
</TableBody>
</Table>
);
}
Asynchronous Fetching
The table layout features a fixed header and a scrollable body in case the data is too large to fit into the viewport.
The body will then become infinitely scrollable, unless a pagination element is added. The table component supports
asynchronous fetching of data via the onLoadMoreItems prop. The table component uses its built-in infinite scrolling
to achieve this. This means that when the user reaches the bottom of the table, the component will automatically load more
data. If you provide an loadingState prop, the table component will show a spinner component, to indicate to the user,
that more data is being fetched.
function Example() {
const columns = [
{ name: 'Name', key: 'name' },
{ name: 'Height', key: 'height' },
{ name: 'Mass', key: 'mass' },
{ name: 'Birth Year', key: 'birth_year' }
];
const list = useAsyncListState({
async loadItems({ signal, cursor }) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json = await response.json();
return {
items: json.results,
cursor: json.next
};
}
});
return (
<Table aria-label="Users" style={{ height: '200px' }}>
<TableHeader columns={columns}>
{(column) => <Column key={column.key}>{column.name}</Column>}
</TableHeader>
<TableBody
items={list.items}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Row key={item.name}>{(key) => <Cell>{item[key]}</Cell>}</Row>
)}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{ name: 'Name', key: 'name' },
{ name: 'Height', key: 'height' },
{ name: 'Mass', key: 'mass' },
{ name: 'Birth Year', key: 'birth_year' }
];
const list = useAsyncListState({
async loadItems({ signal, cursor }) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json = await response.json();
return {
items: json.results,
cursor: json.next
};
}
});
return (
<Table aria-label="Users" style={{ height: '200px' }}>
<TableHeader columns={columns}>
{(column) => (
<Column key={column.key}>{column.name}</Column>
)}
</TableHeader>
<TableBody
items={list.items}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Row key={item.name}>
{(key) => <Cell>{item[key]}</Cell>}
</Row>
)}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{
name: 'Name',
key: 'name'
},
{
name: 'Height',
key: 'height'
},
{
name: 'Mass',
key: 'mass'
},
{
name: 'Birth Year',
key: 'birth_year'
}
];
const list =
useAsyncListState({
async loadItems(
{
signal,
cursor
}
) {
if (cursor) {
cursor = cursor
.replace(
/^http:\/\//i,
'https://'
);
}
const response =
await fetch(
cursor ||
`https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json =
await response
.json();
return {
items:
json.results,
cursor:
json.next
};
}
});
return (
<Table
aria-label="Users"
style={{
height: '200px'
}}
>
<TableHeader
columns={columns}
>
{(column) => (
<Column
key={column
.key}
>
{column.name}
</Column>
)}
</TableHeader>
<TableBody
items={list
.items}
loadingState={list
.loadingState}
onLoadMoreItems={list
.loadMoreItems}
>
{(item) => (
<Row
key={item
.name}
>
{(key) => (
<Cell>
{item[
key
]}
</Cell>
)}
</Row>
)}
</TableBody>
</Table>
);
}
Uncontrolled Selection
The table has selection support built-in. By passing in the selectionMode prop you can specify whether the table should
be selectable. It will automatically add a new row with checkboxes to the very left. Through the defaultSelectedKeys
prop you can handle uncontrolled selection.
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={['2', '3']}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={['2', '3']}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={[
'2',
'3'
]}
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Controlled Selection
You can also make the selection controlled through the selectedKeys and the onSelectionChange props.
function Example() {
const [selectedKeys, setSelectedKeys] = useState(new Set(['2']));
return (
<Table
aria-label="Users"
selectionMode="multiple"
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
);
}
function Example() {
const [selectedKeys, setSelectedKeys] = useState(
new Set(['2'])
);
return (
<Table
aria-label="Users"
selectionMode="multiple"
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
);
}
function Example() {
const [
selectedKeys,
setSelectedKeys
] = useState(
new Set(['2'])
);
return (
<Table
aria-label="Users"
selectionMode="multiple"
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>
John
</Cell>
<Cell>
Doe
</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>
Jeff
</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>
Tony
</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
);
}
Single Selection
If you want to only allow one row to be selected at a time, you can use the selectionMode prop to set it to single.
It will then hide the select all checkbox in the top left corner and only allow you to select one row at a time.
<Table aria-label="Users" selectionMode="single" defaultSelectedKeys={['2']}>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="single"
defaultSelectedKeys={['2']}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="single"
defaultSelectedKeys={[
'2'
]}
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Disallow Empty Selection
To disallow empty selection, you can use the disallowEmptySelection prop.
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={['2']}
disallowEmptySelection
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={['2']}
disallowEmptySelection
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
defaultSelectedKeys={[
'2'
]}
disallowEmptySelection
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Disabled rows
You can disable certain rows from being selected by passing in the disabledKeys prop.
<Table aria-label="Users" selectionMode="multiple" disabledKeys={['2']}>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
disabledKeys={['2']}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column>Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
selectionMode="multiple"
disabledKeys={['2']}
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Sorting
The table has built-in sorting through pressing the header. The columns that you would like to sort by can be passed the allowsSorting prop.
function Example() {
const columns = [
{ name: 'Name', key: 'name' },
{ name: 'Height', key: 'height' },
{ name: 'Mass', key: 'mass' },
{ name: 'Birth Year', key: 'birth_year' }
];
const list = useAsyncListState({
async loadItems({ signal, cursor }) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json = await response.json();
return {
items: json.results,
cursor: json.next
};
},
async sortItems({ items, sortDescriptor }) {
return {
items: items.sort((a, b) => {
let first = a[sortDescriptor.column];
let second = b[sortDescriptor.column];
let cmp = (parseInt(first) || first) < (parseInt(second) || second)
? -1
: 1;
if (sortDescriptor.direction === 'descending') {
cmp *= -1;
}
return cmp;
})
};
}
});
return (
<Table
aria-label="Users"
sortDescriptor={list.sortDescriptor}
onSortChange={list.sortItems}
style={{ height: '200px' }}
>
<TableHeader columns={columns}>
{(column) => (
<Column key={column.key} allowsSorting>{column.name}</Column>
)}
</TableHeader>
<TableBody
items={list.items}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Row key={item.name}>{(key) => <Cell>{item[key]}</Cell>}</Row>
)}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{ name: 'Name', key: 'name' },
{ name: 'Height', key: 'height' },
{ name: 'Mass', key: 'mass' },
{ name: 'Birth Year', key: 'birth_year' }
];
const list = useAsyncListState({
async loadItems({ signal, cursor }) {
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json = await response.json();
return {
items: json.results,
cursor: json.next
};
},
async sortItems({ items, sortDescriptor }) {
return {
items: items.sort((a, b) => {
let first = a[sortDescriptor.column];
let second = b[sortDescriptor.column];
let cmp =
(parseInt(first) || first) <
(parseInt(second) || second)
? -1
: 1;
if (sortDescriptor.direction === 'descending') {
cmp *= -1;
}
return cmp;
})
};
}
});
return (
<Table
aria-label="Users"
sortDescriptor={list.sortDescriptor}
onSortChange={list.sortItems}
style={{ height: '200px' }}
>
<TableHeader columns={columns}>
{(column) => (
<Column key={column.key} allowsSorting>
{column.name}
</Column>
)}
</TableHeader>
<TableBody
items={list.items}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Row key={item.name}>
{(key) => <Cell>{item[key]}</Cell>}
</Row>
)}
</TableBody>
</Table>
);
}
function Example() {
const columns = [
{
name: 'Name',
key: 'name'
},
{
name: 'Height',
key: 'height'
},
{
name: 'Mass',
key: 'mass'
},
{
name: 'Birth Year',
key: 'birth_year'
}
];
const list =
useAsyncListState({
async loadItems(
{
signal,
cursor
}
) {
if (cursor) {
cursor = cursor
.replace(
/^http:\/\//i,
'https://'
);
}
const response =
await fetch(
cursor ||
`https://swapi.dev/api/people/?search=`,
{
signal
}
);
const json =
await response
.json();
return {
items:
json.results,
cursor:
json.next
};
},
async sortItems(
{
items,
sortDescriptor
}
) {
return {
items: items
.sort(
(a, b) => {
let first =
a[
sortDescriptor
.column
];
let second =
b[
sortDescriptor
.column
];
let cmp =
(parseInt(
first
) ||
first) <
(parseInt(
second
) ||
second)
? -1
: 1;
if (
sortDescriptor
.direction ===
'descending'
) {
cmp *=
-1;
}
return cmp;
}
)
};
}
});
return (
<Table
aria-label="Users"
sortDescriptor={list
.sortDescriptor}
onSortChange={list
.sortItems}
style={{
height: '200px'
}}
>
<TableHeader
columns={columns}
>
{(column) => (
<Column
key={column
.key}
allowsSorting
>
{column.name}
</Column>
)}
</TableHeader>
<TableBody
items={list
.items}
loadingState={list
.loadingState}
onLoadMoreItems={list
.loadMoreItems}
>
{(item) => (
<Row
key={item
.name}
>
{(key) => (
<Cell>
{item[
key
]}
</Cell>
)}
</Row>
)}
</TableBody>
</Table>
);
}
Column Alignment
You can easily change the alignment of the content in the columns through the align prop.
<Table aria-label='Users'>
<TableHeader>
<Column align='start'>First Name</Column>
<Column align='center'>Last Name</Column>
<Column align='end'>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label='Users'>
<TableHeader>
<Column align='start'>First Name</Column>
<Column align='center'>Last Name</Column>
<Column align='end'>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column align="start">
First Name
</Column>
<Column align="center">
Last Name
</Column>
<Column align="end">
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Column Widths
You can set the max of the column by setting the minWidth, width or maxWidth props.
<Table aria-label='Users'>
<TableHeader>
<Column minWidth={100}>First Name</Column>
<Column width={120}>Last Name</Column>
<Column maxWidth={300}>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label='Users'>
<TableHeader>
<Column minWidth={100}>First Name</Column>
<Column width={120}>Last Name</Column>
<Column maxWidth={300}>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column
minWidth={100}
>
First Name
</Column>
<Column
width={120}
>
Last Name
</Column>
<Column
maxWidth={300}
>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Resizable Columns
To resize the table columns you can use the allowsResizing prop together with a defaultWidth prop.
<Table aria-label='Users'>
<TableHeader>
<Column allowsResizing defaultWidth="1fr">First Name</Column>
<Column allowsResizing defaultWidth="2fr">Last Name</Column>
<Column allowsResizing defaultWidth="1fr">Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column allowsResizing defaultWidth="1fr">
First Name
</Column>
<Column allowsResizing defaultWidth="2fr">
Last Name
</Column>
<Column allowsResizing defaultWidth="1fr">Age</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column
allowsResizing
defaultWidth="1fr"
>
First Name
</Column>
<Column
allowsResizing
defaultWidth="2fr"
>
Last Name
</Column>
<Column
allowsResizing
defaultWidth="1fr"
>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Column Dividers
You can show dividers for each column through the showDivider prop.
<Table aria-label='Users'>
<TableHeader>
<Column align="start" showDivider>First Name</Column>
<Column showDivider>Last Name</Column>
<Column align="end" showDivider>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label='Users'>
<TableHeader>
<Column align="start" showDivider>First Name</Column>
<Column showDivider>Last Name</Column>
<Column align="end" showDivider>Age</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>24</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key='3'>
<Cell>Jeff</Cell>
<Cell>Bezos</Cell>
<Cell>57</Cell>
</Row>
<Row key='4'>
<Cell>Tony</Cell>
<Cell>Soprano</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column
align="start"
showDivider
>
First Name
</Column>
<Column
showDivider
>
Last Name
</Column>
<Column
align="end"
showDivider
>
Age
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>24</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>34</Cell>
</Row>
<Row key="3">
<Cell>Jeff</Cell>
<Cell>
Bezos
</Cell>
<Cell>57</Cell>
</Row>
<Row key="4">
<Cell>Tony</Cell>
<Cell>
Soprano
</Cell>
<Cell>47</Cell>
</Row>
</TableBody>
</Table>
Hide header
With the hideHeader prop you can hide the headers of certain columns. This is useful to hide the header for action columns.
The label will then be rendered in a tooltip instead to compensate for the lack of a visual label.
<Table aria-label="Users">
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column hideHeader>Action</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
<Column hideHeader>Action</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users">
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
<Column hideHeader>
Action
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>Doe</Cell>
<Cell>
<Button
iconName="PlusIcon"
iconVariant="outline"
variant="text"
size="small"
aria-label="Add more information"
/>
</Cell>
</Row>
</TableBody>
</Table>
Overflow Mode
By default, the text of the table is truncated if it overflows. By setting the overflowMode prop to 'wrap',
you can wrap the text instead.
<Table aria-label='Users' overflowMode='wrap'>
<TableHeader>
<Column>First Name</Column>
<Column width={120}>Last Name</Column>
</TableHeader>
<TableBody>
<Row key='1'>
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
</Row>
<Row key='2'>
<Cell>John</Cell>
<Cell>Some very very very long text as an example</Cell>
</Row>
</TableBody>
</Table>
<Table aria-label="Users" overflowMode="wrap">
<TableHeader>
<Column>First Name</Column>
<Column width={120}>Last Name</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>Alexander</Cell>
<Cell>Ehrenthal</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>
Some very very very long text as an example
</Cell>
</Row>
</TableBody>
</Table>
<Table
aria-label="Users"
overflowMode="wrap"
>
<TableHeader>
<Column>
First Name
</Column>
<Column
width={120}
>
Last Name
</Column>
</TableHeader>
<TableBody>
<Row key="1">
<Cell>
Alexander
</Cell>
<Cell>
Ehrenthal
</Cell>
</Row>
<Row key="2">
<Cell>John</Cell>
<Cell>
Some very very
very long text
as an example
</Cell>
</Row>
</TableBody>
</Table>
Empty State
To show the user that the table is empty when there is no data available you can use the renderEmptyState prop.
<Table
aria-label="Users"
renderEmptyState={() => (
<IllustratedMessage>
<Icon name="MagnifyingGlassIcon" variant="outline" />
<Heading>No results found</Heading>
<Text>
No results could be found. Please create a new entity or modify your
search.
</Text>
</IllustratedMessage>
)}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
</TableHeader>
<TableBody>
{[]}
</TableBody>
</Table>
<Table
aria-label="Users"
renderEmptyState={() => (
<IllustratedMessage>
<Icon
name="MagnifyingGlassIcon"
variant="outline"
/>
<Heading>No results found</Heading>
<Text>
No results could be found. Please create a new
entity or modify your search.
</Text>
</IllustratedMessage>
)}
>
<TableHeader>
<Column>First Name</Column>
<Column>Last Name</Column>
</TableHeader>
<TableBody>
{[]}
</TableBody>
</Table>
<Table
aria-label="Users"
renderEmptyState={() => (
<IllustratedMessage>
<Icon
name="MagnifyingGlassIcon"
variant="outline"
/>
<Heading>
No results
found
</Heading>
<Text>
No results
could be found.
Please create a
new entity or
modify your
search.
</Text>
</IllustratedMessage>
)}
>
<TableHeader>
<Column>
First Name
</Column>
<Column>
Last Name
</Column>
</TableHeader>
<TableBody>
{[]}
</TableBody>
</Table>