1.0.0 • Published 12 months ago

@element-public/react-table v1.0.0

Weekly downloads
-
License
ISC
Repository
github
Last release
12 months ago

Table

Description

The Table is an efficient way to display data in rows and columns.

See live demos on storybook

Storybook Table Demos

Install bundle from npm-e

npm i @element-public/react-components @element-public/themes

Optional: install the component individually

npm i @element-public/react-table @element-public/themes

Open ~/.npmrc in an editor and add the following line to enable the @element-public scope:

@element-public:registry=https://npm.platforms.engineering

Troubleshooting

See below if you have never installed a package from Bayer's npm-enterprise or run into the following error:

npm ERR! code E401
npm ERR! Unable to authenticate, your authentication token seems to be invalid.
npm ERR! To correct this please trying logging in again with:
npm ERR!     npm login

Setup an access token

See the devtools npm-e guide to learn how to create an access token if this is the first time you are using a npm-e package at Bayer or you do not have a line that starts with the following in your ~/.npmrc file:

//npm.platforms.engineering/:_authToken=

Table Modes

The table component can be used in 1 of 3 modes. Composed, generated, and virtualized.

Composed

Composed is the most basic way to use table and is manually built like a traditional table with sub-components including TableHeader, TableBody, TableRow, TableHeaderCell, and TableCell. This mode does not have features such as pagination or sorting - please see Generated below.

Generated

You can create a generated table by passing in your data and a columns schema. The columns and rows will be built for you. This allows for a number of additional features such as pagination, sorting, and more. See the column schema below.

For best performance for larger datasets use pagination or see virtualized below.

Virtualized

Virtualized is a variation of a generated table that can provide much better performance for very large datasets without the need for pagination. It uses and alternate technique that only renders content that is visible in the specified window.

Virtualized tables require a fixed height be set.

Column Schema (Generated/Virtualized)

We use and extend react-table v7 under the hood. See their Column Options for even more documentation.

FieldDescriptionRequiredCategory
accessormost commonly the field name as defined in the data. May be a function (row, rowIndex) => {}.requiredgeneral
headerThe column header content. May be a function.requiredgeneral
alignColumn content alignment.May be left, right, and center.optionalformatting
backgroundColor (deprecated in favor of cellProps)Experimental. Assign a background color to a column. Ex: rgba(0,0,0,0.3)optionalformatting
cellPropsPass custom props directly to each TableCell within the column (generated & virtualized modes only). Can be either an object or a function that receives the following signature (cell).optionallayout
columnsUsed for grouping columns. See grouped columns below.optionallayout
defaultSortDefault sorting for a column on page load.May be asc, desc.Note: id is required for default sorting to work.optionalsorting
defaultSortOrderIf multiple columns ar marked as defaultSort, this will set the order in which they will be applied.optionalsorting
disableResizingPrevents column from being resized.Optionalcolumn resizing
disableSortByDisable sorting for this column (when column sorting is enabled with the Table sorting prop). Note, multi-column sorting is supported by default (requires keyboard). Use shift-click to select additional columns for sorting.optionalgeneral
displayConfigAdd extra props, if needed.Example, add country and type for currency formatter (supports javascript Int format): displayConfig: { country: 'en-US', currency: 'USD' }optionalformatting
displayTemplateA function that can format the displayed value.Note: When using a displayTemplate, displayType will automatically be set to custom.Format: displayTemplate: (value, rowData) => {}. Example, have a boolean field render as Yes/No: displayTemplate: (value) => value ? 'Yes' : 'No'}.optionalformatting
headerTemplateA function that can control the header rendering.Format: displayTemplate: (value, rowData) => {}. Example, have a boolean field render as Yes/No: displayTemplate: (value) => value ? 'Yes' : 'No'}.optionalformatting
displayTypeThe display format to use.May be text, date, time, datetime, number, currency, expander, checkbox (work in progress , switch (work in progress), and custom. Note: The custom type is used with displayTemplate, though as of v5.9.0, displayType will automatically be set to custom when a displayTemplate is used.optionalformatting
editConfigAdditional configuration for controls, if needed.Example, add options for a select list: editConfig: { options: [{ text: 'A', value: 'a' },{ text: 'B', value: 'b' },{ text: 'C', value: 'c' }]}.optionalediting
editTemplateUsed with the editType custom, a function to create a custom editable cell. Format: editTemplate: (value, rowData) => {}Example, use a textarea (value, onUpdate, rowData) => (<Textfield textarea fullWidth dense value={value} onUpdate={handleUpdate} />)}}.optionalediting
editTypeThe edit component to use.May be textfield, number, checkbox, switch, datepicker (work in progress), and custom.optionalediting
editable(work in progress): Enable column editing. Currently I'm taking an edit-on-click approach, but I may make that configurable. Depending on how many rows/columns that could be a performance issue without virtualization.optionalediting
fixedPin the column to the leading or trailing edge of the table.May be leading and trailing.optionallayout
idA custom identifier for the column.*Note: this is required to enable defaultSorting.optional*general
isSortedDescOverrides the internal sorting direction state.optionalsorting
onClickA custom click handler.May be a function (event, column) => {}.optionalgeneral
sortDescFirstThe initial sort direction for this column will be descending instead of ascending.optionalsorting
sortFieldWhen using custom/controlled sorting accessor is not available to use, so we need an custom field to pass to a custom click handler to let us know what to sort on.optionalsorting
sortInvertedThe actual sort direction will be inverted, but the UI will still display normally. May be useful when lower numbers could be considered more positive (for example, golf scores).optionalsorting
sortTypeOverride the default sort method. May be string, number, basic, datetime, alphanumeric (default), or a function (rowA, rowB, columnId, desc) => {}. Please note, there is a known issue with the number sortType and negative numbers. Use basic instead.optionalsorting
widthSpecify a fixed width for the column. Specified in pixels - must be an integer. ex: width: 150optionallayout

Expandable Rows

There are 2 main types of expandable rows. The first is a set of rows that follow the same column layout and may contain one or more sub-rows. The second allows for any arbitrary content to be displayed below an expanded row.

Common expandable rows setup

  1. Enable expandable rows by setting the Table prop expandable to true. Example <Table expandable />
  2. Add a column to your column schema to implement the expansion buttons
    1. Use the column schema headerTemplate field to implement a Button or IconButton that includes {...row.getToggleAllRowsExpandedProps()}.
    2. Use the column schema displayTemplate field to implement a Button or IconButton that includes {...row.getToggleRowExpandedProps()}.

Example:

// Note: This controls/indicates all rows being expanded or collapsed.
const expanderHeaderTemplate = ({
    getToggleAllRowsExpandedProps,
    isAllRowsExpanded
}) => (
    <IconButton dense {...getToggleAllRowsExpandedProps?.()}>
        <Icon
            iconSize='small'
            icon={
                isAllRowsExpanded
                    ? 'keyboard_arrow_down'
                    : 'keyboard_arrow_right'
            }
        />
    </IconButton>
);

// Note: This controls each individual row expanding or being collapsed.
const expanderDisplayTemplate = (
    // Note: This method signature will likely be simplified in v6.
    value,
    original,
    id,
    editable,
    onCellUpdate,
    row
) => (
    <IconButton {...row.getToggleRowExpandedProps()} dense>
        <Icon
            iconSize='small'
            icon={`keyboard_arrow_${row.isExpanded ? 'down' : 'right'}`}
        />
    </IconButton>
);

const columns = [
    {
        id: 'expander',
        headerTemplate: expanderHeaderTemplate,
        displayTemplate: expanderDisplayTemplate
    },
    { header: 'Title', accessor: 'title' },
    { header: 'Cost', accessor: 'cost' },
    { header: 'Count', accessor: 'count' },
    { header: 'Progress', accessor: 'progress' }
];

Same schema expandable rows

After following the common steps above, add the subRows field (object array) to your data schema/shape to hold the expandable/collapsible row data. This should match the parent data schema/shape.

Example:

const data = [
    {
        title: 'device',
        cost: '$11',
        count: 2,
        progress: 52,
        subRows: [
            {
                title: 'transportation',
                cost: '$15',
                count: 0,
                progress: 98
            },
            {
                title: 'wind',
                cost: '$23',
                count: 79,
                progress: 45
            }
        ]
    }
    //... rest of data
];

Custom expandable rows

After following the common steps above you will need to pass an expandableRowTemplate to the Table. This is a function that will return jsx to render your expanded content.

const expandedRowTemplate = ({ row }) => (
    // return any content you want, here we will just output the row values
    <Padding tag='pre'>
        <code>{JSON.stringify({ values: row.values }, null, 2)}</code>
    </Padding>
);

// ...
<Table expandedRowTemplate={expandedRowTemplate} expandable data={data} columns={columns}>

Initially/Always Expanded

You can also have a Table start with all expanded rows already open using initiallyExpanded Table prop. If you don't need collapsible rows, then you can ignore the expander column setup above.

Custom properties

Most properties for a TableRow or TableCell can be customized using one of several methods.

rowProps & headerRowProps

Note: this section will refer to rowProps, but headerRowProps works identically however, it is specific to the header row and can only be passed to the Table component.

To send custom props to TableRow you can use the Table prop rowProps. This can be either a simple object or a function. Using a function gives you the added benefit of receiving rowData and rowIndex, allowing for additional logic.

// Object
<Table
rowProps={{
    className: 'custom-class__x',
    onClick: () => {console.log('You clicked a row.')}
}}
/>

// Function
<Table
rowProps={
    (rowData, rowIndex)=> {
        // example custom props here
        className= !rowData?.firstName ? 'data__missing-name' : '',
        onClick: () => {console.log(`You clicked row ${rowIndex}.`, rowData)}

    }
}
/>

These are just a few examples of custom props, but you can pass in almost any valid TableRow or HtmlElement prop.

Additionally, you can have even more control by adding a rowProps field on a per record basis. This may also be an object or function.

const data = useMemo(() => [
    {
        name: 'Sponge Bob',
        rowProps: { expandable: true }
    },
    {
        name: 'Optimus Prime',
        rowProps: (rowData, rowIndex) => {
            onMouseEnter: () => {
                doSomethingInteresting(rowData, rowIndex);
            };
            onMouseLeave: () => {
                doSomethingInteresting2(rowData, rowIndex);
            };
        }
    },
    { name: 'Pickle Rick' }
]);

return <Table data={data} />;

rowProps Conflicts

In the case of prop collisions between those defined at the Table level and those defined in a data item, the data item wins. Given the following example

const data =[{name: 'A', rowProps: {className:'class_a'}}];

return (<Table rowProps={{className:'class__b'}} data={data}>)

The rendered class for the row with name A will be class\_\_a.

cellProps & headerCellProps

Note: this section will refer to cellProps, but headerCellProps works identically, but is specific to the header cells.

To send custom props to TableCell you can use the Table prop cellProps. This can be either a simple object or a function. Using a function gives you the added benefit of receiving the cell object, allowing for additional logic.

// Object
<Table
cellProps={{
    className: 'custom-class__x',
    onClick: () => {console.log('You clicked a cell.')}
}}
/>

// Function
<Table
cellProps={
    (cell)=> {
        // example custom props here
        className= !cell?.column.header === 'firstName' ? 'data__first-name' : '',
        onClick: () => {console.log(`You clicked '${cell?.column.header}' cell with value '${cell?.value}'.`)}

    }
}
/>

These are just a few examples of custom props, but you can pass in almost any valid TableCell or HtmlElement prop.

Additionally, you can have even more control by adding a cellProps field to a column config object. This may also be an object or function.

const columns = [
    {
        header: 'Name',
        accessor: 'name',
        cellProps: { expandable: true }
    },
    {
        header: 'Age',
        accessor: 'age',
        cellProps: cell => {
            onMouseEnter: () => {
                doSomethingInteresting(cell);
            };
            onMouseLeave: () => {
                doSomethingInteresting2(cell);
            };
        }
    }
];

cellProps Conflicts

In the case of prop collisions between those defined at the Table level and those defined in a column, the column prop wins. Given the following example

const columns = [
    { header: 'Name', accessor: 'name', cellProps: { className: 'class_a' } }
];

return <Table cellProps={{ className: 'class__b' }} columns={columns} />;

The rendered class for the column Name will be class\_\_a.

Using References & Imperative Functions

Table supports the use of react references. You will receive an object with a reference to the table and the following imperative functions:

{
    clearSortBy, // a function to reset sorting
        resetResizing, // a function to reset resizing
        table; // the table reference
}

Example reference usage:

    const tableRef = useRef();
    const handleScroll = () => {
        tableRef.current.table.scrollIntoView();
    };

    return (
        <>
            <Table
                ref={tableRef}
                data={data}
                columns={columns}
            />
            <div style={{ marginTop: '600px' }}>
                <button type='button' onClick={handleScroll}>
                    Scroll Table Into View
                </button>
            </div>
        </>

Table Props

NameTypeDefaultRequiredDescription
alwaysUseDivTagsbooleanfalsefalseThe table will always use div tags instead of standard table tags. Note, disabled when using layout='standard'.
ariaLabelstringnullfalseAccessibility label for assistive technologies.
canUnSortbooleanfalsefalseEnable the user to toggle sortable columns between ascending, descending, and unsorted.
cellPropsobject|functionundefinedfalsePass custom props directly to each TableCell (generated & virtualized modes only). Can be either an object or a function that receives the following signature (cellData).
classNamestringundefinedfalseThe css class name to be passed through to the component markup.
columnReorderingbooleanfalsefalseAllow columns to be re-ordered using drag and drop headers. Note, this cannot be used with fixed columns.
columnStripedbooleanfalsefalseApply a striped effect to columns.
columnsobject[]falseArray of objects describing the table columns, used in conjunction with data prop. See README for the full column schema. Note: do not use children when using data and columns.
containerPropsobjectnullfalseCustom properties to be applied to the table container.
dataobjectnullfalseArray of data. Must be used in conjunction with columns. Note: do not use children when using data and columns.
defaultColumnobjectnullfalseAn object to describe the the default column properties if none else are specified via data.
densebooleanfalsefalseA more compact table with less padding.
disableMultiSortbooleanfalsefalseDisable multi-column sorting.
expandablebooleanundefinedfalseIndicates if the table supports expandable rows. See 'Expandable Rows' in the README for details.
expandedRowTemplatefunctionundefinedfalseUsed to render secondary content when used with expandable. Note: this is only shown when a row is expanded using a button using row.getToggleRowExpandedProps() or row.getToggleAllRowsExpandedProps(). See 'Expandable Rows' in the README for details.
fixedColumnsBorderbooleantruefalseOn by default, setting this to false will remove the additional trailing border added to fixed columns. This should only be used where there is an alternate means of showing which columns are fixed, ie. background color or elevation.
fixedHeaderbooleanfalsefalseThe header row will remain fixed on-screen, while the body may scroll under it. This will automatically be applied when virtualized is true. Warning, fixedHeader requires a layout of standard.
fullWidthbooleanfalsefalseThe table will expand it's width to 100% of it's parent container.
headerCellPropsobject|functionundefinedfalsePass custom props directly to each TableHeaderCell (generated & virtualized modes only). Can be either an object or a function that receives the following signature (column).
headerDividerTallbooleanfalsefalseThe divider between the header and rows will be taller. Used in conjunction with headerDividerThemeColor to add a splash of color to a table.
headerDividerThemeColorstringnullfalseAdd a splash of color to the divider between the header and rows.Accepted Values: primary, secondary, primary-variant, secondary-variant
headerRowPropsobject|functionundefinedfalsePass custom props directly to each TableHeaderRow (generated & virtualized modes only). Can be either an object or a function that receives the following signature (rowData, rowIndex).
headerThemeColorstringnullfalseEnabled the header of the card to use a surface color, similar to the colored header on a Card component. Should be used sparingly.Accepted Values: neutral, primary, secondary, primary-variant, secondary-variant
heightnumbernullfalseFixed height of the table. Required when layout is fixed, virtualized is true or fixedHeader is true. Note, must be a unit-less number (in pixels).
hiddenColumnsstringundefinedfalseAn array of id's for columns to hide.
hideHorizontalScrollbarbooleanfalsefalseHides the horizontal scrollbar. Note: only use when alternative scrolling is offered.
initiallyExpandedbooleanundefinedfalseAll expandable/collapsible rows will be expanded when used with expandable.
layoutstring'block'falseThe style of layout to use. Notes: In most cases the column width will default to 150px, with the exception of standard, which uses browser default table layout. Using resizableColumns or columnReordering will force layout to block. Using virtualized will use it's own layout technique. Fixed headers requires the standard layout. Warning: the default value will change to standard in v6.Accepted Values: standard, block, flex
nestedbooleanfalsefalseRemoves certain margins and paddings for better appearance when nesting tables. Used on the child (nested) table.
noContentIncludeTablebooleanfalsefalseShow the table header with the noContentMessage when there is no table content or data.
noContentMessagestring|function|React.ReactNode'There is no content available.'falseThe message to show when there is no table content or data.
noHoverbooleanfalsefalsePrevents hover highlighting effect on rows.
paginatedbooleanfalsefalseEnable built-in pagination.
paginationPropsobjectundefinedfalsePass custom props through to the Pagination control (when paginated is true).
pluginsobjectnullfalseCustomize table behavior.
resizableColumnsbooleannullfalseEnable columns to be resized by the user.
rowHeightnumber|function50falseWhen using variableHeight rowHeight must be specified as a function, otherwise the default row height of 50 should be used except in cases of custom CSS. The function will receive the rowIndex as a parameter.
rowPropsobject|functionundefinedfalsePass custom props directly to each TableRow (generated & virtualized modes only). Can be either an object or a function that receives the following signature (rowData, rowIndex)..
rowStripedbooleannullfalseApply a stripped effect to the rows.
sortIndicatorOnHoverbooleanfalsefalseEnable the sort indicator to be hidden until the header cell is hovered/activated.
sortablebooleanfalsefalseEnable built-in column sorting.
styleobjectnullfalsePassthrough style object.
variableHeightbooleanfalsefalseUsed with virtualized tables with expandable rows that are of varying sizes or a different size than the original row.
virtualizedbooleanfalsefalseEnable virtualized rendering for high performance with large amounts of data.
widthnumbernullfalseFixed width for the table. Required for a layout of fixed. Note, must be a unit-less number (in pixels).

Table Render Props

NameTypeDefaultRequiredDescription
childrenReact.ReactNodenullfalseThe composed table elements. Expects one each of TableHeader, TableBody.
footerContentReact.ReactNodenullfalseContent to appear at the bottom of the table. Expects a TableFooter component with one or more TableRows and TableCells or TableHeaderCells
headerContentReact.ReactNodenullfalseContent to appear at the top of the table. Expects a TableHeader component with one or more TableRows and TableCells or TableHeaderCells
leadingContentReact.ReactNodenullfalseCustom content to appear inside the table container directly above the actual table.
trailingContentReact.ReactNodenullfalseCustom content to appear inside the table container directly below the actual table.

Table Deprecated Render Props

NameTypeDefaultRequiredDeprecatedDescription
topBarReact.ReactNodefalseUse leadingContent instead.Custom content to appear inside the table container directly above the actual table.

Table Events

NameDefaultRequiredParamsDescription
onStateChangenullfalseEvent that is fired when table data has been updated, such as when a column is dragged and dropped. Warning, this only fires when using data and columns props. It does not work when directly composing Table components.
onWidthChangenullfalseEvent that is fired when table width has been changed, either by the browser window or other layout changes. Warning, this only fires when using data and columns props. It does not work when directly composing Table components.

Table Breaking Changes

Description
Table: Table has been rewritten and its usage has changed. Please see the new Table documentation for examples.,The columns prop has a new schema. See the new Table documentation for examples.
customSort (removed): See column schema in the readme for new sorting options.
fixedColumn (removed): This functionality is implemented differently. See the new Table documentation for examples.
footer (removed): See trailingContent.
label (removed): See ariaLabel
maxHeight (removed): See maxHeight.
overflow (removed): See layout.
remote (removed): No longer needed.
responsive (removed): See layout for similar functionality.
selectable (removed): Not currently supported.
stickyHeader (removed): See fixedHeader.
tall (removed): Not currently supported.

Table Body Props

NameTypeDefaultRequiredDescription
alwaysUseDivTagsbooleanfalsefalseThe table will always use div tags instead of standard table tags. Note, disabled when using layout='standard'.
classNamestringundefinedfalseThe css class name to be passed through to the component markup.
styleobjectnullfalsePassthrough style object.
widthnumbernullfalseFixed width for table body.

Table Body Render Props

NameTypeDefaultRequiredDescription
childrenReact.ReactNodenullfalseExpects one or more TableRow components.

Table Cell Props

NameTypeDefaultRequiredDescription
alignstringundefinedfalseSet the cell's alignment.Accepted Values: start, end, center
alwaysUseDivTagsbooleanfalsefalseThe table will always use div tags instead of standard table tags. Note, disabled when using layout='standard'.
cellobjectnullfalseReact-Table cell object.
classNamestringundefinedfalseThe css class name to be passed through to the component markup.
colSpannumbernullfalseThe cell may span multiple columns. Note: Only supported in composed tables (ie tables that do not have columns/data and are not virtualized).
columnIndexnumberundefinedfalseCustom index to be passed to the actionsRenderer.
customRendererfunctionnullfalseOverride the default rendering of the cell.
fixedbooleanundefinedfalseUsed for sticky columns.
idstringundefinedfalseRandomly generated id. May be overwritten.
layoutstringempty stringfalseThe table's layout value, used internally.
nestablebooleanfalsefalseRemoves certain margins and paddings for better appearance when nesting tables. Used on the parent cell of the nested table.
rowSpannumbernullfalseThe cell may span multiple rows. Note: Only supported in composed tables (ie tables that do not have columns/data and are not virtualized).

Table Cell Deprecated Props

NameTypeDefaultRequiredDeprecatedDescription
backgroundColorstringundefinedfalseThis prop has been deprecated in favor of cellProps in the column schema (see README), which offers more flexibility.Enabled a custom background color.

Table Cell Render Props

NameTypeDefaultRequiredDescription
childrenReact.ReactNodenullfalseMostly commonly a string, accepts any valid markup.

Table Cell Breaking Changes

Description
columnWidth (removed): This feature is not currently supported. See column scheme in the Table readme for similar functionality.
currencyPosition (removed): This feature is not currently supported.See column scheme in the Table readme for similar functionality.
currencySymbol (removed): This feature is not currently supported.See column scheme in the Table readme for similar functionality.
currencySymbolShow (removed): This feature is not currently supported.See column scheme in the Table readme for similar functionality.
headerName (removed): No longer supported.
hidden (removed): No longer supported.
isMobile (removed): This feature is not currently supported.
type (removed): See column scheme in the Table readme for similar functionality.
wrapMode (removed): Not currently supported.

Table Footer Props

NameTypeDefaultRequiredDescription
alwaysUseDivTagsbooleanfalsefalseThe table will always use div tags instead of standard table tags. Note, disabled when using layout='standard'.
classNamestringundefinedfalseThe css class name to be passed through to the component markup.
styleobjectnullfalsePassthrough style object.
widthnumbernullfalseFixed width for table body.

Table Footer Render Props

NameTypeDefaultRequiredDescription
childrenReact.ReactNodenullfalseExpects one or more TableRow components.

Table Header Props

NameTypeDefaultRequiredDescription
alwaysUseDivTagsbooleanfalsefalseThe table will always use div tags instead of standard table tags. Note, disabled when using layout='standard'.
classNamestringundefinedfalseThe css class name to be passed through to the component markup.
styleobjectnullfalsePassthrough style object.