@lightspeed/cirrus-table
v1.0.4
Published
Cirrus Table Component
Downloads
2,804
Keywords
Readme
Table
A set of recommended design rules and reusable React components to create tables of data quickly for specific workflow needs.
They should help customers find, view, organize and interact with their data in easy and convenient ways. The interface should disappear so that retailers can focus on the job at hand.
Installation
First, make sure you have been through the install steps steps required to add Flame in your application. Although it's not required to have Flame installed to use Logo, you will need to install its peer dependencies.
If using Yarn:
yarn add @lightspeed/cirrus-table
Or using npm:
npm i -S @lightspeed/cirrus-table
Styled System props
| Component Name | styled-system props | | -------------- | ----------------------------------------------------------------------------------------------------- | | Td | height | | | width | | | space | | | fontSize | | | textAlign | | | color | | Th | color | | | height | | | width | | | space | | | display | | | alignItems, justifyContent | | | fontSize | | Tr | color | | | space |
Usage
Components
<Table>
Note: you need to manually wrap the <Table>
with a <Card>
to get the recommended visual for tables. This is left to the consumer for maximum flexibility.
Styled table tag (<table>
) with the following props:
| Prop | Type | Description |
| ---------------- | ---------------------------- | ----------------------------------------------------------- |
| loadingStatus?
| "loading"
or "completed"
| Shows a loading indicator between the table header and body |
Base Table Elements
<Thead>
An augmented version of the table header which features a loading bar hooked onto the table's loading state
<Th>
A Styled System augmented table heading tag (<th>
)
<Tr>
A Styled System augmented table row tag (<tr>
)
| Prop | Type | Description |
| ----------- | ------ | ----------------------------------------------------------- |
| isHoverable | bool
| If true, no hover style will be applied, false
by default |
<Td>
A Styled System augmented table cell tag (<td>
).
<Tbody>
An emotion instantiated tbody
. No augmentation added, it's simply there for markup stylistic purposes.
<Tfoot>
An emotion instantiated tfoot
. No augmentation added, it's simply there for markup stylistic purposes.
<NoDataTd>
An extension of <Td>
. It comes with a pre-formatted styles for a no data state.
<Pager />
A simple pager component designed for the table.
| Prop | Type | Description |
| -------------- | ------ | ------------------------------------------------------------ |
| isFirst | bool
| If true, the "back" pager button is disabled |
| isLast | bool
| If true, the "forward" pager button is disabled |
| handleNext | func
| Callback function attached to the the "forward" pager button |
| handlePrevious | func
| Callback function attached to the the "back" pager button |
| children | any
| Element will be added to the front of the Pager button group |
Table Action Components & Filtering
Additional actions can be added to the table.
Table exports an all encompassing Object called TableActions, which contains various components that are hooked to a single context that mostly handles the interconnected ui state possible within actions, e.g: filters and tags.
Using the TableActions.Context and Actions
The TableActions provides many ways to add and remove filters programaticaly.
Here are some of the exposed functions that will come in handy:
| Prop | Description | | ------------------------------------------------------------ | -------------------------------------------------------------- | | applyFilter(filterKey, label, value) | Upserts a new filter with a corresponding label and raw values | | applyFilters(Array<{key:string, value: any, label: string}>) | Replace filters with the current list sent in parameters | | deleteFilter(filterKey) | Delete a filter by key | | deleteAllFilters() | Deletes all filters | | deleteAllFiltersAndSearchTerm | Deletes all filters, including the search term | | getValues(filterKey) | Retrieves a global count of all active filters | | getFilters() | Retrieves the entire list of active filters |
There are two ways of gaining access to these functions:
Using React.useContext
const MyApp = () => {
const { applyFilter, deleteFilter /* ... */ } = React.useContext(TableActions.Context);
};
Via the consumer component
const MyApp = () => {
return (
<TableActions.Consumer>
{({ applyFilter, deleteFilter, /* ... */ }) => (
/* ... */
)}
</TableActions.Consumer>
);
}
TableActions.FilterPanelProvider
A Provider to keep track of active filters and routing of the TableActions.Dropdown
| Prop | Type | Description |
| --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| onFilterChange | func
| Callback function that will receive in parameters the currently selected array of filters |
| initialSearchTerm | string
| The initial search term. If using TableActions.SearchBar, it'll automatically use the initialSearchTerm as it's value |
| initialFilters | Array<{key:string, value: any, label: string}>
| The initial filters for the table actions. See PagerTable on how to set initialFilters properly |
| renderDropdownContent | ReactNode
| The content that will be used within dropdowns. Primarely used to have a single point of entry for dropdown content used by the search bar dropdown and the filter tag dropdowns |
const MyApp = () => (
<TableActions.Provider
initialSearchTerm={{}}
initialFilters={{}}
onFilterChange={() => {}}
renderDropdownContent={<div>The shared content that will appear in the Dropdown</div>}
>
{/*
Render the rest of your content here.
*/}
</TableActions.Provider>
);
TableActions.ActionBar
A wrapper component which sets the right positioning of elements. This is a simple dumb component with no logic.
| Prop | Type | Description |
| ---------------- | ----------- | ------------------------------------------------------------------------------------ |
| leftHandActions | ReactNode
| Render slot the left. Usually, you will want to add you search bar and dropdown here |
| rightHandActions | ReactNode
| Render slot for right. The pager should be here |
| children | ReactNode
| |
const MyApp = () => (
<TableActions.ActionBar
leftHandActions={<div>this div will appear on the left</div>}
rightHandActions={<div>this div will appear on the right</div>}
>
<div>this div will appear underneath the previously set actions</div>
</TableActions.ActionBar>
);
TableActions.SearchBar
A prebuilt search bar component hooked onto the filtering context. Simply add and use.
Pressing enter or clicking on the button will trigger a change to the stored search term within the provider and cause onFilterChange
to be executed.
| Prop | Type | Description |
| ----------- | -------- | ----------------------------------------------------- |
| buttonText | string
| Text that appears on the search button |
| buttonTitle | string
| Text for title
attribute of the search button |
| placeholder | string
| Placeholder text that appears on the search input |
| disabled | bool
| Set to true to disable search input and search button |
const MyApp = () => (
<TableActions.Provider
initalSearchTerm="I will automatically appear in the search bar"
onFilterChange={(filters, searchTerm) => {
// When pressing enter on the search bar or clicking on the submit button,
// This function will be triggered
}}
renderDropdownContent={<div>The shared content that will appear in the Dropdown</div>}
>
<TableActions.SearchBar placeholder="Put your search term here" buttonText="Search" />
</TableActions.Provider>
);
TableActions.FilterTagLister
A list of all Filters active. Automatically hooked to the TableActions.FilterPanelProvider
.
It will automatically select the right dropdown view, based on which tag has been picked.
| Prop | Type | Description |
| -------------- | ----------- | ----------------------------------------------------------- |
| renderDropdown | ReactNode
| (Optional) Override of the provider's renderDropdownContent |
| dropdownWidth | ReactNode
| Width of the dropdown |
| children | ReactNode
| |
const MyApp = () => (
<TableActions.Provider
initalFilters={{
status: {
value: 'Any value to store relevant data for the filtering process'
label: 'This is what will appear on a tag'
}
}}
renderDropdownContent={
<TableActions.Dropdown>
{/*
Notice how the initialFilters object has `status` as an object key
If we want the TableActions.FilterTagLister to pick up the right
TableActions.DropdownSection, ensure that the key and filterKey match!
*/}
<TableActions.DropdownSection title="Just some title" filterKey="status">
Anything in the `TableActions.DropdownSection` will be rendered when clicking
on the Tag generated from `TableActions.FilterTagLister`
</TableActions.DropdownSection>
</TableActions.Dropdown>
}
>
<TableActions.FilterTagLister />
</TableActions.Provider>
);
TableActions.DropdownMenu
A wrapper component which will render automatically render the TableActions.Dropdown
acquired from the provider.
It will handle swapping between a list view or a detailed view.
| Prop | Type | Description |
| -------------- | ----------- | ----------------------------------------------------------- |
| renderDropdown | ReactNode
| (Optional) Override of the provider's renderDropdownContent |
| dropdownWidth | ReactNode
| Width of the dropdown |
| children | ReactNode
| |
const MyApp = () => (
<TableActions.Provider
renderDropdownContent={
<TableActions.Dropdown>
This content will only be rendered within the list view...
<TableActions.DropdownSection title="Just some title" filterKey="status">
Anything in the `TableActions.DropdownSection` will be rendered when clicking on the
corresponding link in the list view.
</TableActions.DropdownSection>
</TableActions.Dropdown>
}
>
<TableActions.DropdownMenu />
</TableActions.Provider>
);
TableActions.Dropdown
The main Dropdown component to be used within the container. This is needed to create the multi-page approach of the filtering dropdown.
Concretely, there are two pages within this dropdown: The listing page and the details page.
The "listing" page is simply whatever components passed in as child to the TableActions.Dropdown
. It'll render whatever is passed in as is, with the exception of TableActions.DropdownSection
.
If a TableActions.DropdownSection
is detected, that component will instead output a clickable menu item. When clicked, the dropdown view will automatically swap to render out the given TableActions.DropdownSection
children elements instead.
| Prop | Type | Description |
| ---------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| goBackText | string
| Text that appears on the "back" button within a details view. Automatically hidden when rendered within a tag. |
TableActions.DropdownSection
A section/page of the dropdown. Use this component to automatically create the filter menu within the Dropdown, as well as rendering a "details" view that will be rendered when selected.
| Prop | Type | Description |
| --------- | ----------- | --------------------------------------------------------------------------------- |
| title | string
| Text that appears on the list view of the dropdown |
| filterKey | string
| Identifier for the section. For syncing the rendering of a section to a given tag |
| children | ReactNode
| |
TableActions.DropdownTitle
A pre configured version of Cirrus Text to fit the dropdown styling.
TableActions.DropdownActions
A pre configured version of Cirrus Flex to fit within dropdown styling. Use this to output your filter buttons.
TableActions.DropdownDivider
A pre configured version of Cirrus Divider. Use this to add a properly padded seperator between elements.
TableActions.DropdownButtonLink
A pre configured version of Cirrus Button that makes a button look like a link for usage within the Dropdown.
TableActions.CheckboxGroup
A prestyled and context aware checkbox group.
| Prop | Type | Description |
| ---------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| checkboxes | Array<{ label: string, value: string}>
| Config object to generate checkboxes. Simply pass in the label and the associated value |
| filterKey | string
| Identifier for the filter. Use this to bind the child functions to the appropriate filter key |
| children | Child function ReactNode
| Accepts a child function. It forwards an object with two following functions: undoChanges and applyFilter. undoChanges will clear the internal state of the checkbox group but not trigger an actual filter change. applyFilter accepts either a string to be applied as the label for a given tag or a callback function that sends the selected items in parameter and expects a string in return |
TableActions.RadioGroup
A prestyled and context aware radio group.
| Prop | Type | Description |
| --------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| radios | Array<{ label: string, value: string}>
| Config object to generate radios. Simply pass in the label and the associated value |
| filterKey | string
| Identifier for the filter. Use this to bind the child functions to the appropriate filter key |
| children | Child function ReactNode
| Accepts a child function. It forwards an object with two following functions: undoChanges and applyFilter. undoChanges will clear the internal state of the radio group but not trigger an actual filter change. applyFilter accepts either a string to be applied as the label for a given tag or a callback function that sends the selected items in parameter and expects a string in return |
All About Table Features
The Basics
By default, the table is completely stateless and allows you to build and structure it however you want, since the table provides multiple small building blocks.
We also provide some pre-built functionality in form of hooks that you can use immediately with the provided components.
The Sort Feature
Sort exports a hook that pre-binds state as well as a convenient SortButton
component and a pre-built Th
component that you can slide into the appropriate spot of the table.
import { Table, Thead, Tr, Td, Sortable } from '@lightspeed/cirrus-table';
const MyComponent = () => {
const { getSortingDirection, toggleSort, sortKey, sortDirection, setSort } = Sortable.useSortable();
// Observe our sortKey/sortDirection for any changes
React.useEffect(() => {
// And trigger a side-effect!
}
}, [sortKey, sortDirection]);
return (
<Card>
{/* You can use setSort to force save a particular sort state */}
<button onClick={() => setSort('animal', 'desc')}>
Descending animals
</button>
<Table>
<Thead>
<Tr>
<Sortable.Th
onClick={toggleSort('animal') /* toggleSort automatically cycle through the sort */}
direction={getSortingDirection('animal') /* Fully uncontrolled props*/}
>
Animal
</Sortable.Th>
</Tr>
</Thead>
<Tr>
<Td>Lion</Td>
</Tr>
<Tr>
<Td>Tiger</Td>
</Tr>
<Tr>
<Td>Bear</Td>
</Tr>
</Table>
</Card>
);
}
Options
Sortable.useSortable
This hook takes two parameters, the first one is the sorting key and second value is the sort order.
Use this to set a default sort order.
const MyComponent = () => {
const { getSortingDirection } = Sortable.useSortable('color', 'asc');
getSortingDirection('color'); // this will return `asc`
toggleSort('color'); // This will cycle through the next sorting direction...
getSortingDirection('color'); // meaning this will now return `desc`
// ...
};
As per UX, only one item can be sorted at a time, and it will always follow the following cycle:
unsorted -> ascending -> descending
Important note: as of right now, we only support 1 sort option at a time.
Components
<Sortable.SortButton>
| Prop | Type | Description |
| ----------- | -------- | -------------------------------------- |
| direction
| string
| The direction of the arrow for sorting |
<Sortable.Th>
Convenience compone that simply wraps the SortButton inside a Th.
| Prop | Type | Description |
| ----------- | ---------- | -------------------------------------- |
| direction
| string
| The direction of the arrow for sorting |
| onClick
| function
| Good ol' function |
Examples
See the examples folder.
The Batch Select Feature
Batch Select exposes multiple components as well a hook to manage state. You may use both of them, or mix and match functionalities.
While there may be many functions to bind, most of them are fairly straight forwards to use and self explanatory.
Example
import { Table, Thead, TBody, Td, BatchSelect } from '@lightspeed/cirrus-table';
const MyApp = () => {
const {
itemsSelectedCount,
toggleAllItems,
isItemSelected,
isAllItemsSelected,
isPartiallySelected,
toggleItem,
} = BatchSelect.useBatchSelect(data, {
idKey: 'id',
initialSelectedItems: [],
});
return (
<Table>
<Thead>
<BatchSelect.HeaderTr
itemsSelectedCount={itemsSelectedCount}
selectedItemsText={`${itemsSelectedCount} selected item${
itemsSelectedCount > 1 ? 's' : ''
}`}
checkboxProps={{
checked: isAllItemsSelected,
indeterminate: isPartiallySelected,
onChange: toggleAllItems,
}}
>
<Th>Name</Th>
<Th>Updated at</Th>
<Th>Visible</Th>
</BatchSelect.HeaderTr>
</Thead>
<TBody>
{data.map(item => (
<BatchSelect.Tr
key={item.id}
selected={isItemSelected(item.id)}
checkboxProps={{
checked: isItemSelected(item.id),
onChange: () => {
toggleItem(item.id);
},
}}
>
<Td>{item.name}</Td>
<Td width="150px">{item.updatedAt}</Td>
<Td width="150px">{item.isVisible}</Td>
</BatchSelect.Tr>
))}
</TBody>
</Table>
);
};
BatchSelect.useBatchSelect
The userBatchSelect
hook is used to track what items have been selected.
useBatchSelect
accepts a configuration object
const {} = BatchSelect.useBatchSelect(data, {
idKey: 'id', // optional
selectedItems: [], // optional
});
| Key | Is Required? | Description |
| ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| data | Required | useBatchSelect
will use that data to appropriately track what items are selected or not. useBatchSelect
will NOT mutate the data |
| idKey | Optional | Change the tracking identifier. By default, useBatchSelect
will only keep track of the id
key within the data
object. |
| selectedItems | Optional | Preselect items by forwarding an array of id
(or whatever unique indentifier). By default, this field is an empty array. |
useBatchSelect
returns the following variables:
| Name | Description | | ------------------- | ------------------------------------------------------------------------------------------------------ | | selectedItems | Get the list of all item IDs that are currently selected | | itemsSelectedCount | Get a numeric count of the selectedItems | | isAllItemsSelected | Return a boolean value of whether or not the selectedItems is empty or has all items contained within | | isPartiallySelected | return a boolean value of whether or not there is at least 1 item selected, but not all items selected |
useBatchSelect
returns the following functions
| Name | Params | Description |
| -------------- | ------ | -------------------------------------------------------------------- |
| isItemSelected | itemID | Checks whether or not an item is selected |
| toggleItem | itemID | Automatically add or remove an item from the selectedItems
list. |
| toggleAllItems | - | Automatically add or remove ALL items from the selectedItems
list. |
| selectItem | itemID | Add an item to the selectedItems
list. |
| deselectItem | itemID | Remove an item from the selectedItems
list. |
| selectAll | - | Add all items to the SelectedItems
list. |
| deselectAll | - | Remove all items from the SelectedItems
list. |
<BatchSelect.HeaderTr>
An augmented header table row <Tr>
that handles adding the initial checkbox column and swapping out the table header row to use an action group when items are selected.
This component is a wrapper on top of <BatchSelect.CheckboxWell>
and handles the logic of replacing table headers with a <BatchSelect.CheckboxWell>
when an item is selected.
| Prop | Type | Description |
| -------------------- | --------------------- | ------------------------------------------------------------------------------------------- |
| itemsSelectedCount
| number
| Quantity of items selected. |
| selectedItemsText
| ReactNode
| The text or element to appear right beside the checkbox when at least one item is selected. |
| innerGroup
| ReactNode
| Slot to render elements to be attached to the right of the checkbox addon |
| outerGroup
| ReactNode
| Slot to render elements outside of the checkbox addon group. |
| checkboxProps
| HTMLCheckboxElement
| Same properties as a regular cirrus Checkbox |
| children
| ReactNode
| Typically, we put the table header (<Th>
or <th>
) elements here |
<BatchSelect.Checkbox>
A specially styled cirrus checkbox for the table. Features a slightly larger hit box.
<BatchSelect.CheckboxWell>
A special checkbox that is wrapped in a container and can have an attached button to it.
| Prop | Type | Description |
| ------------------- | ----------- | ------------------------------------------------------------------------------------------- |
| selectedItemsText
| ReactNode
| The text or element to appear right beside the checkbox when at least one item is selected. |
| innerGroup
| ReactNode
| Slot to render elements to be attached to the right of the checkbox addon |
| outerGroup
| ReactNode
| Slot to render elements outside of the checkbox addon group. |
| checked
| boolean
| - |
| indeterminate
| boolean
| - |
| onChange
| boolean
| Event handler attached on the Checkbox |
| children
| ReactNode
| Typically, we put the table header (<Th>
or <th>
) elements here |
<BatchSelect.Tr>
An augmented table row <Tr>
that injects a checkbox in the first <Td />
. Use this component when you do not want to bother with adjusting padding and margins to match specs.
| Prop | Type | Description |
| --------------- | --------------------- | -------------------------------------------- |
| checkboxProps
| HTMLCheckboxElement
| Same properties as a regular cirrus Checkbox |
| ...restProps
| Tr
| Same properties as the Tr
component |
<BatchSelect.Dropdown>
A custom dropdown component for the table.
| Prop | Type | Description |
| ---------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| title
| string
| The text content of the dropdown button |
| children
| ReactNode
| What will be rendered within the dropdown menu when the dropdown button is clicked. BatchSelect. Dropdown can also take a child function. Destructuring it will give back a toggleOpen
function to programatically close the menu. |
example
<BatchSelect.Dropdown title="Action">
<BatchSelect.DropdownList>
<li>An element</li>
<li>Another element</li>
</BatchSelect.DropdownList>
</BatchSelect.Dropdown>
// If we need access to toggleOpen function...
<BatchSelect.Dropdown title="Action">
{({ toggleOpen }) => (
<BatchSelect.DropdownList>
<li>An element</li>
<li>
<button onClick={toggleOpen}>Close dropdown</button>
</li>
</BatchSelect.DropdownList>
)}
</BatchSelect.Dropdown>
<BatchSelect.DropdownList>
A prestyled <ul>
element to be used within the dropdown.
example:
<BatchSelect.DropdownList>
<li>An element</li>
<li>Another element</li>
</BatchSelect.DropdownList>
<BatchSelect.BulkButton>
A prestyled cirrus <Button>
element to be used within a BatchSelect.HeaderTr
or BatchSelect.CheckboxWell
inner group.
The Draggable feature
Draggable enables to drag and drop rows to for the purpose of sorthing them.
The draggable feature is a collection of pre-bound HOC elements that uses the react-sortable-hoc library.
<Draggable.Table>
A Table that uses the SortableContainer HOC and pre-binds certain props for convenience sake.
It takes care of setting column sizes on drag, adding a helper class and styling the rows.
The <Draggable.Table>
takes the exact same props as a regular <Table>
, as well as all the props provided by SortableContainer react-sortable-hoc.
In general, you will only need to your own sorting callback on the onSortEnd
prop.
The <Draggable.Table>
will automatically enable the useDragHandle
prop, meaning it will be required to add a drag handle to your rows. You may of course always set the useDragHandle
prop to false.
example
import { Tbody, Td, Draggable } from '@lightspeed/cirrus-table';
const MyApp = () => {
return (
<Draggable.Table
onSortEnd={(({oldIndex, newIndex, collection, isKeySorting})) => {
console.log('Do your sorting here');
}}
>
<Tbody>
{/* Add whatever elements */}
</Tbody>
</Draggable.Table>
);
};
<Draggable.Tr>
& <Draggable.DragHandle />
<Draggable.Tr>
is a <Tr />
that uses the SortableElement HOC. Enables the row to be moved.
<Draggable.DragHandle />
is basically a pre-styled Icon
with the SortableHandle HOC applied.
For further details, please consult the react-sortable-hoc library.
Important note: You need to add the index
prop on <Draggable.Tr>
for proper tracking of row position. You will also need to include the <Draggable.DragHandle />
inside the row, or else you will not be able to move the row. Should you wish to be able to drag the row no matter the cursor location, you may set useDragHandle
on the <Draggable.Tbody>
to false.
import { Flex } from '@lightspeed/cirrus/core';
import { Table, Draggable, Td } from '@lightspeed/cirrus-table';
const MyApp = () => {
return (
<Draggable.Table>
<Tbody>
{data.map((item, index) => (
{/* Notice the presence of the `index` prop. This is important to add! */}
<Draggable.Tr key={item.id} index={index}>
<Td>
{/* Use the Draggable.DragHandle. Or else the row cannot be dragged. */}
<Draggable.DragHandle>
{item.name}
</Draggable.DragHandle>
</Td>
<Td textAlign="right">{item.price}</Td>
</Draggable.Tr>
))}
</Tbody>
</Draggable.Table>
);
};