@hackplan/polaris
Version:
Shopify’s product component library
670 lines (569 loc) • 17.4 kB
Markdown
---
name: Resource table
category: Lists and tables
keywords:
- ResourceTable
- data
- table
- tabular
- index
---
# Resource table
Resource tables are used to organize and display all information from a data set. While a data visualization represents part of data set, a resource table lets merchants view details from the entire set. This helps merchants compare and analyze the data.
---
## Examples
### Default resource table
Use to present small amounts of data for merchants to view statically.
```jsx
class ResourceTableExample extends React.Component {
render() {
const rows = [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
],
];
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
onRowClicked={(index) => { console.log(index) }}
/>
</Card>
</Page>
);
}
}
```
### Resource table with drag and drop
Use to present small amounts of data for merchants to view statically.
```jsx
class ResourceTableExample extends React.Component {
state = {
rows: [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
],
],
}
render() {
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={this.state.rows}
totals={['', '', '', 255, '$155,830.00']}
onRowClicked={(index) => { console.log(index) }}
onDragEnd={(result) => {
// dropped outside the list
if (!result.destination) {
return;
}
const {rows} = this.state;
rows.splice(result.destination.index, 0, rows.splice(result.source.index, 1)[0]);
this.setState({
rows
});
}}
/>
</Card>
</Page>
);
}
}
```
### Sortable resource table
Use when clarity of the table’s content is needed. For example, to note the number of rows currently shown in a resource table with pagination.
```jsx
class SortableResourceTableExample extends React.Component {
state = {
sortedRows: null,
};
sortCurrency = (rows, index, direction) => {
return [...rows].sort((rowA, rowB) => {
const amountA = parseFloat(rowA[index].substring(1));
const amountB = parseFloat(rowB[index].substring(1));
return direction === 'descending' ? amountB - amountA : amountA - amountB;
});
};
handleSort = (rows) => (index, direction) => {
this.setState({sortedRows: this.sortCurrency(rows, index, direction)});
};
render() {
const {sortedRows} = this.state;
const initiallySortedRows = [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
],
];
const rows = sortedRows ? sortedRows : initiallySortedRows;
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
sortable={[false, true, false, false, true]}
defaultSortDirection="descending"
initialSortColumnIndex={4}
onSort={this.handleSort(rows)}
/>
</Card>
</Page>
);
}
}
```
### Resource table with footer
Use when clarity of the table’s content is needed. For example, to note the number of rows currently shown in a resource table with pagination.
```jsx
class ResourceTableFooterExample extends React.Component {
render() {
const rows = [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
],
];
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
footerContent={`Showing ${rows.length} of ${rows.length} results`}
/>
</Card>
</Page>
);
}
}
```
### Resource table with row heading links
Use to help merchants find relevant, finer grained data sets.
```jsx
class ResourceTableLinkExample extends React.Component {
render() {
const rows = [
[
<Link url="https://www.example.com">Emerald Silk Gown</Link>,
'$875.00',
124689,
140,
'$122,500.00',
],
[
<Link url="https://www.example.com">Mauve Cashmere Scarf</Link>,
'$230.00',
124533,
83,
'$19,090.00',
],
[
<Link url="https://www.example.com">
Navy Merino Wool Blazer with khaki chinos and yellow belt
</Link>,
'$445.00',
124518,
32,
'$14,240.00',
],
];
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
/>
</Card>
</Page>
);
}
}
```
### Resource table with all of its elements
Use as a broad example that includes most props available to resource table.
```jsx
class FullResourceTableExample extends React.Component {
state = {
sortedRows: null,
};
sortCurrency = (rows, index, direction) => {
return [...rows].sort((rowA, rowB) => {
const amountA = parseFloat(rowA[index].substring(1));
const amountB = parseFloat(rowB[index].substring(1));
return direction === 'descending' ? amountB - amountA : amountA - amountB;
});
};
handleSort = (rows) => (index, direction) => {
this.setState({sortedRows: this.sortCurrency(rows, index, direction)});
};
render() {
const {sortedRows} = this.state;
const initiallySortedRows = [
[
<Link url="https://www.example.com">Emerald Silk Gown</Link>,
'$875.00',
124689,
140,
'$121,500.00',
],
[
<Link url="https://www.example.com">Mauve Cashmere Scarf</Link>,
'$230.00',
124533,
83,
'$19,090.00',
],
[
<Link url="https://www.example.com">
Navy Merino Wool Blazer with khaki chinos and yellow belt
</Link>,
'$445.00',
124518,
32,
'$14,240.00',
],
];
const rows = sortedRows ? sortedRows : initiallySortedRows;
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
sortable={[false, true, false, false, true]}
defaultSortDirection="descending"
initialSortColumnIndex={4}
onSort={this.handleSort(rows)}
footerContent={`Showing ${rows.length} of ${rows.length} results`}
/>
</Card>
</Page>
);
}
}
```
### Loading resource table
Use to present small amounts of data for merchants to view statically.
```jsx
class ResourceTableExample extends React.Component {
render() {
const rows = [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00'],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00'],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
],
];
return (
<Page title="Sales by product">
<Card>
<ResourceTable
loading={true}
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
totals={['', '', '', 255, '$155,830.00']}
/>
</Card>
</Page>
);
}
}
```
### Empty state resource table
Use to present small amounts of data for merchants to view statically.
```jsx
class ResourceTableExample extends React.Component {
render() {
const rows = [
];
return (
<Page title="Sales by product">
<Card>
<ResourceTable
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
]}
rows={rows}
/>
</Card>
</Page>
);
}
}
```
### Resource table with selection
Use to present small amounts of data for merchants to view statically.
```jsx
class ResourceTableExample extends React.Component {
state = {
selectedIndexes: [],
rows: [
['Emerald Silk Gown', '$875.00', 124689, 140, '$122,500.00', <Button size="slim" primary>Button</Button>],
['Mauve Cashmere Scarf', '$230.00', 124533, 83, '$19,090.00', <Button size="slim" primary>Button</Button>],
[
'Navy Merino Wool Blazer with khaki chinos and yellow belt',
'$445.00',
124518,
32,
'$14,240.00',
<Button size="slim" primary onClick={(event) => { console.log('button clicked'); event.stopPropagation(); }}>Button</Button>,
],
]
}
render() {
return (
<Page title="Sales by product">
<Card>
<ResourceTable
selectable={true}
selectedIndexes={this.state.selectedIndexes}
onSelection={(selectedIndexes) => {
this.setState({ selectedIndexes })
console.log(selectedIndexes)
}}
columnContentTypes={[
'text',
'numeric',
'numeric',
'numeric',
'numeric',
'text',
]}
headings={[
'Product',
'Price',
'SKU Number',
'Net quantity',
'Net sales',
'Actions',
]}
rows={this.state.rows}
onRowClicked={(index) => { console.log(index) }}
bulkActions={[
{
content: 'Add tags',
onAction: () => console.log('Todo: implement bulk add tags'),
},
{
content: 'Remove tags',
onAction: () => console.log('Todo: implement bulk remove tags'),
},
{
content: 'Delete customers',
onAction: () => console.log('Todo: implement bulk delete'),
},
]}
headerNode={<Button>Button</Button>}
/>
</Card>
</Page>
);
}
}
```
---
## Best practices
Resource tables should:
- Show values across multiple categories and measures.
- Allow for filtering and ordering when comparison is not a priority.
- Help merchants visualize and scan many values from an entire data set.
- Help merchants find other values in the data hierarchy through use of links.
- Minimize clutter by only including values that supports the data’s purpose.
- Include a summary row to surface the column totals.
- Not include calculations within the summary row.
- Wrap instead of truncate content. This is because if row titles start with the same word, they’ll all appear the same when truncated.
- Not to be used for an actionable list of items that link to details pages. For this functionality, use the [resource list] component.
### Alignment
Column content types are built into the component props so the following alignment rules are followed:
- Numerical = Right aligned
- Textual data = Left aligned
- Align headers with their related data
- Don’t center align
---
## Content guidelines
Headers should:
- Be informative and descriptive
- Concise and scannable
- Include units of measurement symbols so they aren’t repeated throughout the columns
- Use sentence case (first word capitalized, rest lowercase)
<!-- usagelist -->
#### Do
Temperature °C
#### Don’t
Temperature
<!-- end -->
Column content should:
- Be concise and scannable
- Not include units of measurement symbols (put those symbols in the headers)
- Use sentence case (first word capitalized, rest lowercase)
### Decimals
Keep decimals consistent. For example, don’t use 3 decimals in one row and 2 in others.
---
## Related components
- To create an actionable list of related items that link to details pages, such as a list of customers, use the [resource list component](/components/lists-and-tables/resource-list).
---
## Accessibility
<!-- content-for: android -->
See Material Design and development documentation about accessibility for Android:
- [Accessible design on Android](https://material.io/design/usability/accessibility.html)
- [Accessible development on Android](https://developer.android.com/guide/topics/ui/accessibility/)
<!-- /content-for -->
<!-- content-for: ios -->
See Apple’s Human Interface Guidelines and API documentation about accessibility for iOS:
- [Accessible design on iOS](https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/accessibility/)
- [Accessible development on iOS](https://developer.apple.com/accessibility/ios/)
<!-- /content-for -->
<!-- content-for: web -->
### Structure
Native HTML tables provide a large amount of structural information to screen reader users. Merchants who rely on screen readers can navigate tables and identify relationships between data cells (`<td>`) and headers (`<th>`) using keys specific to their screen reader.
Sortable tables use the `aria-sort` attribute to convey which columns are sortable (and in what direction). They also use `aria-label` on sorting buttons to convey what activating the button will do.
<!-- usageblock -->
#### Do
Use tables for tabular data.
#### Don’t
Use tables for layout. For a table-like layout that doesn’t use table HTML elements, use the [resource list component](/components/lists-and-tables/resource-list).
<!-- end -->
### Keyboard support
Sorting controls for the resource table component are implemented with native HTML buttons.
- Give buttons keyboard focus with the <kbd>tab</kbd> key (or <kbd>shift</kbd> + <kbd>tab</kbd> when tabbing backwards)
- Activate buttons with the <kbd>enter</kbd>/<kbd>return</kbd> and <kbd>space</kbd> keys
<!-- /content-for -->