diff --git a/apps/material-react-table-docs/components/prop-tables/stateOptions.ts b/apps/material-react-table-docs/components/prop-tables/stateOptions.ts index f2de34e5a..434243d6c 100644 --- a/apps/material-react-table-docs/components/prop-tables/stateOptions.ts +++ b/apps/material-react-table-docs/components/prop-tables/stateOptions.ts @@ -11,6 +11,15 @@ export type StateOption = { }; export const stateOptions: StateOption[] = [ + { + defaultValue: '', + description: 'a variable will be used to render the leaf node.', + link: '', + linkText: '', + source: 'MRT', + stateOption: 'addColumnToLeafNode', + type: 'string', + }, { defaultValue: '', description: 'a variable representing the currently creating row', diff --git a/apps/material-react-table-docs/components/prop-tables/tableOptions.ts b/apps/material-react-table-docs/components/prop-tables/tableOptions.ts index f93c71a3c..eab782268 100644 --- a/apps/material-react-table-docs/components/prop-tables/tableOptions.ts +++ b/apps/material-react-table-docs/components/prop-tables/tableOptions.ts @@ -357,6 +357,17 @@ export const tableOptions: TableOption[] = [ source: 'MRT', type: 'boolean', }, + { + tableOption: 'enableGroupingSingleColumn', + defaultValue: '', + description: + 'Single group column is automatically added by the grid containing all row groups under a single row group hierarchy.', + link: '', + linkText: '', + required: false, + source: 'MRT', + type: 'boolean', + }, { tableOption: 'enableFacetedValues', defaultValue: 'true', @@ -2060,6 +2071,17 @@ export const tableOptions: TableOption[] = [ source: 'TanStack Table', type: 'Record', }, + { + tableOption: 'showOpenedGroup', + defaultValue: '', + description: + 'This option will show the name of the opened group inside the group column.', + link: '', + linkText: '', + required: false, + source: 'MRT', + type: 'boolean', + }, { tableOption: 'state', defaultValue: '', diff --git a/packages/material-react-table/src/hooks/useMRT_DisplayColumns.tsx b/packages/material-react-table/src/hooks/useMRT_DisplayColumns.tsx index 9926b880c..415742186 100644 --- a/packages/material-react-table/src/hooks/useMRT_DisplayColumns.tsx +++ b/packages/material-react-table/src/hooks/useMRT_DisplayColumns.tsx @@ -56,6 +56,7 @@ export const useMRT_DisplayColumns = ( tableOptions.enableEditing, tableOptions.enableExpandAll, tableOptions.enableExpanding, + tableOptions.enableGroupingSingleColumn, tableOptions.enableGrouping, tableOptions.enableRowActions, tableOptions.enableRowDragging, @@ -68,8 +69,10 @@ export const useMRT_DisplayColumns = ( tableOptions.renderDetailPanel, tableOptions.renderRowActionMenuItems, tableOptions.renderRowActions, + tableOptions.state?.addColumnToLeafNode, tableOptions.state?.columnOrder, tableOptions.state?.grouping, + tableOptions.showOpenedGroup, ], ); }; @@ -157,11 +160,45 @@ function makeRowExpandColumn( order.includes(id) && showExpandColumn(tableOptions, tableOptions.state?.grouping ?? grouping) ) { + const arrGrouping = tableOptions.state?.grouping ?? grouping; return { - Cell: ({ row, table }) => , - Header: tableOptions.enableExpandAll - ? ({ table }) => - : undefined, + Cell: ({ row, table }) => { + const isGroupedSingleColumn = + tableOptions?.enableGroupingSingleColumn && + tableOptions?.groupedColumnMode === 'remove'; + return ( + <> + + {isGroupedSingleColumn && row.groupingValue} + {isGroupedSingleColumn && row.getCanExpand() && ( + <> ({row.subRows?.length}) + )} + {isGroupedSingleColumn && + tableOptions?.showOpenedGroup && + !row.getCanExpand() && + row.original?.[ + table.getState().addColumnToLeafNode ?? + arrGrouping[arrGrouping.length - 1] + ]} + + ); + }, + Header: ({ table }) => { + return ( + <> + {tableOptions.enableExpandAll && ( + + )} + {tableOptions?.enableGroupingSingleColumn && + tableOptions?.groupedColumnMode === 'remove' + ? table.getState().addColumnToLeafNode + ? table.getColumn?.(table.getState().addColumnToLeafNode ?? '') + ?.columnDef?.header + : 'Group' /* Add key localization */ + : undefined} + + ); + }, ...defaultDisplayColumnProps(tableOptions, id, 'expand'), }; } diff --git a/packages/material-react-table/src/types.ts b/packages/material-react-table/src/types.ts index 31f150d37..1ede2ef9e 100644 --- a/packages/material-react-table/src/types.ts +++ b/packages/material-react-table/src/types.ts @@ -338,6 +338,7 @@ export type MRT_DefinedTableOptions = }; export type MRT_TableState = TableState & { + addColumnToLeafNode?: string; columnFilterFns: MRT_ColumnFilterFnsState; creatingRow: MRT_Row | null; density: MRT_DensityState; @@ -808,6 +809,7 @@ export type MRT_TableOptions = Omit< enableFullScreenToggle?: boolean; enableGlobalFilterModes?: boolean; enableGlobalFilterRankedResults?: boolean; + enableGroupingSingleColumn?: boolean; enablePagination?: boolean; enableRowActions?: boolean; enableRowDragging?: boolean; @@ -1199,6 +1201,7 @@ export type MRT_TableOptions = Omit< }) => Partial>) | Partial>; selectAllMode?: 'all' | 'page'; + showOpenedGroup?: boolean; /** * Manage state externally any way you want, then pass it back into MRT. */ diff --git a/packages/material-react-table/stories/features/ColumnGrouping.stories.tsx b/packages/material-react-table/stories/features/ColumnGrouping.stories.tsx index 9fde8034d..bbf190122 100644 --- a/packages/material-react-table/stories/features/ColumnGrouping.stories.tsx +++ b/packages/material-react-table/stories/features/ColumnGrouping.stories.tsx @@ -1,7 +1,11 @@ -import { useEffect, useMemo, useState } from 'react'; +import { type ReactNode, useEffect, useMemo, useState } from 'react'; +import IconButton from '@mui/material/IconButton'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; import { type MRT_Column, type MRT_ColumnDef, + type MRT_Row, MaterialReactTable, } from '../../src'; import { faker } from '@faker-js/faker'; @@ -281,3 +285,168 @@ export const GroupingAndDraggingWithSomeDisabledGrouping = () => { /> ); }; + +export const GroupingWithSingleColumn = () => { + const _columns = useMemo[]>( + () => [ + { + accessorKey: 'firstName', + header: 'First Name', + }, + { + accessorKey: 'lastName', + header: 'Last Name', + }, + { + accessorKey: 'gender', + header: 'Gender', + }, + { + accessorKey: 'city', + header: 'City', + }, + { + accessorKey: 'state', + header: 'State', + }, + ], + [], + ); + + return ( + + ); +}; + +export const GroupingWithSingleColumnWithShowOpenedGroup = () => { + const _columns = useMemo[]>( + () => [ + { + accessorKey: 'firstName', + header: 'First Name', + }, + { + accessorKey: 'lastName', + header: 'Last Name', + }, + { + accessorKey: 'gender', + header: 'Gender', + }, + { + accessorKey: 'city', + header: 'City', + }, + { + accessorKey: 'state', + header: 'State', + }, + ], + [], + ); + + return ( + + ); +}; + +export const GroupingWithSingleColumnWithRowVirtualization = () => { + const _columns = useMemo[]>( + () => [ + { + accessorKey: 'firstName', + header: 'First Name', + }, + { + accessorKey: 'lastName', + header: 'Last Name', + }, + { + accessorKey: 'gender', + header: 'Gender', + }, + { + accessorKey: 'city', + header: 'City', + }, + { + accessorKey: 'state', + header: 'State', + }, + ], + [], + ); + + return ( + { + return { + sx: { + bgcolor: + row.depth === 0 + ? 'darkgreen' + : row.depth === 1 + ? 'slategrey' + : 'inherit', + }, + }; + }} + muiTableContainerProps={{ sx: { maxHeight: 600 } }} + showOpenedGroup + /> + ); +};