diff --git a/src/app/data-grid-render/components/data-grid-render-demo.tsx b/src/app/data-grid-render/components/data-grid-render-demo.tsx index a7670df38..72a02e729 100644 --- a/src/app/data-grid-render/components/data-grid-render-demo.tsx +++ b/src/app/data-grid-render/components/data-grid-render-demo.tsx @@ -107,7 +107,7 @@ export function DataGridRenderDemo() { onCheckedChange={(value) => { const onRowSelect = table.options.meta?.onRowSelect; if (onRowSelect) { - onRowSelect(row.index, !!value, false); + onRowSelect(row.id, !!value, false); } else { row.toggleSelected(!!value); } @@ -117,7 +117,7 @@ export function DataGridRenderDemo() { event.preventDefault(); const onRowSelect = table.options.meta?.onRowSelect; if (onRowSelect) { - onRowSelect(row.index, !row.getIsSelected(), true); + onRowSelect(row.id, !row.getIsSelected(), true); } } }} diff --git a/src/components/data-grid/data-grid-select-column.tsx b/src/components/data-grid/data-grid-select-column.tsx index ab3ef8503..d019659c2 100644 --- a/src/components/data-grid/data-grid-select-column.tsx +++ b/src/components/data-grid/data-grid-select-column.tsx @@ -167,7 +167,7 @@ function DataGridSelectCell({ const onCheckedChange = React.useCallback( (value: boolean) => { if (meta?.onRowSelect) { - meta.onRowSelect(row.index, value, false); + meta.onRowSelect(row.id, value, false); } else { row.toggleSelected(value); } @@ -179,7 +179,7 @@ function DataGridSelectCell({ (event: React.MouseEvent) => { if (event.shiftKey) { event.preventDefault(); - meta?.onRowSelect?.(row.index, !row.getIsSelected(), true); + meta?.onRowSelect?.(row.id, !row.getIsSelected(), true); } }, [meta, row], diff --git a/src/hooks/test/use-data-grid.test.tsx b/src/hooks/test/use-data-grid.test.tsx index be5dd3511..6f4515d1e 100644 --- a/src/hooks/test/use-data-grid.test.tsx +++ b/src/hooks/test/use-data-grid.test.tsx @@ -1447,12 +1447,14 @@ describe("useDataGrid", () => { useDataGrid({ data: testData, columns: testColumns, + getRowId: (row) => row.id, }), { wrapper: createWrapper() }, ); + const firstRowId = result.current.table.getRowModel().rows[0]?.id; act(() => { - result.current.tableMeta.onRowSelect?.(0, true, false); + result.current.tableMeta.onRowSelect?.(firstRowId ?? "1", true, false); }); const rowSelection = result.current.table.getState().rowSelection; @@ -1465,18 +1467,23 @@ describe("useDataGrid", () => { useDataGrid({ data: testData, columns: testColumns, + getRowId: (row) => row.id, }), { wrapper: createWrapper() }, ); + const rows = result.current.table.getRowModel().rows; + const firstRowId = rows[0]?.id; + const thirdRowId = rows[2]?.id; + // Select first row act(() => { - result.current.tableMeta.onRowSelect?.(0, true, false); + result.current.tableMeta.onRowSelect?.(firstRowId ?? "1", true, false); }); // Select third row with shift act(() => { - result.current.tableMeta.onRowSelect?.(2, true, true); + result.current.tableMeta.onRowSelect?.(thirdRowId ?? "3", true, true); }); const rowSelection = result.current.table.getState().rowSelection; @@ -1489,23 +1496,59 @@ describe("useDataGrid", () => { useDataGrid({ data: testData, columns: testColumns, + getRowId: (row) => row.id, }), { wrapper: createWrapper() }, ); + const firstRowId = result.current.table.getRowModel().rows[0]?.id; + // Select row act(() => { - result.current.tableMeta.onRowSelect?.(0, true, false); + result.current.tableMeta.onRowSelect?.(firstRowId ?? "1", true, false); }); // Deselect row act(() => { - result.current.tableMeta.onRowSelect?.(0, false, false); + result.current.tableMeta.onRowSelect?.(firstRowId ?? "1", false, false); + }); + + const rowSelection = result.current.table.getState().rowSelection; + expect(rowSelection[firstRowId ?? "1"]).toBeFalsy(); + }); + + it("should select correct row when filtering is applied", () => { + const { result } = renderHook( + () => + useDataGrid({ + data: testData, + columns: testColumns, + getRowId: (row) => row.id, + initialState: { + columnFilters: [{ id: "name", value: "Tony" }], + }, + }), + { wrapper: createWrapper() }, + ); + + // With filter "name contains Tony", only Tony Hawk (id: "1") should be visible + const rows = result.current.table.getRowModel().rows; + expect(rows.length).toBe(1); + const visibleRowId = rows[0]?.id; + expect(visibleRowId).toBe("1"); + + // Select the visible (filtered) row + act(() => { + result.current.tableMeta.onRowSelect?.( + visibleRowId ?? "1", + true, + false, + ); }); const rowSelection = result.current.table.getState().rowSelection; - const row = result.current.table.getRowModel().rows[0]; - expect(rowSelection[row?.id ?? "0"]).toBeFalsy(); + expect(rowSelection["1"]).toBe(true); + expect(Object.keys(rowSelection).length).toBe(1); }); }); diff --git a/src/hooks/use-data-grid.ts b/src/hooks/use-data-grid.ts index 7ec11b045..22323e4ac 100644 --- a/src/hooks/use-data-grid.ts +++ b/src/hooks/use-data-grid.ts @@ -83,7 +83,7 @@ interface DataGridState { searchMatches: CellPosition[]; matchIndex: number; searchOpen: boolean; - lastClickedRowIndex: number | null; + lastClickedRowId: string | null; pasteDialog: PasteDialogState; } @@ -193,7 +193,7 @@ function useDataGrid({ searchMatches: [], matchIndex: -1, searchOpen: false, - lastClickedRowIndex: null, + lastClickedRowId: null, pasteDialog: { open: false, rowsNeeded: 0, @@ -1939,28 +1939,39 @@ function useDataGrid({ ); const onRowSelect = React.useCallback( - (rowIndex: number, selected: boolean, shiftKey: boolean) => { + (rowId: string, selected: boolean, shiftKey: boolean) => { const currentState = store.getState(); const rows = tableRef.current?.getRowModel().rows ?? []; - const currentRow = rows[rowIndex]; + const currentRowIndex = rows.findIndex((r) => r.id === rowId); + const currentRow = currentRowIndex >= 0 ? rows[currentRowIndex] : null; if (!currentRow) return; - if (shiftKey && currentState.lastClickedRowIndex !== null) { - const startIndex = Math.min(currentState.lastClickedRowIndex, rowIndex); - const endIndex = Math.max(currentState.lastClickedRowIndex, rowIndex); - - const newRowSelection: RowSelectionState = { - ...currentState.rowSelection, - }; - - for (let i = startIndex; i <= endIndex; i++) { - const row = rows[i]; - if (row) { - newRowSelection[row.id] = selected; + if (shiftKey && currentState.lastClickedRowId !== null) { + const lastClickedRowIndex = rows.findIndex( + (r) => r.id === currentState.lastClickedRowId, + ); + if (lastClickedRowIndex >= 0) { + const startIndex = Math.min(lastClickedRowIndex, currentRowIndex); + const endIndex = Math.max(lastClickedRowIndex, currentRowIndex); + + const newRowSelection: RowSelectionState = { + ...currentState.rowSelection, + }; + + for (let i = startIndex; i <= endIndex; i++) { + const row = rows[i]; + if (row) { + newRowSelection[row.id] = selected; + } } - } - onRowSelectionChange(newRowSelection); + onRowSelectionChange(newRowSelection); + } else { + onRowSelectionChange({ + ...currentState.rowSelection, + [currentRow.id]: selected, + }); + } } else { onRowSelectionChange({ ...currentState.rowSelection, @@ -1968,7 +1979,7 @@ function useDataGrid({ }); } - store.setState("lastClickedRowIndex", rowIndex); + store.setState("lastClickedRowId", rowId); }, [store, onRowSelectionChange], ); diff --git a/src/types/data-grid.ts b/src/types/data-grid.ts index 4a28b5116..772066a34 100644 --- a/src/types/data-grid.ts +++ b/src/types/data-grid.ts @@ -76,11 +76,7 @@ declare module "@tanstack/react-table" { getVisualRowIndex?: (rowId: string) => number | undefined; rowHeight?: RowHeightValue; onRowHeightChange?: (value: RowHeightValue) => void; - onRowSelect?: ( - rowIndex: number, - checked: boolean, - shiftKey: boolean, - ) => void; + onRowSelect?: (rowId: string, checked: boolean, shiftKey: boolean) => void; onDataUpdate?: (params: CellUpdate | Array) => void; onRowsDelete?: (rowIndices: number[]) => void | Promise; onColumnClick?: (columnId: string) => void;