From 6169a91520f17dc39c930547bd7e2f2fdc6b1d93 Mon Sep 17 00:00:00 2001 From: Rajat Date: Thu, 26 Dec 2024 22:56:11 +0530 Subject: [PATCH 1/5] Make row grouping work with negative filters --- .../rowGrouping/gridRowGroupingUtils.ts | 56 +++++++++++++------ .../useGridRowGroupingPreProcessors.ts | 3 +- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 353b51936bd4..edbef2fd437b 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -55,6 +55,7 @@ interface FilterRowTreeFromTreeDataParams { isRowMatchingFilters: GridAggregatedFilterItemApplier | null; filterModel: GridFilterModel; apiRef: React.MutableRefObject; + rowGroupingColumnMode: 'single' | 'multiple'; } /** @@ -79,11 +80,36 @@ const shouldApplyFilterItemOnGroup = (columnField: string, node: GridGroupNode) export const filterRowTreeFromGroupingColumns = ( params: FilterRowTreeFromTreeDataParams, ): Omit => { - const { apiRef, rowTree, isRowMatchingFilters, filterModel } = params; + const { apiRef, rowTree, isRowMatchingFilters, filterModel, rowGroupingColumnMode } = params; const filteredRowsLookup: Record = {}; const filteredChildrenCountLookup: Record = {}; const filteredDescendantCountLookup: Record = {}; const filterCache = {}; + const isFilterItemAppliedOnGroup = (node: GridTreeNode): boolean => { + if (node.type === 'group' && node.isAutoGenerated) { + const groupFilterItems: string[] = []; + const leafFilterItems: string[] = []; + filterModel.items.forEach((item) => { + if (isGroupingColumn(item.field)) { + groupFilterItems.push(item.field); + } else { + leafFilterItems.push(item.field); + } + }); + const groupingField = + rowGroupingColumnMode === 'multiple' + ? getRowGroupingFieldFromGroupingCriteria(node.groupingField!) + : GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD; + + const shouldApplyFilterOnGroupingField = + groupingField === groupFilterItems[groupFilterItems.length - 1]; + + if (shouldApplyFilterOnGroupingField && leafFilterItems.length === 0) { + return true; + } + } + return false; + }; const filterTreeNode = ( node: GridTreeNode, @@ -102,15 +128,23 @@ export const filterRowTreeFromGroupingColumns = ( node.type === 'group' && node.isAutoGenerated ? (columnField: string) => shouldApplyFilterItemOnGroup(columnField, node) : undefined; - const row = apiRef.current.getRow(node.id); isRowMatchingFilters(row, shouldApplyItem, filterResults); + const allResults = [...ancestorsResults, filterResults]; + isPassingFiltering = passFilterLogic( + allResults.map((result) => result.passingFilterItems), + allResults.map((result) => result.passingQuickFilterValues), + filterModel, + params.apiRef, + filterCache, + ); } else { isPassingFiltering = true; } let filteredChildrenCount = 0; let filteredDescendantCount = 0; + if (node.type === 'group') { node.children.forEach((childId) => { const childNode = rowTree[childId]; @@ -126,20 +160,10 @@ export const filterRowTreeFromGroupingColumns = ( }); } - if (isPassingFiltering === false) { - if (node.type === 'group') { - // If node has children - it's passing if at least one child passes filters - isPassingFiltering = filteredDescendantCount > 0; - } else { - const allResults = [...ancestorsResults, filterResults]; - isPassingFiltering = passFilterLogic( - allResults.map((result) => result.passingFilterItems), - allResults.map((result) => result.passingQuickFilterValues), - filterModel, - params.apiRef, - filterCache, - ); - } + const filterItemOnlyAppliedOnGroup = isFilterItemAppliedOnGroup(node); + if (node.type === 'group' && !filterItemOnlyAppliedOnGroup) { + // If node has children - it's passing if at least one child passes filters + isPassingFiltering = filteredDescendantCount > 0; } filteredRowsLookup[node.id] = isPassingFiltering; diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts index 435ac1ca1a7e..858616f0a1f1 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts @@ -225,9 +225,10 @@ export const useGridRowGroupingPreProcessors = ( isRowMatchingFilters: params.isRowMatchingFilters, filterModel: params.filterModel, apiRef, + rowGroupingColumnMode: props.rowGroupingColumnMode, }); }, - [apiRef], + [apiRef, props.rowGroupingColumnMode], ); const sortRows = React.useCallback>( From 3302b418d35e22c7653aaf9958ea2957e02f422b Mon Sep 17 00:00:00 2001 From: Rajat Date: Sat, 28 Dec 2024 20:17:01 +0530 Subject: [PATCH 2/5] fixup --- .../rowGrouping/gridRowGroupingUtils.ts | 112 ++++++++++++------ .../useGridRowGroupingPreProcessors.ts | 3 +- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index edbef2fd437b..0513a2c7d835 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -10,6 +10,7 @@ import { GridColDef, GridKeyValue, GridDataSource, + gridColumnFieldsSelector, } from '@mui/x-data-grid-pro'; import { passFilterLogic, @@ -50,12 +51,27 @@ export const getRowGroupingFieldFromGroupingCriteria = (groupingCriteria: string return `__row_group_by_columns_group_${groupingCriteria}__`; }; +export const getColDefOverrides = ( + groupingColDefProp: DataGridPremiumProcessedProps['groupingColDef'], + fields: string[], + strategy?: RowGroupingStrategy, +) => { + if (typeof groupingColDefProp === 'function') { + return groupingColDefProp({ + groupingName: strategy ?? RowGroupingStrategy.Default, + fields, + }); + } + return groupingColDefProp; +}; + interface FilterRowTreeFromTreeDataParams { rowTree: GridRowTreeConfig; isRowMatchingFilters: GridAggregatedFilterItemApplier | null; filterModel: GridFilterModel; apiRef: React.MutableRefObject; rowGroupingColumnMode: 'single' | 'multiple'; + groupingColDef: DataGridPremiumProcessedProps['groupingColDef']; } /** @@ -80,12 +96,35 @@ const shouldApplyFilterItemOnGroup = (columnField: string, node: GridGroupNode) export const filterRowTreeFromGroupingColumns = ( params: FilterRowTreeFromTreeDataParams, ): Omit => { - const { apiRef, rowTree, isRowMatchingFilters, filterModel, rowGroupingColumnMode } = params; + const { + apiRef, + rowTree, + isRowMatchingFilters, + filterModel, + rowGroupingColumnMode, + groupingColDef, + } = params; const filteredRowsLookup: Record = {}; const filteredChildrenCountLookup: Record = {}; const filteredDescendantCountLookup: Record = {}; const filterCache = {}; - const isFilterItemAppliedOnGroup = (node: GridTreeNode): boolean => { + + const getGroupingCriteria = () => { + const rowGroupingModel = gridRowGroupingSanitizedModelSelector(apiRef); + const colDefOverride = getColDefOverrides(groupingColDef, rowGroupingModel); + const orderedFields = gridColumnFieldsSelector(apiRef); + const { leafField, mainGroupingCriteria } = colDefOverride ?? {}; + const isLeafField = leafField ? orderedFields.includes(leafField) : null; + if (mainGroupingCriteria && rowGroupingModel.includes(mainGroupingCriteria)) { + return mainGroupingCriteria; + } + if (isLeafField) { + return null; + } + return rowGroupingModel[0]; + }; + + const shouldFilterGroup = (node: GridTreeNode): boolean => { if (node.type === 'group' && node.isAutoGenerated) { const groupFilterItems: string[] = []; const leafFilterItems: string[] = []; @@ -96,25 +135,39 @@ export const filterRowTreeFromGroupingColumns = ( leafFilterItems.push(item.field); } }); - const groupingField = - rowGroupingColumnMode === 'multiple' - ? getRowGroupingFieldFromGroupingCriteria(node.groupingField!) - : GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD; - const shouldApplyFilterOnGroupingField = - groupingField === groupFilterItems[groupFilterItems.length - 1]; + if (leafFilterItems.length > 0 || filterModel.quickFilterValues?.length! > 0) { + return false; + } + + const groupingCriteria = getGroupingCriteria(); + + if (!groupingCriteria) { + return false; + } - if (shouldApplyFilterOnGroupingField && leafFilterItems.length === 0) { - return true; + if (rowGroupingColumnMode === 'single') { + return node.groupingField === groupingCriteria; + } + if (rowGroupingColumnMode === 'multiple') { + if (groupFilterItems.length === 0 && node.groupingField === groupingCriteria) { + return true; + } + return ( + getRowGroupingFieldFromGroupingCriteria(node.groupingField!) === + groupFilterItems[groupFilterItems.length - 1] + ); } } + return false; }; const filterTreeNode = ( node: GridTreeNode, areAncestorsExpanded: boolean, - ancestorsResults: GridAggregatedFilterItemApplierResult[], + isParentPassingFilter: boolean, + shouldFilterParent: boolean, ): number => { const filterResults: GridAggregatedFilterItemApplierResult = { passingFilterItems: null, @@ -122,18 +175,19 @@ export const filterRowTreeFromGroupingColumns = ( }; let isPassingFiltering = false; - + let allowGroupToFilter = shouldFilterGroup(node); if (isRowMatchingFilters && node.type !== 'footer') { const shouldApplyItem = node.type === 'group' && node.isAutoGenerated ? (columnField: string) => shouldApplyFilterItemOnGroup(columnField, node) : undefined; + const row = apiRef.current.getRow(node.id); isRowMatchingFilters(row, shouldApplyItem, filterResults); - const allResults = [...ancestorsResults, filterResults]; + // const allResults = [...ancestorsResults, filterResults]; isPassingFiltering = passFilterLogic( - allResults.map((result) => result.passingFilterItems), - allResults.map((result) => result.passingQuickFilterValues), + [filterResults.passingFilterItems], + [filterResults.passingQuickFilterValues], filterModel, params.apiRef, filterCache, @@ -145,13 +199,18 @@ export const filterRowTreeFromGroupingColumns = ( let filteredChildrenCount = 0; let filteredDescendantCount = 0; + if (shouldFilterParent) { + isPassingFiltering = isParentPassingFilter; + allowGroupToFilter = shouldFilterParent; + } if (node.type === 'group') { node.children.forEach((childId) => { const childNode = rowTree[childId]; const childSubTreeSize = filterTreeNode( childNode, areAncestorsExpanded && !!node.childrenExpanded, - [...ancestorsResults, filterResults], + isPassingFiltering, + allowGroupToFilter, ); filteredDescendantCount += childSubTreeSize; if (childSubTreeSize > 0) { @@ -160,9 +219,7 @@ export const filterRowTreeFromGroupingColumns = ( }); } - const filterItemOnlyAppliedOnGroup = isFilterItemAppliedOnGroup(node); - if (node.type === 'group' && !filterItemOnlyAppliedOnGroup) { - // If node has children - it's passing if at least one child passes filters + if (node.type === 'group' && !shouldFilterParent) { isPassingFiltering = filteredDescendantCount > 0; } @@ -186,7 +243,7 @@ export const filterRowTreeFromGroupingColumns = ( for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; if (node.depth === 0) { - filterTreeNode(node, true, []); + filterTreeNode(node, true, true, false); } } @@ -197,21 +254,6 @@ export const filterRowTreeFromGroupingColumns = ( }; }; -export const getColDefOverrides = ( - groupingColDefProp: DataGridPremiumProcessedProps['groupingColDef'], - fields: string[], - strategy?: RowGroupingStrategy, -) => { - if (typeof groupingColDefProp === 'function') { - return groupingColDefProp({ - groupingName: strategy ?? RowGroupingStrategy.Default, - fields, - }); - } - - return groupingColDefProp; -}; - export const mergeStateWithRowGroupingModel = (rowGroupingModel: GridRowGroupingModel) => (state: GridStatePremium): GridStatePremium => ({ diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts index 858616f0a1f1..be0b6c0e33da 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/useGridRowGroupingPreProcessors.ts @@ -226,9 +226,10 @@ export const useGridRowGroupingPreProcessors = ( filterModel: params.filterModel, apiRef, rowGroupingColumnMode: props.rowGroupingColumnMode, + groupingColDef: props.groupingColDef, }); }, - [apiRef, props.rowGroupingColumnMode], + [apiRef, props.groupingColDef, props.rowGroupingColumnMode], ); const sortRows = React.useCallback>( From 1af2bd7a9871e1e3f3f22cd16527790e85fdd4c9 Mon Sep 17 00:00:00 2001 From: Rajat Date: Sat, 28 Dec 2024 22:18:24 +0530 Subject: [PATCH 3/5] refactor --- .../rowGrouping/gridRowGroupingUtils.ts | 28 +++++++------------ .../rowGrouping.DataGridPremium.test.tsx | 4 +-- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 0513a2c7d835..270cd806c38f 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -126,7 +126,12 @@ export const filterRowTreeFromGroupingColumns = ( const shouldFilterGroup = (node: GridTreeNode): boolean => { if (node.type === 'group' && node.isAutoGenerated) { - const groupFilterItems: string[] = []; + const groupingCriteria = getGroupingCriteria(); + if(!groupingCriteria) { + return false + } + const groupingField = getRowGroupingFieldFromGroupingCriteria(groupingCriteria) + const groupFilterItems: string[] = [groupingField]; const leafFilterItems: string[] = []; filterModel.items.forEach((item) => { if (isGroupingColumn(item.field)) { @@ -140,24 +145,12 @@ export const filterRowTreeFromGroupingColumns = ( return false; } - const groupingCriteria = getGroupingCriteria(); + const nodeGroupingField = getRowGroupingFieldFromGroupingCriteria(node.groupingField); - if (!groupingCriteria) { - return false; - } - - if (rowGroupingColumnMode === 'single') { - return node.groupingField === groupingCriteria; - } if (rowGroupingColumnMode === 'multiple') { - if (groupFilterItems.length === 0 && node.groupingField === groupingCriteria) { - return true; - } - return ( - getRowGroupingFieldFromGroupingCriteria(node.groupingField!) === - groupFilterItems[groupFilterItems.length - 1] - ); + return nodeGroupingField === groupFilterItems[groupFilterItems.length-1]; } + return nodeGroupingField === groupFilterItems[0] } return false; @@ -184,7 +177,6 @@ export const filterRowTreeFromGroupingColumns = ( const row = apiRef.current.getRow(node.id); isRowMatchingFilters(row, shouldApplyItem, filterResults); - // const allResults = [...ancestorsResults, filterResults]; isPassingFiltering = passFilterLogic( [filterResults.passingFilterItems], [filterResults.passingQuickFilterValues], @@ -199,7 +191,7 @@ export const filterRowTreeFromGroupingColumns = ( let filteredChildrenCount = 0; let filteredDescendantCount = 0; - if (shouldFilterParent) { + if (shouldFilterParent && node.type !=='footer') { isPassingFiltering = isParentPassingFilter; allowGroupToFilter = shouldFilterParent; } diff --git a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index 6a0979cff342..b7bbe0301e42 100644 --- a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -2406,8 +2406,8 @@ describe(' - Row grouping', () => { />, ); - // Corresponds to rows id 0, 1, 2 because of Cat A, ann id 4 because of Cat 1 - expect(getColumnValues(1)).to.deep.equal(['', '0', '1', '2', '', '4']); + // Corresponds to rows id 0 because of Cat A, ann id 4 because of Cat 1 + expect(getColumnValues(1)).to.deep.equal(['', '0', '', '4']); }); it('should keep the correct count of the children and descendants in the filter state', () => { From ae6ba642ab684e1bf786dd383eaaf5c162129359 Mon Sep 17 00:00:00 2001 From: Rajat Date: Sat, 28 Dec 2024 22:45:36 +0530 Subject: [PATCH 4/5] prettier --- .../features/rowGrouping/gridRowGroupingUtils.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 270cd806c38f..5672ca27a77c 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -127,10 +127,10 @@ export const filterRowTreeFromGroupingColumns = ( const shouldFilterGroup = (node: GridTreeNode): boolean => { if (node.type === 'group' && node.isAutoGenerated) { const groupingCriteria = getGroupingCriteria(); - if(!groupingCriteria) { - return false + if (!groupingCriteria) { + return false; } - const groupingField = getRowGroupingFieldFromGroupingCriteria(groupingCriteria) + const groupingField = getRowGroupingFieldFromGroupingCriteria(groupingCriteria); const groupFilterItems: string[] = [groupingField]; const leafFilterItems: string[] = []; filterModel.items.forEach((item) => { @@ -145,12 +145,12 @@ export const filterRowTreeFromGroupingColumns = ( return false; } - const nodeGroupingField = getRowGroupingFieldFromGroupingCriteria(node.groupingField); + const nodeGroupingField = getRowGroupingFieldFromGroupingCriteria(node.groupingField); if (rowGroupingColumnMode === 'multiple') { - return nodeGroupingField === groupFilterItems[groupFilterItems.length-1]; + return nodeGroupingField === groupFilterItems[groupFilterItems.length - 1]; } - return nodeGroupingField === groupFilterItems[0] + return nodeGroupingField === groupFilterItems[0]; } return false; @@ -191,7 +191,7 @@ export const filterRowTreeFromGroupingColumns = ( let filteredChildrenCount = 0; let filteredDescendantCount = 0; - if (shouldFilterParent && node.type !=='footer') { + if (shouldFilterParent && node.type !== 'footer') { isPassingFiltering = isParentPassingFilter; allowGroupToFilter = shouldFilterParent; } From a55ab953054158fc078e2f69fbe70c893b1775e3 Mon Sep 17 00:00:00 2001 From: Rajat Date: Sun, 29 Dec 2024 00:24:03 +0530 Subject: [PATCH 5/5] pass filterResults in child node --- .../hooks/features/rowGrouping/gridRowGroupingUtils.ts | 9 ++++++--- .../src/tests/rowGrouping.DataGridPremium.test.tsx | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 5672ca27a77c..38d201c989d7 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -161,6 +161,7 @@ export const filterRowTreeFromGroupingColumns = ( areAncestorsExpanded: boolean, isParentPassingFilter: boolean, shouldFilterParent: boolean, + ancestorsResults: GridAggregatedFilterItemApplierResult[], ): number => { const filterResults: GridAggregatedFilterItemApplierResult = { passingFilterItems: null, @@ -177,9 +178,10 @@ export const filterRowTreeFromGroupingColumns = ( const row = apiRef.current.getRow(node.id); isRowMatchingFilters(row, shouldApplyItem, filterResults); + const allResults = [...ancestorsResults, filterResults]; isPassingFiltering = passFilterLogic( - [filterResults.passingFilterItems], - [filterResults.passingQuickFilterValues], + allResults.map((result) => result.passingFilterItems), + allResults.map((result) => result.passingQuickFilterValues), filterModel, params.apiRef, filterCache, @@ -203,6 +205,7 @@ export const filterRowTreeFromGroupingColumns = ( areAncestorsExpanded && !!node.childrenExpanded, isPassingFiltering, allowGroupToFilter, + [...ancestorsResults, filterResults], ); filteredDescendantCount += childSubTreeSize; if (childSubTreeSize > 0) { @@ -235,7 +238,7 @@ export const filterRowTreeFromGroupingColumns = ( for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; if (node.depth === 0) { - filterTreeNode(node, true, true, false); + filterTreeNode(node, true, true, false, []); } } diff --git a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index b7bbe0301e42..0ff9f89787fd 100644 --- a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -2407,7 +2407,7 @@ describe(' - Row grouping', () => { ); // Corresponds to rows id 0 because of Cat A, ann id 4 because of Cat 1 - expect(getColumnValues(1)).to.deep.equal(['', '0', '', '4']); + expect(getColumnValues(1)).to.deep.equal(['', '0', '1', '2', '', '4']); }); it('should keep the correct count of the children and descendants in the filter state', () => {