diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/table/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/table/Examples.tsx index 9070a0e6a22..7b069bde357 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/table/Examples.tsx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/table/Examples.tsx @@ -36,6 +36,21 @@ export const TableVariantBasic = () => ( column2: { direction: 'desc', modes: ['asc', 'desc'] }, }) + // Handle your "column1" logic + React.useEffect(() => { + switch (sortState.column1.direction) { + case 'asc': + break + + case 'desc': + break + + default: + case 'off': + break + } + }, [sortState.column1.direction]) + return ( diff --git a/packages/dnb-eufemia/src/components/table/__tests__/useHandleSortState.test.tsx b/packages/dnb-eufemia/src/components/table/__tests__/useHandleSortState.test.tsx index b1a64f176e3..1fc651ba85d 100644 --- a/packages/dnb-eufemia/src/components/table/__tests__/useHandleSortState.test.tsx +++ b/packages/dnb-eufemia/src/components/table/__tests__/useHandleSortState.test.tsx @@ -28,9 +28,9 @@ describe('useHandleSortState', () => { }, sortState: { one: { - active: undefined, - reversed: false, - direction: 'asc', + active: false, + reversed: undefined, + direction: 'off', }, }, }) @@ -102,7 +102,7 @@ describe('useHandleSortState', () => { }) }) - it('should return active with reverted direction', () => { + it('should return active with default direction', () => { const { result } = renderHook(useHandleSortState, { initialProps: { one: { active: true }, @@ -117,8 +117,8 @@ describe('useHandleSortState', () => { sortState: { one: { active: true, - reversed: false, - direction: 'asc', + reversed: undefined, + direction: 'off', }, }, }) @@ -237,6 +237,20 @@ describe('useHandleSortState', () => { simulate() + expect(result.current).toEqual({ + activeSortName: 'one', + sortHandler, + sortState: { + one: { + active: true, + reversed: false, + direction: 'asc', + }, + }, + }) + + simulate() + expect(result.current).toEqual({ activeSortName: null, sortHandler, @@ -282,6 +296,25 @@ describe('useHandleSortState', () => { simulateOne() + expect(result.current).toEqual({ + activeSortName: 'one', + sortHandler, + sortState: { + one: { + active: true, + reversed: false, + direction: 'asc', + }, + two: { + active: false, + reversed: true, + direction: 'desc', + }, + }, + }) + + simulateOne() + expect(result.current).toEqual({ activeSortName: 'one', sortHandler, @@ -326,8 +359,8 @@ describe('useHandleSortState', () => { sortState: { one: { active: true, - reversed: false, direction: 'asc', + reversed: false, }, two: { active: false, @@ -345,8 +378,8 @@ describe('useHandleSortState', () => { sortState: { one: { active: true, - reversed: true, direction: 'desc', + reversed: true, }, two: { active: false, diff --git a/packages/dnb-eufemia/src/components/table/stories/Table.stories.tsx b/packages/dnb-eufemia/src/components/table/stories/Table.stories.tsx index 31a397e02f8..814a2c69ec8 100644 --- a/packages/dnb-eufemia/src/components/table/stories/Table.stories.tsx +++ b/packages/dnb-eufemia/src/components/table/stories/Table.stories.tsx @@ -274,7 +274,7 @@ export const ContainerTable = () => { -

Footer

+ {/*

Footer

*/}
) @@ -614,3 +614,80 @@ export const TableAccordion = () => { ) } + +export function TableSort() { + const { sortState, sortHandler } = useHandleSortState({ + column1: { + active: true, + direction: 'off', + }, + }) + + interface Column1 { + name: string + minAmount: number + } + + const product1: Column1 = { name: 'cab', minAmount: 1 } + const product2: Column1 = { name: 'abc', minAmount: 3 } + const product3: Column1 = { name: 'bac', minAmount: 2 } + + const mockData = [product1, product2, product3] + + const [sortedColumn1, setColumn1Data] = + React.useState(mockData) + + React.useEffect(() => { + switch (sortState.column1.direction) { + case 'asc': + setColumn1Data([...mockData].sort(compareAsc)) + break + + case 'desc': + setColumn1Data([...mockData].sort(compareDesc)) + break + + default: + case 'off': + setColumn1Data(mockData) + break + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sortState.column1.direction]) + + return ( +
+ + + + + + + + {sortedColumn1.map((product) => ( + + + + + ))} + +
+ + + +
{product.name}{product.minAmount}
+ ) + + function compareDesc(a: Column1, b: Column1) { + return b.name.localeCompare(a.name) + } + + function compareAsc(a: Column1, b: Column1) { + return a.name.localeCompare(b.name) + } +} diff --git a/packages/dnb-eufemia/src/components/table/useHandleSortState.tsx b/packages/dnb-eufemia/src/components/table/useHandleSortState.tsx index 531d0a056d5..0f869aa3e2a 100644 --- a/packages/dnb-eufemia/src/components/table/useHandleSortState.tsx +++ b/packages/dnb-eufemia/src/components/table/useHandleSortState.tsx @@ -8,8 +8,8 @@ export type useHandleSortStateOptions = { active?: boolean /** - * Define the sorting direction. Can be "asc" or "desc". - * Defaults to "asc". + * Define the sorting direction. Can be "asc", "desc" or "off". + * Defaults to "off". */ direction?: useHandleSortStateDirection @@ -19,7 +19,7 @@ export type useHandleSortStateOptions = { */ modes?: Array } -export type useHandleSortStateDirection = 'asc' | 'desc' +export type useHandleSortStateDirection = 'asc' | 'desc' | 'off' export type useHandleSortStateMode = 'asc' | 'desc' | 'off' export type useHandleSortStateName = string export type useHandleSortStateConfig = Record< @@ -50,7 +50,7 @@ type SortStateInternalEntry = Record< SortStateInternalStateOptions > type GetNextMode = { - state: SortStateInternalState + direction: useHandleSortStateDirection opts: SortStateInternalStateOptions defaults: useHandleSortStateOptions } @@ -58,16 +58,14 @@ type GetNextMode = { export function useHandleSortState( config: useHandleSortStateConfig, defaults: useHandleSortStateOptions = { - direction: 'asc', + direction: 'off', modes: ['asc', 'desc', 'off'], } ) { const initialState = React.useMemo(() => { return Object.entries(config).reduce((acc, [name, opts]) => { acc[name] = { ...defaults, ...opts } - if (!acc[name].active && !acc[name].lastDirection) { - acc[name].lastDirection = acc[name].direction - } + return acc }, {}) }, [config, defaults]) @@ -86,7 +84,11 @@ export function useHandleSortState( state.active = true state.lastDirection = null } else { - state.direction = getNextMode({ state, opts, defaults }) + state.direction = getNextMode({ + direction: state.direction, + opts, + defaults, + }) state.active = state.direction !== 'off' } @@ -113,11 +115,14 @@ export function useHandleSortState( const reversed = direction === 'off' ? undefined : direction === 'desc' - acc[name] = { active, direction, reversed } - if (active) { activeSortName = name + } else { + active = false } + + acc[name] = { active, direction, reversed } + return acc }, {} @@ -125,25 +130,31 @@ export function useHandleSortState( return { sortState, sortHandler, activeSortName } - function getNextMode({ state, opts, defaults }: GetNextMode) { + function getNextMode({ direction, opts, defaults }: GetNextMode) { const modes = defaults.modes.filter((mode) => { return opts.modes.includes(mode) }) + if (!modes.includes(direction)) { + direction = modes[0] + } + + let next = direction + for (let i = 0, l = modes.length; i < l; i++) { const mode = modes[i] - if (mode === state.direction) { + if (direction === mode) { let c = i + 1 if (c >= l) { c = 0 } - state.direction = modes[c] + next = modes[c] break } } - return state.direction + return next } }