diff --git a/packages/material-react-table/src/components/inputs/MRT_SelectCheckbox.tsx b/packages/material-react-table/src/components/inputs/MRT_SelectCheckbox.tsx index 03c3f53d6..346c14673 100644 --- a/packages/material-react-table/src/components/inputs/MRT_SelectCheckbox.tsx +++ b/packages/material-react-table/src/components/inputs/MRT_SelectCheckbox.tsx @@ -43,10 +43,15 @@ export const MRT_SelectCheckbox = ({ const selectAll = !row; + const getLeafRows = () => ( + selectAllMode === 'page' + ? table.getPaginationRowModel().flatRows + : table.getPrePaginationRowModel().flatRows + ).filter((r) => !r.getIsGrouped()); + + // Fix: getIsAllPageRowsSelected() / getIsAllRowsSelected() include group rows const allRowsSelected = selectAll - ? selectAllMode === 'page' - ? table.getIsAllPageRowsSelected() - : table.getIsAllRowsSelected() + ? getLeafRows().every((r) => r.getIsSelected()) : undefined; const isChecked = selectAll @@ -122,7 +127,8 @@ export const MRT_SelectCheckbox = ({ r.getIsSelected()) : row?.getIsSomeSelected() && row.getCanSelectSubRows() } {...commonProps} diff --git a/packages/material-react-table/src/utils/row.utils.ts b/packages/material-react-table/src/utils/row.utils.ts index 576bc0060..fe07714b9 100644 --- a/packages/material-react-table/src/utils/row.utils.ts +++ b/packages/material-react-table/src/utils/row.utils.ts @@ -175,6 +175,19 @@ export const getMRT_RowSelectionHandler = const wasCurrentRowChecked = getIsRowSelected({ row, table }); + // If this is a group row, toggle all its leaf sub-rows instead of itself + // to prevent the group row ID from entering rowSelection + if (row.getIsGrouped()) { + const newValue = value ?? !wasCurrentRowChecked; + row.getLeafRows().forEach((leafRow) => { + if (leafRow.getCanSelect()) { + leafRow.toggleSelected(newValue); + } + }); + lastSelectedRowId.current = row.id; + return; + } + // toggle selection of this row row.toggleSelected(value ?? !wasCurrentRowChecked); @@ -250,9 +263,17 @@ export const getMRT_SelectAllHandler = refs: { lastSelectedRowId }, } = table; - selectAllMode === 'all' || forceAll - ? table.toggleAllRowsSelected(value ?? (event as any).target.checked) - : table.toggleAllPageRowsSelected(value ?? (event as any).target.checked); + // Fix: replaces toggleAllPageRowsSelected() / toggleAllRowsSelected() which + // include group rows in flatRows, causing group IDs to pollute rowSelection. + const checked = value ?? (event as any).target.checked; + const rows = (selectAllMode === 'all' || forceAll + ? table.getPrePaginationRowModel().flatRows + : table.getPaginationRowModel().flatRows + ).filter((row) => !row.getIsGrouped()); + rows.forEach((row) => { + if (row.getCanSelect()) row.toggleSelected(checked); + }); + if (enableRowPinning && rowPinningDisplayMode?.includes('select')) { table.setRowPinning({ bottom: [], top: [] }); } diff --git a/packages/material-react-table/stories/fixed-bugs/groupingRowselection.stories.tsx b/packages/material-react-table/stories/fixed-bugs/groupingRowselection.stories.tsx new file mode 100644 index 000000000..cd283e3ea --- /dev/null +++ b/packages/material-react-table/stories/fixed-bugs/groupingRowselection.stories.tsx @@ -0,0 +1,68 @@ +import { type MRT_ColumnDef, MaterialReactTable, useMaterialReactTable } from '../../src'; +import { faker } from '@faker-js/faker'; +import { type Meta } from '@storybook/react'; + +const meta: Meta = { + title: 'Fixed Bugs/Grouping RowSelection', +}; + +export default meta; + +type Person = { + address: string; + city: string; + firstName: string; + lastName: string; + state: string; + gender: string; +}; + +const columns: MRT_ColumnDef[] = [ + { + accessorKey: 'firstName', + header: 'First Name', + }, + { + accessorKey: 'lastName', + header: 'Last Name', + }, + { + accessorKey: 'address', + header: 'Address', + }, + { + accessorKey: 'city', + header: 'City', + }, + { + accessorKey: 'state', + header: 'State', + }, + { + accessorKey: 'gender', + header: 'Gender', + }, +]; + +const data = [...Array(300)].map(() => ({ + address: faker.location.streetAddress(), + city: faker.location.city(), + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + state: faker.location.state(), + gender: ["Male", "Female"][faker.number.int({min: 0, max: 1})] +})); + +export const GroupingRowSelection = () => { + const table = useMaterialReactTable({ + columns, + data, + enableGrouping: true, + enableRowSelection: true, + initialState: { + grouping: ["gender"] + } + }) + console.log(table.getState().rowSelection); + return +};