Table
Tables are containers for displaying information. They allow users to quickly scan, sort, compare, and take action on large amounts of data.
- Install
npm install @pluralsight/ps-design-system-table
- Import
import Table from '@pluralsight/ps-design-system-table'
Examples
Basic table
import { layout } from '@pluralsight/ps-design-system-core'import Avatar from '@pluralsight/ps-design-system-avatar'import Button from '@pluralsight/ps-design-system-button'import { MoreIcon } from '@pluralsight/ps-design-system-icon'import Table from '@pluralsight/ps-design-system-table'import React from 'react'const Example: React.FC = props => {const [users] = React.useState([])return (<Table><Table.Head><Table.Row><Table.Header role="columnheader" scope="col">First name</Table.Header><Table.Header role="columnheader" scope="col">Last name</Table.Header><Table.Header role="columnheader" scope="col"></Table.Header><Table.Header role="columnheader" scope="col"></Table.Header></Table.Row></Table.Head><Table.Body>{users.map((user, i) => (<Table.Row key={i}><Table.Header role="rowheader" scope="row"><FlexContainer><Avatar alt="avatar" name={user.firstName} size="xSmall" /><HorzSpacer /><span>{user.firstName}</span></FlexContainer></Table.Header><Table.Cell>{user.lastName}</Table.Cell><Table.Cell>{user.email}</Table.Cell><Table.Cell align="right"><Buttonicon={<MoreIcon />}appearance={Button.appearances.flat}size={Button.sizes.small}/></Table.Cell></Table.Row>))}</Table.Body></Table>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<divstyle={{display: 'inline-block',width: layout.spacingSmall}}{...props}/>)export default Example
Table with drawers
import { layout } from '@pluralsight/ps-design-system-core'import Avatar from '@pluralsight/ps-design-system-avatar'import Button from '@pluralsight/ps-design-system-button'import { CaretDownIcon, CaretRightIcon, MoreIcon } from '@pluralsight/ps-design-system-icon'import Table from '@pluralsight/ps-design-system-table'import React from 'react'const Example: React.FC = props => {const [users] = React.useState([])const initialState = [users[1].id]const [expandedIds, setExpandedIds] = React.useState(initialState)const expand = (user: { id: string }) => {const next = expandedIds.concat(user.id)setExpandedIds(next)}const collapse = (user: { id: string }) => {const next = expandedIds.filter(str => str !== user.id)setExpandedIds(next)}return (<Table><Table.Head><Table.Row><Table.Cell /><Table.Header role="columnheader" scope="col">First name</Table.Header><Table.Header role="columnheader" scope="col">Last name</Table.Header><Table.Header role="columnheader" scope="col"></Table.Header></Table.Row></Table.Head><Table.Body>{users.map((user, i) => {const expanded = expandedIds.includes(user.id)const toggle = expanded ? collapse : expandreturn (<React.Fragment key={i}><Table.Row><Table.Cell><ExpandButtonexpanded={expanded}onClick={() => toggle(user)}/></Table.Cell><Table.Cell>{user.firstName}</Table.Cell><Table.Cell>{user.lastName}</Table.Cell><Table.Cell>{user.email}</Table.Cell></Table.Row><Table.Drawer expanded={expanded} colSpan={4}><div>Drawer Content</div></Table.Drawer></React.Fragment>)})}</Table.Body></Table>)}interface ExpandButtonProps extends React.ComponentProps<typeof Button> {expanded: boolean}const ExpandButton: React.FC<ExpandButtonProps> = props => {const {appearance = 'flat',expanded,size = 'xSmall',title = 'Expand/Collapse additional content',...rest} = propsconst icon = expanded ? <CaretDownIcon /> : <CaretRightIcon />return (<Buttonappearance={appearance}icon={icon}size={size}title={title}{...rest}/>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<divstyle={{display: 'inline-block',width: layout.spacingSmall}}{...props}/>)export default Example
Sticky headers
Column headers can be stuck relative to a scrollable container.
import { layout } from '@pluralsight/ps-design-system-core'import Avatar from '@pluralsight/ps-design-system-avatar'import Table from '@pluralsight/ps-design-system-table'import React from 'react'const Example: React.FC = props => {const [users] = React.useState([])return (<div style={{ height: 200 }}><Table scrollable><Table.Head><Table.Row><Table.Header role="columnheader" scope="col" sticky>First name</Table.Header><Table.Header role="columnheader" scope="col" sticky>Last name</Table.Header><Table.Header role="columnheader" scope="col" sticky></Table.Header></Table.Row></Table.Head><Table.Body>{users.map((user, i) => (<Table.Row key={i}><Table.Header role="rowheader" scope="row"><FlexContainer><Avatar alt="avatar" name={user.firstName} size="xSmall" /><HorzSpacer /><span>{user.firstName}</span></FlexContainer></Table.Header><Table.Cell>{user.lastName}</Table.Cell><Table.Cell>{user.email}</Table.Cell></Table.Row>))}</Table.Body></Table></div>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<divstyle={{display: 'inline-block',width: layout.spacingSmall}}{...props}/>)export default Example
Row headers can also be stuck relative to a scrollable container.
import { layout } from '@pluralsight/ps-design-system-core'import Avatar from '@pluralsight/ps-design-system-avatar'import Checkbox from '@pluralsight/ps-design-system-checkbox'import Table from '@pluralsight/ps-design-system-table'import React from 'react'const Example: React.FC = props => {const [users] = React.useState([])return (<div style={{ width: 250 }}><Table scrollable><Table.Head><Table.Row><Table.Cell /><Table.Header role="columnheader" scope="col">First name</Table.Header><Table.Header role="columnheader" scope="col">Last name</Table.Header><Table.Header role="columnheader" scope="col"></Table.Header></Table.Row></Table.Head><Table.Body>{users.map((user, i) => (<Table.Row key={i}><Table.Header role="rowheader" scope="row" sticky><Checkbox /></Table.Header><Table.Header role="rowheader" scope="row"><FlexContainer><Avatar alt="avatar" name={user.firstName} size="xSmall" /><HorzSpacer /><span>{user.firstName}</span></FlexContainer></Table.Header><Table.Cell>{user.lastName}</Table.Cell><Table.Cell>{user.email}</Table.Cell></Table.Row>))}</Table.Body></Table></div>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<divstyle={{display: 'inline-block',width: layout.spacingSmall}}{...props}/>)export default Example
To position the sticky header relative to the viewport, the Design System exports a renderContainer component to track positioning and update the column headers accordingly.
import { layout } from '@pluralsight/ps-design-system-core'import Avatar from '@pluralsight/ps-design-system-avatar'import Table, { StickyContainer } from '@pluralsight/ps-design-system-table'import React from 'react'const Example: React.FC = props => {const [users] = React.useState([])return (<Table renderContainer={StickyContainer}><Table.Head><Table.Row><Table.Header role="columnheader" scope="col" sticky>First name</Table.Header><Table.Header role="columnheader" scope="col" sticky>Last name</Table.Header><Table.Header role="columnheader" scope="col" sticky></Table.Header></Table.Row></Table.Head><Table.Body>{users.map((user, i) => (<Table.Row key={i}><Table.Header role="rowheader" scope="row"><FlexContainer><Avatar alt="avatar" name={user.firstName} size="xSmall" /><HorzSpacer /><span>{user.firstName}</span></FlexContainer></Table.Header><Table.Cell>{user.lastName}</Table.Cell><Table.Cell>{user.email}</Table.Cell></Table.Row>))}</Table.Body></Table>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<divstyle={{display: 'inline-block',width: layout.spacingSmall}}{...props}/>)export default Example
NOTE: Sticky headers are an advanced feature and are expected to gracefully degrade in non evergreen/modern browsers.
Usage with react-table
The Design System aims to provide the UI building block necessary for great interfaces but does not implement some of the more advanced features you might need to build an interactive table. When you need more advanced features we suggest you use a library like react-table to ease implementation.
Basic react-table usage
import Table from '@pluralsight/ps-design-system-table'import React from 'react'import { Column, useTable } from 'react-table'const Example: React.FC = () => {const columns = React.useMemo<Column[]>(() => [{ Header: 'First name', accessor: user => user.firstName, title: "First name" },{ Header: 'Last name', accessor: user => user.lastName, title: 'Last name' }], [])const data = React.useMemo(() => [{ firstName: 'Lucy', lastName: 'Peck' },{ firstName: 'Jayden', lastName: 'Morales' },{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' }], [])const table = useTable({ columns, data })return (<Table {...table.getTableProps()}><Table.Head>{table.headerGroups.map(group => (<Table.Row {...group.getHeaderGroupProps()}>{group.headers.map(column => (<Table.Header {...column.getHeaderProps({ title: column.title })}>{column.render('Header')}</Table.Header>))}</Table.Row>))}</Table.Head><Table.Body {...table.getTableBodyProps}>{table.rows.map(row => {table.prepareRow(row)return row}).map(row => (<Table.Row {...row.getRowProps()}>{row.cells.map(cell => (<Table.Cell {...cell.getCellProps()}>{cell.render('Cell')}</Table.Cell>))}</Table.Row>))}</Table.Body></Table>)}export default Example
Sorting
import ScreenReaderOnly from '@pluralsight/ps-design-system-screenreaderonly'import Table from '@pluralsight/ps-design-system-table'import { useUniqueId } from '@pluralsight/ps-design-system-util'import React from 'react'import { Column, useTable, useSortBy } from 'react-table'const Example: React.FC = () => {const columns = React.useMemo<Column[]>(() => [{ Header: 'First name', accessor: user => user.firstName, title: "First name" },{ Header: 'Last name', accessor: user => user.lastName, title: 'Last name' }], [])const data = React.useMemo(() => [{ firstName: 'Lucy', lastName: 'Peck' },{ firstName: 'Jayden', lastName: 'Morales' },{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' }], [])const table = useTable({ columns, data }, useSortBy)const captionId = useUniqueId()const title = 'Employees'const initialCaption = `${title}: Not sorted`const [caption, setCaption] = React.useState(initialCaption)React.useEffect(() => {const [activeSortRule] = table.state.sortByif (!activeSortRule) {setCaption(initialCaption)return}const { id: ruleId, desc } = activeSortRuleconst ruleDir = desc ? 'descending' : 'ascending'setCaption(`${title} sorted by ${ruleId}: ${ruleDir} order`)}, [table.state.sortBy, initialCaption])return (<Table {...table.getTableProps()} aria-labelledby={captionId}><caption aria-live="polite" id={captionId}><ScreenReaderOnly>{caption}</ScreenReaderOnly></caption><Table.Head>{table.headerGroups.map(group => (<Table.Row {...group.getHeaderGroupProps()}>{group.headers.map(column => {const { canSort, isSorted, isSortedDesc } = columnconst sort = isSorted ? (isSortedDesc ? 'desc' : 'asc') : falseconst title: string = (column as any).titleconst sortByProps = column.getSortByToggleProps()const headerProps = column.getHeaderProps(sortByProps)return (<Table.Header{...headerProps}role="columnheader"scope="col"sort={canSort ? sort : undefined}title={title}>{column.render('Header')}</Table.Header>)})}</Table.Row>))}</Table.Head><Table.Body {...table.getTableBodyProps}>{table.rows.map(row => {table.prepareRow(row)return row}).map(row => (<Table.Row {...row.getRowProps()}>{row.cells.map(cell => (<Table.Cell {...cell.getCellProps()}>{cell.render('Cell')}</Table.Cell>))}</Table.Row>))}</Table.Body></Table>)}export default Example
Multi select
import Button from '@pluralsight/ps-design-system-button'import Checkbox from '@pluralsight/ps-design-system-checkbox'import { layout } from '@pluralsight/ps-design-system-core'import { ChatIcon, MoveIcon } from '@pluralsight/ps-design-system-icon'import ScreenReaderOnly from '@pluralsight/ps-design-system-screenreaderonly'import Table from '@pluralsight/ps-design-system-table'import React from 'react'import { Column, useTable, useRowSelect } from 'react-table'const selectionHook = (hooks: Hooks<any>) => {hooks.visibleColumns.push(columns => [{Cell: SelectionCell,Header: SelectionHeader,disableGroupBy: true,id: '_selection'},...columns])}const SelectionCell: React.FC<CellProps<any>> = props => {const { row } = propsreturn <TableCheckbox {...row.getToggleRowSelectedProps()} />}const SelectionHeader: React.FC<HeaderProps<any>> = props => {const { getToggleAllRowsSelectedProps } = propsconst style = { width: 1 }return <TableCheckbox {...getToggleAllRowsSelectedProps({ style })} />}interface TableCheckboxProps extends Omit<HTMLPropsFor<'input'>, 'ref'> {indeterminate?: boolean}const TableCheckbox: React.FC<TableCheckboxProps> = props => {const { onChange, ...rest } = propsreturn <Checkbox onCheck={onChange} {...rest} />}const Example: React.FC = () => {const columns = React.useMemo<Column[]>(() => [{ Header: 'First name', accessor: user => user.firstName, title: "First name" },{ Header: 'Last name', accessor: user => user.lastName, title: 'Last name' }], [])const data = React.useMemo(() => [{ firstName: 'Lucy', lastName: 'Peck' },{ firstName: 'Jayden', lastName: 'Morales' },{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' }], [])const table = useTable({ columns, data }, useRowSelect, selectionHook)const actionsDisabled = React.useMemo(() => Object.keys(table.state.selectedRowIds).length <= 0,[table.state.selectedRowIds])return (<div><FlexContainer><Buttonappearance="secondary"disabled={actionsDisabled}icon={<MoveIcon />}>Move to team</Button><HorzSpacer /><Buttonappearance="secondary"disabled={actionsDisabled}icon={<ChatIcon />}>Send message</Button></FlexContainer><br /><Table {...table.getTableProps()}><Table.Head>{table.headerGroups.map(group => (<Table.Row {...group.getHeaderGroupProps()}>{group.headers.map(column => {const title: string = (column as any).titlereturn (<Table.Header{...column.getHeaderProps()}role="columnheader"scope="col"style={{ width: column.id === '_selection' ? 1 : undefined }}title={title}>{column.render('Header')}</Table.Header>)})}</Table.Row>))}</Table.Head><Table.Body {...table.getTableBodyProps}>{table.rows.map(row => {table.prepareRow(row)return row}).map(row => (<Table.Row selected={row.isSelected} {...row.getRowProps()}>{row.cells.map(cell => (<Table.Cell {...cell.getCellProps()}>{cell.render('Cell')}</Table.Cell>))}</Table.Row>))}</Table.Body></Table></div>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<div style={{ display: 'inline-block', width: layout.spacingSmall }} {...props} />)export default Example
Row expand/collapse
import Button from '@pluralsight/ps-design-system-button'import Checkbox from '@pluralsight/ps-design-system-checkbox'import { layout } from '@pluralsight/ps-design-system-core'import { CaretDownIcon, CaretLeftIcon, CaretRightIcon } from '@pluralsight/ps-design-system-icon'import ScreenReaderOnly from '@pluralsight/ps-design-system-screenreaderonly'import Table from '@pluralsight/ps-design-system-table'import React from 'react'import { Column, useExpanded, useTable } from 'react-table'const expanderHook = (hooks: Hooks<any>) => {hooks.visibleColumns.push(columns => [{Cell: ExpanderCell,Header: ExpanderHeader,disableGroupBy: true,id: '_expander'},...columns])}const ExpanderCell: React.FC<CellProps<any>> = props => {const { row } = propsif (!row.canExpand) return nullconst style = {paddingLeft: `calc(${layout.spacingLarge} * ${row.depth})`}const icon = row.isExpanded ? <CaretDownIcon /> : <CaretRightIcon />return (<span {...row.getToggleRowExpandedProps({ style })}><Buttonappearance="flat"icon={icon}size="xSmall"title="Expand/Collapse additional content"/></span>)}const ExpanderHeader: React.FC<HeaderProps<any>> = props => {const { getToggleAllRowsExpandedProps, isAllRowsExpanded } = propsconst icon = isAllRowsExpanded ? <CaretDownIcon /> : <CaretRightIcon />return (<span {...getToggleAllRowsExpandedProps()}><Buttonappearance="flat"icon={icon}size="xSmall"title="Expand/Collapse additional content"{...getToggleAllRowsExpandedProps()}/></span>)}const Example: React.FC = () => {const columns = React.useMemo<Column[]>(() => [{ Header: 'First name', accessor: user => user.firstName, title: "First name" },{ Header: 'Last name', accessor: user => user.lastName, title: 'Last name' }], [])const data = React.useMemo(() => [{firstName: 'Lucy',lastName: 'Peck',subRows: [{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' }]},{firstName: 'Jayden',lastName: 'Morales',subRows: [{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' }]},], [])const table = useTable({ columns, data }, useExpanded, expanderHook)return (<Table {...table.getTableProps()}><Table.Head>{table.headerGroups.map(group => (<Table.Row {...group.getHeaderGroupProps()}>{group.headers.map(column => {const title: string = (column as any).titlereturn (<Table.Header{...column.getHeaderProps()}role="columnheader"scope="col"style={{ width: column.id === '_expander' ? 1 : undefined }}title={title}>{column.render('Header')}</Table.Header>)})}</Table.Row>))}</Table.Head><Table.Body {...table.getTableBodyProps}>{table.rows.map(row => {table.prepareRow(row)return row}).map(row => (<Table.Row {...row.getRowProps()}>{row.cells.map(cell => (<Table.Cell {...cell.getCellProps()}>{cell.render('Cell')}</Table.Cell>))}</Table.Row>))}</Table.Body></Table>)}export default Example
Pagination
import Button from '@pluralsight/ps-design-system-button'import { layout } from '@pluralsight/ps-design-system-core'import Dropdown from '@pluralsight/ps-design-system-dropdown'import { CaretLeftIcon, CaretRightIcon } from '@pluralsight/ps-design-system-icon'import Table from '@pluralsight/ps-design-system-table'import { P } from '@pluralsight/ps-design-system-text'import React from 'react'import { TableInstance, Column, useTable, usePagination } from 'react-table'const Example: React.FC = () => {const columns = React.useMemo<Column[]>(() => [{ Header: 'First name', accessor: user => user.firstName, title: "First name" },{ Header: 'Last name', accessor: user => user.lastName, title: 'Last name' }], [])const data = React.useMemo(() => [{ firstName: 'Lucy', lastName: 'Peck' },{ firstName: 'Jayden', lastName: 'Morales' },{ firstName: 'Milton', lastName: 'Lane' },{ firstName: 'Dwayne', lastName: 'Kelly' },{ firstName: 'Don', lastName: 'Morgan' },{ firstName: 'Camila', lastName: 'Turner' },{ firstName: 'Gabe', lastName: 'Austin' },{ firstName: 'Jeanne', lastName: 'Pierce' },{ firstName: 'Scarlette', lastName: 'Obrien' },{ firstName: 'Jimmie', lastName: 'Carpenter' }], [])const initialState = { pageSize: 2 }const table = useTable({ columns, data, initialState }, usePagination)return (<div><Table {...table.getTableProps()}><Table.Head>{table.headerGroups.map(group => (<Table.Row {...group.getHeaderGroupProps()}>{group.headers.map(column => (<Table.Header {...column.getHeaderProps({ title: column.title })}>{column.render('Header')}</Table.Header>))}</Table.Row>))}</Table.Head><Table.Body {...table.getTableBodyProps}>{table.page.map(row => {table.prepareRow(row)return row}).map(row => (<Table.Row {...row.getRowProps()}>{row.cells.map(cell => (<Table.Cell {...cell.getCellProps()}>{cell.render('Cell')}</Table.Cell>))}</Table.Row>))}</Table.Body></Table><FlexContainer><Paginator table={table} /></FlexContainer></div>)}interface PaginatorProps {perPageOptions?: number[]table: TableInstance}const Paginator: React.FC<PaginatorProps> = props => {const { perPageOptions = [2, 5, 10], table } = propsconst { pageIndex, pageSize } = table.stateconst handlePrevPage = () => table.previousPage()const handleNextPage = () => table.nextPage()const total = table.rows.lengthconst cursorStart = pageIndex * pageSize + 1const cursorEnd = Math.min(cursorStart + pageSize - 1, total)return (<div style={{ display: 'flex', marginBottom: layout.spacingMedium }}><Buttonappearance="secondary"disabled={!table.canPreviousPage}icon={<CaretLeftIcon />}onClick={handlePrevPage}title="Previous page"/><HorzSpacer /><Buttonappearance="secondary"disabled={!table.canNextPage}icon={<CaretRightIcon />}onClick={handleNextPage}title="Next page"/><HorzSpacer /><P>{cursorStart.toLocaleString()}-{cursorEnd.toLocaleString()} of{' '}{total.toLocaleString()}</P><HorzSpacer /><Dropdownappearance="subtle"onChange={(_evt, value) => {table.setPageSize(Number(value))}}menu={<>{perPageOptions.map(option => (<Dropdown.Item key={option} value={option}>{String(option) + ' rows'}</Dropdown.Item>))}</>}value={pageSize}/></div>)}const FlexContainer: React.FC = props => (<div style={{ display: 'flex', alignItems: 'center' }} {...props} />)const HorzSpacer: React.FC = props => (<div style={{ display: 'inline-block', width: layout.spacingSmall }} {...props} />)export default Example
Usage with react-beautiful-dnd
Table row drag and drop can be accomplished using a library such as react-beautiful-dnd.
import { colorsTextIcon } from '@pluralsight/ps-design-system-core'import Table from '@pluralsight/ps-design-system-table'import { useTheme } from '@pluralsight/ps-design-system-theme'import React from 'react'import { DragDropContext, DraggableProvided, DraggableStateSnapshot, DropResult, Droppable, DroppableProvided, Draggable } from 'react-beautiful-dnd'const Example: React.FC = () => {const [data, setData] = React.useState(() => [{ id: 'lucy.peck', firstName: 'Lucy', lastName: 'Peck' },{ id: 'jayden-morales', firstName: 'Jayden', lastName: 'Morales' },{ id: 'milton-lane', firstName: 'Milton', lastName: 'Lane' },{ id: 'dwayne-kelly', firstName: 'Dwayne', lastName: 'Kelly' },{ id: 'don-morgan', firstName: 'Don', lastName: 'Morgan' },{ id: 'camila-turner', firstName: 'Camila', lastName: 'Turner' },{ id: 'gabe-austin', firstName: 'Gabe', lastName: 'Austin' },{ id: 'jeanne-pierce', firstName: 'Jeanne', lastName: 'Pierce' },{ id: 'scarlette-obrien', firstName: 'Scarlette', lastName: 'Obrien' },{ id: 'jimmie-carpenter', firstName: 'Jimmie', lastName: 'Carpenter' }])const handleDragEnd = (result: DropResult) => {const { destination, source } = resultif (!destination) returnif (destination.index === source.index) returnconst nextData = reorder(data, source.index, destination.index)setData(nextData)}return (<DragDropContext onDragEnd={handleDragEnd}><Table><Table.Head><Table.Row><Table.Header role="columnheader" scope="col" /><Table.Header role="columnheader" scope="col">First name</Table.Header><Table.Header role="columnheader" scope="col">Last name</Table.Header></Table.Row></Table.Head><Droppable droppableId="droppable-body">{({ droppableProps, placeholder, innerRef }: DroppableProvided) => (<Table.Body ref={ref => { innerRef(ref) }} {...droppableProps}>{data.map((user, i) => (<Draggable draggableId={user.id} key={user.id} index={i}>{(provided: DraggableProvided,snapshot: DraggableStateSnapshot) => (<DraggableRowkey={i}ref={provided.innerRef}provided={provided}snapshot={snapshot}><Table.Cell>{user.firstName}</Table.Cell><Table.Cell>{user.lastName}</Table.Cell></DraggableRow>)}</Draggable>))}{placeholder}</Table.Body>)}</Droppable></Table></DragDropContext>)}interface DraggableRowProps extends React.ComponentProps<typeof Table.Row> {provided: DraggableProvidedsnapshot: DraggableStateSnapshot}const DraggableRow = React.forwardRef<HTMLTableRowElement, DraggableRowProps>((props, ref) => {const { children, provided, snapshot, ...rest } = propsreturn (<Table.Row ref={ref} {...provided.draggableProps} {...rest}><Table.Cell style={{ width: 1 }}><Handle {...provided.dragHandleProps} /></Table.Cell>{children}</Table.Row>)})const Handle: React.FC = props => {const dark = useTheme() === 'dark'const [cols, rows, gutter, size] = [2, 4, 2, 2]const matrix = new Array(rows).fill(new Array(cols).fill(null))const shadow = matrix.map((c, i: number) =>c.map((_r: any, j: number) => {const x = j * size + j * gutterconst y = i * size + i * gutterreturn `${x}px ${y}px`}))return (<><div {...props} className="handle" /><style jsx>{`.handle {color: ${dark ? colorsTextIcon.lowOnDark : colorsTextIcon.lowOnLight};cursor: grab;display: inline-block;height: ${(size + gutter) * rows}px;position: relative;width: ${(size + gutter) * cols}px;}.handle:before {background-color: currentColor;box-shadow: ${shadow.toString()};content: ' ';height: ${size}px;left: ${gutter}px;position: absolute;top: ${gutter}px;width: ${size}px;}`}</style></>)}const reorder = (list: unknown[], startIndex: number, endIndex: number): any[] => {const result = Array.from(list)const [removed] = result.splice(startIndex, 1)result.splice(endIndex, 0, removed)return result}export default Example
Accessibility
WCAG 2.1 AA Compliance
100% axe-core testsManual audit
Props
Table
Name | Type | Description | Default |
---|---|---|---|
renderContainer | (props) => React.ReactNode | container render prop | (p) => <div {...p} /> |
scrollable | boolean | enables horizontal scrolling | false |
Table.Cell
Name | Type | Description | Default |
---|---|---|---|
align |
| text alignment | left |
Table.Header
Name | Type | Description | Default |
---|---|---|---|
align |
| text alignment | left |
role Required | columnheader | rowheader |
| |
scope Required | col | row |
| |
sort | true | asc | desc | column sorting options |
|
sticky | boolean |
| |
title | string | accessible title. required when using sortable columns |
|
Table.Row
Name | Type | Description | Default |
---|---|---|---|
expanded | boolean | false | |
selected | boolean | false |