Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/data/data-grid/pivoting/GridGetPivotDerivedColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ const columns = [
type: 'date',
headerName: 'Transaction Date',
width: 140,
valueGetter: (value) => new Date(value),
groupingValueGetter: (value) => value,
valueGetter: (value) => (value ? new Date(value) : null),
},
{ field: 'ticker', headerName: 'Ticker' },
{
Expand Down
3 changes: 1 addition & 2 deletions docs/data/data-grid/pivoting/GridGetPivotDerivedColumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ const columns: GridColDef[] = [
type: 'date',
headerName: 'Transaction Date',
width: 140,
valueGetter: (value) => new Date(value),
groupingValueGetter: (value) => value,
valueGetter: (value) => (value ? new Date(value) : null),
},
{ field: 'ticker', headerName: 'Ticker' },
{
Expand Down
3 changes: 1 addition & 2 deletions docs/data/data-grid/pivoting/GridPivotingFinancial.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ const columns = [
type: 'date',
headerName: 'Transaction Date',
width: 140,
valueGetter: (value) => new Date(value),
groupingValueGetter: (value) => value,
valueGetter: (value) => (value ? new Date(value) : null),
},
{ field: 'ticker', headerName: 'Ticker' },
{
Expand Down
3 changes: 1 addition & 2 deletions docs/data/data-grid/pivoting/GridPivotingFinancial.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ const columns: GridColDef[] = [
type: 'date',
headerName: 'Transaction Date',
width: 140,
valueGetter: (value) => new Date(value),
groupingValueGetter: (value) => value,
Copy link
Contributor

@arminmeh arminmeh Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update the docs at https://mui.com/x/react-data-grid/row-grouping/#using-groupingvaluegetter-for-complex-grouping-value to state that Date is also allowed as a grouping value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
Also updated the demo for groupingValueGetter, because now groupingValueGetter is not necessary if you have valueGetter, unless you want to use a different value for grouping than the one in the cell.
https://deploy-preview-18967--material-ui-x.netlify.app/x/react-data-grid/row-grouping/#using-groupingvaluegetter-for-complex-grouping-value
WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is a nice addition

valueGetter: (value) => (value ? new Date(value) : null),
},
{ field: 'ticker', headerName: 'Ticker' },
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD,
} from './gridRowGroupingUtils';
import { gridRowGroupingSanitizedModelSelector } from './gridRowGroupingSelector';
import type { GridRowGroupingModel } from './gridRowGroupingInterfaces';
import type { DataGridPremiumProcessedProps } from '../../../models/dataGridPremiumProps';

const GROUPING_COL_DEF_DEFAULT_PROPERTIES: Omit<GridColDef, 'field'> = {
...GRID_STRING_COL_DEF,
Expand Down Expand Up @@ -110,13 +112,58 @@ const groupedByColValueFormatter: (
return value;
};

const getGroupingCriteriaProperties = (groupedByColDef: GridColDef, applyHeaderName: boolean) => {
function getGroupingCriteriaProperties(
groupedByColDef: GridColDef,
rowGroupingColumnMode: 'single',
rowGroupingModel: GridRowGroupingModel,
columnsLookup: GridColumnRawLookup,
): Partial<GridColDef>;
function getGroupingCriteriaProperties(
groupedByColDef: GridColDef,
rowGroupingColumnMode: 'multiple',
): Partial<GridColDef>;
function getGroupingCriteriaProperties(
groupedByColDef: GridColDef,
rowGroupingColumnMode: DataGridPremiumProcessedProps['rowGroupingColumnMode'],
rowGroupingModel: GridRowGroupingModel = [],
columnsLookup: GridColumnRawLookup = {},
): Partial<GridColDef> {
let valueFormatter: GridColDef['valueFormatter'] | undefined;

if (rowGroupingColumnMode === 'single' && rowGroupingModel.length > 1) {
// In single column grouping mode, the `valueFormatter` of the grouping column uses
// value formatters from original columns for each of the grouping criteria
Comment on lines +134 to +135
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the only way valueFormatter could work in rowGroupingColumnMode="single".

const originalColDefs: Map<string, GridColDef> = new Map();
rowGroupingModel.forEach((field) => {
const colDef = columnsLookup[field];
originalColDefs.set(field, colDef);
});
valueFormatter = (value, row, column, apiRef) => {
const rowId = gridRowIdSelector(apiRef, row);
const rowNode = gridRowNodeSelector(apiRef, rowId);
if (rowNode.type === 'group') {
const originalColDef = originalColDefs.get(rowNode.groupingField!)!;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this just be
columnsLookup[rowNode.groupingField]
so that you avoid building originalColDefs?

if (originalColDef.type === 'singleSelect') {
// the default valueFormatter of a singleSelect colDef won't work with the grouping column values
return value;
}
const columnValueFormatter = originalColDef.valueFormatter;
if (typeof columnValueFormatter === 'function') {
return columnValueFormatter(value as never, row, column, apiRef);
}
}
return value;
};
} else {
valueFormatter = groupedByColDef.valueFormatter
? groupedByColValueFormatter(groupedByColDef)
: undefined;
}

const properties: Partial<GridColDef> = {
sortable: groupedByColDef.sortable,
filterable: groupedByColDef.filterable,
valueFormatter: groupedByColDef.valueFormatter
? groupedByColValueFormatter(groupedByColDef)
: undefined,
valueFormatter,
valueOptions: isSingleSelectColDef(groupedByColDef) ? groupedByColDef.valueOptions : undefined,
sortComparator: (v1, v2, cellParams1, cellParams2) => {
// We only want to sort the groups of the current grouping criteria
Expand All @@ -134,12 +181,13 @@ const getGroupingCriteriaProperties = (groupedByColDef: GridColDef, applyHeaderN
filterOperators: groupedByColDef.filterOperators,
};

const applyHeaderName = !(rowGroupingColumnMode === 'single' && rowGroupingModel.length > 1);
if (applyHeaderName) {
properties.headerName = groupedByColDef.headerName ?? groupedByColDef.field;
}

return properties;
};
}

interface CreateGroupingColDefMonoCriteriaParams {
columnsLookup: GridColumnRawLookup;
Expand Down Expand Up @@ -253,11 +301,11 @@ export const createGroupingColDefForOneGroupingCriteria = ({
// By default, we apply the sorting / filtering on the groups of this column's grouping criteria based on the properties of `groupedColDef`.
let sourceProperties: Partial<GridColDef>;
if (mainGroupingCriteria && mainGroupingCriteria === groupingCriteria) {
sourceProperties = getGroupingCriteriaProperties(groupedByColDef, true);
sourceProperties = getGroupingCriteriaProperties(groupedByColDef, 'multiple');
} else if (leafColDef) {
sourceProperties = getLeafProperties(leafColDef);
} else {
sourceProperties = getGroupingCriteriaProperties(groupedByColDef, true);
sourceProperties = getGroupingCriteriaProperties(groupedByColDef, 'multiple');
}

// The properties that can't be overridden with `colDefOverride`
Expand All @@ -281,7 +329,7 @@ interface CreateGroupingColDefSeveralCriteriaParams {
/**
* The fields from which we are grouping the rows.
*/
rowGroupingModel: string[];
rowGroupingModel: GridRowGroupingModel;
/**
* The col def properties the user wants to override.
* This value comes `prop.groupingColDef`.
Expand Down Expand Up @@ -379,13 +427,20 @@ export const createGroupingColDefForAllGroupingCriteria = ({
// By default, we apply the sorting / filtering on the groups of the top level grouping criteria based on the properties of `columnsLookup[orderedGroupedByFields[0]]`.
let sourceProperties: Partial<GridColDef>;
if (mainGroupingCriteria && rowGroupingModel.includes(mainGroupingCriteria)) {
sourceProperties = getGroupingCriteriaProperties(columnsLookup[mainGroupingCriteria], true);
sourceProperties = getGroupingCriteriaProperties(
columnsLookup[mainGroupingCriteria],
'single',
rowGroupingModel,
columnsLookup,
);
} else if (leafColDef) {
sourceProperties = getLeafProperties(leafColDef);
} else {
sourceProperties = getGroupingCriteriaProperties(
columnsLookup[rowGroupingModel[0]],
rowGroupingModel.length === 1,
'single',
rowGroupingModel,
columnsLookup,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1529,4 +1529,39 @@ describe('<DataGridPremium /> - Row grouping', () => {

expect(getColumnValues(0)).to.deep.equal(['2025 (1)', 'MUI (1)', '']);
});

it('should use the valueFormatter of the respective original column for each of the grouping criteria in single column grouping mode', () => {
render(
<Test
columns={[
{ field: 'id' },
{
field: 'date',
type: 'date',
valueGetter: (value) => (value ? new Date(value) : null),
},
{
field: 'price',
type: 'number',
valueFormatter: (value: number | undefined) => (value ? `$${value.toFixed(2)}` : null),
},
]}
rows={[
{ id: 1, date: '2025-01-01', price: 100 },
{ id: 2, date: '2025-01-02', price: 200 },
]}
rowGroupingModel={['price', 'date']}
defaultGroupingExpansionDepth={-1}
/>,
);

expect(getColumnValues(0)).to.deep.equal([
'$100.00 (1)',
'1/1/2025 (1)',
'',
'$200.00 (1)',
'1/2/2025 (1)',
'',
]);
});
});