TableV2 is the new version of the Atlas Table.
Example
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
return <TableV2 data={data} columns={columns} />;
}
Variants
Alignment
left
, center
and medium
are the supported alignments, accessed using align prop under columns, left
as default.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name'
},
{
Header: 'Age',
accessor: 'age',
align: 'center'
},
{
Header: 'Room',
accessor: 'room',
align: 'right'
}
];
const data = [
{
name: 'Just a name',
age: 21,
room: 1
},
{
name: 'Another name',
age: 25,
room: 2
}
];
return <TableV2 data={data} columns={columns} />;
}
Column Resizing
The Table supports the column resizing function. You can use isResizable
prop for making desired columns resizable. If you want to limit resizable column width
value, you can use for the column maxWidth
property.
You can use the onColumnResized
callback property to get the new width
of the column
that was resized. The callback will be passed a column argument which is of type TableColumn
.
You can use the minWidth
and maxWidth
properties to limitization the reached resized value. For more information pleace check the Min and Max width example
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
isResizable: true
},
{
Header: 'Last',
accessor: 'lastName',
isResizable: false
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
room: 1
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
room: 2
}
];
const handleColumnResized = (column) => {
console.log(column.Header);
console.log(column.accessor);
console.log(column.isResizable);
console.log(column.width);
};
return (
<TableV2
data={data}
columns={columns}
onColumnResized={handleColumnResized}
/>
);
}
Compact Mode
There is a compact mode enabled by setting compact
to true.
Here we will be reducing the padding by half from left and right.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name'
},
{
Header: 'Role',
accessor: 'role'
}
];
const data = [
{
name: 'Just a name',
role: 'User'
},
{
name: 'Role',
role: 'Admin'
}
];
const visualPropertiesBordered = {
areColumnsBordered: true
};
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
compact
/>
);
}
Custom Cell Renderer
There are Header
, Footer
, Cell
property within the column configuration.
With them you can define the custom render function for the header, the footer and a table cell accordingly.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: ({ value }) => <Badge label={value} />
},
{
Header: 'Points',
accessor: 'points',
align: 'right',
alignHeader: 'right',
Footer: (info) => {
const { rows } = info;
const totalValue = rows.reduce((sum, row) => {
const { values } = row;
const value = values['points'];
if (!value) {
return sum;
}
return sum + parseFloat(value);
}, 0);
return totalValue;
}
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Some lastName',
points: 10
},
{
firstName: 'Another name',
lastName: 'Another lastName',
points: 11
},
{
firstName: 'Random name',
lastName: 'Random lastName',
points: 700
}
];
return (
<TableV2 height={300} data={data} columns={columns} />
);
}
Custom Cell Renderer on Row Hover
If you want to update your custom Cell component when the row it belongs to changes, you can use the getHoveredRowId
method to get the id of the row currently being hovered.
function MyTable() {
const [rowIdHovered, setRowIdHovered] = useState();
const CustomCellComp = ({ value, rowId }) => {
if (rowIdHovered === rowId) {
return <div>row hovered</div>;
} else {
return <div>row not hovered</div>;
}
};
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: ({ value }) => <Badge label={value} />
},
{
Header: 'Points',
accessor: 'points',
align: 'right',
alignHeader: 'right',
Cell: ({ value, row: { id } }) => (
<CustomCellComp value={value} rowId={id} />
),
Footer: (info) => {
const { rows } = info;
const totalValue = rows.reduce((sum, row) => {
const { values } = row;
const value = values['points'];
if (!value) {
return sum;
}
return sum + parseFloat(value);
}, 0);
return totalValue;
}
}
];
const data = Array.from({ length: 1000 }, (_, index) => ({
firstName: `firstName_${index + 1}`,
lastName: `lastName_${index + 1}`,
room: index + 1
}));
return (
<TableV2 data={data} columns={columns} getHoveredRowId={setRowIdHovered} height={500} />
);
}
Custom backgroundColor for the Cells
Changing the default cell background color requires passing custom cell markup. Since table cells have a default padding, in order to render a custom cell that takes the full cell width and height, it must be turned off by setting hasPadding
to false
.
function MyTable() {
const getCustomCell = (value) => {
let color = 'unset';
if (value === 'created') color = ColorPositive10;
if (value === 'removed') color = ColorNegative10;
if (value === 'changed') color = ColorNeutral10;
const style = {
backgroundColor: color,
height: '100%',
display: 'flex',
alignItems: 'center',
padding: '4px 16px'
}
return <div style={style}>{value}</div>;
}
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Activity',
accessor: 'activity',
Cell: ({value}) => getCustomCell(value),
hasPadding: false
},
{
Header: 'Age',
accessor: 'age',
align: 'left'
},
{
Header: 'City',
accessor: (rows) => rows['[user].city'],
id: '[user].city'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
activity: 'created',
age: 36,
job: {
title: 'job_title_1'
},
'[user].city': 'Berlin'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
activity: 'changed',
age: 29,
job: {
title: 'job_title_2'
},
'[user].city': 'Sydney'
},
{
firstName: 'firstName_3',
lastName: 'lastName_3',
activity: 'removed',
age: 30,
job: {
title: 'job_title_3'
},
'[user].city': 'London'
}
];
return (
<TableV2
data={data}
columns={columns}
/>
);
}
DND Column re-ordering
The Table supports the drag-and-drop column re-ordering function. You can use isDraggable
prop for making desired columns draggable for re-ordering.
You can also use onColumnRearrange
callback property to get an ordered array of column accessors as a first argument and perform any action after column is rearranged.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
room: 1
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
room: 2
}
];
const onColumnRearrange = (columnsOrder) => {
console.log('Column Rearranged!', columnsOrder);
};
return (
<TableV2
data={data}
columns={columns}
onColumnRearrange={onColumnRearrange}
/>
);
}
Empty State
You can add an Empty State by using prop emptyState
. It is shown if there is no data.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true,
Footer: () => 'FirstName'
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
const emptyStateProps = {
headLine: 'Opps! No data',
bodyText: 'Please check your searching or filtering.',
buttonLabel: 'Retry',
buttonIconName: 'ArrowCircleRight',
buttonIconAlignment: 'left',
onClickButton: () => {
console.log('clicked');
}
};
return <TableV2 data={[]} columns={columns} emptyState={emptyStateProps} />;
}
First and Last Column padding
You can align first and last columns using the firstColumnLeftPadding
& lastColumnRightPadding
props under visualProperties
.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
const visualProperties = {
firstColumnLeftPadding: 24,
lastColumnRightPadding: 0
};
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualProperties}
/>
);
}
The table also supports the footer cells by adding Footer
in column options.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'FirstName'
},
{
Header: 'Last Name',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
},
{
firstName: 'firstName_3',
lastName: 'lastName_3'
},
{
firstName: 'firstName_4',
lastName: 'lastName_4'
},
{
firstName: 'firstName_5',
lastName: 'lastName_5'
},
{
firstName: 'firstName_6',
lastName: 'lastName_6'
}
];
return <TableV2 data={data} columns={columns} height={240} />;
}
The prop isFooterless
is used for hiding table footers.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'FirstName'
},
{
Header: 'Last Name',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
}
];
return <TableV2 data={data} columns={columns} isFooterless />;
}
Flex Table
The prop flex
supports the flex layout feature by using width
as the flex-basis and flex-grow. This feature becomes useful when implementing both virtualized and resizable tables that must also be able to stretch to fill all available space
You can also add disableResizing: true
to column option to create a fixed width column. That column will not grow to fill positive free space.
To deal with the core column options width
, minWidth
, maxWidth
in a flex table, you should pay the attention as follows:
minWidth
is only used to limit column resizing. It does not define the minimum width for a column.width
is used as both the flex-basis and flex-grow. This means that it essentially acts as both the minimum width and flex-ratio of the column.maxWidth
is only used to limit column resizing. It does not define the maximum width for a column
Flex table doesn't support column virtualization. Please be careful with number of columns will be rendered.
function MyTable() {
const visualPropertiesBordered = {
areColumnsBordered: true
};
const columnsWithSingleFlexGrow = [
{
Header: 'First Name',
accessor: 'firstName',
width: 300,
isDraggable: true,
isResizable: true,
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 200,
isDraggable: true,
isResizable: true,
},
{
Header: 'Room',
accessor: 'room',
width: 100,
minWidth: 100,
isDraggable: true,
isResizable: true,
},
{
Header: 'Age',
accessor: 'age',
width: 70,
minWidth: 70,
disableResizing: true,
isDraggable: true
},
];
const columnsWithGroupHeader = [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
width: 300,
isDraggable: true,
},
{
Header: 'Last Name',
accessor: 'lastName',
isDraggable: true,
},
]
},
{
Header: 'Info',
columns: [
{
Header: 'Age',
accessor: 'age',
width: 70,
minWidth: 70,
disableResizing: true,
isDraggable: true
},
{
Header: 'Room',
accessor: 'room',
isDraggable: true
},
]
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Some lastName',
room: 1234,
age: 34
},
{
firstName: 'Another name',
lastName: 'Another lastName',
room: 1111,
age: 40
}
];
return (
<>
<TableV2
data={data}
columns={columnsWithSingleFlexGrow}
visualProperties={visualPropertiesBordered}
height={200}
flex
/>
<TableV2
data={data}
columns={columnsWithGroupHeader}
visualProperties={visualPropertiesBordered}
height={200}
flex
/>
</>
);
}
Global Search Filter
You can use this feature to globally search through all columns in the table. To exclude specific columns from global searching you can use the disableGlobalFilter
property in your column object.
function MyTable() {
const [searchTerm, setSearchTerm] = useState('');
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'FirstName'
},
{
Header: 'Last Name',
accessor: 'lastName',
disableGlobalFilter: true
}
];
const data = [
{
firstName: 'John',
lastName: 'Doe'
},
{
firstName: 'Richard',
lastName: 'Amstrong'
},
{
firstName: 'Ama',
lastName: 'Doe'
}
];
return (
<div style={{ height: 300, overflow: 'auto' }}>
<div style={{ padding: '25px 15px 35px 15px' }}>
<Input
label="Search"
type="search"
value={searchTerm}
onClear={() => {
setSearchTerm('');
}}
onChange={(e) => {
setSearchTerm(e.target.value);
}}
/>
</div>
<TableV2
data={data}
columns={columns}
searchTerm={searchTerm}
getFilteredData={(filteredValues) => console.log({ filteredValues })}
/>
</div>
);
}
Header cells support using additional actions, such as an Icon Button
for setting filters on the specific column.
As shown in the example, you can use the headerActions
property inside the column
object to assign custom actions to your column.
headerActions
accepts a ReactNode
type value, which means you can compose your custom components for this area.
It's recommended to exclusively employ the IconButton
component as the action trigger element within the headerActions
. Moreover, it's essential to ensure that all IconButton
s adhere to a uniform size="small"
prop. While exceptions can be contemplated, they should undergo a meticulous review process to ensure compatibility.
It's recommended to use isAutoWidth: true
in the Header options when the space is not enough for actions.
function MyTable() {
const HeaderActionItem = () => {
return (
<ActionMenu
items={[
{ label: 'Accept', onClick: () => {}, iconName: 'Check' },
{ label: 'Decline', onClick: () => {}, iconName: 'Cross' },
{ type: 'divider' },
{
label: 'Abort',
onClick: () => {}
}
]}
>
<IconButton iconName="Filter" size="small" aria-label="Filter" />
</ActionMenu>
);
};
const HeaderActions = () => {
return <HeaderActionItem />;
};
const MultipleHeaderActions = () => {
return (
<>
<HeaderActionItem />
<HeaderActionItem />
<HeaderActionItem />
</>
);
};
const columns = [
{
Header: 'First',
accessor: 'firstName',
headerActions: <HeaderActions />
},
{
Header: 'Last',
accessor: 'lastName',
headerActions: <MultipleHeaderActions />
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
return (
<TableV2
data={data}
columns={columns}
visualProperties={{ areColumnsBordered: true }}
/>
);
}
By default, header cell and column cells are aligned according to align
property but there're use-cases when header cell needs a different alignment (e.g. RTL text direction). To align header-cell, alignHeader
property can be specified for any column within columns
array.
The alignHeader
property supports the following options (please refer to the table below which shows all of supported options):
left
- defaultleft-reversed
right
right-reversed
center
center-reversed
space-between
space-between-reversed
function MyTable() {
const longText =
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
const width = 250;
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const popover = {
isIconHighlighted: true,
iconName: 'Star',
isPopoverOpen: isPopoverOpen,
onClick: () => setIsPopoverOpen(true),
content: (
<Tile>
<span>Popover content</span>
</Tile>
)
};
const columns = [
{
popover,
width,
sortable: true,
Header: 'Default',
accessor: 'default'
},
{
popover,
width,
sortable: true,
Header: 'Left',
accessor: 'left',
align: 'right',
alignHeader: 'left'
},
{
popover,
width,
sortable: true,
Header: 'Left reversed',
accessor: 'left-reversed',
align: 'right',
alignHeader: 'left-reversed'
},
{
popover,
width,
sortable: true,
Header: 'Right',
accessor: 'right',
align: 'right',
alignHeader: 'right'
},
{
popover,
width,
sortable: true,
Header: 'Right reversed',
accessor: 'right-reversed',
align: 'right',
alignHeader: 'right-reversed'
},
{
popover,
width,
sortable: true,
Header: 'Center',
accessor: 'center',
align: 'center',
alignHeader: 'center'
},
{
popover,
width,
sortable: true,
Header: 'Center reversed',
accessor: 'center-reversed',
align: 'center',
alignHeader: 'center-reversed'
},
{
popover,
width,
sortable: true,
Header: 'Space between',
accessor: 'space-between',
align: 'right',
alignHeader: 'space-between'
},
{
popover,
width: 350,
sortable: true,
Header: 'Space between reversed',
accessor: 'space-between-reversed',
align: 'right',
alignHeader: 'space-between-reversed'
}
];
const data = [
{
default: 'A',
left: 'A',
'left-reversed': 'A',
right: 'A',
'right-reversed': 'A',
center: 'A',
'center-reversed': 'A',
'space-between': 'A',
'space-between-reversed': 'A'
},
{
default: 'B',
left: 'B',
'left-reversed': 'B',
right: 'B',
'right-reversed': 'B',
center: 'B',
'center-reversed': 'B',
'space-between': 'B',
'space-between-reversed': 'B'
},
{
default: longText,
left: longText,
'left-reversed': longText,
right: longText,
'right-reversed': longText,
center: longText,
'center-reversed': longText,
'space-between': longText,
'space-between-reversed': longText
}
];
return (
<TableV2
height={250}
data={data}
columns={columns}
activeSorting={{ columnKey: 'default', direction: 'asc' }}
visualProperties={{ areColumnsBordered: true }}
onSortingChange={(columnKey) => console.log(`onSortingChange: ${columnKey}`)}
/>
);
}
To use the grouped header feature, you must configure your columns as nested columns, as in the example below.
function MyTable() {
const visualPropertiesBordered = {
areColumnsBordered: true
};
const columns = [
{
Header: 'Name',
columns: [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true,
isResizable: true
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true,
sortable: false
},
{
Header: 'Given name',
accessor: 'givenName',
isDraggable: true,
sortable: false
}
]
},
{
Header: 'Age',
accessor: 'age',
isDraggable: true,
sortable: false
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
givenName: 'givenName_1',
age: 36
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
givenName: 'givenName_2',
age: 29
},
{
firstName: 'firstName_3',
lastName: 'lastName_3',
age: 30
},
{
firstName: 'firstName_4',
lastName: 'lastName_4',
age: 28
},
{
firstName: 'firstName_5',
lastName: 'lastName_5',
age: 35
},
{
firstName: 'firstName_6',
lastName: 'lastName_6',
age: 32
}
];
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
/>
);
}
You can visually hide your group headers with the hideHeaderGroupTitle
prop.
function MyTable() {
const visualPropertiesBordered = {
areColumnsBordered: true
};
const columns = [
{
Header: 'Name',
columns: [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true,
isResizable: true
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true,
sortable: false
},
{
Header: 'Given name',
accessor: 'givenName',
isDraggable: true,
sortable: false
}
]
},
{
Header: 'Age',
accessor: 'age',
isDraggable: true,
sortable: false
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
givenName: 'givenName_1',
age: 36
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
givenName: 'givenName_2',
age: 29
}
];
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
hideHeaderGroupTitle
/>
);
}
You can set border based on grouping using areColumnGroupsBordered
prop.
function MyTable() {
const visualPropertiesBordered = {
areColumnGroupsBordered: true
};
const columns = [
{
Header: 'Name',
columns: [
{
Header: 'First',
accessor: 'firstName',
isDraggable: true,
isResizable: true
},
{
Header: 'Last',
accessor: 'lastName',
isDraggable: true,
sortable: false
},
{
Header: 'Given name',
accessor: 'givenName',
isDraggable: true,
sortable: false
}
]
},
{
Header: 'More Info',
columns: [
{
Header: 'Phone',
accessor: 'phone'
},
{
Header: 'Email',
accessor: 'email'
},
{
Header: 'Address',
accessor: 'address'
},
]
},
{
Header: 'More Info 2',
columns: [
{
Header: 'City',
accessor: 'city'
},
{
Header: 'Pincode',
accessor: 'pincode'
},
{
Header: 'Country',
accessor: 'country'
},
]
},
{
Header: 'Age',
accessor: 'age',
isDraggable: true,
sortable: false
},
{
Header: 'Work',
accessor: 'work',
isDraggable: true,
sortable: false
},
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1',
givenName: 'givenName_1',
phone: 111111,
email: 'ab_1@mail.com',
address: 'somewhere_1',
city: 'Berlin',
pincode: 111,
country: 'Deutschland',
age: 11,
work: 'work_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
givenName: 'givenName_2',
phone: 2222,
email: 'ab_2@mail.com',
address: 'somewhere_2',
city: 'Berlin_2',
pincode: 222,
country: 'Deutschland_2',
age: 2,
work: 'work_2'
},
{
firstName: 'firstName_3',
lastName: 'lastName_3',
givenName: 'givenName_3',
phone: 333333,
email: 'ab_3@mail.com',
address: 'somewhere_3',
city: 'Berlin_3',
pincode: 3,
country: 'Deutschland_3',
age: 333,
work: 'work_3'
},
{
firstName: 'firstName_4',
lastName: 'lastName_4',
givenName: 'givenName_4',
phone: 444444,
email: 'ab_4@mail.com',
address: 'somewhere_4',
city: 'Berlin_4',
pincode: 444,
country: 'Deutschland_4',
age: 4,
work: 'work_4'
},
{
firstName: 'firstName_5',
lastName: 'lastName_5',
givenName: 'givenName_5',
phone: 5555,
email: 'ab_5@mail.com',
address: 'somewhere_5',
city: 'Berlin_5',
pincode: 555,
country: 'Deutschland_5',
age: 5,
work: 'work_5'
},
{
firstName: 'firstName_6',
lastName: 'lastName_6',
givenName: 'givenName_6',
phone: 66666,
email: 'ab_6@mail.com',
address: 'somewhere_6',
city: 'Berlin_6',
pincode: 666,
country: 'Deutschland_6',
age: 6,
work: 'work_6'
}
];
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
/>
);
}
You can display a menu in header cells by providing your menu content with headerMenuItems
property. You can see the example usage of the headerMenuItems
property below.
The table uses the Action Menu component inside the header cells. You can also check the Action Menu component
examples to get more info about different usages of menu content.
function MyTable() {
const menuItems = [
{
label: 'Duplicate',
onClick: () => {},
iconName: 'Copy'
},
{
label: 'Edit',
onClick: () => {},
iconName: 'Edit'
},
{
type: 'submenu',
label: 'Export',
iconName: 'Download',
submenuItems: [
{ label: 'PNG', onClick: () => {} },
{ label: 'PDF', onClick: () => {} },
{ label: 'CSV', onClick: () => {} }
]
},
{
label: 'Delete',
onClick: () => {},
iconName: 'Trash'
}
];
const columns = [
{
Header: 'Name',
accessor: 'name',
headerMenuItems: menuItems
},
{
Header: 'Age',
accessor: 'age',
align: 'center'
},
{
Header: 'Room',
accessor: 'room',
align: 'right'
}
];
const data = [
{
name: 'Just a name',
age: 21,
room: 1
},
{
name: 'Another name',
age: 25,
room: 2
}
];
const visualProperties = {
isStriped: true
};
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualProperties}
/>
);
}
The prop isHeaderless
is used for hiding table headers.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
return <TableV2 data={data} columns={columns} isHeaderless />;
}
Hover Highlighting
isHoverable
prop can be used to toggle hover highlighting on table rows
. By default, it is set to false.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
return <TableV2 data={data} columns={columns} isHoverable />;
}
Inline editing in Table
You can have inline editing in Table by using updateData
prop and defining custom cell which includes needed editable component such as Input
, ComboBox
, Checkbox
, etc.
function MyTable() {
const InputCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const [value, setValue] = React.useState(initialValue);
const openSnackbar = useContext(SnackbarContext);
const onChange = (e) => {
setValue(e.target.value);
};
const onBlur = () => {
updateData(index, id, value);
openSnackbar({
title: 'Item successfully updated',
kind: 'positive'
});
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return (
<Input
aria-label="age"
value={value}
onChange={onChange}
onBlur={initialValue !== value ? onBlur : undefined}
/>
);
};
const columns = [
{
Header: 'Name',
accessor: 'name',
Cell: InputCell,
isEditable: true
},
{
Header: 'Age',
accessor: 'age'
},
{
Header: 'Team',
accessor: 'team',
Cell: InputCell,
isEditable: true
}
];
const initData = [
{
name: 'Name_1',
age: 36,
team: 'Team_1'
},
{
name: 'Name_2',
age: 29,
team: 'Team_2'
},
{
name: 'Name_3',
age: 30,
team: 'Team_3'
},
{
name: 'Name_4',
age: 28,
team: 'Team_4'
},
{
name: 'Name_5',
age: 35,
team: 'Team_5'
},
{
name: 'Name_6',
age: 32,
team: 'Team_6'
}
];
const [data, setData] = React.useState(initData);
const updateData = (rowIndex, columnId, value) => {
setData((old) =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value
};
}
return row;
})
);
};
return (
<SnackbarProvider offsetTop={76}>
<TableV2 data={data} updateData={updateData} columns={columns} height={200} />
</SnackbarProvider>
);
}
Here is another example with more editable cells with ComboBox, Checkbox, SegmentedButton and nested Objects:
function MyTable() {
const InputCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
setValue(e.target.value);
};
const onBlur = () => {
updateData(index, id, value);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return (
<Input
aria-label="age"
value={value}
onChange={onChange}
onBlur={onBlur}
/>
);
};
const SegmentedButtonCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const cities = [
{ value: 'shanghai', label: 'Shanghai' },
{ value: 'moscow', label: 'Moscow' },
{ value: 'berlin', label: 'Berlin' },
{ value: 'paris', label: 'Paris' }
];
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
const updatedValue = e.target.value;
setValue(updatedValue);
updateData(index, id, updatedValue);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return <SegmentedButton items={cities} value={value} onChange={onChange} />;
};
const JobCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
const { name, value } = e.target;
setValue((prevState) => ({
...prevState,
[name]: value
}));
};
const onBlur = () => {
updateData(index, id, value);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return (
<div style={{ display: 'flex' }}>
<Input
css={{ marginRight: 8 }}
aria-label="age"
name="title"
value={value.title}
onChange={onChange}
onBlur={onBlur}
/>
<Input
aria-label="age"
name="team"
value={value.team}
onChange={onChange}
onBlur={onBlur}
/>
</div>
);
};
const EditableCheckboxCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
setValue(e.target.checked);
updateData(index, id, e.target.checked);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return <Checkbox aria-label="job" checked={value} onChange={onChange} />;
};
const EditableComboBoxCell = ({
value: initialValue,
row: { index },
column: { id },
updateData
}) => {
const options = [
{ value: 'notDefined', label: 'Not defined' },
{ value: 'female', label: 'Female' },
{ value: 'male', label: 'Male' }
];
const [value, setValue] = React.useState({
label: initialValue,
value: initialValue
});
const onBlur = () => {
updateData(index, id, value.value);
};
React.useEffect(() => {
const updatedValue = { label: initialValue, value: initialValue };
setValue(updatedValue);
}, [initialValue]);
return (
<ComboBox
aria-label="Gender"
options={options}
value={value}
placeholder="Gender"
onChange={(value) => setValue(value)}
onBlur={onBlur}
/>
);
};
const columns = [
{
Header: 'Name',
accessor: 'name',
Cell: InputCell,
isEditable: true
},
{
Header: 'Gender',
accessor: 'gender',
Cell: EditableComboBoxCell,
isEditable: true
},
{
Header: 'Job',
accessor: (row) => row.job,
id: 'job',
Cell: JobCell,
isEditable: true,
width: 300
},
{
Header: 'City',
accessor: 'city',
Cell: SegmentedButtonCell,
isEditable: true,
width: 320
},
{
Header: 'Is Active',
accessor: 'isActive',
Cell: EditableCheckboxCell,
isEditable: true
}
];
const initData = [
{
name: 'Name_1',
age: 36,
gender: 'notDefined',
city: 'berlin',
job: {
title: 'job_title_1',
team: 'team_1'
},
isActive: true
},
{
name: 'Name_2',
age: 29,
gender: 'male',
city: 'paris',
job: {
title: 'job_title_2',
team: 'team_2'
},
isActive: false
},
{
name: 'Name_3',
age: 30,
gender: 'female',
city: 'moscow',
job: {
title: 'job_title_3',
team: 'team_3'
},
isActive: true
},
{
name: 'Name_4',
age: 28,
gender: 'notDefined',
city: 'berlin',
job: {
title: 'job_title_4',
team: 'team_4'
},
isActive: false
},
{
name: 'Name_5',
age: 35,
gender: 'female',
city: 'paris',
job: {
title: 'job_title_5',
team: 'team_5'
},
isActive: true
},
{
name: 'Name_6',
age: 32,
gender: 'male',
city: 'berlin',
job: {
title: 'job_title_6',
team: 'team_6'
},
isActive: true
}
];
const [data, setData] = React.useState(initData);
const updateData = (rowIndex, columnId, value) => {
setData((old) =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value
};
}
return row;
})
);
};
return (
<TableV2 data={data} updateData={updateData} columns={columns} height={200} />
);
}
Link Cell Renderer
There are Header
, Footer
, Cell
property within the column configuration.
With them you can define the custom render function for the header, the footer and a table cell accordingly.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: ({ value }) => <a href="#">value</a>
},
{
Header: 'Points',
accessor: 'points',
align: 'right',
alignHeader: 'right',
Footer: (info) => {
const { rows } = info;
const totalValue = rows.reduce((sum, row) => {
const { values } = row;
const value = values['points'];
if (!value) {
return sum;
}
return sum + parseFloat(value);
}, 0);
return totalValue;
}
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Some lastName',
points: 10
},
{
firstName: 'Another name',
lastName: 'Another lastName',
points: 11
},
{
firstName: 'Random name',
lastName: 'Random lastName',
points: 700
}
];
return (
<TableV2 height={300} data={data} columns={columns} />
);
}
Link Row Renderer
External link component (eg: react-router-dom)by adding property as
and its attributes to
, href
, replace
etc., within the data configuration.
On top of it the Cell Renderer can be used for Custom Link cells.
import { Link } from 'react-router-dom';
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: ({ value }) => <a href="#">value</a>
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Some lastName',
as: Link,
to: 'https://www.example.com'
},
{
firstName: 'Another name',
lastName: 'Another lastName'
as: Link,
to: {
pathname: "/courses",
hash: "#courses",
}
},
{
firstName: 'Random name',
lastName: 'Random lastName',
as: Link,
to: {
pathname: "/courses",
search: "?sort=name",
hash: "#random",
},
target: '_blank'
}
];
return (
<TableV2 data={data} columns={columns} />
);
}
Loading More Data (aka Infinite loading)
Table
supports infinite loading by calling onLoadMoreData
callback when Table gets scrolled to the end. To integrate infinite loading with React Query please refer to useInfiniteQuery hook and this example.
function MyTable() {
const generateMoreData = useCallback(
(offset = 0, chunkSize = 25) =>
Array.from({ length: chunkSize }, (_, index) => {
const id = offset + index + 1;
return {
firstName: `firstName_${id}`,
lastName: `lastName_${id}`,
points: id
};
}),
[]
);
const initialData = useMemo(() => generateMoreData(), [generateMoreData]);
const [data, setData] = useState(initialData);
const dataLength = data.length;
const [isLoadingMoreData, setIsLoadingMoreData] = useState(false);
const columns = useMemo(
() => [
{
Header: 'First Name',
accessor: 'firstName'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: ({ value }) => <Badge label={value} />
},
{
Header: 'Points',
accessor: 'points',
align: 'right'
}
],
[]
);
const onLoadMoreData = useCallback(() => {
setIsLoadingMoreData(true);
setTimeout(() => {
const generatedData = generateMoreData(dataLength);
setData((initialData) => [...initialData, ...generatedData]);
setIsLoadingMoreData(false);
}, 3000);
}, [dataLength, generateMoreData]);
const maxNumberOrRows = 200;
return (
<TableV2
data={data}
columns={columns}
hasMoreData={dataLength <= maxNumberOrRows}
isLoadingMoreData={isLoadingMoreData}
onLoadMoreData={onLoadMoreData}
height={400}
/>
);
}
Loading State
You can active a loading state for the table by passing isLoading
to be true. This helps users to see a skeleton cell when data is loading.
Also the loader height adapts to the typeScales
of cellTypeScale
and width
is random, and also adapts the column alignments.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
return <TableV2 data={data} columns={columns} isLoading />;
}
Min and Max width
The table supports a custom minimum width option for desired columns. You can use the minWidth
property for each column to change the minimum width value.
If you do not use the minWidth property for the column, column will take the minWidth 150px
width value by default.
The table supports a custom maximum width option for desired columns. You can use the maxWidth
property for each column to change the maximum width value.
If you do not use the maxWidth property for the column, column will take the maxWidth 500px width value by default.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
minWidth: 600
},
{
Header: 'Last Name',
accessor: 'lastName',
maxWidth: 200
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Last name 1'
},
{
firstName: 'Very very very long name',
lastName: 'Last name 2'
},
{
firstName: 'Random name',
lastName: 'Last name 3'
}
];
return <TableV2 data={data} columns={columns} />;
}
Nested Rows
You can have nested rows by adding subRows
to data as the following example.
You can use the following callbacks to customize toggle in expandable rows by passing them to the Cell
render prop .
toggleRowExpanded(rowId, isExpanded?)
: toggle a specific row.toggleAllRowsExpanded(isExpanded?)
: toggle all rows.
You can add prop autoResetExpanded
to component to allow the expanded state to automatically reset if data changes, default is false.
function MyTable() {
const [data, setData] = useState([
{
firstName: '',
lastName: '',
age: '',
job: ''
},
{
firstName: 'firstName_1',
lastName: 'lastName_1',
age: 36,
job: {
title: 'job_title_1'
},
subRows: [
{
firstName: 'sub firstName_1',
lastName: 'sub lastName_1',
job: {
title: 'sub job_title_1'
}
},
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
age: 306,
job: {
title: 'sub job_title_2'
},
subRows: [
{
firstName: 'sub firstName_2_1',
lastName: 'sub lastName_2_1',
job: {
title: 'sub job_title_2_1'
}
},
{
firstName: 'firstName_2_2',
lastName: 'lastName_2_2',
job: {
title: 'job_title_2_2'
},
subRows: [
{
firstName: 'last firstName_1',
lastName: 'last lastName_1',
job: {
title: 'last job_title_1'
}
},
{
firstName: 'last firstName_2',
lastName: 'last lastName_2',
job: {
title: 'last job_title_2'
}
},
{
firstName: 'last firstName_3',
lastName: 'last lastName_3',
job: {
title: 'last job_title_3'
}
},
]
}
]
},
{
firstName: 'sub firstName_3',
lastName: 'sub lastName_3',
job: {
title: 'sub job_title_3'
}
}
]
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
age: 36,
job: {
title: 'job_title_2'
},
subRows: [
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
job: {
title: 'sub job_title_2'
}
},
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
age: 306,
job: {
title: 'sub job_title_2'
}
}
]
},
{
firstName: 'firstName_3',
lastName: 'lastName_3',
age: 28,
job: {
title: 'job_title_3'
},
subRows: [
{
firstName: 'sub firstName_3_1',
lastName: 'sub lastName_3_1',
job: {
title: 'sub job_title_3_1'
}
},
{
firstName: 'sub firstName_3_2',
lastName: 'sub lastName_3_2',
job: {
title: 'sub job_title_3_2'
}
}
]
},
{
firstName: 'firstName_4',
lastName: 'lastName_4',
age: 48,
job: {
title: 'job_title_4'
},
}
]);
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
minWidth: 220
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Age',
accessor: 'age',
align: 'left'
},
{
Header: 'Job',
accessor: (rows) => rows.job.title,
id: 'title'
},
{
Header: 'Action',
accessor: '-',
Cell: ({ row, toggleRowExpanded, toggleAllRowsExpanded }) => {
if (row.index === 0 && row.depth === 0) {
return (<Button
label="Toggle All"
onClick={() => {
toggleAllRowsExpanded();
}}
/>)
}
return row.depth === 0 ? (
<Button
label="Collapse"
onClick={() => {
toggleRowExpanded(row.id, false);
}}
/>
) : null;
}
}
];
const visualPropertiesStripe = {
isStriped: true,
areColumnsBordered: true
};
return (
<TableV2
height={300}
data={data}
columns={columns}
visualProperties={visualPropertiesStripe}
/>
);
}
Nested Rows Indentation
You can add or remove the indentation for the non-expandable rows of the table using isNestedTable
prop
function MyTable() {
const [data, setData] = useState([
{
firstName: '',
lastName: '',
age: '',
job: ''
},
{
firstName: 'firstName_1',
lastName: 'lastName_1',
age: 36,
job: {
title: 'job_title_1'
},
subRows: [
{
firstName: 'sub firstName_1',
lastName: 'sub lastName_1',
job: {
title: 'sub job_title_1'
}
},
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
age: 306,
job: {
title: 'sub job_title_2'
},
subRows: [
{
firstName: 'sub firstName_2_1',
lastName: 'sub lastName_2_1',
job: {
title: 'sub job_title_2_1'
}
},
{
firstName: 'firstName_2_2',
lastName: 'lastName_2_2',
job: {
title: 'job_title_2_2'
},
subRows: [
{
firstName: 'last firstName_1',
lastName: 'last lastName_1',
job: {
title: 'last job_title_1'
}
},
{
firstName: 'last firstName_2',
lastName: 'last lastName_2',
job: {
title: 'last job_title_2'
}
},
{
firstName: 'last firstName_3',
lastName: 'last lastName_3',
job: {
title: 'last job_title_3'
}
},
]
}
]
},
{
firstName: 'sub firstName_3',
lastName: 'sub lastName_3',
job: {
title: 'sub job_title_3'
}
}
]
},
{
firstName: 'firstName_2',
lastName: 'lastName_2',
age: 36,
job: {
title: 'job_title_2'
},
subRows: [
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
job: {
title: 'sub job_title_2'
}
},
{
firstName: 'sub firstName_2',
lastName: 'sub lastName_2',
age: 306,
job: {
title: 'sub job_title_2'
}
}
]
},
{
firstName: 'firstName_3',
lastName: 'lastName_3',
age: 28,
job: {
title: 'job_title_3'
},
subRows: [
{
firstName: 'sub firstName_3_1',
lastName: 'sub lastName_3_1',
job: {
title: 'sub job_title_3_1'
}
},
{
firstName: 'sub firstName_3_2',
lastName: 'sub lastName_3_2',
job: {
title: 'sub job_title_3_2'
}
}
]
},
{
firstName: 'firstName_4',
lastName: 'lastName_4',
age: 48,
job: {
title: 'job_title_4'
},
}
]);
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
minWidth: 220
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Age',
accessor: 'age',
align: 'left'
},
{
Header: 'Job',
accessor: (rows) => rows.job.title,
id: 'title'
},
{
Header: 'Action',
accessor: '-',
Cell: ({ row, toggleRowExpanded, toggleAllRowsExpanded }) => {
if (row.index === 0 && row.depth === 0) {
return (<Button
label="Toggle All"
onClick={() => {
toggleAllRowsExpanded();
}}
/>)
}
return row.depth === 0 ? (
<Button
label="Collapse"
onClick={() => {
toggleRowExpanded(row.id, false);
}}
/>
) : null;
}
}
];
const visualPropertiesStripe = {
isStriped: true,
areColumnsBordered: true
};
return (
<TableV2
height={300}
data={data}
columns={columns}
visualProperties={visualPropertiesStripe}
isNestedTable
/>
);
}
Nested Rows with Navigation
- The Nested Rows also supports a navigation in the header, which able to collapse the expanded rows of its group data.
- You need to define an array of
headerNavigationItems
. It will map the group data and the row depth.
Header Navigation doesn't support isAutoWidth
.
function MyTable() {
const columns = [
{
Header: 'Network',
accessor: 'network',
minWidth: 400
},
{
Header: 'Date',
accessor: 'date'
},
{
Header: 'Country',
accessor: 'country'
},
{
Header: 'Status',
accessor: 'status'
}
];
const data = [
{
network: 'Network A',
date: '2023',
country: 'A',
status: 'Online',
subRows: [
{
network: 'Campaign 1',
date: '2023',
country: 'A',
status: 'Online',
subRows: [
{
network: 'Cohort 1.1',
date: '2023',
country: 'B',
status: 'Online',
subRows: [
{
network: 'Ads 1.1.1',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Ads 1.1.2',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Ads 1.1.3',
date: '2023',
country: 'B',
status: 'Online'
}
]
},
{
network: 'Cohort 1.2',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Cohort 1.3',
date: '2023',
country: 'B',
status: 'Online'
}
]
},
{
network: 'Campaign 2',
date: '2023',
country: 'A',
status: 'Online'
},
{
network: 'Campaign 3',
date: '2023',
country: 'A',
status: 'Online'
}
]
},
{
network: 'Network B',
date: '2023',
country: 'B',
status: 'Online',
subRows: [
{
network: 'Campaign 1',
date: '2023',
country: 'B',
status: 'Online',
subRows: [
{
network: 'Cohort 1.1',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Cohort 1.2',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Cohort 1.3',
date: '2023',
country: 'B',
status: 'Online'
}
]
},
{
network: 'Campaign 2',
date: '2023',
country: 'B',
status: 'Online'
},
{
network: 'Campaign 3',
date: '2023',
country: 'B',
status: 'Online'
}
]
}
];
const headerNavigationItems = [
{
title: 'Network'
},
{
title: 'Campaign'
},
{
title: 'Cohort'
},
{
title: 'Ads'
}
]
const visualPropertiesStripe = {
isStriped: true,
areColumnsBordered: true
};
return (
<TableV2
data={data}
columns={columns}
height={300}
visualProperties={visualPropertiesStripe}
headerNavigationItems={headerNavigationItems}
/>
);
}
Page level Scrolling
When table height is set to full-height
, page level scrolling is enabled. Please find the example below.
align
on Table columns
prop can't be used with flex
at the same time unless the table takes the full width
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'This is a footer'
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Room',
accessor: 'room',
align: 'right'
},
{
Header: 'points',
accessor: 'points',
align: 'right'
},
{
Header: 'cats',
accessor: 'cats',
align: 'right'
},
{
Header: 'birds',
accessor: 'birds',
align: 'right'
},
{
Header: 'fruits',
accessor: 'fruits',
align: 'right'
}
];
const data = Array.from({ length: 20 }, (_, index) => ({
firstName: `firstName_${index + 1}`,
lastName: `lastName_${index + 1}`,
room: index + 1
}));
return (
<div style={{ width: 600 }}>
<TableV2 data={data} columns={columns} height="full-height"/>
</div>
);
}
Popover
You can display popover
in header cell
and, inside it, custom components by providing the popover
object with the column
where you want to show the popover.
You can use the following options inside the popover
object; iconName
: specify the icon for the icon button which opens the popover. iconSize
is an optional property for setting the icon size (small | medium)
. isIconHighlighted
is an optional boolean property for highlighting the popover icon button.
isPopoverOpen
: is an optional boolean property for controlling popover visibility externally. onClick
is an optional property to define the custom onClick function for the popover button.
content
is a required property for providing the content we want to show in popover area. You can send your custom component as popover content with this property.
You can also use the optional onPopoverClick
callback. It's sent to parent component the id
of the clicked popover
button.
function MyTable() {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const ComboBoxRenderer = ({ setIsPopoverOpen }) => {
const openSnackbar = useContext(SnackbarContext);
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' }
];
const handleOnChange = (e) => {
openSnackbar({
title: `onChange happened. Value selected is ${e.value}`,
kind: 'positive'
});
};
return (
<Tile>
<div style={{ margin: '10px 0 20px 0', minWidth: '300px' }}>
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
onChange={handleOnChange}
/>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
label="Cancel"
kind="secondary"
style={{ marginRight: '8px' }}
onClick={() => setIsPopoverOpen(false)}
/>
<Button
label="Apply"
kind="primary"
onClick={() => setIsPopoverOpen(false)}
/>
</div>
</Tile>
);
};
const columns = [
{
Header: 'First',
accessor: 'firstName',
popover: {
isIconHighlighted: true,
iconName: 'Star',
isPopoverOpen: isPopoverOpen,
onClick: () => setIsPopoverOpen(true),
content: <ComboBoxRenderer setIsPopoverOpen={setIsPopoverOpen} />
}
},
{
Header: 'Last',
accessor: 'lastName',
popover: {
iconName: 'Star',
onClick: () => console.log('popover clicked'),
content: (
<Tile>
<span>Popover content</span>
</Tile>
)
}
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
const onPopoverClick = (id) => {
return console.log('clicked popover id', id);
};
return (
<TableV2 data={data} columns={columns} onPopoverClick={onPopoverClick} />
);
}
Row onClick handler
You can pass a method to the table that would return the Row object on click of a row.
Be mindful of onClick events on the cell content when using this feature.
Check the code for an example of a cell with a button that includes a `e.stopPropagation` call to avoid triggering the row method when the button is clicked.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
const rowOnClickHandler = (rowData) => {
console.log(rowData);
};
return (
<TableV2
data={data}
columns={columns}
rowOnClickHandler={rowOnClickHandler}
/>
);
}
Row Selection (Checkboxes)
You can add the boolean prop rowSelection
to component to allow row selection with checkboxes. Beside that, you can control the selection state with following props:
selectedRowId: Record<string, boolean>
to set selected rows and subRows.onRowSelect: (selectedRowIds: Record<string, boolean>) => void
is a callback function when rows are selected.
You can also disable the row selection by adding prop disabled
to row data.
If you want to use selectedRowId
to check a row and its subRows, please add all subRow's ids to selectedRowId
(not included disabled subRows).
❌ Incorrect: Only row id 2 was checked, its subRows were not checked.
<TableV2
height={300}
data={data}
columns={columns}
rowSelection
selectedRowIds={ '2': true }
onRowSelect={onRowSelect}
/>
✅ Correct: row id 2 was checked and its subRows were also checked.
<TableV2
height={300}
data={data}
columns={columns}
rowSelection
selectedRowIds={ '2.0': true, '2.1': true }
onRowSelect={onRowSelect}
/>
Following example is how to manage selection state from outside of table.
function MyTable() {
const data = [
{
firstName: 'first_0',
lastName: 'last_0',
age: 36,
job: {
title: 'job_0'
},
subRows: [
{
firstName: 'first_0_0',
lastName: 'last_0_0',
job: {
title: 'sub job_title_1'
}
},
{
firstName: 'first_0_1',
lastName: 'last_0_1',
age: 306,
job: {
title: 'job_0_1'
},
subRows: [
{
firstName: 'first_0_1_0',
lastName: 'last_0_1_0',
job: {
title: 'job_0_1_0'
}
},
{
firstName: 'first_0_1_1',
lastName: 'last_0_1_1',
job: {
title: 'job_0_1_1'
},
disabled: true,
subRows: [
{
firstName: 'first_0_1_1_0',
lastName: 'last_0_1_1_0',
job: {
title: 'job_0_1_1_0'
}
},
{
firstName: 'first_0_1_1_1',
lastName: 'last_0_1_1_1',
job: {
title: 'job_0_1_1_1'
}
},
{
firstName: 'first_0_1_1_2',
lastName: 'last_0_1_1_2',
job: {
title: 'job_0_1_1_2'
}
},
]
}
]
},
{
firstName: 'first_0_2',
lastName: 'last_0_2',
job: {
title: 'job_0_2'
}
}
]
},
{
firstName: 'first_1',
lastName: 'last_1',
age: 36,
job: {
title: 'job_1'
},
disabled: true,
subRows: [
{
firstName: 'first_1_0',
lastName: 'last_1_0',
job: {
title: 'job_1_0'
}
},
{
firstName: 'first_1_1',
lastName: 'last_1_1',
age: 306,
job: {
title: 'job_1_1'
}
}
]
},
{
firstName: 'first_2',
lastName: 'last_2',
age: 28,
job: {
title: 'job_2'
},
subRows: [
{
firstName: 'first_2_0',
lastName: 'last_2_0',
job: {
title: 'job_2_0'
},
},
{
firstName: 'first_2_1',
lastName: 'last_2_1',
job: {
title: 'job_2_1'
},
}
]
},
{
firstName: 'first_3',
lastName: 'last_3',
age: 28,
job: {
title: 'job_3'
},
subRows: [
{
firstName: 'first_3_0',
lastName: 'last_3_0',
job: {
title: 'job_3_0'
},
disabled: true
},
{
firstName: 'first_3_1',
lastName: 'last_3_1',
job: {
title: 'job_3_1'
},
disabled: true
}
]
},
{
firstName: 'first_4',
lastName: 'last_4',
age: 28,
job: {
title: 'job_4'
},
subRows: [
{
firstName: 'first_4_0',
lastName: 'last_4_0',
job: {
title: 'job_4_0'
},
},
{
firstName: 'first_4_1',
lastName: 'last_4_1',
job: {
title: 'job_4_1'
}
}
]
}
];
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
minWidth: 220
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Age',
accessor: 'age',
align: 'left'
},
{
Header: 'Job',
accessor: (rows) => rows.job.title,
id: 'title'
}
];
const visualPropertiesStripe = {
isStriped: true,
areColumnsBordered: true
};
const initialSelectedRowIds = { '2.0': true, '2.1': true }
const [selectedRowIds, setSelectedRowIds] = useState(initialSelectedRowIds);
const onRowSelect = (selected) => {
console.log('onRowSelect', selected);
setSelectedRowIds(selected);
}
return (
<>
<Button label='Clear' onClick={() => setSelectedRowIds({})} />
<Button label='Reset' onClick={() => setSelectedRowIds(initialSelectedRowIds)} />
<Button label='New check' onClick={() => setSelectedRowIds({'4.0': true, '4.1': true})} />
<TableV2
height={300}
data={data}
columns={columns}
visualProperties={visualPropertiesStripe}
rowSelection
selectedRowIds={selectedRowIds}
onRowSelect={onRowSelect}
/>
</>
);
}
Moreover, you can set checkboxes column as sticky column by adding stickyRowSelection
to table prop.
function MyTable() {
const columns = Array.from({ length: 10 }, (_, index) => {
return {
Header: `Header ${index}`,
accessor: `column_${index}`,
sticky: index < 1 ? 'left' : undefined,
Footer: () => `Footer_${index}`
}
});
const data = Array.from({ length: 20 }, (_, i) => {
let obj = {};
Array.from({ length: 10 }, (_, index) => {
obj[`column_${index}`] = `data_${i}_${index} test`
});
return obj;
});
return <TableV2 data={data} columns={columns} height={400} rowSelection stickyRowSelection/>;
}
Row size
small
, medium
and large
are the supported row sizes, accessed using rowSize prop, medium
as default.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name'
},
{
Header: 'Role',
accessor: 'role'
}
];
const data = [
{
name: 'Just a name',
role: 'User'
},
{
name: 'Role',
role: 'Admin'
}
];
return (
<div
style={{
height: 400,
overflow: 'auto',
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: Spacing20
}}
>
<TableV2 data={data} columns={columns} rowSize="small" />
<TableV2 data={data} columns={columns} rowSize="medium" />
<TableV2 data={data} columns={columns} rowSize="large" />
</div>
);
}
Row with Auto Height
As the default, the height of rows respect to the rowSize
. But when autoRowsHeight
prop is used, rows in the table will grow in respect to their content.
To remeasure the height of specific cell, you can use the callback measureRow(rowId, cellIndex)
which is passed to Cell
render prop.
If migrating this feature from Table V1, the callback measureRow(rowId, cellIndex)
will be a breaking change.
In the example below, typing in any of the ComboBox will grow the table row, measureRow
has been used to remeasure the row height.
function MyTable() {
const MultiCreatable = ({
measure,
rowId
}) => {
const initialValues = useMemo(() => {
const randomStr = () => Math.random().toString(36).substring(7);
return Array.from({ length: 8 }, () => {
const label = randomStr();
return {
label,
value: label
};
});
}, []);
const [value, setValue] = useState(initialValues);
const [options, setOptions] = useState([]);
const [inputValue, setInputValue] = useState('');
useEffect(() => {
if (measure) measure(rowId, 1);
}, [value, options, inputValue]);
const handleChange = (value) => {
setValue(value);
};
const handleInput = (inputValue) => {
setInputValue(inputValue);
};
const onCreateOption = useCallback(
(inputValue) => {
const newOption = {
value: `${inputValue}-${value.length}`,
label: inputValue
};
setValue([...value, newOption]);
setOptions([...options, newOption]);
setInputValue('');
},
[options, value]
);
const handleKeyDown = (event) => {
if (!inputValue) return;
switch (event.key) {
case 'Enter':
onCreateOption(inputValue);
event.preventDefault();
return;
}
};
return (
<ComboBox
kind="multi-creatable"
value={value}
inputValue={inputValue}
options={options}
onCreateOption={onCreateOption}
onChange={handleChange}
onInputChange={handleInput}
onKeyDown={handleKeyDown}
placeholder="Type anything and press `Enter`"
aria-label="creatable-example"
/>
);
};
const data = [
{
firstName: 'first_0',
lastName: 'last_0',
age: 36,
subRows: [
{
firstName: 'first_0_0',
lastName: 'last_0_0'
},
{
firstName: 'first_0_1',
lastName: 'last_0_1',
age: 306,
subRows: [
{
firstName: 'first_0_1_0',
lastName: 'last_0_1_0',
},
{
firstName: 'first_0_1_1',
lastName: 'last_0_1_1',
disabled: true,
subRows: [
{
firstName: 'first_0_1_1_0',
lastName: 'last_0_1_1_0',
},
{
firstName: 'first_0_1_1_1',
lastName: 'last_0_1_1_1',
},
{
firstName: 'first_0_1_1_2',
lastName: 'last_0_1_1_2',
}
]
}
]
},
{
firstName: 'first_0_2',
lastName: 'last_0_2',
}
]
},
{
firstName: 'first_1',
lastName: 'last_1',
age: 36,
disabled: true,
subRows: [
{
firstName: 'first_1_0',
lastName: 'last_1_0',
},
{
firstName: 'first_1_1',
lastName: 'last_1_1',
age: 306,
}
]
},
{
firstName: 'first_2',
lastName: 'last_2',
age: 28
},
{
firstName: 'first_3',
lastName: 'last_3',
age: 28
}
];
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
minWidth: 220,
isResizable: true
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 400,
Cell: ({
row: { id },
measureRow,
value
}) => {
if (id === '2') {
return <MultiCreatable measure={measureRow} rowId={id} />;
}
if (id === '3') {
return (
<div>
{Array(12)
.fill('Adjust rocks')
.map((v, i) => (
<Badge key={`${v}-${i}`} label={`${v}-${i}`} />
))}
</div>
);
}
if (id === '0.1') {
return (
<div>
{Array(12)
.fill('Sub rows')
.map((v, i) => (
<Badge key={`${v}-${i}`} label={`${v}-${i}`} />
))}
</div>
);
}
return value;
}
},
{
Header: 'Age',
accessor: 'age',
align: 'left'
}
];
const visualPropertiesBordered = {
areColumnsBordered: true
};
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
autoRowsHeight
/>
);
}
Sorting
The table always shows data in the provided order and the actual sorting functionality should be implemented on your side.
You can specify the activeSorting
to visually highlight the sorting for a column and onSortingChange
to handle clicks within the header cell.
onSortingChange
will return the columnKey and sorting direction will be determined by the activeSorting you provide.
You can disable sorting for a specific column by specifying sortable
in the columns
array.
You can also provide multiple values with activeSorting
prop to visually highlight the active sorting of multiple columns. Please check initialSortingMultiple
in the below example.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name',
sortable: true
},
{
Header: 'Age',
accessor: 'age',
align: 'center'
},
{
Header: 'Room',
accessor: 'room',
align: 'right',
sortable: true
}
];
const data = [
{
name: 'Just a name',
age: 21,
room: 1
},
{
name: 'Another name',
age: 25,
room: 2
}
];
const initialSorting = {
columnKey: 'name',
direction: 'asc'
};
const initialSortingMultiple = [
{
columnKey: 'name',
direction: 'asc'
},
{
columnKey: 'room',
direction: 'desc'
}
];
const handleSorting = (id) => {
console.log('column key:', id);
};
return (
<>
<TableV2
data={data}
columns={columns}
activeSorting={initialSorting}
onSortingChange={handleSorting}
/>
<TableV2
data={data}
columns={columns}
activeSorting={initialSortingMultiple}
onSortingChange={handleSorting}
/>
</>
);
}
Sticky Columns
You can specify sticky
field within the column configuration to make the column "stick" either to the left or to the right side of the table.
Columns with sticky: 'right'
should be placed in the end of the columns
array.
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
sticky: 'left',
Footer: 'Total'
},
{
Header: 'Last',
accessor: 'lastName',
width: 400
},
{
Header: 'City',
accessor: 'city'
},
{
Header: 'Age',
accessor: 'age'
},
{
Header: 'Gender',
accessor: 'gender',
},
{
Header: 'Room',
accessor: 'room',
sticky: 'right',
Footer: '3'
}
];
const data = [
{
firstName: 'Just a name',
lastName: 'Last name',
city: 'Berlin',
age: 21,
gender: 'Male',
room: 1
},
{
firstName: 'Another name',
lastName: 'Another Last name',
city: 'Berlin',
age: 25,
gender: 'Female',
room: 2
}
];
return <TableV2 data={data} columns={columns} />;
}
Table Height
The prop height
supports value type with number
and string
. And it also supports full-height
to allow Page level Scrolling.
if height
is not providded, the height of table will be the height of the total rows but the rows are not virtualized. Therefore it's not recommended for large data.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'This is a footer'
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'Room',
accessor: 'room',
align: 'right'
},
{
Header: 'Points',
accessor: 'points',
align: 'right'
},
];
const data = Array.from({ length: 20 }, (_, index) => ({
firstName: `firstName_${index + 1}`,
lastName: `lastName_${index + 1}`,
room: index + 1
}));
const [tableHeight, setTableHeight] = useState(350);
const [containerHeight, setContainerHeight] = useState(undefined);
const handleFixedHeight = () => {
setContainerHeight(undefined);
setTableHeight(350);
}
const handlePercentHeight = () => {
setContainerHeight(350);
setTableHeight('100%');
}
return (
<>
<div style={{marginBottom: '16px'}}>
<Button label="Fixed Height" size="small" onClick={handleFixedHeight} />
<Button label="100% Height" size="small" onClick={handlePercentHeight} />
</div>
<Text type="subline">Table height is: {tableHeight == '100%' ? '100% of container': `${tableHeight}px`}</Text>
<div style={{ height: containerHeight }}>
<TableV2 data={data} columns={columns} height={tableHeight}/>
</div>
</>
);
}
Type Scales
Typescale is the combination of font-size
, line-height
, and letter-spacing
. By default, the type scale of all cells is defined by the size variant of the table.
Adding typeScales
enforces a distinct type scale for the specific table cell(header, body or footer).
function MyTable() {
const columns = [
{
Header: 'First',
accessor: 'firstName',
Footer: 'First Name Footer'
},
{
Header: 'Last',
accessor: 'lastName'
}
];
const data = [
{
firstName: 'firstName_1',
lastName: 'lastName_1'
},
{
firstName: 'firstName_2',
lastName: 'lastName_2'
}
];
const typeScales = {
headerTypeScale: 1,
cellTypeScale: 2,
footerTypeScale: 2
};
return <TableV2 data={data} columns={columns} typeScales={typeScales} />;
}
UnstickThreshold
This component supports the automatic limitation of sticky
columns based on its width. You may need to use a more scrollable area for non-sticky
columns when having both sticky and non-sticky columns together in the Table.
Use unstickThreshold
to specify the percentage of the limitation for sticky columns. For instance, if unstickThreshold
is 50
, the Table will change sticky
columns to non-sticky
when the total width of sticky columns is greater than 50%
of the table width.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
sticky: 'left',
width: 200,
isResizable: true,
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
sticky: 'left',
isResizable: true,
width: 200
},
{
Header: 'Job',
accessor: 'job'
},
{
Header: 'Age',
accessor: 'age'
},
{
Header: 'City',
accessor: 'city'
},
{
Header: 'Gender',
accessor: 'gender'
},
{
Header: 'Birthdate',
accessor: 'birthdate',
align: 'right'
}
];
const data = [
{
firstName: 'name',
lastName: 'lastName',
job: 'engineer',
age: 34,
city: 'Berlin',
gender: 'male',
birthdate: 'November'
},
{
firstName: 'name two',
lastName: 'lastName two',
job: 'developer',
age: 28,
city: 'Berlin',
gender: 'female',
birthdate: 'December'
}
];
return (
<TableV2
data={data}
columns={columns}
unstickThreshold={50}
/>
);
}
UnstickResolution
This feature makes columns to be non-sticky
if the window width resizes under a given value.
Use unstickResolution
to activate this feature, as in the below example.
This feature is an additional feature of unstickThreshold
. It is recommended to be used with the unstickThreshold
while the Atlas responsive table feature is an upcoming plan.
function MyTable() {
const columns = [
{
Header: 'First Name',
accessor: 'firstName',
sticky: 'left',
width: 200,
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
sticky: 'left',
width: 200
},
{
Header: 'Job',
accessor: 'job'
},
{
Header: 'Age',
accessor: 'age'
},
{
Header: 'City',
accessor: 'city'
},
{
Header: 'Gender',
accessor: 'gender'
},
{
Header: 'Birthdate',
accessor: 'birthdate',
align: 'right'
}
];
const data = [
{
firstName: 'name',
lastName: 'lastName',
job: 'engineer',
age: 34,
city: 'Berlin',
gender: 'male',
birthdate: 'November'
},
{
firstName: 'name two',
lastName: 'lastName two',
job: 'developer',
age: 28,
city: 'Berlin',
gender: 'female',
birthdate: 'December'
}
];
return (
<TableV2
data={data}
columns={columns}
unstickThreshold={50}
unstickResolution={1200}
/>
);
}
Virtualized Rows & Columns
Table support virtualization of rows and columns as default, but except for Flex table.
function MyTable() {
const columns = Array.from({ length: 100 }, (_, index) => {
return {
Header: `Header ${index}`,
accessor: `column_${index}`,
sticky: index > 98 ? 'right' : index < 2 ? 'left' : undefined,
isResizable: true
}
});
const data = Array.from({ length: 1000 }, (_, index) => {
let obj = {};
Array.from({ length: 100 }, (_, index) => {
obj[`column_${index}`] = `data_${index}`
});
return obj;
});
return <TableV2 data={data} columns={columns} height={400} />;
}
Visual properties
visualProperties
are used to show visual representation of records. For instance, showing bordered
columns or greyBackgroundHeader
.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name'
},
{
Header: 'Role',
accessor: 'role'
}
];
const data = [
{
name: 'Just a name',
role: 'User'
},
{
name: 'Role',
role: 'Admin'
}
];
const visualPropertiesBordered = {
areColumnsBordered: true
};
const visualPropertiesGreyHeader = {
hasGreyBackgroundHeader: true
};
const visualPropertiesGreyHeaderAndRow = {
hasGreyBackgroundHeader: true,
hasGreyBackgroundRow: true
};
const visualPropertiesDisabledRowBorder = {
disableRowBorder: true
};
return (
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: Spacing20,
height: 320
}}
>
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesBordered}
/>
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesGreyHeader}
/>
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesGreyHeaderAndRow}
/>
<TableV2
data={data}
columns={columns}
visualProperties={visualPropertiesDisabledRowBorder}
/>
</div>
);
}
Zebra Stripe
You can activate zebra-striping of rows by adding isStriped
to the visualProperties
. This helps users orient themselves in long and complex tables.
function MyTable() {
const columns = [
{
Header: 'Name',
accessor: 'name'
},
{
Header: 'Age',
accessor: 'age',
align: 'center'
},
{
Header: 'Room',
accessor: 'room',
align: 'right'
}
];
const data = [
{
name: 'Just a name',
age: 21,
room: 1
},
{
name: 'Another name',
age: 25,
room: 2
}
];
const visualProperties = {
isStriped: true
};
return (
<TableV2
data={data}
columns={columns}
visualProperties={visualProperties}
/>
);
}
Custom Hooks
Custom Initial Widths
TableMeasurement
is a custom hook of TableV2 to customize the Initial Column Widths by measuring the text content of each cell and return the array of new column widths.
By adding initialMinWidth
, initialMaxWidth
or isAutoWidth
to the columns
option, the hook will recalculate the minWidth
, width
, maxWidth
and isResizable
of the column, and overwriting the default behavior of these options.
This hook only supports for the body cells which render their value (text content) from data
, not support for custom cells without data
value. Please be careful if body cells are using Custom Cell Renderer
<TableMeasurement columns={columns} data={data}>
{({ isMeasuring, measuredColumns }) =>
!isMeasuring && (
<TableV2 data={data} columns={measuredColumns}/>
)
}
</TableMeasurement>
Return states
The hook returns TableMeasurementType
as following:
State | Type | Desc |
---|
measuredColumns | TableColumnTypes[] | The array of columns which is passed initially and including more: {id, width, minWidth, maxWidth, isResizable} |
isMeasuring | boolean | true if the hook is measuring the column widths and hasn't returned the result |
Examples
You can refer following examples for the usage.
- Initial Min Width
- Initial Max Width
- Initial Min & Max Width
- Auto Width
initialMinWidth
allows you to set initial width and minWidth of a column and able to resize to fit the longest content width (which is smaller than the initial width).
initialMinWidth
will recalculate the width
and minWidth
of a column and overwrite the default behavior of these column options.
function InitialMinTable () {
const data = useMemo(
() => [
{
no: 1,
fullName: 'Jane Doe',
city: 'Very long name'
},
{
no: 2,
fullName: 'Jane Doe Jane Doe',
city: 'Very very very long name'
},
{
no: 3,
fullName: 'Jane Doe Jane Doe Jane Doe',
city: 'Very very very very long name'
}
],
[]
);
const columns = useMemo(
() => [
{
Header: 'No.',
accessor: 'no',
isResizable: true,
initialMinWidth: 100,
Cell: ({ value }) => <Badge label={value} />
},
{
Header: 'Full Name',
accessor: 'fullName',
isResizable: true,
minWidth: 300,
initialMinWidth: 200
},
{
Header: 'City',
accessor: 'city',
isResizable: true,
minWidth: 100,
width: 200,
maxWidth: 400,
initialMinWidth: 300
}
],
[]
);
return (
<TableMeasurement columns={columns} data={data}>
{({ isMeasuring, measuredColumns }) =>
!isMeasuring && (
<TableV2 data={data} columns={measuredColumns}/>
)
}
</TableMeasurement>
)
}
Examples
Cases of Auto Width
Auto Width Columns with unstickThreshold and sub Rows.
function AutoWidthMoreCasesTable () {
const initialColumns = [
{
Header: 'Emp No.',
accessor: 'no',
sticky: 'left',
isAutoWidth: true,
Footer: () => '100,000,000'
},
{
Header: 'First',
accessor: 'firstName',
sticky: 'left',
isResizable: true,
isAutoWidth: true
},
{
Header: 'Last Name',
accessor: 'lastName',
isResizable: true,
isAutoWidth: true
},
{
Header: 'Long Age',
accessor: 'age',
isAutoWidth: true
}
];
const initialData = [
{
firstName: 'First',
lastName: 'LastName',
no: 'A1000',
age: 30
},
{
firstName: 'First 1',
lastName: 'LastName 1',
no: 'A1010',
age: 30
},
{
firstName: 'First 2',
lastName: 'Last Name 2',
no: 'B1010',
age: 40,
subRows: [
{
firstName: 'Sub First 10',
lastName: 'Sub Last 10',
no: 'Sub B1010-1010',
age: 30
},
{
firstName: 'Sub First Name 20',
lastName: 'Sub Last Name 20',
no: 'B1020-20',
age: 30,
subRows: [
{
firstName: 'Sub First 1010',
lastName: 'Sub Last 1010',
no: 'Sub B1010-101010',
age: 30
},
{
firstName: 'Sub First Name 2020',
lastName: 'Sub Last Name 2020',
no: 'B1020-2020',
age: 30
}
]
}
]
}
];
const [columns, setColumns] = useState(initialColumns);
const [data, setData] = useState(initialData);
const onOrderColumns = () => {
setColumns(columns.toReversed());
};
const onAddLastColumn = () => {
setColumns([
...columns,
{
Header: `New-${Date.now()}`,
accessor: (rows) => `${rows.firstName} ${rows.lastName}`,
id: `new-${Date.now()}`,
isAutoWidth: true
}
]);
};
const onAddStickyColumns = () => {
setColumns(columns.toSpliced(2, 0, {
Header: `Sticky-${Date.now()}`,
accessor: (rows) => 'sticky left',
sticky: 'left',
isResizable: true,
initialMinWidth: 100
}))
};
const onAddData = () => {
setData([
...data,
{
firstName: `First ${Date.now()}`,
lastName: `Last Name ${Date.now()} ${Date.now()}`,
no: Date.now(),
age: Date.now()
}
]);
};
const onRemoveColumn = () => {
if (columns.length > 1) {
setColumns(columns.toSpliced(columns.length - 1, 1));
}
};
const onRemoveData = () => {
if (data.length > 1) {
setData(data.toSpliced(data.length - 1, 1));
}
};
const onRefresh = () => {
setColumns(initialColumns);
setData(initialData);
};
const visualPropertiesBordered = {
areColumnsBordered: true
};
return (
<>
<Button label="Reverse Columns" size="small" onClick={onOrderColumns} />
<Button label="Add Last Column" size="small" onClick={onAddLastColumn} />
<Button
label="Add Sticky Column"
size="small"
onClick={onAddStickyColumns}
/>
<Button label="Remove Columns" size="small" onClick={onRemoveColumn} />
<Button label="Add Data" size="small" onClick={onAddData} />
<Button label="Remove Data" size="small" onClick={onRemoveData} />
<Button label="Refresh Table" size="small" onClick={onRefresh} />
<TableMeasurement data={data} columns={columns}>
{({ isMeasuring, measuredColumns }) => !isMeasuring && (
<TableV2
data={data}
columns={measuredColumns}
visualProperties={visualPropertiesBordered}
unstickThreshold={50}
/>
)
}
</TableMeasurement>
</>
)
}
Props
|
data * The data array that you want to display on the Table . It should be memoized. | Record<string, unknown>[]
| — |
columns * The core columns configuration object for the entire Table . It should be memoized. | | — |
autoRowsHeight Allow the height of body rows to grow respectively to content height | | — |
activeSorting The sorting applied to the Table . | | — |
compact Enable the compact version when true | | — |
emptyState The props of Empty State component, it will be shown if there is no data | Omit<EmptyStateProps, "size" | "background" | "iconKey">
| — |
flex It is used for using the Table with flex layout | | — |
getFilteredData callback to get filtered data after global filter is applied | ((filteredData?: any[]) => void)
| — |
isHeaderless If set to true table headers will be hidden | | — |
isFooterless If set to true table footers will be hidden | | — |
isHoverable If set to true, the table body rows can show hover state | | — |
getHoveredRowId pass this method to access the id of the row being hovered | | — |
isLoading Show loading state of the table | | — |
height Visible table view height. The default value of this prop is the height of the total rows. If set to full-height , `100%` will be applied. | | — |
hideHeaderGroupTitle If set to true table group header title will be hidden | | — |
isLoadingMoreData table is loading more data | | — |
loadingMoreDataLabel Custom label to be shown while loading more data | | — |
headerNavigationItems Array of Navigation Items in header if having Sub rows | | — |
onColumnRearrange Callback prop onColumnRearrange when dropping column on rearrange | ((columnsOrder: string[]) => void)
| — |
onColumnResized Callback prop onColumnResized when column is resized | | — |
onSortingChange The handler will be called when a user clicks to sorting actions | ((columnName: string) => void)
| — |
onPopoverClick The handler will be called when a user clicks to icon for opening popover | ((columnName: string) => void)
| — |
rowOnClickHandler Handler that returns row data on click | | — |
rowSize The size of rows | "small" | "medium" | "large"
| — |
searchTerm Search term to be used to do global filtering of data | | — |
typeScales Provide type scales for the table head, body and footer | | — |
visualProperties Provide visual properties to Table | | — |
updateData The callback to use for updating table data. the use case is inline editing mode of Table which requires updateData callback | Record<string, unknown>[]
| — |
hasMoreData There's still some data to be loaded | | — |
onLoadMoreData It's called when user scrolls to the end of table in order to load more data | ((startIndex?: number, stopIndex?: number) => void | Promise<void>) | undefined
| — |
unstickThreshold The maximum percentage value that fixed columns can occupy over the table width. If the total fixed columns width exceeds this value, they will automatically turn into unfixed columns | | — |
unstickResolution The number value to make columns unstick if the window width is smaller than this | | — |
rowSelection Allow to select rows with checkboxes | | — |
selectedRowIds Allow to set initial selected rows | | — |
stickyRowSelection Allow to set checkboxes column as sticky column. | | — |
onRowSelect A callback for row selection | ((selectedRowIds: Record<string, boolean>) => void)
| — |
isNestedTable A boolean to know if the table is nested | | — |
autoResetSelectedRows A boolean to allow the selectedRowIds state to automatically reset if data is changed, default is false. | | — |
autoResetExpanded A boolean to allow the expanded state to automatically reset if data is changed, default is false | | — |
data-{foo} Data attributes can be used by testing libraries to retrieve components or assert their existence | | — |
* - the prop is required. |