DataTable
The DataTable component is a wrapper that uses the react-table library to create tables. It can be used as is, or its subcomponents can be used on their own, allowing the developer full control.
Paragon also exports all React hooks from react-table
allowing the developers to use them and make customizations more freely without adding react-table
as a separate dependency to their project.
For full list of available hooks view react-table API reference.
How children get information
The table context gets the current react-table
instance of the table from the DataTable
component and makes it available to any child component within the DataTable
provider.
In addition to the react-table
instance, itemCount
, numBreakoutFilters
, and bulkActions
, and any props that are not listed in the
props table below are available to child components through the DataTableContext
.
How to use context in a custom component:
const instance = useContext(DataTableContext)
Frontend filtering and sorting
For small tables (less than ~10,000 rows), filtering, sorting and pagination can be done quickly and easily on the frontend.
In this example, a default TextFilter component is used for all columns. A default filter can be passed in, or a filter component can be defined on the column. See react-table filters documentation for more information.
Any Paragon component or export may be added to the code example.
<DataTableisPaginatedisSelectableinitialState={{pageSize: 2,}}isFilterableisSortabledefaultColumnValues={{ Filter: TextFilter }}itemCount={7}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',},{name: 'Long Cat',color: 'russian white',famous_for:'being loooooooooooooooooooooooooooooooooooooooooooooooooooooong',},{name: 'Zeno',color: 'brown tabby',famous_for: 'getting halfway there'},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',Filter: CheckboxFilter,filter: 'includesValue',filterChoices: [{name: 'russian white',number: 1,value: 'russian white',},{name: 'orange tabby',number: 2,value: 'orange tabby',},{name: 'brown tabby',number: 3,value: 'brown tabby',},{name: 'siamese',number: 1,value: 'siamese',}]},]}><DataTable.TableControlBar /><DataTable.Table /><DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>
Backend filtering and sorting
For larger tables, it may make sense to do filtering, pagination and sorting on the backend.
The user must pass a fetchData
function, which will be called when the filtering, sorting, or pagination
data changes. The manualFilters
, manualPagination
, and manualSortBy
props may also be needed.
When fetchData
is called, it is given the necessary data to send a backend API request using the appropriate filters, page number, and/or ordering parameter. Once the request resolves, be sure to update the data
prop to reflect the updated data.
Paginated selection
To enable proper selection behavior with backend pagination (i.e., when isSelectable
is provided), for both individual rows and bulk actions, the controlled selection status and controlled select components must be used. When used, these components keep track of a user's row selections in a React context provider such that they persist during pagination, even when only 1 page of data is known at any given time. The following components must be used, as shown in the below example:
DataTable.ControlledSelectionStatus
DataTable.ControlledSelectHeader
DataTable.ControlledSelect
Any Paragon component or export may be added to the code example.
() => {const PAGINATED_DATA = [[{id: '2baf70d1-42bb-4437-b551-e5fed5a87abe',title: 'Castle in the Sky',director: 'Hayao Miyazaki',producer: 'Isao Takahata',release_date: 1986,rt_score: 95,}, {id: '12cfb892-aac0-4c5b-94af-521852e46d6a',title: 'Grave of the Fireflies',director: 'Isao Takahata',producer: 'Toru Hara',release_date: 1988,rt_score: 97,},{id: '58611129-2dbc-4a81-a72f-77ddfc1b1b49',title: 'My Neighbor Totoro',director: 'Hayao Miyazaki',producer: 'Hayao Miyazaki',release_date: 1988,rt_score: 93,},],[{id: 'ea660b10-85c4-4ae3-8a5f-41cea3648e3e',title: 'Kiki\'s Delivery Service',director: 'Hayao Miyazaki',producer: 'Hayao Miyazaki',release_date: 1989,rt_score: 96,},{id: '4e236f34-b981-41c3-8c65-f8c9000b94e7',title: 'Only Yesterday',director: 'Isao Takahata',producer: 'Toshio Suzuki',release_date: 1991,rt_score: 100,},{id: 'ebbb6b7c-945c-41ee-a792-de0e43191bd8',title: 'Porco Rosso',director: 'Hayao Miyazaki',producer: 'Toshio Suzuki',release_date: 1992,rt_score: 94,},],[{id: '1b67aa9a-2e4a-45af-ac98-64d6ad15b16c',title: 'Pom Poko',director: 'Isao Takahata',producer: 'Toshio Suzuki',release_date: 1994,rt_score: 78,},],];const [data, setData] = useState(PAGINATED_DATA[0]);const fetchData = useCallback((args) => {setTimeout(() => {setData(PAGINATED_DATA[args.pageIndex]);}, 1000);},[],);const selectColumn = {id: 'selection',Header: DataTable.ControlledSelectHeader,Cell: DataTable.ControlledSelect,disableSortBy: true,};const DownloadCSVAction = ({ as: Component, selectedFlatRows, ...rest }) => (<Component onClick={() => console.log('Download CSV', selectedFlatRows, rest)}>Download CSV</Component>);const ClearAction = ({ as: Component, tableInstance, ...rest }) => (<Componentvariant="danger"onClick={() => {console.log('Clear Selection');tableInstance.clearSelection();}}>Clear Selection</Component>);return (<DataTableisSelectablemanualSelectColumn={selectColumn}SelectionStatusComponent={DataTable.ControlledSelectionStatus}isFilterablemanualFiltersdefaultColumnValues={{ Filter: TextFilter }}isPaginatedmanualPaginationisSortablemanualSortByinitialState={{pageSize: 3,pageIndex: 0}}initialTableOptions={{getRowId: row => row.id,}}itemCount={7}pageCount={3}fetchData={fetchData}data={data}columns={[{Header: 'Title',accessor: 'title',},{Header: 'Director',accessor: 'director',},{Header: 'Release date',accessor: 'release_date',},]}bulkActions={[<DownloadCSVAction />,<ClearAction />,]}/>);}
View Switching
Card view is default when isDataViewToggleEnabled
is true.
See dataViewToggleOptions
props documentation for all supported props.
NOTE: you have to memoize data
to persist filters choices during view switch, see code example below.
Any Paragon component or export may be added to the code example.
() => {const [currentView, setCurrentView] = useState('card');const togglePlacement = 'left'; // 'bottom' is the only other supported valueconst ExampleCard = ({ className, original }) => {const { name, color, famous_for: famousFor } = original;return (<Card className={className}><Card.ImageCap src="https://picsum.photos/360/200/" srcAlt="Card image" /><Card.Header title={name} /><Card.Section><dl><dt>Color</dt><dd>{color}</dd><dt>Famous For</dt><dd>{famousFor}</dd></dl></Card.Section></Card>);};// memoize data, otherwise the filters will get reset after switching viewconst data = useMemo(() => [{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},], [])return (<DataTableisFilterableisSelectabledataViewToggleOptions={{isDataViewToggleEnabled: true,onDataViewToggle: val => setCurrentView(val),togglePlacement,}}isSortabledefaultColumnValues={{ Filter: TextFilter }}itemCount={3}data={data}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',Filter: CheckboxFilter,filter: 'includesValue',filterChoices: [{name: 'russian white',number: 1,value: 'russian white',},{name: 'orange tabby',number: 2,value: 'orange tabby',},{name: 'brown tabby',number: 3,value: 'brown tabby',},{name: 'siamese',number: 1,value: 'siamese',}]},]}><DataTable.TableControlBar />{/* which kind of body content to show */}{ currentView === "card" && <CardView CardComponent={ExampleCard} /> }{ currentView === "list" && <DataTable.Table /> }<DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>)}
With a default active state specified
Any Paragon component or export may be added to the code example.
() => {const defaultVal = "list";const [currentView, setCurrentView] = useState(defaultVal);return (<DataTableisFilterabledataViewToggleOptions={{isDataViewToggleEnabled: true,onDataViewToggle: val => setCurrentView(val),defaultActiveStateValue: defaultVal,}}isSortabledefaultColumnValues={{ Filter: TextFilter }}itemCount={7}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',Filter: CheckboxFilter,filter: 'includesValue',filterChoices: [{name: 'russian white',number: 1,value: 'russian white',},{name: 'orange tabby',number: 2,value: 'orange tabby',},{name: 'brown tabby',number: 3,value: 'brown tabby',},{name: 'siamese',number: 1,value: 'siamese',}]},]}><DataTable.TableControlBar />{/* which kind of body content to show */}{ currentView === "card" && <CardView CardComponent={MiyazakiCard} /> }{ currentView === "list" && <DataTable.Table /> }<DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>)}
Loading state
Can be used to show the loading state when DataTable
is asynchronously fetching new data.
Any Paragon component or export may be added to the code example.
() => {{/* start example state */}const [hasData, setHasData] = useState(false)const data = [{id: '2baf70d1-42bb-4437-b551-e5fed5a87abe',title: 'Castle in the Sky',director: 'Hayao Miyazaki',producer: 'Isao Takahata',release_date: 1986,rt_score: 95,}, {id: '12cfb892-aac0-4c5b-94af-521852e46d6a',title: 'Grave of the Fireflies',director: 'Isao Takahata',producer: 'Toru Hara',release_date: 1988,rt_score: 97,},{id: '58611129-2dbc-4a81-a72f-77ddfc1b1b49',title: 'My Neighbor Totoro',director: 'Hayao Miyazaki',producer: 'Hayao Miyazaki',release_date: 1988,rt_score: 93,},];{/* end example state */}return (<>{/* start example form block */}<ExamplePropsForminputs={[{ value: hasData, setValue: () => setHasData(!hasData), name: 'data' },]}/>{/* end example form block */}<DataTableisLoadingitemCount={data.length}data={hasData ? data : []}columns={[{Header: 'Title',accessor: 'title',},{Header: 'Director',accessor: 'director',},{Header: 'Release date',accessor: 'release_date',},]}/></>);}
Actions, Table actions and Bulk actions
Actions and bulk actions are actions that are performed on table rows, though bulk actions can be used for actions that apply to the whole table. It is up to the user to determine what the action does.
Table Actions
Table actions are actions that are enacted on the entire table. Their click handler is called with the react-table instance. The first two table actions will be displayed as buttons, while the remaining actions will be displayed in an overflow dropdown menu. Table actions are not visible if bulk actions are available and there are selected rows.
Bulk Actions
Bulk actions are action that are enacted on specific table rows. The bulk action click handler is called with the selected rows. The first two bulk actions will be displayed as buttons, while the remaining bulk actions will be displayed in a overflow dropdown menu. Bulk actions are not visible unless rows have been selected.
Actions
An action can also be defined as an additional column on the table. The Cell property can be defined to display any component that a user requires. It will receive the row as props. You can pass a function to render custom components for bulk actions and table actions.
Any Paragon component or export may be added to the code example.
() => {const TableAction = ({ tableInstance }) => (// Here is access to the tableInstance<Button onClick={() => console.log('TableAction', tableInstance)}>Enroll</Button>);const EnrollAction = ({ selectedFlatRows, ...rest }) => (// Here is access to the selectedFlatRows, isEntireTableSelected, tableInstance<Button variant="danger" onClick={() => console.log('Enroll', selectedFlatRows, rest)}>Enroll</Button>);const AssignAction = (props) => (<Button onClick={() => console.log('Assign', props)}>Assign</Button>);const ExtraAction = ({ text, selectedFlatRows, ...rest }) => (<Button onClick={() => console.log(`Extra Action ${text}`, selectedFlatRows, rest)}>{`Extra Action ${text}`}</Button>);return (<DataTableisSelectableitemCount={7}tableActions={[<TableAction />,]}bulkActions={[<EnrollAction />,<AssignAction />,<ExtraAction text="1" />,<ExtraAction text="2" />,]}additionalColumns={[{id: 'action',Header: 'Action',Cell: ({ row }) => <Button variant="link" size="sm" onClick={() => console.log(`Assigning ${row.values.name}`)}>Assign</Button>,}]}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',},{name: 'Long Cat',color: 'russian white',famous_for:'being loooooooooooooooooooooooooooooooooooooooooooooooooooooong',},{name: 'Zeno',color: 'brown tabby',famous_for: 'getting halfway there'},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',},]}><DataTable.TableControlBar /><DataTable.Table /><DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>)}
Actions with Data view toggle enabled
Any Paragon component or export may be added to the code example.
() => {const [currentView, setCurrentView] = useState('card');const TableAction = (props) => (// Here is access to the tableInstance<Button onClick={() => console.log('TableAction', props)}>Enroll</Button>);const EnrollAction = ({ selectedFlatRows, ...rest }) => (// Here is access to the selectedFlatRows, isEntireTableSelected, tableInstance<Button variant="danger" onClick={() => console.log('Enroll', selectedFlatRows, rest)}>Enroll ({selectedFlatRows.length})</Button>);const AssignAction = ({ selectedFlatRows, ...rest }) => (<Button onClick={() => console.log('Assign', selectedFlatRows, rest)}>Assign ({selectedFlatRows.length})</Button>);const ExtraAction = ({ text, selectedFlatRows, ...rest }) => (<Button onClick={() => console.log(`Extra Action ${text}`, selectedFlatRows, rest)}>{`Extra Action ${text}`}</Button>);return (<DataTabledataViewToggleOptions={{isDataViewToggleEnabled: true,onDataViewToggle: val => setCurrentView(val),defaultActiveStateValue: "card",}}isSelectableitemCount={7}tableActions={[<TableAction />,]}bulkActions={[<EnrollAction />,// Default value for `as` property is `Button`<AssignAction as={Button} />,<ExtraAction text="1" />,<ExtraAction text="2" />,]}additionalColumns={[{id: 'action',Header: 'Action',Cell: ({ row }) => <Button variant="link" size="sm" onClick={() => console.log(`Assigning ${row.values.name}`)}>Assign</Button>,}]}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',},{name: 'Long Cat',color: 'russian white',famous_for:'being loooooooooooooooooooooooooooooooooooooooooooooooooooooong',},{name: 'Zeno',color: 'brown tabby',famous_for: 'getting halfway there'},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',},]}><DataTable.TableControlBar />{/* which kind of body content to show */}{ currentView === "card" && <CardView CardComponent={MiyazakiCard} /> }{ currentView === "list" && <DataTable.Table /> }<DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>);}
CardView and alternate table components
You may choose to use any table component by using the following code in your display component:
const instance = useContext(DataTableContext)
The CardView takes a CardComponent
that is personalized to the table in question and displays
a responsive grid of cards.
Any Paragon component or export may be added to the code example.
() => {const DownloadCSVAction = ({ as: Component, selectedFlatRows, ...rest }) => (<Component onClick={() => console.log('Download CSV', selectedFlatRows, rest)}>Download CSV</Component>);const ClearAction = ({ as: Component, tableInstance, ...rest }) => (<Componentvariant="danger"onClick={() => {console.log('Clear Selection');tableInstance.clearSelection();}}>Clear Selection</Component>);return (<DataTableisFilterabledefaultColumnValues={{ Filter: TextFilter }}isPaginatedisSortableinitialState={{pageSize: 3,pageIndex: 0}}itemCount={20}fetchData={(data) => console.log(`This function will be called with the value: ${JSON.stringify(data)}}`)}data={[{'id': '2baf70d1-42bb-4437-b551-e5fed5a87abe','title': 'Castle in the Sky','director': 'Hayao Miyazaki','producer': 'Isao Takahata','release_date': '1986','rt_score': '95',},{'id': '12cfb892-aac0-4c5b-94af-521852e46d6a','title': 'Grave of the Fireflies','director': 'Isao Takahata','producer': 'Toru Hara','release_date': '1988','rt_score': '97',},{'id': '58611129-2dbc-4a81-a72f-77ddfc1b1b49','title': 'My Neighbor Totoro','director': 'Hayao Miyazaki','producer': 'Hayao Miyazaki','release_date': '1988','rt_score': '93',},{'id': 'ea660b10-85c4-4ae3-8a5f-41cea3648e3e','title': 'Kiki\'s Delivery Service','director': 'Hayao Miyazaki','producer': 'Hayao Miyazaki','release_date': '1989','rt_score': '96',},{'id': '4e236f34-b981-41c3-8c65-f8c9000b94e7','title': 'Only Yesterday','director': 'Isao Takahata','producer': 'Toshio Suzuki','release_date': '1991','rt_score': '100',},{'id': 'ebbb6b7c-945c-41ee-a792-de0e43191bd8','title': 'Porco Rosso','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '1992','rt_score': '94',},{'id': '1b67aa9a-2e4a-45af-ac98-64d6ad15b16c','title': 'Pom Poko','director': 'Isao Takahata','producer': 'Toshio Suzuki','release_date': '1994','rt_score': '78',},{'id': 'ff24da26-a969-4f0e-ba1e-a122ead6c6e3','title': 'Whisper of the Heart','director': 'Yoshifumi Kondō','producer': 'Toshio Suzuki','release_date': '1995','rt_score': '91',},{'id': '0440483e-ca0e-4120-8c50-4c8cd9b965d6','title': 'Princess Mononoke','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '1997','rt_score': '92',},{'id': '45204234-adfd-45cb-a505-a8e7a676b114','title': 'My Neighbors the Yamadas','director': 'Isao Takahata','producer': 'Toshio Suzuki','release_date': '1999','rt_score': '75',},{'id': 'dc2e6bd1-8156-4886-adff-b39e6043af0c','title': 'Spirited Away','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '2001','rt_score': '97',},{'id': '90b72513-afd4-4570-84de-a56c312fdf81','title': 'The Cat Returns','director': 'Hiroyuki Morita','producer': 'Toshio Suzuki','release_date': '2002','rt_score': '89',},{'id': 'cd3d059c-09f4-4ff3-8d63-bc765a5184fa','title': 'Howl\'s Moving Castle','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '2004','rt_score': '87',},{'id': '112c1e67-726f-40b1-ac17-6974127bb9b9','title': 'Tales from Earthsea','director': 'Gorō Miyazaki','producer': 'Toshio Suzuki','release_date': '2006','rt_score': '41',},{'id': '758bf02e-3122-46e0-884e-67cf83df1786','title': 'Ponyo','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '2008','rt_score': '92',},{'id': '2de9426b-914a-4a06-a3a0-5e6d9d3886f6','title': 'Arrietty','director': 'Hiromasa Yonebayashi','producer': 'Toshio Suzuki','release_date': '2010','rt_score': '95',},{'id': '45db04e4-304a-4933-9823-33f389e8d74d','title': 'From Up on Poppy Hill','director': 'Gorō Miyazaki','producer': 'Toshio Suzuki','release_date': '2011','rt_score': '83',},{'id': '67405111-37a5-438f-81cc-4666af60c800','title': 'The Wind Rises','director': 'Hayao Miyazaki','producer': 'Toshio Suzuki','release_date': '2013','rt_score': '89',},{'id': '578ae244-7750-4d9f-867b-f3cd3d6fecf4','title': 'The Tale of the Princess Kaguya','director': 'Isao Takahata','producer': 'Yoshiaki Nishimura','release_date': '2013','rt_score': '100',},{'id': '5fdfb320-2a02-49a7-94ff-5ca418cae602','title': 'When Marnie Was There','director': 'Hiromasa Yonebayashi','producer': 'Yoshiaki Nishimura','release_date': '2014','rt_score': '92',}]}columns={[{Header: 'Title',accessor: 'title',},{Header: 'Director',accessor: 'director',},{Header: 'Release date',accessor: 'release_date',},]}bulkActions={[<DownloadCSVAction />,<ClearAction />,]}><TableControlBar /><CardView CardComponent={MiyazakiCard} /><TableFooter /></DataTable>);};
Customizing number of Cards shown per row
Use columnSizes
prop of CardView
component to define how many Cards
are shown per row at each breakpoint.
columnSizes
is an object containing the desired column size at each breakpoint. The example below shows 1 Card
per row at xs
breakpoint, 2 Cards
at sm
and md
, and 4 Cards
at lg
and higher. You can read more about the API at https://react-bootstrap.netlify.app/layout/grid/.
Any Paragon component or export may be added to the code example.
() => {const columnSizes = { xs: 12, sm: 6, lg: 3 };const ExampleCard = ({ className, original }) => {const { name, color, famous_for: famousFor } = original;return (<Card className={className}><Card.ImageCap src="https://picsum.photos/360/200/" srcAlt="Card image" /><Card.Body><Card.Header title={name} /><Card.Section><dl><dt>Color</dt><dd>{color}</dd><dt>Famous For</dt><dd>{famousFor}</dd></dl></Card.Section></Card.Body></Card>);};return (<DataTableitemCount={5}defaultColumnValues={{ Filter: TextFilter }}isFilterableisSortabledata={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Color',accessor: 'color',},{Header: 'Famous For',accessor: 'famous_for',},]}><TableControlBar /><CardView CardComponent={ExampleCard} columnSizes={columnSizes} /><TableFooter /></DataTable>);};
Horizontal view
You can also display Cards
with horizontal view. If the table is selectable control position of selection checkbox with selectionPlacement
prop, accepts right
or left
positions (relative to the Card
).
Any Paragon component or export may be added to the code example.
() => {const columnSizes = { xs: 12 };const ExampleCard = ({ className, original }) => {const { name, color, famous_for: famousFor } = original;return (<Card className={className} orientation="horizontal"><Card.ImageCap src="https://picsum.photos/360/200/" srcAlt="Card image" /><Card.Body><Card.Header title={name} /><Card.Section><dl><dt>Color</dt><dd>{color}</dd><dt>Famous For</dt><dd>{famousFor}</dd></dl></Card.Section></Card.Body></Card>);};return (<DataTableisSelectabledefaultColumnValues={{ Filter: TextFilter }}isFilterableitemCount={3}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Color',accessor: 'color',},{Header: 'Famous For',accessor: 'famous_for',},]}><TableControlBar /><CardView CardComponent={ExampleCard} columnSizes={columnSizes} selectionPlacement="left" /><TableFooter /></DataTable>);};
Sidebar Filter
For a more desktop friendly view, you can move filters into a sidebar by providing showFiltersInSidebar
prop, try it out!
Any Paragon component or export may be added to the code example.
<DataTableshowFiltersInSidebarisFilterableisSortabledefaultColumnValues={{ Filter: TextFilter }}itemCount={5}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',}]}columns={[{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',Filter: CheckboxFilter,filter: 'includesValue',filterChoices: [{name: 'russian white',number: 1,value: 'russian white',},{name: 'orange tabby',number: 2,value: 'orange tabby',},{name: 'brown tabby',number: 3,value: 'brown tabby',},{name: 'siamese',number: 1,value: 'siamese',}]},]}><DataTable.TableControlBar /><DataTable.Table /><DataTable.EmptyTable content="No results found" /><DataTable.TableFooter /></DataTable>
Expandable rows
DataTable
supports expandable rows which once expanded render additional content under the row. Displayed content
is controlled by the renderRowSubComponent
prop, which is a function that receives row
as its single prop and renders expanded view, you also
need to pass isEpandable
prop to DataTable
to indicate that it should support expand behavior for rows.
Finally, an additional column is required to be included into columns
prop which will contain handlers for expand / collapse behavior, see examples below.
Default view
Here we use default expander column offered by Paragon and for each row render value of the name
attribute as its subcomponent.
Any Paragon component or export may be added to the code example.
<DataTableisExpandableitemCount={7}renderRowSubComponent={({ row }) => <div className='text-center'>{row.values.name}</div>}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},{name: 'Maru',color: 'brown tabby',famous_for: 'being a lovable oaf',},{name: 'Keyboard Cat',color: 'orange tabby',famous_for: 'piano virtuoso',},{name: 'Long Cat',color: 'russian white',famous_for: 'being loooooooooooooooooooooooooooooooooooooooooooooooooooooong',},{name: 'Zeno',color: 'brown tabby',famous_for: 'getting halfway there'},]}columns={[{id: 'expander',Header: DataTable.ExpandAll,Cell: DataTable.ExpandRow,},{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',},]}><DataTable.TableControlBar /><DataTable.Table /><DataTable.TableFooter /></DataTable>
With custom expander column
You can create your own custom expander column and use it, see code example below.
Any Paragon component or export may be added to the code example.
() => {const expanderColumn = {id: 'expander',// getToggleAllRowsExpandedProps and isAllRowsExpanded props will be automatically passed to the componentHeader: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }) => (<span {...getToggleAllRowsExpandedProps()}>{isAllRowsExpanded ? <Remove /> : <Add />}</span>),// Cell will receive row propCell: ({ row }) => (<span {...row.getToggleRowExpandedProps()}>{row.isExpanded ? 'Collapse' : 'Expand'}</span>),};const currentDate = new Date().toDateString();const renderSubComponent = ({ row }) => (<div className="ml-5 w-50"><DataTableitemCount={1}data={[{...row.original},]}columns={[{Header: 'Date modified',accessor: 'date_modified',},{Header: 'Modified by',accessor: 'modified_by',},{Header: 'Reason',accessor: 'reason',},]}><DataTable.Table/></DataTable></div>)return (<DataTableisExpandablerenderRowSubComponent={renderSubComponent}itemCount={3}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',date_modified: currentDate,modified_by: 'Jane Doe',reason: 'Unknown',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',date_modified: currentDate,modified_by: 'Jane Doe',reason: 'Felt like it',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',date_modified: currentDate,modified_by: 'Jane Doe',reason: 'Felt like it',},]}columns={[{ ...expanderColumn },{Header: 'Name',accessor: 'name',},{Header: 'Famous For',accessor: 'famous_for',},{Header: 'Coat Color',accessor: 'color',},]}><DataTable.TableControlBar/><DataTable.Table/><DataTable.TableFooter/></DataTable>);}
Custom cell content
You can create your own cell content by passing the Cell
property to a specific column.
Any Paragon component or export may be added to the code example.
() => {const variants = ['primary', 'warning', 'success', 'danger'];const [cellColors, setCellColors] = useState([0, 1, 2]);const handleColorChange = (index) => {const newColors = cellColors.slice();newColors[index] = cellColors[index] < 3 ? cellColors[index] + 1 : 0;setCellColors(newColors);};return (<DataTableisExpandableitemCount={3}data={[{name: 'Lil Bub',color: 'brown tabby',famous_for: 'weird tongue',},{name: 'Grumpy Cat',color: 'siamese',famous_for: 'serving moods',},{name: 'Smoothie',color: 'orange tabby',famous_for: 'modeling',},]}columns={[{Header: 'Name',Cell: ({ row }) => (<Badge variant={variants[cellColors[row.id] % 4]}>{row.original.name}</Badge>),},{Header: 'Famous For',Cell: ({ row }) => (<Badge variant={variants[(cellColors[row.id] + 1) % 4]}>{row.original.famous_for}</Badge>),},{Header: 'Coat Color',Cell: ({ row }) => (<Badge variant={variants[(cellColors[row.id] + 2) % 4]}>{row.original.color}</Badge>),},]}additionalColumns={[{id: 'action',Header: 'Action',Cell: ({ row }) => <Button variant="link" size="sm" onClick={() => handleColorChange(row.id)}>Change</Button>,}]}><DataTable.TableControlBar/><DataTable.Table/><DataTable.TableFooter/></DataTable>);}
Theme Variables (SCSS)#
$data-table-background-color: $white !default;$data-table-border: 1px solid $gray-200 !default;$data-table-box-shadow: $box-shadow-sm !default;$data-table-padding-x: .75rem !default;$data-table-padding-y: .75rem !default;$data-table-padding-small: .5rem !default;$data-table-cell-padding: .5rem .75rem !default;$data-table-footer-position: center !default;$data-table-pagination-dropdown-max-height: 60vh !default;$data-table-pagination-dropdown-min-width: 6rem !default;$data-table-layout-sidebar-width: 12rem !default;
Props API#
- columns
shape
{Header:func
|node
Required,accessor:requiredWhenNot(PropTypes.string, 'Cell')
,Cell:func
|element
,Filter:func
,filter:string
,filterChoices:}shape
{name:string
,number:number
,value:}string
,[]
,[]
RequiredDefinition of table columns
- data
shape
{}[]
RequiredData to be displayed in the table
- isSelectable
bool
Defaultfalsetable rows can be selected
- manualSelectColumn
shape
{id:string
Required,Header:func
|node
Required,Cell:func
Required,disableSortBy:}bool
Required,Alternate column for selecting rows. See react table useSort docs for more information
- isSortable
bool
DefaultfalseTable columns can be sorted
- manualSortBy
bool
DefaultfalseIndicates that sorting will be done via backend API. A fetchData function must be provided
- isPaginated
bool
DefaultfalsePaginate the table
- manualPagination
bool
DefaultfalseIndicates that pagination will be done manually. A fetchData function must be provided
- pageCount
requiredWhen(PropTypes.number, 'manualPagination')
- isFilterable
bool
DefaultfalseTable rows can be filtered, using a default filter in the default column values, or in the column definition
- manualFilters
bool
DefaultfalseIndicates that filtering will be done via a backend API. A fetchData function must be provided
- defaultColumnValues
shape
{Filter:}func
|node
,Default{}defaults that will be set on each column. Will be overridden by individual column values
- additionalColumns
shape
{id:string
Required,Header:string
|node
,Cell:}func
|node
,[]
Default[]Actions or other additional non-data columns can be added here
- fetchData
func
DefaultnullFunction that will fetch table data. Called when page size, page index or filters change. Meant to be used with manual filters and pagination
- initialState
shape
{pageSize:requiredWhen(PropTypes.number, 'isPaginated')
,pageIndex:requiredWhen(PropTypes.number, 'isPaginated')
,filters:requiredWhen(PropTypes.arrayOf(PropTypes.shape()), 'manualFilters')
,sortBy:requiredWhen(PropTypes.arrayOf(PropTypes.shape()), 'manualSortBy')
,selectedRowIds:}shape
{0:any
,1:any
,2:any
,3:any
,4:any
,5:any
,6:any
,7:any
,8:any
,9:any
,10:any
,11:any
,12:any
,13:any
,14:any
,15:any
,16:},any
,Default{}Initial state passed to react-table's documentation https://react-table.tanstack.com/docs/api/useTable
- initialTableOptions
shape
{}Default{}Table options passed to react-table's useTable hook. Will override some options passed in to DataTable, such as: data, columns, defaultColumn, manualFilters, manualPagination, manualSortBy, and initialState
- itemCount
number
RequiredActions to be performed on the table. Called with the table instance. Not displayed if rows are selected.
- bulkActions
shape
{buttonText:string
Required,handleClick:func
Required,className:string
,variant:string
,disabled:} |bool
,func
|element
[]
|func
|element
Default[]Actions to be performed on selected rows of the table. Called with the selected rows. Only displayed if rows are selected.
- tableActions
shape
{buttonText:string
Required,handleClick:func
Required,className:string
,variant:string
,disabled:} |bool
,func
|element
[]
|func
|element
Default[]Function for rendering custom components, called with the table instance
- numBreakoutFilters
enum
1 | 2 | 3 | 4Default1Number between one and four filters that can be shown on the top row.
- EmptyTableComponent
func
DefaultEmptyTableContentComponent to be displayed when the table is empty
- RowStatusComponent
func
DefaultRowStatusComponent to be displayed for row status, ie, 10 of 20 rows. Displayed by default in the TableControlBar
- SelectionStatusComponent
func
DefaultSelectionStatusComponent to be displayed for selection status. Displayed when there are selected rows and no active filters
- FilterStatusComponent
func
DefaultFilterStatusComponent to be displayed for filter status. Displayed when there are active filters.
- children
node
[]
|node
DefaultnullIf children are not provided a table with control bar and footer will be rendered
- showFiltersInSidebar
bool
DefaultfalseIf true filters will be shown on sidebar instead
- dataViewToggleOptions
shape
{isDataViewToggleEnabled:bool
,onDataViewToggle:func
,defaultActiveStateValue:string
,togglePlacement:}string
,Default{ isDataViewToggleEnabled: false, onDataViewToggle: () => {}, defaultActiveStateValue: 'card', togglePlacement: 'left', }options for data view toggle
- disableElevation
bool
DefaultfalseRemove the default box shadow on the component
- renderRowSubComponent
func
A function that will render contents of expanded row, accepts
row
as a prop. - isExpandable
bool
DefaultfalseIndicates whether table supports expandable rows.
- isLoading
bool
DefaultfalseIndicates whether the table should show loading states.
- onSelectedRowsChanged
func
Callback function called when row selections change.
- className
string
Defaultnullclass names for the div wrapping the button components
- className
string
class names for the div wrapping the button components
- className
string
The class name for the CardGrid component
- columnSizes
shape
{xs:number
,sm:number
,md:number
,lg:number
,xl:}number
,Default{ xs: 12, lg: 6, xl: 4, }An object containing the desired column size at each breakpoint, following a similar props API as
react-bootstrap/Col
- CardComponent
func
RequiredYour card component must be individualized to your table. It will be called with props from the "row" of data it will display
- selectionPlacement
enum
'left' | 'right'Default'right'If the Cards are selectable this prop determines from which side of the Card to show selection component.
- SkeletonCardComponent
func
Overrides default skeleton card component for loading state in CardView
- skeletonCardCount
number
Default8Customize the number of loading skeleton cards to display in CardView
- getCellProps
func
RequiredProps for the td element
- render
func
RequiredFunction that renders the cell contents. Will be called with the string 'Cell'
- column
shape
{cellClassName:} Requiredstring
,Table column
- getHeaderProps
func
RequiredReturns props for the th element
- isSorted
bool
DefaultfalseIndicates whether or not a column is sorted
- render
func
RequiredRenders the header content. Passed the string 'Header'
- isSortedDesc
bool
DefaultfalseIndicates whether the column is sorted in descending order
- getSortByToggleProps
func
Default() => {}Gets props related to sorting that will be passed to th
- canSort
bool
DefaultfalseIndicates whether a column is sortable
- headerClassName
string
DefaultnullClass(es) to be applied to header cells
- headerGroups
shape
{headers:shape
{getHeaderProps:}func
Required,[]
Required,getHeaderGroupProps:}func
Required,[]
Required
- row
shape
{getRowProps:func
Required,cells:shape
{}[]
Required,id:string
Required,isSelected:bool
,isExpanded:} Requiredbool
,Row data that is received from
react-table
API.
- classNameDefaultnull
string
- buttonClassNameDefault'pgn__smart-status-button'
string
- variantDefault'link'
string
- sizeDefault'inline'
string
- clearFiltersText
element
|string
- showFilteredFieldsDefaulttrue
bool
- className
string
A class name to append to the base element
- clearSelectionText
string
|element
A text that appears on the
Clear selection
button, defaults to 'Clear Selection'
- column
shape
{setFilter:func
Required,Header:func
|node
Required,getHeaderProps:func
Required,filterValue:} Requiredstring
,Specifies a column object.
setFilter
: Function to set the filter value.Header
: Column header used for labels and placeholders.getHeaderProps
: Generates a key unique to the column being filtered.filterValue
: Value for the filter input.
Usage Insights#
CardView
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
frontend-app-admin-portal | 20.26.3 | 1 | |
frontend-app-enterprise-public-catalog | 20.29.0 | 2 | |
frontend-app-learning | 20.28.4 | 1 |
DataTable
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
credentials | 19.19.1 | 1 | |
edx-ora2 | 20.9.2 | 1 | |
frontend-app-admin-portal | 20.26.3 | 15 | |
frontend-app-communications | 20.30.1 | 2 | |
frontend-app-course-authoring | 20.32.0 | 1 | |
frontend-app-enterprise-public-catalog | 20.29.0 | 2 | |
frontend-app-gradebook | 19.25.4 | 3 | |
frontend-app-learner-record | 20.32.0 | 1 | |
frontend-app-learning | 20.28.4 | 3 | |
frontend-app-ora-grading | 20.30.0 | 2 | |
frontend-app-publisher | 20.28.5 | 1 | |
frontend-app-support-tools | 20.26.0 | 1 |
DataTableEmptyTable
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
frontend-app-admin-portal | 20.26.3 | 8 | |
frontend-app-gradebook | 19.25.4 | 1 | |
frontend-app-learner-record | 20.32.0 | 1 |
DataTableRowStatus
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
frontend-app-admin-portal | 20.26.3 | 2 |
DataTableTable
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
edx-ora2 | 20.9.2 | 1 | |
frontend-app-admin-portal | 20.26.3 | 6 | |
frontend-app-course-authoring | 20.32.0 | 1 | |
frontend-app-enterprise-public-catalog | 20.29.0 | 1 | |
frontend-app-gradebook | 19.25.4 | 1 | |
frontend-app-learner-record | 20.32.0 | 1 | |
frontend-app-learning | 20.28.4 | 2 | |
frontend-app-ora-grading | 20.30.0 | 2 |
DataTableTableControlBar
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
edx-ora2 | 20.9.2 | 1 | |
frontend-app-admin-portal | 20.26.3 | 6 | |
frontend-app-enterprise-public-catalog | 20.29.0 | 1 | |
frontend-app-gradebook | 19.25.4 | 1 | |
frontend-app-ora-grading | 20.30.0 | 1 |
DataTableTableFooter
Project Name | Paragon Version | Instance Count | |
---|---|---|---|
edx-ora2 | 20.9.2 | 1 | |
frontend-app-admin-portal | 20.26.3 | 5 | |
frontend-app-enterprise-public-catalog | 20.29.0 | 1 | |
frontend-app-learning | 20.28.4 | 1 | |
frontend-app-ora-grading | 20.30.0 | 1 |