UNPKG

@vtex/styleguide

Version:

> VTEX Styleguide React components ([Docs](https://vtex.github.io/styleguide))

677 lines (590 loc) 16.1 kB
### Measures The table measures are the current density, rowHeight, tableHeight and setCurrentDensity. They are calculated by the `useTableMeasures` hook. ```ts // Density shape enum Density { Compact = 'compact', Regular = 'regular', Comfortable = 'comfortable', } ``` ##### useTableMeasures Inputs | Property | Type | Required | Default | Description | | -------- | ------- | -------- | --------------- | ------------------------- | | size | number | ✅ | 0 | Length of the table items | | density | Density | 🚫 | Density.Regular | Initial Density | ##### useTableMeasures Outputs | Property | Type | Description | | ----------------- | -------------------------- | ------------------------- | | currentDensity | Density | The table current density | | rowHeight | number | The height of each row | | tableHeight | number | Table's full height | | bodyHeight | number | Table's body height | | setCurrentDensity | (density: Density) => void | Sets the current density | ##### Example ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import { customers } from './sampleData' const columns = [ { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, { id: 'location', title: 'Location', }, ] const items = customers.slice(0, 5) function MeasuresExample() { const measures = useTableMeasures({ size: items.length }) return <Table measures={measures} columns={columns} items={items} /> } ;<MeasuresExample /> ``` ### Proportional columns In some cases, the columns must be proportional within a ratio. This is achievable through the `useTableProportion` hook. ##### useTableProportion inputs | Property | Type | Required | Default | Description | | -------- | -------- | -------- | ------- | -------------------- | | columns | Column[] | ✅ | 🚫 | Columns of the table | | ratio | number[] | ✅ | 🚫 | Ratio of each column | ##### useTableProportion outputs | Property | Type | Description | | ------------ | -------- | --------------------------------------------- | | sizedColumns | Column[] | Columns of the table with the selected ratios | ##### Example ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import useTableProportion from '../hooks/useTableProportion' import ButtonGroup from '../../ButtonGroup' import Button from '../../Button' import { customers } from './sampleData' const columns = [ { id: 'id', title: 'ID', }, { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, ] const items = customers.slice(0, 5) const ratios = [ { id: 1, title: '1 | 1 | 1', value: [1, 1, 1], }, { id: 2, title: '0.1 | 0.5 | 1', value: [0.1, 0.5, 1], }, { id: 3, title: '0.5 | 0.5 | 1', value: [0.5, 0.5, 1], }, ] function ProportionExample() { const [currentRatio, setCurrentRatio] = React.useState(ratios[0]) const measures = useTableMeasures({ size: items.length, }) const { sizedColumns } = useTableProportion({ columns, ratio: currentRatio.value, }) return ( <> <div className="mb5"> <ButtonGroup buttons={ratios.map(ratio => ( <Button size="small" isActiveOfGroup={ratio.id === currentRatio.id} onClick={() => setCurrentRatio(ratio)}> Ratio: {ratio.title} </Button> ))} /> </div> <Table measures={measures} columns={sizedColumns} items={items} /> </> ) } ;<ProportionExample /> ``` ### Toggle columns visibility The column's visibility can be toggled using `useTableVisibility`. This is useful to hide optional properties, providing a more distinct visualization. ##### useTableVisibility Inputs | Property | Type | Required | Default | Description | | ------------- | -------- | -------- | ------- | -------------------------------- | | columns | Column[] | ✅ | 🚫 | Columns of the table | | hiddenColumns | string[] | 🚫 | [] | Columns that are initially hidden | ##### useTableVisibility Outputs | Property | Type | Description | | -------------- | -------------------- | -------------------------------- | | visibleColumns | Column[] | Columns that are visible | | hiddenColumns | string[] | Columns that are hidden | | toggleColumn | (id: string) => void | Toggle a column visibility by id | | showColumn | (id: string) => void | Show a column visibility by id | | hideColumn | (id: string) => void | Hide a column visibility by id | | showAllColumns | () => void | Make all columns visible | | hideAllColumns | () => void | Make all columns hidden | ##### Example ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import useTableVisibility from '../hooks/useTableVisibility' import data from './sampleData' const columns = [ { id: 'id', title: 'ID', }, { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, { id: 'location', title: 'location', }, ] const items = data.customers.slice(0, 5) function VisibilityExample() { const measures = useTableMeasures({ size: items.length, }) const visibility = useTableVisibility({ columns, hiddenColumns: ['id', 'location'], }) const buttonColumns = { label: 'Toggle visible fields', showAllLabel: 'Show All', hideAllLabel: 'Hide All', visibility, } return ( <> <Table measures={measures} columns={visibility.visibleColumns} items={items}> <Table.Toolbar> <Table.Toolbar.ButtonGroup> <Table.Toolbar.ButtonGroup.Columns {...buttonColumns} /> </Table.Toolbar.ButtonGroup> </Table.Toolbar> </Table> </> ) } ;<VisibilityExample /> ``` ### Sortable columns The `useTableSort` hook is designed to easily maintain columns sorting state. You may pass its return through the `sorting` prop. ```ts enum SortOrder { ASC = 'ASC', DSC = 'DSC', } type Sorted = { /** order of the sorting */ order?: SortOrder /** reference prop */ by?: string } ``` ##### useTableSort outputs | Property | Type | Description | | -------- | -------------------- | --------------------------------------- | | sorted | Sorted | Order and reference prop of the sorting | | clear | () => void | Clears sorting | | sort | (id: string) => void | Toggle sorting by some prop | ##### Example ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import useTableSort from '../hooks/useTableSort' import data from './sampleData' const columns = [ { id: 'name', title: 'Name', sortable: true, }, { id: 'qty', title: 'Qty', sortable: true, }, { id: 'costPrice', title: 'Cost', sortable: true, }, { id: 'retailPrice', title: 'Retail', sortable: true, }, ] const products = data.products.slice(0, 5) const ascOrdering = prop => (a, b) => a[prop] > b[prop] ? 1 : a[prop] < b[prop] ? -1 : 0 const dscOrdering = prop => (a, b) => a[prop] > b[prop] ? -1 : a[prop] < b[prop] ? 1 : 0 function SortExample() { const sorting = useTableSort() const measures = useTableMeasures({ size: products.length, }) const items = React.useMemo(() => { const { sorted: { order, by }, } = sorting if (!order) { return products } const ascending = order === 'ASC' const comparator = ascending ? ascOrdering(by) : dscOrdering(by) return products.sort(comparator) }, [sorting.sorted, data.products]) return ( <Table measures={measures} columns={columns} items={items} sorting={sorting} /> ) } ;<SortExample /> ``` ### Loading state ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import Toggle from '../../Toggle' import Spinner from '../../Spinner' function LoadingExample() { const measures = useTableMeasures({ size: 5 }) const [custom, setCustom] = React.useState(false) const loading = custom ? { renderAs: () => { return <Spinner color="#F71963" size={64} /> }, } : true return ( <Table loading={loading} measures={measures} columns={[ { id: 'name', title: 'Name', }, { id: 'location', title: 'Location', }, ]}> <Table.ActionBar> <Toggle label="Show custom loading" checked={custom} onChange={() => setCustom(custom => !custom)} /> </Table.ActionBar> </Table> ) } ;<LoadingExample /> ``` ### Empty state ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import Toggle from '../../Toggle' import Button from '../../Button' function EmptyExample() { const [custom, setCustom] = React.useState(false) const measures = useTableMeasures({ size: custom ? 8 : 5 }) const emptyState = { label: 'This is a default empty state for title', } const customEmptyState = { label: 'This is a default empty state for title', children: ( <React.Fragment> <p> A longer explanation of what should be here, and why should I care about what should be here. </p> <div className="pt5"> <Button variation="secondary" size="small"> <span className="flex align-baseline">Suggested action</span> </Button> </div> </React.Fragment> ), } return ( <Table empty={true} emptyState={custom ? customEmptyState : emptyState} measures={measures} columns={[ { id: 'name', title: 'Name', }, { id: 'location', title: 'Location', }, ]}> <Table.ActionBar> <Toggle label="Show custom empty state" checked={custom} onChange={() => setCustom(custom => !custom)} /> </Table.ActionBar> </Table> ) } ;<EmptyExample /> ``` ### Row highlight on hover ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import { customers } from './sampleData' function ClickableExample() { const measures = useTableMeasures({ size: 5 }) return ( <Table highlightOnHover measures={measures} items={customers} columns={[ { id: 'id', title: 'ID', }, { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, { id: 'location', title: 'Location', }, ]} /> ) } ;<ClickableExample /> ``` ### Clickable rows ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import { customers } from './sampleData' function ClickableExample() { const measures = useTableMeasures({ size: 5 }) return ( <Table onRowClick={({ rowData }) => alert(`Clicked ${rowData.name} from ${rowData.location}`) } measures={measures} items={customers} columns={[ { id: 'id', title: 'ID', }, { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, { id: 'location', title: 'Location', }, ]} /> ) } ;<ClickableExample /> ``` ### Sticky Header To achieve a Table with a sticky header and a scrollable body, we can combine `useTableMeasures` hook with the `stickyHeader` prop. ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import { customers } from './sampleData' function StickyExample() { // Define the max number of items that will be displayed const measures = useTableMeasures({ size: 5 }) return ( <Table stickyHeader measures={measures} items={customers} columns={[ { id: 'id', title: 'ID', }, { id: 'name', title: 'Name', }, { id: 'email', title: 'Email', }, { id: 'location', title: 'Location', }, ]} /> ) } ;<StickyExample /> ``` #### With Checkboxes A recurrent feature is to add checkboxes to rows. This is achievable with the table features and should be controlled by the user. Luckily for us, the [`EXPERIMENTAL_useCheckboxTree` hook](https://styleguide.vtex.com/#/Components/%F0%9F%91%BB%20Experimental/EXPERIMENTALUseCheckboxTree) helps to keep track of tree data checked states. The following example presents one way of doing it: ```js import Table from '../index' import useTableMeasures from '../hooks/useTableMeasures' import useCheckboxTree from '../../EXPERIMENTAL_useCheckboxTree' import { customers } from './sampleData' import Checkbox from '../../Checkbox' const items = customers.slice(0, 5) function CheckboxesExample() { const measures = useTableMeasures({ size: items.length }) // using the hook defined down bellow const [ withCheckboxes, isRowActive, checkedItems, allChecked, ] = useColumnsWithCheckboxes({ columns: [ { id: 'name', title: 'Name', }, { id: 'location', title: 'Location', }, ], items, }) return ( <Table isRowActive={isRowActive} measures={measures} columns={withCheckboxes} items={items}> <Table.ActionBar className={`flex items-center justify-end pv4 ph3 f5 br2 ${ allChecked ? 'bg-warning--faded c-warning' : 'bg-action-secondary c-on-action-secondary' }`}> <div> {allChecked ? ( <b>All items are checked!</b> ) : ( <> Checked items count: <b>{checkedItems.length}</b> </> )} </div> </Table.ActionBar> </Table> ) } // custom hook that handle checkboxes state and rendering function useColumnsWithCheckboxes({ columns, items }) { const checkboxes = useCheckboxTree({ items }) // maps the checkboxes from itemTree to actual elements const mappedCheckboxes = checkboxes.itemTree.children.map(item => { const id = `${item.id}` return ( <Checkbox key={id} id={id} checked={checkboxes.isChecked(item)} partial={checkboxes.isPartiallyChecked(item)} disabled={checkboxes.isDisabled(item)} onChange={() => checkboxes.toggle(item)} /> ) }) // adds the checkboxes column const withCheckboxes = [ { id: 'checkbox', title: ( <Checkbox id={`${checkboxes.itemTree.id}`} checked={checkboxes.allChecked} partial={checkboxes.someChecked} onChange={checkboxes.toggleAll} /> ), width: 32, extended: true, cellRenderer: ({ data }) => { return <div>{mappedCheckboxes[data.id - 1]}</div> }, }, ...columns, ] // [parsed columns, isRowActive function, checked items, allChecked] return [ withCheckboxes, data => checkboxes.isChecked(data), checkboxes.checkedItems, checkboxes.allChecked, ] } ;<CheckboxesExample /> ```