@lightspeed/cirrus-table

1.0.4 • Public • Published

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>
  );
};

Readme

Keywords

none

Package Sidebar

Install

npm i @lightspeed/cirrus-table

Weekly Downloads

527

Version

1.0.4

License

MIT

Unpacked Size

426 kB

Total Files

127

Last publish

Collaborators

  • kurt.bergeron
  • lightspeedhq
  • ls-guillaume-lambert
  • ls-frederic-bouchard
  • anomen