diff --git a/.changeset/salty-bikes-learn.md b/.changeset/salty-bikes-learn.md new file mode 100644 index 00000000000..0a6bf99c7af --- /dev/null +++ b/.changeset/salty-bikes-learn.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Add support for the `onToggleSort` prop to `DataTable` diff --git a/packages/react/src/DataTable/DataTable.docs.json b/packages/react/src/DataTable/DataTable.docs.json index 88d0373ae34..738d9276a8c 100644 --- a/packages/react/src/DataTable/DataTable.docs.json +++ b/packages/react/src/DataTable/DataTable.docs.json @@ -98,6 +98,13 @@ "required": false, "description": "Provide the sort direction that the table should be sorted by on the\ncurrently sorted column", "defaultValue": "" + }, + { + "name": "onToggleSort", + "type": "(columnId: ObjectPaths | string | number, direction: 'ASC' | 'DESC') => void", + "required": false, + "description": "Fires every time the user clicks a sortable column header. It reports the column id that is now sorted and the direction after the toggle (never 'NONE').", + "defaultValue": "" } ], "subcomponents": [ diff --git a/packages/react/src/DataTable/DataTable.features.stories.tsx b/packages/react/src/DataTable/DataTable.features.stories.tsx index ec2a50ba94d..d1e88a6a42f 100644 --- a/packages/react/src/DataTable/DataTable.features.stories.tsx +++ b/packages/react/src/DataTable/DataTable.features.stories.tsx @@ -1446,6 +1446,69 @@ export const WithRightAlignedColumns = () => { ) } +export const WithSortEvents = () => ( + + + Repositories + + + Click any sortable header and watch the Actions panel. + + + action('onToggleSort')({columnId, direction})} + columns={[ + { + header: 'Repository', + field: 'name', + rowHeader: true, + sortBy: 'alphanumeric', + }, + { + header: 'Type', + field: 'type', + renderCell: row => , + }, + { + header: 'Updated', + field: 'updatedAt', + sortBy: 'datetime', + renderCell: row => , + }, + { + header: 'Dependabot', + field: 'securityFeatures.dependabot', + renderCell: row => + row.securityFeatures.dependabot.length ? ( + + {row.securityFeatures.dependabot.map(feature => ( + + ))} + + ) : null, + }, + { + header: 'Code scanning', + field: 'securityFeatures.codeScanning', + renderCell: row => + row.securityFeatures.codeScanning.length ? ( + + {row.securityFeatures.codeScanning.map(feature => ( + + ))} + + ) : null, + }, + ]} + initialSortColumn="updatedAt" + initialSortDirection="DESC" + /> + +) + export const WithPagination = () => { const pageSize = 10 const [pageIndex, setPageIndex] = React.useState(0) diff --git a/packages/react/src/DataTable/DataTable.tsx b/packages/react/src/DataTable/DataTable.tsx index 275c693e872..3fe478b7fb5 100644 --- a/packages/react/src/DataTable/DataTable.tsx +++ b/packages/react/src/DataTable/DataTable.tsx @@ -59,6 +59,13 @@ export type DataTableProps = { * @returns The unique identifier for the row, which can be a string or number. */ getRowId?: (rowData: Data) => string | number + + /** + * Fires every time the user clicks a sortable column header. It reports + * the column id that is now sorted and the direction after the toggle + * (never `"NONE"`). + */ + onToggleSort?: (columnId: ObjectPaths | string | number, direction: Exclude) => void } function defaultGetRowId(row: D) { @@ -74,6 +81,7 @@ function DataTable({ initialSortColumn, initialSortDirection, getRowId = defaultGetRowId, + onToggleSort, }: DataTableProps) { const {headers, rows, actions, gridTemplateColumns} = useTable({ data, @@ -100,7 +108,10 @@ function DataTable({ align={header.column.align} direction={header.getSortDirection()} onToggleSort={() => { + const nextDirection: Exclude = + header.getSortDirection() === 'ASC' ? 'DESC' : 'ASC' actions.sortBy(header) + onToggleSort?.(header.id, nextDirection) }} > {typeof header.column.header === 'string' ? header.column.header : header.column.header()} diff --git a/packages/react/src/DataTable/__tests__/DataTable.test.tsx b/packages/react/src/DataTable/__tests__/DataTable.test.tsx index 543d3ba9155..f045f00f311 100644 --- a/packages/react/src/DataTable/__tests__/DataTable.test.tsx +++ b/packages/react/src/DataTable/__tests__/DataTable.test.tsx @@ -886,6 +886,41 @@ describe('DataTable', () => { expect(customSortFn).toHaveBeenCalled() expect(getRowOrder()).toEqual(['3', '2', '1']) }) + + it('invokes onToggleSort with column id and next direction', async () => { + const user = userEvent.setup() + const handler = vi.fn() + + render( + , + ) + + // No calls on initial render + expect(handler).not.toHaveBeenCalled() + + // Same column, flips ASC to DESC + await user.click(screen.getByText('First')) + expect(handler).toHaveBeenLastCalledWith('first', 'DESC') + + // Different column, resets to ASC on that column + await user.click(screen.getByText('Second')) + expect(handler).toHaveBeenLastCalledWith('second', 'ASC') + + expect(handler).toHaveBeenCalledTimes(2) + }) }) describe('column widths', () => {