From 6a06afa8404431728816d970ed1acde63b1434c4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 21 Nov 2025 16:27:23 +0000
Subject: [PATCH 1/3] Initial plan
From c1e8b49bd1571cae3fc5ff2a99b58b643a265ecc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 21 Nov 2025 16:49:26 +0000
Subject: [PATCH 2/3] Convert FilteringLogic enum to string union type with
backwards compatibility
Co-authored-by: damyanpetev <3198469+damyanpetev@users.noreply.github.com>
---
.../filtering-expression.interface.ts | 15 ++++++---
.../base/grid-filtering-row.component.html | 4 +--
.../base/grid-filtering-row.component.ts | 17 +++++++++-
...excel-style-date-expression.component.html | 4 +--
...el-style-default-expression.component.html | 4 +--
...xcel-style-default-expression.component.ts | 17 ++++++++--
.../excel-style-filtering.component.ts | 4 +--
.../src/filtering/grid-filtering.service.ts | 2 +-
.../grids/core/src/state-base.directive.ts | 31 ++++++++++++++++++-
.../query-builder-tree.component.ts | 13 ++++----
10 files changed, 87 insertions(+), 24 deletions(-)
diff --git a/projects/igniteui-angular/core/src/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/core/src/data-operations/filtering-expression.interface.ts
index 78076df46b3..2720b2a424e 100644
--- a/projects/igniteui-angular/core/src/data-operations/filtering-expression.interface.ts
+++ b/projects/igniteui-angular/core/src/data-operations/filtering-expression.interface.ts
@@ -1,11 +1,16 @@
import { IFilteringOperation } from './filtering-condition';
import { IExpressionTree } from './filtering-expressions-tree';
-/* mustCoerceToInt */
-export enum FilteringLogic {
- And,
- Or
-}
+/**
+ * Enumeration representing different filtering logic operators.
+ * - And: Logical AND operator - all conditions must be met.
+ * - Or: Logical OR operator - at least one condition must be met.
+ */
+export const FilteringLogic = {
+ And: 'and',
+ Or: 'or'
+} as const;
+export type FilteringLogic = (typeof FilteringLogic)[keyof typeof FilteringLogic];
/* marshalByValue */
/**
diff --git a/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.html b/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.html
index ce397fa1313..a5d0998ea78 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.html
+++ b/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.html
@@ -235,8 +235,8 @@
{{filteringService.getOperatorAsString(item.afterOperator)}}
- {{filteringService.grid.resourceStrings.igx_grid_filter_operator_and}}
- {{filteringService.grid.resourceStrings.igx_grid_filter_operator_or}}
+ {{filteringService.grid.resourceStrings.igx_grid_filter_operator_and}}
+ {{filteringService.grid.resourceStrings.igx_grid_filter_operator_or}}
}
diff --git a/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.ts b/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.ts
index 13e234e0f0a..4abb40bba67 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.ts
+++ b/projects/igniteui-angular/grids/core/src/filtering/base/grid-filtering-row.component.ts
@@ -662,7 +662,9 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
*/
public onLogicOperatorChanged(eventArgs: ISelectionEventArgs, expression: ExpressionUI) {
if (eventArgs.oldSelection) {
- expression.afterOperator = (eventArgs.newSelection as IgxDropDownItemComponent).value;
+ const value = (eventArgs.newSelection as IgxDropDownItemComponent).value;
+ // Convert numeric values to FilteringLogic constants for backwards compatibility
+ expression.afterOperator = value === 0 ? FilteringLogic.And : (value === 1 ? FilteringLogic.Or : value);
this.expressionsList[this.expressionsList.indexOf(expression) + 1].beforeOperator = expression.afterOperator;
// update grid's filtering on the next cycle to ensure the drop-down is closed
@@ -671,6 +673,19 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy {
}
}
+ /**
+ * Check if the given button index matches the expression's afterOperator.
+ * Handles backwards compatibility with old numeric enum values (0, 1).
+ */
+ public isOperatorSelected(expression: ExpressionUI, buttonIndex: number): boolean {
+ const operator = expression?.afterOperator;
+ if (buttonIndex === 0) {
+ return operator === FilteringLogic.And || (operator as any) === 0;
+ } else {
+ return operator === FilteringLogic.Or || (operator as any) === 1;
+ }
+ }
+
/**
* Scrolls the chips into the chip area when left or right arrows are pressed.
*/
diff --git a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-date-expression.component.html b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-date-expression.component.html
index 2f08599dcda..e85023c5f02 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-date-expression.component.html
+++ b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-date-expression.component.html
@@ -81,7 +81,7 @@
#andButton
(keydown)="onLogicOperatorKeyDown($event, 0)"
tabindex="0"
- [selected]="expressionUI.afterOperator === 0"
+ [selected]="isOperatorSelected(0)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 0)">
{{ grid.resourceStrings.igx_grid_filter_operator_and }}
@@ -90,7 +90,7 @@
#orButton
tabindex="0"
(keydown)="onLogicOperatorKeyDown($event, 1)"
- [selected]="expressionUI.afterOperator === 1"
+ [selected]="isOperatorSelected(1)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 1)">
{{ grid.resourceStrings.igx_grid_filter_operator_or }}
diff --git a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.html b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.html
index bd551204afb..dffca062057 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.html
+++ b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.html
@@ -47,7 +47,7 @@
tabindex="0"
#andButton
(keydown)="onLogicOperatorKeyDown($event, 0)"
- [selected]="expressionUI.afterOperator === 0"
+ [selected]="isOperatorSelected(0)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 0)">
{{ grid.resourceStrings.igx_grid_filter_operator_and }}
@@ -56,7 +56,7 @@
tabindex="0"
#orButton
(keydown)="onLogicOperatorKeyDown($event, 1)"
- [selected]="expressionUI.afterOperator === 1"
+ [selected]="isOperatorSelected(1)"
type="button"
(click)="onLogicOperatorButtonClicked($event, 1)">
{{ grid.resourceStrings.igx_grid_filter_operator_or }}
diff --git a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.ts b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.ts
index 7ef21aab955..1a019020baa 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.ts
+++ b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-default-expression.component.ts
@@ -163,7 +163,7 @@ export class IgxExcelStyleDefaultExpressionComponent implements AfterViewInit {
} else {
this.logicOperatorChanged.emit({
target: this.expressionUI,
- newValue: buttonIndex as FilteringLogic
+ newValue: buttonIndex === 0 ? FilteringLogic.And : FilteringLogic.Or
});
}
}
@@ -173,11 +173,24 @@ export class IgxExcelStyleDefaultExpressionComponent implements AfterViewInit {
this.logicOperatorButtonGroup.selectButton(buttonIndex);
this.logicOperatorChanged.emit({
target: this.expressionUI,
- newValue: buttonIndex as FilteringLogic
+ newValue: buttonIndex === 0 ? FilteringLogic.And : FilteringLogic.Or
});
}
}
+ /**
+ * Check if the given button index matches the expression's afterOperator.
+ * Handles backwards compatibility with old numeric enum values (0, 1).
+ */
+ public isOperatorSelected(buttonIndex: number): boolean {
+ const operator = this.expressionUI?.afterOperator;
+ if (buttonIndex === 0) {
+ return operator === FilteringLogic.And || (operator as any) === 0;
+ } else {
+ return operator === FilteringLogic.Or || (operator as any) === 1;
+ }
+ }
+
public onRemoveButtonClick() {
this.expressionRemoved.emit(this.expressionUI);
}
diff --git a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-filtering.component.ts b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-filtering.component.ts
index 4812a3fcaf8..4b821d6a989 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-filtering.component.ts
+++ b/projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-filtering.component.ts
@@ -35,7 +35,7 @@ import { IgxExcelStylePinningComponent } from './excel-style-pinning.component';
import { IgxExcelStyleMovingComponent } from './excel-style-moving.component';
import { IgxExcelStyleSortingComponent } from './excel-style-sorting.component';
import { IgxExcelStyleHeaderComponent } from './excel-style-header.component';
-import { ColumnType, FilteringExpressionsTree, GridColumnDataType, GridTypeBase, IFilteringExpressionsTree, IgxFilterItem, IgxOverlayService, isTree, PlatformUtil, SortingDirection } from 'igniteui-angular/core';
+import { ColumnType, FilteringExpressionsTree, FilteringLogic, GridColumnDataType, GridTypeBase, IFilteringExpressionsTree, IgxFilterItem, IgxOverlayService, isTree, PlatformUtil, SortingDirection } from 'igniteui-angular/core';
@Directive({
selector: 'igx-excel-style-column-operations,[igxExcelStyleColumnOperations]',
@@ -461,7 +461,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent
}
const selectableExpressionsCount = this.expressionsList.filter(exp =>
- (exp.beforeOperator === 1 || exp.afterOperator === 1) &&
+ (exp.beforeOperator === FilteringLogic.Or || exp.afterOperator === FilteringLogic.Or) &&
(exp.expression.condition.name === 'equals' ||
exp.expression.condition.name === 'at' ||
exp.expression.condition.name === 'true' ||
diff --git a/projects/igniteui-angular/grids/core/src/filtering/grid-filtering.service.ts b/projects/igniteui-angular/grids/core/src/filtering/grid-filtering.service.ts
index a34d528c139..6f9babba120 100644
--- a/projects/igniteui-angular/grids/core/src/filtering/grid-filtering.service.ts
+++ b/projects/igniteui-angular/grids/core/src/filtering/grid-filtering.service.ts
@@ -447,7 +447,7 @@ export class IgxFilteringService implements OnDestroy {
* Returns the string representation of the FilteringLogic operator.
*/
public getOperatorAsString(operator: FilteringLogic): any {
- if (operator === 0) {
+ if (operator === FilteringLogic.And) {
return this.grid.resourceStrings.igx_grid_filter_operator_and;
} else {
return this.grid.resourceStrings.igx_grid_filter_operator_or;
diff --git a/projects/igniteui-angular/grids/core/src/state-base.directive.ts b/projects/igniteui-angular/grids/core/src/state-base.directive.ts
index 0cba4f5798d..b39b1331369 100644
--- a/projects/igniteui-angular/grids/core/src/state-base.directive.ts
+++ b/projects/igniteui-angular/grids/core/src/state-base.directive.ts
@@ -3,7 +3,7 @@ import { IgxColumnComponent } from './columns/column.component';
import { IgxColumnGroupComponent } from './columns/column-group.component';
import { GridSelectionRange } from './common/types';
import { GridType, IGX_GRID_BASE, IPinningConfig, PivotGridType } from './common/grid.interface';
-import { cloneArray, cloneValue, ColumnType, FieldType, GridColumnDataType, IExpressionTree, IFilteringExpressionsTree, IGroupByExpandState, IGroupingExpression, IGroupingState, IPagingState, ISortingExpression, recreateTreeFromFields } from 'igniteui-angular/core';
+import { cloneArray, cloneValue, ColumnType, FieldType, FilteringLogic, GridColumnDataType, IExpressionTree, IFilteringExpressionsTree, IGroupByExpandState, IGroupingExpression, IGroupingState, IPagingState, ISortingExpression, recreateTreeFromFields } from 'igniteui-angular/core';
import { IgxColumnLayoutComponent } from './columns/column-layout.component';
import { IPivotConfiguration, IPivotDimension } from './pivot-grid.interface';
import { PivotUtil } from './pivot-util';
@@ -670,6 +670,9 @@ export class IgxGridStateBaseDirective {
return null;
}
+ // Normalize operator to handle both old enum values (0, 1) and new string values ('and', 'or')
+ this.normalizeOperators(exprTreeObject);
+
if (this.currGrid.type === 'pivot') {
return recreateTreeFromFields(exprTreeObject, this.currGrid.allDimensions.map(d => ({ dataType: d.dataType, field: d.memberName })) as FieldType[]) as IExpressionTree;
}
@@ -677,6 +680,32 @@ export class IgxGridStateBaseDirective {
return recreateTreeFromFields(exprTreeObject, this.currGrid.columns) as IExpressionTree;
}
+ /**
+ * Recursively normalize FilteringLogic operators to handle backwards compatibility
+ * with old numeric enum values (0, 1) and new string values ('and', 'or')
+ */
+ private normalizeOperators(tree: IExpressionTree): void {
+ if (!tree) {
+ return;
+ }
+
+ // Normalize the operator at this level - convert old numeric values to new string values
+ if ((tree.operator as any) === 0) {
+ tree.operator = FilteringLogic.And;
+ } else if ((tree.operator as any) === 1) {
+ tree.operator = FilteringLogic.Or;
+ }
+
+ // Recursively normalize child trees
+ if (tree.filteringOperands) {
+ for (const operand of tree.filteringOperands) {
+ if (operand && 'operator' in operand && 'filteringOperands' in operand) {
+ this.normalizeOperators(operand as IExpressionTree);
+ }
+ }
+ }
+ }
+
protected stringifyCallback(key: string, val: any) {
if (key === 'searchVal' && val instanceof Set) {
return Array.from(val);
diff --git a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts
index feab3103635..99753b7c439 100644
--- a/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts
+++ b/projects/igniteui-angular/query-builder/src/query-builder/query-builder-tree.component.ts
@@ -420,7 +420,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
/**
* @hidden @internal
*/
- public initialOperator = 0;
+ public initialOperator: FilteringLogic = FilteringLogic.And;
/**
* @hidden @internal
@@ -1313,7 +1313,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
event.cancel = true;
if (event.newSelection.value === 'switchCondition') {
- const newOperator = (!this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator) === 0 ? 1 : 0;
+ const currentOperator = !this.expressionTree ? this.initialOperator : (this.contextualGroup ?? this._expressionTree).operator;
+ const newOperator = currentOperator === FilteringLogic.And ? FilteringLogic.Or : FilteringLogic.And;
this.selectFilteringLogic(newOperator);
} else if (event.newSelection.value === 'ungroup') {
this.ungroup();
@@ -1342,17 +1343,17 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
/**
* @hidden @internal
*/
- public selectFilteringLogic(index: number) {
+ public selectFilteringLogic(operator: FilteringLogic) {
if (!this.expressionTree) {
- this.initialOperator = index;
+ this.initialOperator = operator;
return;
}
if (this.contextualGroup) {
- this.contextualGroup.operator = index as FilteringLogic;
+ this.contextualGroup.operator = operator;
this.commitOperandEdit();
} else if (this.expressionTree) {
- this._expressionTree.operator = index as FilteringLogic;
+ this._expressionTree.operator = operator;
}
this.initialOperator = null;
From dca92f49ab3b4a56a08bc1790e56161491123e48 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 21 Nov 2025 16:53:17 +0000
Subject: [PATCH 3/3] Update test files to use FilteringLogic constants instead
of numeric values
Co-authored-by: damyanpetev <3198469+damyanpetev@users.noreply.github.com>
---
.../grids/grid/src/grid-filtering-advanced.spec.ts | 12 ++++++------
.../igniteui-angular/test-utils/grid-samples.spec.ts | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/projects/igniteui-angular/grids/grid/src/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/grids/grid/src/grid-filtering-advanced.spec.ts
index 8a842c94980..61486183e0b 100644
--- a/projects/igniteui-angular/grids/grid/src/grid-filtering-advanced.spec.ts
+++ b/projects/igniteui-angular/grids/grid/src/grid-filtering-advanced.spec.ts
@@ -1687,7 +1687,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
// Spy for error messages in the console
const consoleSpy = spyOn(console, 'error');
// Apply advanced filter through API.
- const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
+ const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
@@ -1695,7 +1695,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
searchVal: '39'
});
- const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
+ const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
@@ -1719,7 +1719,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
// Spy for error messages in the console
const consoleSpy = spyOn(console, 'error');
- const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
+ const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
@@ -1727,7 +1727,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
searchVal: '39'
});
- const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
+ const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
@@ -1820,7 +1820,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
tick(200);
fix.detectChanges();
- const innerTree = new FilteringExpressionsTree(0, undefined, 'childData', ['ID']);
+ const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'childData', ['ID']);
innerTree.filteringOperands.push({
fieldName: 'ID',
ignoreCase: false,
@@ -1828,7 +1828,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
searchVal: '39'
});
- const tree = new FilteringExpressionsTree(0, undefined, 'rootData', ['ID']);
+ const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'rootData', ['ID']);
tree.filteringOperands.push({
fieldName: 'ID',
conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
diff --git a/projects/igniteui-angular/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/test-utils/grid-samples.spec.ts
index 05685a5cd69..864e06fc99e 100644
--- a/projects/igniteui-angular/test-utils/grid-samples.spec.ts
+++ b/projects/igniteui-angular/test-utils/grid-samples.spec.ts
@@ -2133,7 +2133,7 @@ export class IgxGridAdvancedFilteringSerializedTreeComponent extends BasicGridCo
"searchVal": 200
}
],
- "operator": 0
+ "operator": "and"
}`);
this.filterTreeObject = {
@@ -2151,7 +2151,7 @@ export class IgxGridAdvancedFilteringSerializedTreeComponent extends BasicGridCo
"searchTree": null
}
],
- "operator": 1,
+ "operator": "or",
"returnFields": [
"ID",
"ProductName"