Table
Table is used to display a large amount of information to users in a way that is categorized, scannable, and actionable.
Component
Retailer | Status | Market | Last Order | QA Errors | Menus | |
---|---|---|---|---|---|---|
Empire Merchants | Inactive | Chicago | 5/2/24 | 121 | ||
Fogo de Chao - Addison - #101 | Active | Texas | 6/2/24 | 0 | ||
Southern Glazer's Wine & Spirits | Active | Atlanta | 3/2/24 | 0 |
Storybook
Storybook - Table DocumentationFigma Demo
Guidelines
Variations
Table is offered as a single layout that works across screen sizes, and will overflow as needed on smaller screens (scroll horizontally).
Cells
Table cells are divided into two main variations: header cells and default (body) cells.
Header cells may be the select (checkbox), header (column title), or menu (blank) variant.
Default cells may include text, a link, or a badge.
Sort Controls
Columns may be sortable and are indicated with the arrow-up-down icon. One column in a table is always pre-sorted. Unless the table is set up otherwise, the first column with data is pre-sorted ascending.
Sort order is ascending → descending → unsorted.
Column Width
Columns/cells are generally set to fill, but the select and action cell types are set to hug because their size is always known. It is up to individual designers and their engineering partners to determine if a column (or columns) need a fixed width or min-width to preserve visibility of that content across screen sizes. A fixed or min-width may be set on a column by adding the class
property to the column object.
Usage
Use Table when a large amount of information needs to be presented in a way that users may scan and compare. Remember that more columns there are in a table, the harder it is to scan across. Consider shifting content to deeper pages as needed, allowing users a high-level view from the table, and a deeper view by clicking through a row.
Best Practices
Consider the hierarchy of information from left to right.
Content
Content types are currently limited to text, badges, links, checkboxes, and menu buttons. As this component develops other content types and ways to display that content may be added. For example, table cells with primarily numeric information are often right-aligned.
Component Specs
API
- action:
boolean
| Default:false
Whether the table has an action column or not. - columns:
Array<{cellVariant?: ComponentProps<TableBodyCell>['variant'] | undefined;class?: string;key: T;label: string;sort?: boolean;}>
| Default:[]
The columns of the table. - data:
Array<{id: number;badgeVariant?: ComponentProps<Badge>['variant'];checked?: boolean;value?: ComponentProps<Checkbox>['value'];} & Record<string, any>>
| Default:[]
- defaultSortColumn:
string
| Default:''
The column to sort by default. - handleMenuClick:
() => void
| Default:undefined
Function to be called when the menu is clicked. - handleRowSelection:
(row: Record<string, any>) => void
| Default:undefined
Function to be called when a row is selected. - selectable:
boolean
| Default:false
Whether the table is selectable or not. - testId:
string
| Default:undefined
Test id for the component.
Example
<script>
import { Table, MenuLink } from '@getprovi/craft-svelte';
import { ListSolid, ArrowTrayDown, TrashcanOutline } from '@getprovi/craft-svelte-icons';
const dataChecked = [
{
id: 1,
retailer: 'Fogo de Chao - Addison - #101',
status: 'Active',
badgeVariant: 'success',
market: 'Texas',
lastOrder: '6/2/24',
qaErrors: 0
},
{
id: 2,
retailer: 'Empire Merchants',
status: 'Inactive',
badgeVariant: 'neutral',
market: 'Chicago',
lastOrder: '5/2/24',
qaErrors: 121
},
{
id: 3,
retailer: "Southern Glazer's Wine & Spirits",
status: 'Active',
badgeVariant: 'success',
market: 'Atlanta',
lastOrder: '3/2/24',
qaErrors: 0
}
];
const columnSort = [
{ label: 'Retailer', value: 'retailer', key: 'retailer', sort: true },
{ label: 'Status', value: 'status', key: 'status', sort: true },
{ label: 'Market', value: 'market', key: 'market', sort: true },
{ label: 'Last Order', value: 'lastOrder', key: 'lastOrder', sort: true },
{ label: 'QA Errors', value: 'qaErrors', key: 'qaErrors', sort: true }
];
const dataMapChecked = dataChecked.map((item, index) => ({
...item,
id: index + 1
}));
const menuItems = [
{
href: '#',
iconName: ListSolid,
title: 'View details',
subtitle: 'View & track orders'
},
{
href: '#',
iconName: ArrowTrayDown,
title: 'Archive'
},
{
href: '#',
iconName: TrashcanOutline,
title: 'Delete'
}
];
</script>
<table
columns="{columnSort}"
data="{dataMapChecked}"
selectable
action
dropdownClasses="max-w-[220px]"
>
<svelte:fragment slot="dropdown">
{#each menuItems as { href, iconName, title, subtitle }}
<MenuLink {href} {iconName} {title} {subtitle} />
{/each}
</svelte:fragment>
</table>
Types
type TableColumn<T extends string = string> = {
cellVariant?: ComponentProps<TableBodyCell>['variant'] | undefined;
class?: string;
key: T;
label: string;
sort?: boolean;
};
type TableRow = {
id: number;
badgeVariant?: ComponentProps<Badge>['variant'];
checked?: boolean;
value?: ComponentProps<Checkbox>['value'];
} & Record<string, any>;
interface Props {
action?: boolean;
columns: Column[];
data: Row[];
defaultSortColumn?: keyof Row;
dropdownClasses?: string;
handleMenuClick?: (event: Event) => void;
handleRowSelection?: (event: Event, id: number) => void;
selectable?: boolean;
testId?: string;
}