diff --git a/frontend/src/utils/withTableState.test.tsx b/frontend/src/utils/withTableState.test.tsx new file mode 100644 index 0000000000..caeea0cf18 --- /dev/null +++ b/frontend/src/utils/withTableState.test.tsx @@ -0,0 +1,307 @@ +import { vi } from 'vitest'; +import { renderHook } from '@testing-library/react-hooks'; +import { useReactTable } from '@tanstack/react-table'; +import { withTableState } from './withTableState'; +import { useState } from 'react'; +import { render } from '@testing-library/react'; + +describe('withTableState', () => { + it('should create paginated and sorted table state', () => { + const mockTableState = { + limit: 10, + offset: 10, + sortBy: 'name', + sortOrder: 'asc', + }; + const mockSetTableState = vi.fn(); + const mockOptions = { data: [], columns: [] }; + + const result = withTableState( + mockTableState, + mockSetTableState, + mockOptions, + ); + + expect(result.state).toEqual({ + pagination: { + pageIndex: 1, + pageSize: 10, + }, + sorting: [ + { + id: 'name', + desc: false, + }, + ], + }); + }); + + it('sets default options', () => { + expect( + withTableState( + { + limit: 10, + offset: 10, + sortBy: 'name', + sortOrder: 'asc', + }, + vi.fn(), + { data: [], columns: [] }, + ), + ).toMatchObject({ + getCoreRowModel: expect.any(Function), + enableSorting: true, + enableMultiSort: false, + manualPagination: true, + manualSorting: true, + enableSortingRemoval: false, + enableHiding: true, + onPaginationChange: expect.any(Function), + onSortingChange: expect.any(Function), + onColumnVisibilityChange: expect.any(Function), + }); + }); + + it('should update page index and size', () => { + const mockTableState = { + limit: 10, + offset: 10, + sortBy: 'name', + sortOrder: 'asc', + }; + const mockSetTableState = vi.fn(); + const mockOptions = { data: [], columns: [] }; + + const { result } = renderHook(() => + useReactTable( + withTableState(mockTableState, mockSetTableState, mockOptions), + ), + ); + + result.current.setPagination({ + pageIndex: 3, + pageSize: 5, + }); + + expect(mockSetTableState).toHaveBeenCalledWith({ + limit: 5, + offset: 15, + }); + }); + + it('should update sorting', () => { + const mockTableState = { + limit: 10, + offset: 10, + sortBy: 'name', + sortOrder: 'asc', + }; + const mockSetTableState = vi.fn(); + const mockOptions = { data: [], columns: [] }; + + const { result } = renderHook(() => + useReactTable( + withTableState(mockTableState, mockSetTableState, mockOptions), + ), + ); + + result.current.setSorting([ + { + id: 'createdAt', + desc: true, + }, + ]); + + expect(mockSetTableState).toHaveBeenCalledWith({ + sortBy: 'createdAt', + sortOrder: 'desc', + }); + }); + + it('should handle column visibility', () => { + const mockTableState = { + limit: 10, + offset: 10, + sortBy: 'name', + sortOrder: 'asc', + columns: ['name'], + }; + const mockSetTableState = vi.fn(); + const mockOptions = { + data: [], + columns: [ + { + id: 'name', + show: true, + }, + { + id: 'createdAt', + show: false, + }, + ], + }; + + const { result } = renderHook(() => + useReactTable( + withTableState(mockTableState, mockSetTableState, mockOptions), + ), + ); + + expect(result.current.getState().columnVisibility).toMatchObject({ + name: true, + }); + + result.current.setColumnVisibility({ name: false, createdAt: true }); + + expect(mockSetTableState).toHaveBeenCalledWith({ + columns: ['createdAt'], + }); + }); + + it('is always using external state', () => { + const initialProps = { + limit: 5, + offset: 40, + sortBy: 'name', + sortOrder: 'desc', + }; + + const { result, rerender } = renderHook( + (state) => + useReactTable( + withTableState(state as any, vi.fn(), { + data: [], + columns: [], + }), + ), + { initialProps }, + ); + + expect(result.current.getState()).toMatchObject({ + pagination: { + pageIndex: 8, + pageSize: 5, + }, + sorting: [ + { + id: 'name', + desc: true, + }, + ], + }); + + rerender({ + limit: 10, + offset: 10, + sortBy: 'createdAt', + sortOrder: 'asc', + }); + + expect(result.current.getState()).toMatchObject({ + pagination: { + pageIndex: 1, + pageSize: 10, + }, + sorting: [ + { + id: 'createdAt', + desc: false, + }, + ], + }); + }); + + it('works end-to-end with useReactTable', () => { + const Component = () => { + const [state, setState] = useState({ + limit: 5, + offset: 40, + sortBy: 'name', + sortOrder: 'desc', + }); + + const setTableState = (newState: any) => { + setState((state) => ({ ...state, ...newState })); + }; + + const table = useReactTable( + withTableState(state, setTableState, { + data: [], + columns: [], + }), + ); + + return ( + <> + + + + +