From 039f2fa5fe92d77c3904ce45d4adf6d6250f089b Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:51:22 +0100 Subject: [PATCH] feat: add onSortChange callback to DataTable for server-side sorting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When onSortChange is provided, DataTable operates in controlled mode: it renders sort indicators and fires the callback on header clicks, but skips client-side sorting — the caller provides pre-sorted data. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../composites/DataTable/DataTable.tsx | 13 ++++++++++--- src/design-system/composites/DataTable/types.ts | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/design-system/composites/DataTable/DataTable.tsx b/src/design-system/composites/DataTable/DataTable.tsx index 3021bb6..37403a9 100644 --- a/src/design-system/composites/DataTable/DataTable.tsx +++ b/src/design-system/composites/DataTable/DataTable.tsx @@ -24,6 +24,7 @@ export function DataTable({ rowAccent, expandedContent, flush = false, + onSortChange, }: DataTableProps) { const [sortKey, setSortKey] = useState(null) const [sortDir, setSortDir] = useState('asc') @@ -31,14 +32,16 @@ export function DataTable({ const [pageSize, setPageSize] = useState(initialPageSize) const [expandedId, setExpandedId] = useState(null) + // When onSortChange is provided (controlled mode), skip client-side sorting const sorted = useMemo(() => { + if (onSortChange) return data if (!sortKey) return data return [...data].sort((a, b) => { const av = (a as Record)[sortKey] const bv = (b as Record)[sortKey] return compareValues(av, bv, sortDir) }) - }, [data, sortKey, sortDir]) + }, [data, sortKey, sortDir, onSortChange]) const totalRows = sorted.length const totalPages = Math.max(1, Math.ceil(totalRows / pageSize)) @@ -52,13 +55,17 @@ export function DataTable({ function handleHeaderClick(col: Column) { if (!sortable && !col.sortable) return + let newDir: SortDir if (sortKey === col.key) { - setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')) + newDir = sortDir === 'asc' ? 'desc' : 'asc' + setSortDir(newDir) } else { + newDir = 'asc' setSortKey(col.key) - setSortDir('asc') + setSortDir(newDir) } setPage(1) + onSortChange?.(col.key, newDir) } function handleRowClick(row: T) { diff --git a/src/design-system/composites/DataTable/types.ts b/src/design-system/composites/DataTable/types.ts index a358d89..efdaa42 100644 --- a/src/design-system/composites/DataTable/types.ts +++ b/src/design-system/composites/DataTable/types.ts @@ -20,4 +20,8 @@ export interface DataTableProps { expandedContent?: (row: T) => ReactNode | null /** Strip border, radius, and shadow so the table sits flush inside a parent container. */ flush?: boolean + /** Controlled sort: called when the user clicks a sortable column header. + * When provided, the component skips client-side sorting — the caller is + * responsible for providing `data` in the desired order. */ + onSortChange?: (key: string, dir: 'asc' | 'desc') => void }