From 0dc2e9bc4361671720dfccb116eb28a68bdafd30 Mon Sep 17 00:00:00 2001 From: imikulec Date: Tue, 23 Jan 2024 22:32:23 +0100 Subject: [PATCH] #182 - Enable Multi-column sorting in DataTable --- libs/data-display/src/DataTable.tsx | 12 +++- storybook/src/data-display/DataTable.mdx | 61 +++++++++++++++++++ .../src/data-display/DataTable.stories.tsx | 50 +++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/libs/data-display/src/DataTable.tsx b/libs/data-display/src/DataTable.tsx index 7cd65d1..54856df 100644 --- a/libs/data-display/src/DataTable.tsx +++ b/libs/data-display/src/DataTable.tsx @@ -126,6 +126,12 @@ export type DataTableProps = { * Content to be displayed when the dataset is empty. */ emptyState?: React.ReactNode; + + /** + * Enable or disable multi-column sorting. + * When set to true, users can sort by multiple columns simultaneously. + */ + enableMultiSort?: boolean; } & DataTableTokensProps; type DataTableTokensProps = { @@ -463,6 +469,7 @@ function DataTable({ firstColumnFixed, lastColumnFixed, className, + enableMultiSort = false, ...props }: DataTableProps) { const primaryRow = findChild("DataTablePrimaryRow", children); @@ -500,7 +507,7 @@ function DataTable({ [getItemId] ); const tokens = useTokens("DataTable", props.tokens); - const { getTableProps, getTableBodyProps, headerGroups, footerGroups, rows, prepareRow, visibleColumns, state } = + const { getTableProps, getTableBodyProps, headerGroups, footerGroups, rows, prepareRow, visibleColumns, state, toggleSortBy } = useTable( { columns, @@ -614,6 +621,9 @@ function DataTable({ {...column.getHeaderProps(column.getSortByToggleProps())} className={tableHeaderClassName} title={column.title} + onClick={() => { + toggleSortBy(column.id, undefined, enableMultiSort); + }} > {column.render("Header")} diff --git a/storybook/src/data-display/DataTable.mdx b/storybook/src/data-display/DataTable.mdx index 634a2e5..83e7853 100644 --- a/storybook/src/data-display/DataTable.mdx +++ b/storybook/src/data-display/DataTable.mdx @@ -111,6 +111,67 @@ Here is an example utilizing the `useMemo` hook. This specific logic is applied Now you have a DataTable component with sorting enabled for the specified column, and you can implement your own sorting logic based on your application requirements. +## Multi-column Sorting in DataTable + +The `DataTable` component in `@tiller-ds` empowers you to implement **multi-column sorting**, allowing users to sort data across multiple columns simultaneously. +This enhanced functionality is achieved through the `toggleSortBy` method provided by the `useTable` hook. + +To enable multi-column sorting, follow these steps: + +**1.** Ensure that the columns for which you want to implement sorting have the `canSort` prop set to `true`: +```tsx + + +``` + +**2.** For multi-column sorting, apply sorting logic based on the priority of columns specified in the sort state: +```tsx + const [dataTableState, dataTableHook] = useDataTable({ + defaultSortBy: [ + { + column: "name", + sortDirection: "ASCENDING", + }, + ], + }); + + const columnMapping = { + name: "name", + surname: "surname", + }; + + const sortedData = React.useMemo(() => { + const sortInstructions = dataTableState.sortBy; + + if (!sortInstructions || sortInstructions.length === 0) { + return [...allData]; + } + + return [...allData].sort((a, b) => + sortInstructions.reduce((result, sortInfo) => { + const columnKey = columnMapping[sortInfo.column]; + + if (result === 0 && a[columnKey] !== undefined && b[columnKey] !== undefined) { + const result = a[columnKey].localeCompare(b[columnKey]); + return sortInfo.sortDirection === "DESCENDING" ? -result : result; + } + + return result; + }, 0) + ); + }, [dataTableState.sortBy]); +``` + +**3.** Set the `enableMultiSort` prop to `true` in the DataTable component to enable multi-column sorting: +```tsx + + + + + + +``` + ## Best Practices Data Tables should: diff --git a/storybook/src/data-display/DataTable.stories.tsx b/storybook/src/data-display/DataTable.stories.tsx index 70ba2b2..b4451ee 100644 --- a/storybook/src/data-display/DataTable.stories.tsx +++ b/storybook/src/data-display/DataTable.stories.tsx @@ -847,6 +847,55 @@ export const WithDefaultAscendingSortByName = () => { ) }; +export const WithDefaultAscendingMultiSort = () => { + const [dataTableState, dataTableHook] = useDataTable({ + defaultSortBy: [ + { + column: "name", + sortDirection: "ASCENDING", + }, + { + column: "surname", + sortDirection: "ASCENDING", + }, + ], + }); + + const columnMapping = { + name: "name", + surname: "surname", + }; + + const sortedData = React.useMemo(() => { + const sortInstructions = dataTableState.sortBy; + + if (!sortInstructions || sortInstructions.length === 0) { + return [...allData]; + } + + return [...allData].sort((a, b) => + sortInstructions.reduce((result, sortInfo) => { + const columnKey = columnMapping[sortInfo.column]; + + if (result === 0 && a[columnKey] !== undefined && b[columnKey] !== undefined) { + const result = a[columnKey].localeCompare(b[columnKey]); + return sortInfo.sortDirection === "DESCENDING" ? -result : result; + } + + return result; + }, 0) + ); + }, [dataTableState.sortBy]); + + return ( + + + + + + ) +}; + export const WithIconButtons = () => ( @@ -995,6 +1044,7 @@ WithHorizontalScroll.argTypes = HideControls; WithHorizontalScrollAndFirstColumnFixed.argTypes = HideControls; WithHorizontalScrollAndLastColumnFixed.argTypes = HideControls; WithDefaultAscendingSortByName.argTypes = HideControls; +WithDefaultAscendingMultiSort.argTypes = HideControls; WithIconButtons.argTypes = HideControls; WithPrimaryAndSecondaryRows.argTypes = HideControls; WithPrimaryAndSecondaryRowsAndComplexValues.argTypes = HideControls;