From 9d8119dcd8c435222626de633b7927628b194533 Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Wed, 24 Jul 2024 16:37:35 +0300 Subject: [PATCH 1/2] fix(advance-filtering): handle error when columns are dynamically changed --- .../grid/grid-filtering-advanced.spec.ts | 85 ++++++++++++++++++- .../query-builder/query-builder.component.ts | 38 +++++---- .../src/lib/test-utils/grid-samples.spec.ts | 31 +++++++ 3 files changed, 134 insertions(+), 20 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index ca69837bb04..50792ec137c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -15,13 +15,15 @@ import { IgxGridAdvancedFilteringComponent, IgxGridExternalAdvancedFilteringComponent, IgxGridAdvancedFilteringBindingComponent, - IgxGridAdvancedFilteringOverlaySettingsComponent + IgxGridAdvancedFilteringOverlaySettingsComponent, + IgxGridAdvancedFilteringDynamicColumnsComponent } from '../../test-utils/grid-samples.spec'; import { ControlsFunction } from '../../test-utils/controls-functions.spec'; import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy'; import { IgxHierGridExternalAdvancedFilteringComponent } from '../../test-utils/hierarchical-grid-components.spec'; import { IgxHierarchicalGridComponent } from '../hierarchical-grid/public_api'; import { IFilteringEventArgs } from '../public_api'; +import { SampleTestData } from '../../test-utils/sample-test-data.spec'; const ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS = 'igx-filter-tree__line--and'; const ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS = 'igx-filter-tree__line--or'; @@ -38,7 +40,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { IgxGridAdvancedFilteringComponent, IgxGridExternalAdvancedFilteringComponent, IgxGridAdvancedFilteringBindingComponent, - IgxHierGridExternalAdvancedFilteringComponent + IgxHierGridExternalAdvancedFilteringComponent, + IgxGridAdvancedFilteringDynamicColumnsComponent ] }); })); @@ -2048,6 +2051,84 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(rows.length).toEqual(1, 'Wrong filtered rows count'); })); + it('should handle advanced filtering correctly when grid columns and data are dynamically changed', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxGridAdvancedFilteringDynamicColumnsComponent); + grid = fixture.componentInstance.grid; + fixture.detectChanges(); + + grid.height = '800px'; + fixture.detectChanges(); + tick(50); + + expect(grid.filteredData).toBeNull(); + expect(grid.rowList.length).toBe(8); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + + // Open Advanced Filtering dialog + GridFunctions.clickAdvancedFilteringButton(fixture); + fixture.detectChanges(); + + // Click the initial 'Add And Group' button + GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fixture, 0); + tick(100); + fixture.detectChanges(); + + // Populate edit inputs + selectColumnInEditModeExpression(fixture, 1); + selectOperatorInEditModeExpression(fixture, 2); + const input = GridFunctions.getAdvancedFilteringValueInput(fixture).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'ign', fixture); + + // Commit the populated expression + GridFunctions.clickAdvancedFilteringExpressionCommitButton(fixture); + fixture.detectChanges(); + + // Apply the filters + GridFunctions.clickAdvancedFilteringApplyButton(fixture); + fixture.detectChanges(); + + // Verify the filter results + expect(grid.filteredData.length).toEqual(2); + expect(grid.rowList.length).toBe(2); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); + + // Change the grid's columns collection + fixture.componentInstance.columns = [ + { field: 'ID', header: 'ID', width: '200px', type: 'string' }, + { field: 'CompanyName', header: 'Company Name', width: '200px', type: 'string'}, + { field: 'ContactName', header: 'Contact Name', width: '200px', type: 'string' }, + { field: 'ContactTitle', header: 'Contact Title', width: '200px', type: 'string' }, + { field: 'City', header: 'City', width: '200px', type: 'string' }, + { field: 'Country', header: 'Country', width: '200px', type: 'string' }, + ]; + fixture.detectChanges(); + flush(); + + // Change the grid's data collection + setTimeout(() => { + grid.data = SampleTestData.contactInfoDataFull(); + fixture.detectChanges(); + flush(); + }); + + // Spy for error messages in the console + const consoleSpy = spyOn(console, 'error'); + + // Open Advanced Filtering dialog + GridFunctions.clickAdvancedFilteringButton(fixture); + fixture.detectChanges(); + flush(); + + // Verify the filters are cleared + expect(grid.filteredData).toEqual([]); + expect(grid.rowList.length).toBe(0); + + // Check for error messages in the console + expect(consoleSpy).not.toHaveBeenCalled(); + })); + describe('Context Menu - ', () => { it('Should discard added group when clicking its operator line without having a single expression.', fakeAsync(() => { // Open Advanced Filtering dialog. diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index efbff961c69..0453a728d01 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -988,31 +988,33 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.currentGroup = groupItem; } - private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem { - let groupItem: ExpressionGroupItem; - if (expressionTree) { - groupItem = new ExpressionGroupItem(expressionTree.operator, parent); - - for (const expr of expressionTree.filteringOperands) { - if (expr instanceof FilteringExpressionsTree) { - groupItem.children.push(this.createExpressionGroupItem(expr, groupItem)); - } else { - const filteringExpr = expr as IFilteringExpression; - const exprCopy: IFilteringExpression = { - fieldName: filteringExpr.fieldName, - condition: filteringExpr.condition, - searchVal: filteringExpr.searchVal, - ignoreCase: filteringExpr.ignoreCase - }; + private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem | null { + if (!expressionTree) { + return null; + } + + const groupItem = new ExpressionGroupItem(expressionTree.operator, parent); + + for (const expr of expressionTree.filteringOperands) { + if (expr instanceof FilteringExpressionsTree) { + const childGroup = this.createExpressionGroupItem(expr, groupItem); + if (childGroup) { + groupItem.children.push(childGroup); + } + } else { + const filteringExpr = expr as IFilteringExpression; + const field = this.fields.find(el => el.field === filteringExpr.fieldName); + + if (field) { + const exprCopy: IFilteringExpression = { ...filteringExpr }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); - const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); } } } - return groupItem; + return groupItem.children.length > 0 ? groupItem : null; } private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem): FilteringExpressionsTree { diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts index 32d20e821eb..f435bd2df80 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts @@ -1174,6 +1174,37 @@ export class IgxGridAdvancedFilteringComponent extends BasicGridComponent { } } +@Component({ + template: ` + + + + `, + standalone: true, + imports: [NgFor, IgxGridComponent, IgxColumnComponent, IgxGridToolbarComponent] +}) +export class IgxGridAdvancedFilteringDynamicColumnsComponent extends BasicGridComponent { + public override data = []; + public columns = []; + + public ngOnInit(): void { + this.columns = [ + { field: 'ID', header: 'HeaderID', width: '100px', type: 'number' }, + { field: 'ProductName', header: 'Product Name', width: '200px', type: 'string'}, + { field: 'Downloads', header: 'Downloads', width: '100px', type: 'number' }, + { field: 'Released', header: 'Released', width: '100px', type: 'boolean' }, + { field: 'ReleaseDate', header: 'Release Date', width: '200px', type: 'date' }, + { field: 'AnotherField', header: 'Another Field', width: '200px', type: 'string' }, + ]; + this.data = SampleTestData.excelFilteringData(); + } +} + @Component({ template: ` From 19045a44d6d543bf4baf84f5ba4246e51eb22ddc Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Thu, 25 Jul 2024 11:59:01 +0300 Subject: [PATCH 2/2] test(advance-filtering): remove grid resize and setTimeout usage --- .../lib/grids/grid/grid-filtering-advanced.spec.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index 50792ec137c..0cdd651c53a 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -2056,10 +2056,6 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid = fixture.componentInstance.grid; fixture.detectChanges(); - grid.height = '800px'; - fixture.detectChanges(); - tick(50); - expect(grid.filteredData).toBeNull(); expect(grid.rowList.length).toBe(8); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); @@ -2107,11 +2103,9 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { flush(); // Change the grid's data collection - setTimeout(() => { - grid.data = SampleTestData.contactInfoDataFull(); - fixture.detectChanges(); - flush(); - }); + grid.data = SampleTestData.contactInfoDataFull(); + fixture.detectChanges(); + flush(); // Spy for error messages in the console const consoleSpy = spyOn(console, 'error');