From 79bf0b8b5ede033f1a42ea54285184c423e027e9 Mon Sep 17 00:00:00 2001 From: emrberk Date: Mon, 3 Jun 2024 16:32:44 +0300 Subject: [PATCH] Add manual sorting props to Table for server-side sorting [MC-2868] --- packages/ui/__stories__/Table.stories.tsx | 44 +++++++++++++++++++++++ packages/ui/src/Table/Table.tsx | 23 ++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/ui/__stories__/Table.stories.tsx b/packages/ui/__stories__/Table.stories.tsx index 25f0bf86c..cb4893ff7 100644 --- a/packages/ui/__stories__/Table.stories.tsx +++ b/packages/ui/__stories__/Table.stories.tsx @@ -9,6 +9,7 @@ import { TextField } from '../src' import storyStyles from './TextField.stories.module.scss' import styles from './utils.scss' +import { SortingRule } from 'react-table' export default { title: 'Components/Table', @@ -132,6 +133,49 @@ export const WithControlledPagination = () => { ) } +export const WithControlledSorting = () => { + const columns = useMemo(() => getColumns({}), []) + + const [loading, setLoading] = useState(false) + // We'll start our table with initial data + const [data, setData] = useState(bigDataSet.slice(0, 10)) + // Since we don't have real server here, we'll fake total page count. + const fetchIdRef = useRef(0) + const firstUpdate = useRef(true) + + // This will get called when the table needs new data. + const onSortingChange = useCallback((sortBy: SortingRule[]) => { + if (firstUpdate.current) { + firstUpdate.current = false + return + } + if (sortBy.length === 0) { + setData(bigDataSet) + return + } + + // Give this fetch an ID + const fetchId = ++fetchIdRef.current + + setLoading(true) + + // Let's simulate server delay + setTimeout(() => { + // Only update the data if this is the latest fetch + if (fetchId === fetchIdRef.current) { + const { id, desc: isDescending } = sortBy[0] + const sortColumn = id as keyof Person + const newData = [...bigDataSet] + newData.sort((a, b) => (isDescending ? b[sortColumn] - a[sortColumn] : a[sortColumn] - b[sortColumn])) + setData(newData) + setLoading(false) + } + }, 750) + }, []) + + return +} + export const WithGlobalSearch = () => { const [searchValue, setSearchValue] = useState('') diff --git a/packages/ui/src/Table/Table.tsx b/packages/ui/src/Table/Table.tsx index 2b1f52cba..885a5fb3e 100644 --- a/packages/ui/src/Table/Table.tsx +++ b/packages/ui/src/Table/Table.tsx @@ -29,6 +29,7 @@ import { useColumnOrder, useExpanded, TableInstance, + SortingRule, } from 'react-table' import { AlertTriangle, ChevronDown, ChevronUp } from 'react-feather' @@ -176,6 +177,11 @@ export type ControlledPaginationProps = { onPaginationChange?: (paginationChangeProps: PaginationChangeProps) => void } +export type ControlledSortingProps = { + manualSortBy?: boolean + onSortingChange?: (sortBy: SortingRule[]) => void +} + type CustomTableRowClickProps = | { // When using `onRowClick` it's a good practice to also make one of the cells in the table interactive by providing a button, link or something else. @@ -219,7 +225,11 @@ type CustomTableProps = { } & CustomTableRowClickProps & DataTestProp -export type TableProps = TableOptions & ExtendedPaginationProps & ControlledPaginationProps & CustomTableProps +export type TableProps = TableOptions & + ExtendedPaginationProps & + ControlledPaginationProps & + ControlledSortingProps & + CustomTableProps export type { ColumnType, RowType, CellType } @@ -249,7 +259,9 @@ export const Table = ({ defaultColumn = column, disableSortBy, onPaginationChange, + onSortingChange, manualPagination, + manualSortBy, onRenderedContentChange, autoResetPage = false, hidePagination = false, @@ -338,6 +350,7 @@ export const Table = ({ initialState, // Tell the usePagination hook that we'll handle our own data fetching manualPagination: manualPagination, + manualSortBy, // This means we'll also have to provide our own pageCount pageCount: controlledPageCount, autoResetPage, @@ -372,7 +385,7 @@ export const Table = ({ previousPage, setPageSize, // Get the state from the instance - state: { pageIndex, pageSize, columnResizing, selectedRowIds }, + state: { pageIndex, pageSize, sortBy, columnResizing, selectedRowIds }, setGlobalFilter, setColumnOrder, } = tableInstance @@ -438,6 +451,12 @@ export const Table = ({ } }, [onPaginationChange, onPaginationChangeDebounced, pageIndex, pageSize]) + useEffect(() => { + if (onSortingChange) { + onSortingChange(sortBy) + } + }, [sortBy, onSortingChange]) + // Debounce our `fetchData` call for 200ms. // We can use non-null assertion here since we're checking existence of `fetchData` in the `useEffect` below // eslint-disable-next-line @typescript-eslint/no-non-null-assertion