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 }