diff --git a/packages/comet-uswds/src/components/step-indicator/step-indicator.tsx b/packages/comet-uswds/src/components/step-indicator/step-indicator.tsx index 0d9373b9..29026492 100644 --- a/packages/comet-uswds/src/components/step-indicator/step-indicator.tsx +++ b/packages/comet-uswds/src/components/step-indicator/step-indicator.tsx @@ -76,7 +76,7 @@ export const StepIndicator = ({ } return ( -
  • +
  • {step} {segmentSrLabel !== '' && {segmentSrLabel}} diff --git a/packages/comet-uswds/src/components/table/table.stories.tsx b/packages/comet-uswds/src/components/table/table.stories.tsx index e2512558..89bd3fef 100644 --- a/packages/comet-uswds/src/components/table/table.stories.tsx +++ b/packages/comet-uswds/src/components/table/table.stories.tsx @@ -24,6 +24,7 @@ const Template: StoryFn = (args: TableProps) => { { id: 'estimatedPopulation', name: 'Estimated population at time of admission', + sortable: false, }, ]; @@ -84,6 +85,7 @@ const Template: StoryFn = (args: TableProps) => { sortable={args.sortable} sortDir={args.sortDir} sortIndex={args.sortIndex} + onSort={args.onSort} /> ); }; @@ -100,3 +102,19 @@ Default.args = { sortDir: 'ascending', sortIndex: 0, }; + +export const OnSort = Template.bind({}); +OnSort.args = { + id: 'table-1', + tabIndex: 1, + caption: 'Voter Data', + borderless: false, + striped: false, + scrollable: false, + sortable: true, + sortDir: 'ascending', + sortIndex: 0, + onSort: () => { + console.log('Sorting...'); + }, +}; diff --git a/packages/comet-uswds/src/components/table/table.test.tsx b/packages/comet-uswds/src/components/table/table.test.tsx index eba79024..b72dbb4f 100644 --- a/packages/comet-uswds/src/components/table/table.test.tsx +++ b/packages/comet-uswds/src/components/table/table.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render } from '@testing-library/react'; import { axe } from 'jest-axe'; import Table, { TableColumn } from './table'; @@ -142,6 +142,19 @@ describe('Table', () => { expect(th[0].getAttribute('aria-sort')).toBeNull(); }); + test('should render a custom sortable table successfully', () => { + const customColumns = columns.map((column, index) => ({ + ...column, + sortable: index === 0 ? false : true, + })); + + const { baseElement } = render( + , + ); + const th = baseElement.querySelectorAll('th'); + expect(th[0].getAttribute('aria-sort')).toBeNull(); + }); + test('should render a descending sortable table successfully', () => { const { baseElement } = render(
    { expect(th[0].getAttribute('aria-sort')).not.toBeNull(); }); + test('should call onSort function when sorting', async () => { + const onSort = vi.fn(); + const { baseElement } = render( +
    , + ); + expect(onSort).not.toHaveBeenCalled(); + + const th = baseElement.querySelectorAll('th')[0]; + const thButton = th?.querySelector('button') as Element; + await act(async () => { + fireEvent.click(thButton); + }); + expect(onSort).toHaveBeenCalled(); + }); + describe('sorted table', () => { test('should match data before sorting', () => { const { baseElement } = render( diff --git a/packages/comet-uswds/src/components/table/table.tsx b/packages/comet-uswds/src/components/table/table.tsx index 6785b499..b0f90d89 100644 --- a/packages/comet-uswds/src/components/table/table.tsx +++ b/packages/comet-uswds/src/components/table/table.tsx @@ -31,6 +31,10 @@ export interface TableProps { * The default sort direction if sortIndex is provided */ sortDir?: 'ascending' | 'descending'; + /** + * A function to call when the table is sorted + */ + onSort?: () => void; /** * A boolean indicating if the table is scrollable or not */ @@ -56,6 +60,7 @@ export interface TableProps { export interface TableColumn { id: string; name: string; + sortable?: boolean; } export interface TableCell { @@ -74,6 +79,7 @@ export const Table = ({ sortable = false, sortIndex = 0, sortDir = 'ascending', + onSort, scrollable = false, borderless = false, striped = false, @@ -99,6 +105,26 @@ export const Table = ({ }; }); + // If onSort, add a MutationObserver to listen for changes in aria-sort + useEffect(() => { + const callback = (mutationsList: MutationRecord[]) => { + for (const mutation of mutationsList) { + if (onSort && mutation.attributeName === 'aria-sort') { + const currentSortValue = (mutation.target as HTMLElement).getAttribute('aria-sort'); + if (currentSortValue) { + onSort(); + } + } + } + }; + + const observer = new MutationObserver(callback); + if (tableRef.current && onSort) { + const tableHeaders = tableRef.current.querySelectorAll('th'); + tableHeaders.forEach((th) => observer.observe(th, { attributes: true })); + } + }, [columns]); + return ( - {columns.map((column: TableColumn, index: number) => ( - - ))} + {columns + .map((obj) => ({ + ...obj, + sortable: obj.sortable !== undefined ? obj.sortable : true, + })) + .map((column: TableColumn, index: number) => ( + + ))}
    - {column.name} - + {column.name} +