From 37a33f6cad6b6a25622de33df2852342ef85d869 Mon Sep 17 00:00:00 2001 From: Marin Popov Date: Tue, 30 Sep 2025 10:40:27 +0300 Subject: [PATCH 1/9] fix(button-group): remove unnecessary bootstrap-specific styles (#16259) - We should not remove a border or any schema-connected style from the theme. Rather, we should update the variable in the schema, which in this case is already done. --- .../styles/components/button-group/_button-group-theme.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss index 38959c2b0af..88a1fa99381 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss @@ -65,11 +65,6 @@ &[igxButton] { border-radius: 0; border-color: var-get($theme, 'item-border-color'); - - @if $bootstrap-theme { - margin: 0 !important; - border: none; - } } igx-icon { From 315a224d0f2a78563514593218d17e274296b3fb Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Tue, 30 Sep 2025 11:04:23 +0300 Subject: [PATCH 2/9] Revert "fix(button-group): remove unnecessary bootstrap-specific styles (#16259)" (#16261) This reverts commit 37a33f6cad6b6a25622de33df2852342ef85d869. --- .../styles/components/button-group/_button-group-theme.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss index 88a1fa99381..38959c2b0af 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/button-group/_button-group-theme.scss @@ -65,6 +65,11 @@ &[igxButton] { border-radius: 0; border-color: var-get($theme, 'item-border-color'); + + @if $bootstrap-theme { + margin: 0 !important; + border: none; + } } igx-icon { From dfc4e333e6cfcd22c014c33d09e75083dd2df3d3 Mon Sep 17 00:00:00 2001 From: Radoslav Mirchev <52001020+radomirchev@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:46:06 +0300 Subject: [PATCH 3/9] Update ROADMAP 02 Oct 2025 --- ROADMAP.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 2987ab4a93f..a40d76bb5a2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,17 +2,21 @@ # Current Milestone -## Milestone 38, version 20.1 (Due by Sep, 2025) -1. Grid cell merging feature [#3514](https://github.com/IgniteUI/igniteui-angular/issues/3514) -2. “Clear Selection” button in Combo component is keyboard accessible [#15841](https://github.com/IgniteUI/igniteui-angular/issues/15841) -3. Selecting a slide by index in the Carousel component [#16046](https://github.com/IgniteUI/igniteui-angular/issues/16046) +## Milestone 40, version 20.1 (Due by Nov, 2025) +1. Support for Angular 21 +2. AI Chat UI component [#16094](https://github.com/IgniteUI/igniteui-angular/issues/16094) ## Going down the road -1. AI Chat UI component [#16094](https://github.com/IgniteUI/igniteui-angular/issues/16094) +1. Grids performance # Previous Milestone +## Milestone 39, version 20.1 (Released Sep 25th, 2025) +1. **[DONE]** Grid cell merging feature [#3514](https://github.com/IgniteUI/igniteui-angular/issues/3514) +2. **[DONE]** “Clear Selection” button in Combo component is keyboard accessible [#15841](https://github.com/IgniteUI/igniteui-angular/issues/15841) +3. **[DONE]** Selecting a slide by index in the Carousel component [#16046](https://github.com/IgniteUI/igniteui-angular/issues/16046) + ## Milestone 38, version 20.0 (Released Jun 09th, 2025) 1. **[DONE]** Support of Angular 20.0 From 7d649d95dd81ff1f386303e180b658574c4a5c18 Mon Sep 17 00:00:00 2001 From: Martin Dragnev <37667452+mddragnev@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:22:45 +0300 Subject: [PATCH 4/9] perf(filtering): Optimize filtering performance (#16208) --------- Co-authored-by: Radoslav Karaivanov --- .../src/lib/data-operations/data-util.ts | 13 ++- .../lib/data-operations/filtering-strategy.ts | 92 +++++++++++-------- .../excel-style/base-filtering.component.ts | 1 + .../excel-style-clear-filters.component.ts | 1 + .../excel-style-filtering.component.ts | 90 ++++++++++-------- .../excel-style-search.component.ts | 64 ++++++++----- .../grids/filtering/grid-filtering.service.ts | 8 +- .../src/lib/grids/grid-base.directive.ts | 1 + .../grids/pivot-grid/pivot-grid.component.ts | 2 + .../tree-grid/tree-grid.filtering.strategy.ts | 4 + 10 files changed, 174 insertions(+), 102 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/data-util.ts b/projects/igniteui-angular/src/lib/data-operations/data-util.ts index 8b00f88fcf3..4827928e04f 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.ts @@ -5,7 +5,7 @@ import { IPagingState, PagingError } from './paging-state.interface'; import { IGroupByKey } from './groupby-expand-state.interface'; import { IGroupByRecord } from './groupby-record.interface'; import { IGroupingState } from './groupby-state.interface'; -import { mergeObjects } from '../core/utils'; +import { cloneArray, mergeObjects } from '../core/utils'; import { Transaction, TransactionType, HierarchicalTransaction } from '../services/transaction/transaction'; import { getHierarchy, isHierarchyMatch } from './operations'; import { ColumnType, GridType } from '../grids/common/grid.interface'; @@ -21,6 +21,8 @@ import { import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy'; import { IGroupingExpression } from './grouping-expression.interface'; import { DefaultMergeStrategy, IGridMergeStrategy } from './merge-strategy'; +import { IFilteringExpressionsTree } from './filtering-expressions-tree'; +import { FilteringStrategy, FilterUtil } from './filtering-strategy'; /** * @hidden @@ -278,6 +280,15 @@ export class DataUtil { return value; } + public static filterDataByExpressions(data: any[], expressionsTree: IFilteringExpressionsTree, grid: GridType): any { + if (expressionsTree.filteringOperands.length) { + const state = { expressionsTree, strategy: FilteringStrategy.instance() }; + data = FilterUtil.filter(cloneArray(data), state, grid); + } + + return data; + } + private static findParentFromPath(data: any[], primaryKey: any, childDataKey: any, path: any[]): any { let collection: any[] = data; let result: any; diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts index 6a5f0c932fa..c21291b4b3b 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -2,12 +2,13 @@ import { FilteringLogic, type IFilteringExpression } from './filtering-expressio import { FilteringExpressionsTree, type IFilteringExpressionsTree } from './filtering-expressions-tree'; import { resolveNestedPath, parseDate, formatDate, formatCurrency, columnFieldPath } from '../core/utils'; import type { ColumnType, EntityType, GridType } from '../grids/common/grid.interface'; -import { GridColumnDataType } from './data-util'; +import { DataUtil, GridColumnDataType } from './data-util'; import { SortingDirection } from './sorting-strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import type { IFilteringState } from './filtering-state.interface'; import { isTree } from './expressions-tree-util'; import type { IgxHierarchicalGridComponent } from '../grids/hierarchical-grid/hierarchical-grid.component'; +import { IgxSorting } from '../grids/common/strategy'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -132,27 +133,35 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { const applyFormatter = column.formatter && this.shouldFormatFilterValues(column); - let data = column.grid.gridAPI.filterDataByExpressions(tree); - data = column.grid.gridAPI.sortDataByExpressions(data, - [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }]); - + const data = this.getFilteredData(column, tree); const pathParts = columnFieldPath(column.field) - let filterItems: IgxFilterItem[] = data.map(record => { - const value = applyFormatter ? - column.formatter(resolveNestedPath(record, pathParts), record) : - resolveNestedPath(record, pathParts); - - return { - value, - label: this.getFilterItemLabel(column, value, !applyFormatter, record) - }; - }); - filterItems = this.getUniqueFilterItems(column, filterItems); + const seenFormattedFilterItems = new Map() + + for (let i = 0; i < data.length; ++i) { + const record = data[i] + const rawValue = resolveNestedPath(record, pathParts); + const formattedValue = applyFormatter ? column.formatter(rawValue, record) : rawValue; + const { key, finalValue } = this.getFilterItemKeyValue(formattedValue, column); + // Deduplicate by normalized key + if (!seenFormattedFilterItems.has(key)) { + const label = this.getFilterItemLabel(column, finalValue, !applyFormatter, record); + seenFormattedFilterItems.set(key, { value: finalValue, label }); + } + } + + let filterItems: IgxFilterItem[] = Array.from(seenFormattedFilterItems.values()); + + filterItems = DataUtil.sort(filterItems, + [{ fieldName: 'value', dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }], new IgxSorting()) return Promise.resolve(filterItems); } + protected getFilteredData(column: ColumnType, tree: IFilteringExpressionsTree) { + return column.grid.gridAPI.filterDataByExpressions(tree); + } + protected getFilterItemLabel(column: ColumnType, value: any, applyFormatter: boolean, data: any) { if (column.formatter) { if (applyFormatter) { @@ -180,30 +189,33 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { } } - protected getUniqueFilterItems(column: ColumnType, filterItems: IgxFilterItem[]) { - const filteredUniqueValues = filterItems.reduce((map, item) => { - let key = item.value; - - if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { - key = key?.toString().toLowerCase(); - } else if (column.dataType === GridColumnDataType.DateTime) { - key = item.value?.toString(); - item.value = key ? new Date(key) : key; - } else if (column.dataType === GridColumnDataType.Time) { - const date = key ? new Date(key) : key; - key = date ? new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()) : key; - item.value = key ? new Date(key) : key; - } else if (column.dataType === GridColumnDataType.Date) { - const date = key ? new Date(key) : key; - key = date ? new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString() : key; - item.value = date; - } - - return map.has(key) ? map : map.set(key, item) - }, new Map()); - const uniqueValues = Array.from(filteredUniqueValues.values()); - - return uniqueValues; + protected getFilterItemKeyValue(value: any, column: ColumnType) { + let key: any = value; + let finalValue = value; + if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { + key = key?.toString().toLowerCase(); + } else if (column.dataType === GridColumnDataType.DateTime) { + key = value?.toString(); + finalValue = key ? new Date(key) : key; + } else if (column.dataType === GridColumnDataType.Time) { + const date = key ? new Date(key) : key; + key = date + ? new Date().setHours( + date.getHours(), + date.getMinutes(), + date.getSeconds(), + date.getMilliseconds() + ) + : date; + finalValue = key ? new Date(key) : key; + } else if (column.dataType === GridColumnDataType.Date) { + const date = key ? new Date(key) : key; + key = date + ? new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString() + : date; + finalValue = date; + } + return { key, finalValue }; } protected shouldFormatFilterValues(_column: ColumnType): boolean { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/base-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/base-filtering.component.ts index 73980ec8c8c..97a98e67e4d 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/base-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/base-filtering.component.ts @@ -23,6 +23,7 @@ export abstract class BaseFilteringComponent { public abstract columnChange: EventEmitter; public abstract sortingChanged: EventEmitter; public abstract listDataLoaded: EventEmitter; + public abstract filterCleared: EventEmitter; constructor( protected cdr: ChangeDetectorRef, diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-clear-filters.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-clear-filters.component.ts index 65449e4ad25..3342e5c8bd5 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-clear-filters.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-clear-filters.component.ts @@ -34,6 +34,7 @@ export class IgxExcelStyleClearFiltersComponent { */ public clearFilter() { this.esf.grid.filteringService.clearFilter(this.esf.column.field); + this.esf.filterCleared.emit(); this.selectAllFilterItems(); } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts index fc77989a402..856f4e7e3ac 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts @@ -128,6 +128,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent @Output() public listDataLoaded = new EventEmitter(); + /** + * @hidden @internal + */ + @Output() + public filterCleared = new EventEmitter(); + @ViewChild('mainDropdown', { read: ElementRef }) public mainDropdown: ElementRef; @@ -160,29 +166,13 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent */ @Input() public set column(value: ColumnType) { - this._column = value; - this.listData = new Array(); - this.columnChange.emit(this._column); - - this.subscriptions?.unsubscribe(); - - if (this._column) { - this.grid.filteringService.registerSVGIcons(); - this.init(); - this.sortingChanged.emit(); - - this.subscriptions = this.grid.columnPin.subscribe(() => { - requestAnimationFrame(() => { - if (!(this.cdr as ViewRef).destroyed) { - this.cdr.detectChanges(); - } - }); - }); - - this.subscriptions.add(this.grid.columnVisibilityChanged.subscribe(() => this.detectChanges())); - this.subscriptions.add(this.grid.sortingExpressionsChange.subscribe(() => this.sortingChanged.emit())); - this.subscriptions.add(this.grid.filteringExpressionsTreeChange.subscribe(() => this.init())); - this.subscriptions.add(this.grid.columnMovingEnd.subscribe(() => this.cdr.markForCheck())); + if (value) { + this._column = value; + this.columnChange.emit(this._column); + if (this.inline) { + // In case external filtering + this.populateData(); + } } } @@ -326,6 +316,16 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.inline = false; this.column = column; this.overlayService = overlayService; + + } + + /** + * @hidden @internal + */ + public populateData() { + if (this.column) { + this.afterColumnChange(); + } if (this._originalDisplay) { this.element.nativeElement.style.display = this._originalDisplay; } @@ -421,6 +421,34 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return this.computedStyles?.getPropertyValue('--component-size'); } + protected afterColumnChange() { + this.listData = new Array(); + this.subscriptions?.unsubscribe(); + + if (this._column) { + this.grid.filteringService.registerSVGIcons(); + this.init(); + this.sortingChanged.emit(); + + this.subscriptions = this.grid.columnPin.subscribe(() => { + requestAnimationFrame(() => { + if (!(this.cdr as ViewRef).destroyed) { + this.cdr.detectChanges(); + } + }); + }); + + this.subscriptions.add(this.grid.columnVisibilityChanged.subscribe(() => this.detectChanges())); + this.subscriptions.add(this.grid.sortingExpressionsChange.subscribe(() => this.sortingChanged.emit())); + this.subscriptions.add(this.grid.filteringExpressionsTreeChange.subscribe(() => { + this.expressionsList = new Array(); + generateExpressionsList(this.column.filteringExpressionsTree, this.grid.filteringLogic, this.expressionsList); + this.cdr.detectChanges(); + })); + this.subscriptions.add(this.grid.columnMovingEnd.subscribe(() => this.cdr.markForCheck())); + } + } + private init() { this.expressionsList = new Array(); generateExpressionsList(this.column.filteringExpressionsTree, this.grid.filteringLogic, this.expressionsList); @@ -510,12 +538,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private renderValues() { this.filterValues = this.generateFilterValues(); this.generateListData(); - this.expressionsList.forEach(expr => { - if (this.column.dataType === GridColumnDataType.String && this.column.filteringIgnoreCase - && expr.expression.searchVal && expr.expression.searchVal instanceof Set) { - this.modifyExpression(expr); - } - }); } private generateFilterValues() { @@ -546,16 +568,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return filterValues; } - private modifyExpression(expr: ExpressionUI) { - const lowerCaseFilterValues = new Set(Array.from(expr.expression.searchVal).map((value: string) => value.toLowerCase())); - - this.grid.data.forEach(item => { - if (typeof item[this.column.field] === "string" && lowerCaseFilterValues.has(item[this.column.field]?.toLowerCase())) { - expr.expression.searchVal.add(item[this.column.field]); - } - }); - } - private generateListData() { this.listData = new Array(); const shouldUpdateSelection = this.areExpressionsSelectable(); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts index d366da1a43c..4e8dd6d47a3 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts @@ -76,7 +76,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { @ViewChild('input', { read: IgxInputDirective, static: true }) public searchInput: IgxInputDirective; - @ViewChild('cancelButton', {read: IgxButtonDirective, static: true }) + @ViewChild('cancelButton', { read: IgxButtonDirective, static: true }) protected cancelButton: IgxButtonDirective; /** @@ -203,7 +203,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { protected activeDescendant = ''; private _id = `igx-excel-style-search-${NEXT_ID++}`; - private _isLoading; + private _isLoading = true; private _addToCurrentFilterItem: FilterListItem; private _selectAllItem: FilterListItem; private _hierarchicalSelectedItems: FilterListItem[]; @@ -251,6 +251,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { this.searchInput.nativeElement.focus(); }); }); + + esf.filterCleared.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.clearInput(); + }); } public ngAfterViewInit() { @@ -396,7 +400,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return `${this.id}-item-${index}`; } - protected setActiveDescendant() : void { + protected setActiveDescendant(): void { this.activeDescendant = this.focusedItem?.id || ''; } @@ -585,20 +589,38 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { blanksItem = selectedItems[blanksItemIndex]; selectedItems.splice(blanksItemIndex, 1); } + let searchVal; + switch (this.esf.column.dataType) { + case GridColumnDataType.Date: + searchVal = new Set(selectedItems.map(d => d.value.toDateString())); + break; + case GridColumnDataType.DateTime: + searchVal = new Set(selectedItems.map(d => d.value.toISOString())); + break; + case GridColumnDataType.Time: + searchVal = new Set(selectedItems.map(e => e.value.toLocaleTimeString())); + break; + case GridColumnDataType.String: + if (this.esf.column.filteringIgnoreCase) { + const selectedValues = new Set(selectedItems.map(item => item.value.toLowerCase())); + searchVal = new Set(); + + this.esf.grid.data.forEach(item => { + if (typeof item[this.esf.column.field] === "string" && selectedValues.has(item[this.esf.column.field]?.toLowerCase())) { + searchVal.add(item[this.esf.column.field]); + } + }); + break; + } + default: + searchVal = new Set(selectedItems.map(e => e.value)) + } filterTree.filteringOperands.push({ condition: this.createCondition('in'), conditionName: 'in', fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, - searchVal: new Set( - this.esf.column.dataType === GridColumnDataType.Date ? - selectedItems.map(d => d.value.toDateString()) : - this.esf.column.dataType === GridColumnDataType.DateTime ? - selectedItems.map(d => d.value.toISOString()) : - this.esf.column.dataType === GridColumnDataType.Time ? - selectedItems.map(e => e.value.toLocaleTimeString()) : - selectedItems.map(e => e.value) - ) + searchVal }); if (blanksItem) { @@ -628,12 +650,12 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { if (event) { const key = event.key.toLowerCase(); const navKeys = ['space', 'spacebar', ' ', - 'arrowup', 'up', 'arrowdown', 'down', 'home', 'end']; - if (navKeys.indexOf(key) === -1) { // If key has appropriate function in DD - return; - } - event.preventDefault(); - event.stopPropagation(); + 'arrowup', 'up', 'arrowdown', 'down', 'home', 'end']; + if (navKeys.indexOf(key) === -1) { // If key has appropriate function in DD + return; + } + event.preventDefault(); + event.stopPropagation(); switch (key) { case 'arrowup': case 'up': @@ -827,9 +849,9 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { const direction = index > (this.focusedItem ? this.focusedItem.index : -1) ? Navigate.Down : Navigate.Up; const scrollRequired = this.isIndexOutOfBounds(index, direction); this.focusedItem = { - id: this.getItemId(index), - index: index, - checked: this.virtDir.igxForOf[index].isSelected + id: this.getItemId(index), + index: index, + checked: this.virtDir.igxForOf[index].isSelected }; if (scrollRequired) { this.virtDir.scrollTo(index); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts index 2d86933cb6c..c2f42863f1d 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts @@ -9,7 +9,7 @@ import { takeUntil, first } from 'rxjs/operators'; import { IForOfState } from '../../directives/for-of/for_of.directive'; import { IFilteringOperation } from '../../data-operations/filtering-condition'; import { IColumnResizeEventArgs, IFilteringEventArgs } from '../common/events'; -import { OverlayCancelableEventArgs, OverlaySettings, VerticalAlignment } from '../../services/overlay/utilities'; +import { OverlayCancelableEventArgs, OverlayEventArgs, OverlaySettings, VerticalAlignment } from '../../services/overlay/utilities'; import { IgxOverlayService } from '../../services/overlay/overlay'; import { useAnimation } from '@angular/animations'; import { AbsoluteScrollStrategy } from '../../services/overlay/scroll/absolute-scroll-strategy'; @@ -86,6 +86,12 @@ export class IgxFilteringService implements OnDestroy { this.lastActiveNode = this.grid.navigation.activeNode; }); + this._overlayService.opened.pipe(first(overlay => overlay.id === id), takeUntil(this.destroy$)).subscribe((event: OverlayEventArgs) => { + if (event.componentRef) { + event.componentRef.instance.populateData(); + } + }); + this._overlayService.closed .pipe( first(overlay => overlay.id === id), diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index fdd88ba2794..8c7333526cf 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4143,6 +4143,7 @@ export abstract class IgxGridBaseDirective implements GridType, options.outlet = this.outlet; if (this.excelStyleFilteringComponent) { this.excelStyleFilteringComponent.initialize(column, this.overlayService); + this.excelStyleFilteringComponent.populateData(); const id = this.overlayService.attach(this.excelStyleFilteringComponent.element, options); this.excelStyleFilteringComponent.overlayComponentId = id; return id; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts index 57890443e81..ee74558720d 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.component.ts @@ -1164,6 +1164,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni options.outlet = this.outlet; if (dropdown) { dropdown.initialize(column, this.overlayService); + dropdown.populateData(); if (shouldReatach) { const id = this.overlayService.attach(dropdown.element, options); dropdown.overlayComponentId = id; @@ -2219,6 +2220,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni ref.instance.resizable = this.rowDimensionResizing; ref.instance.sortable = dim.sortable === undefined ? true : dim.sortable; ref.instance.width = this.rowDimensionWidth(dim); + ref.instance.filteringIgnoreCase = false; ref.changeDetectorRef.detectChanges(); columns.push(ref.instance); }); diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts index ae016c28bdd..e049a86580f 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts @@ -89,6 +89,10 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return Promise.resolve(items); } + protected override getFilteredData(column: ColumnType, tree: IFilteringExpressionsTree) { + return DataUtil.filterDataByExpressions(column.grid.flatData, tree, column.grid); + } + private getHierarchicalFilterItems(records: ITreeGridRecord[], column: ColumnType, parent?: IgxFilterItem): IgxFilterItem[] { const pathParts = columnFieldPath(column.field); return records?.map(record => { From a35e3fa7cacdf64af794acb02f383467a05c982a Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Tue, 14 Oct 2025 14:30:40 +0300 Subject: [PATCH 5/9] test(sorting): Add a test --- .../data-operations/sorting-strategy.spec.ts | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/sorting-strategy.spec.ts b/projects/igniteui-angular/src/lib/data-operations/sorting-strategy.spec.ts index 152ec5abef3..20428ffa6b1 100644 --- a/projects/igniteui-angular/src/lib/data-operations/sorting-strategy.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/sorting-strategy.spec.ts @@ -24,34 +24,46 @@ describe('Unit testing SortingStrategy', () => { strategy: DefaultSortingStrategy.instance() }]); expect(dataGenerator.getValuesForColumn(res, 'number')) - .toEqual([4, 2, 0, 3, 1]); + .toEqual([4, 2, 0, 3, 1]); }); it('tests `compareObjects`', () => { const strategy = DefaultSortingStrategy.instance(); expect(strategy.compareValues(1, 0) === 1 && - strategy.compareValues(true, false) === 1 && - strategy.compareValues('bc', 'adfc') === 1) + strategy.compareValues(true, false) === 1 && + strategy.compareValues('bc', 'adfc') === 1) .toBeTruthy('compare first argument greater than second'); expect(strategy.compareValues(1, 2) === -1 && - strategy.compareValues('a', 'b') === -1 && - strategy.compareValues(false, true) === -1) + strategy.compareValues('a', 'b') === -1 && + strategy.compareValues(false, true) === -1) .toBeTruthy('compare 0, 1'); expect(strategy.compareValues(0, 0) === 0 && - strategy.compareValues(true, true) === 0 && - strategy.compareValues('test', 'test') === 0 - ) + strategy.compareValues(true, true) === 0 && + strategy.compareValues('test', 'test') === 0 + ) .toBeTruthy('Comare equal variables'); }); it('tests default settings', () => { (data[4] as { string: string }).string = 'ROW'; const res = sorting.sort(data, [{ - dir: SortingDirection.Asc, - fieldName: 'string', - ignoreCase: true, - strategy: DefaultSortingStrategy.instance() - }]); + dir: SortingDirection.Asc, + fieldName: 'string', + ignoreCase: true, + strategy: DefaultSortingStrategy.instance() + }]); expect(dataGenerator.getValuesForColumn(res, 'number')) - .toEqual([4, 0, 1, 2, 3]); + .toEqual([4, 0, 1, 2, 3]); + }); + + it('should not sort when sorting direction is None', () => { + const unsortedData = [{ number: 3 }, { number: 1 }, { number: 4 }, { number: 0 }, { number: 2 }]; + const res = sorting.sort(unsortedData, [{ + dir: SortingDirection.None, + fieldName: 'number', + ignoreCase: false, + strategy: DefaultSortingStrategy.instance() + }]); + expect(res.map(d => d.number)) + .toEqual([3, 1, 4, 0, 2]); }); }); From d793bd2cf5848d3aebd57eb89f740cc31eb60cf1 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Tue, 14 Oct 2025 16:06:13 +0300 Subject: [PATCH 6/9] fix(*): Add a check if a sorting direction is None --- projects/igniteui-angular/src/lib/grids/common/strategy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/strategy.ts b/projects/igniteui-angular/src/lib/grids/common/strategy.ts index d951a4d38bc..3da2f12876e 100644 --- a/projects/igniteui-angular/src/lib/grids/common/strategy.ts +++ b/projects/igniteui-angular/src/lib/grids/common/strategy.ts @@ -5,7 +5,7 @@ import { IGroupingState } from '../../data-operations/groupby-state.interface'; import { IGroupingExpression } from '../../data-operations/grouping-expression.interface'; import { IGroupByResult } from '../../data-operations/grouping-result.interface'; import { getHierarchy, isHierarchyMatch } from '../../data-operations/operations'; -import { DefaultSortingStrategy, ISortingExpression } from '../../data-operations/sorting-strategy'; +import { DefaultSortingStrategy, ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy'; import { GridType } from './grid.interface'; const DATE_TYPE = 'date'; @@ -141,6 +141,9 @@ export class IgxSorting implements IGridSortingStrategy { private prepareExpressions(expressions: ISortingExpression[], grid: GridType): IGridInternalSortingExpression[] { const multipleSortingExpressions: IGridInternalSortingExpression[] = []; for (const expr of expressions) { + if (expr.dir === SortingDirection.None) { + continue; + } if (!expr.strategy) { expr.strategy = DefaultSortingStrategy.instance(); } From b15664f367e1b6321473a93f86d80b6327099189 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Wed, 15 Oct 2025 13:06:35 +0300 Subject: [PATCH 7/9] test(pivot): Update base pivot tests --- .../lib/grids/pivot-grid/pivot-grid.spec.ts | 135 +++++++++--------- 1 file changed, 71 insertions(+), 64 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index 481c29cfec1..223899f40d7 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { CellType, FilteringExpressionsTree, FilteringLogic, GridColumnDataType, IGridCellEventArgs, IgxIconComponent, IgxPivotGridComponent, IgxStringFilteringOperand } from 'igniteui-angular'; +import { FilteringExpressionsTree, FilteringLogic, GridColumnDataType, IGridCellEventArgs, IgxGridCell, IgxIconComponent, IgxPivotGridComponent, IgxStringFilteringOperand } from 'igniteui-angular'; import { IgxChipComponent } from '../../chips/chip.component'; import { IgxChipsAreaComponent } from '../../chips/chips-area.component'; import { DefaultPivotSortingStrategy } from '../../data-operations/pivot-sort-strategy'; @@ -22,7 +22,6 @@ import { Size } from '../common/enums'; import { setElementSize } from '../../test-utils/helper-utils.spec'; import { IgxPivotRowDimensionMrlRowComponent } from './pivot-row-dimension-mrl-row.component'; import { IgxPivotRowDimensionContentComponent } from './pivot-row-dimension-content.component'; -import { IgxGridCellComponent } from '../cell.component'; const CSS_CLASS_LIST = 'igx-drop-down__list'; const CSS_CLASS_ITEM = 'igx-drop-down__item'; @@ -760,7 +759,7 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(pivotGrid.columns.length).toBe(3); }); - it('should calculate row headers according to grid size', async() => { + it('should calculate row headers according to grid size', async () => { const pivotGrid = fixture.componentInstance.pivotGrid; const rowHeightSmall = 32; const rowHeightMedium = 40; @@ -797,7 +796,7 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(rowHeader[0].nativeElement.offsetHeight).toBe(rowHeightMedium); }); - it('should render with correct width when set to 100% inside of flex container', async() => { + it('should render with correct width when set to 100% inside of flex container', async () => { fixture = TestBed.createComponent(IgxPivotGridFlexContainerComponent); fixture.detectChanges(); await wait(100); @@ -1415,7 +1414,7 @@ describe('IgxPivotGrid #pivotGrid', () => { GridFunctions.clickHeaderSortIcon(headerCell); fixture.detectChanges(); expect(pivotGrid.sortingExpressions.length).toBe(0); - expectedOrder = [829, 293, undefined, 296, 240]; + expectedOrder = [829, 296, undefined, 293, 240]; columnValues = pivotGrid.dataView.map(x => (x as IPivotGridRecord).aggregationValues.get('USA-UnitsSold')); expect(columnValues).toEqual(expectedOrder); }); @@ -2113,15 +2112,16 @@ describe('IgxPivotGrid #pivotGrid', () => { spyOn(pivotGrid.cellClick, 'emit').and.callThrough(); fixture.detectChanges(); - const cell = pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria-UnitsSold') as CellType; + const cell = pivotGrid.gridAPI.get_cell_by_index(0, 'Bulgaria-UnitsSold'); - pivotGrid.cellClick.emit({ cell, event: null }); - cell.nativeElement.click(); - const cellClickargs: IGridCellEventArgs = { cell, event: new MouseEvent('click') }; + const event = new Event('click'); + cell.nativeElement.dispatchEvent(event); + fixture.detectChanges(); + + const expectedCell = new IgxGridCell(pivotGrid, 0, cell.column); - const gridCell = cellClickargs.cell as IgxGridCellComponent; - const firstEntry = gridCell.rowData.aggregationValues.entries().next().value; - expect(firstEntry).toEqual(['USA-UnitsSold', 829]); + const cellClickArgs: IGridCellEventArgs = { cell: expectedCell, event }; + expect(pivotGrid.cellClick.emit).toHaveBeenCalledOnceWith(cellClickArgs); }); }); }); @@ -2139,7 +2139,7 @@ describe('IgxPivotGrid #pivotGrid', () => { const pivotGrid = fixture.componentInstance.pivotGrid; expect(pivotGrid.selectedRows).toEqual([]); const pivotRows = GridFunctions.getPivotRows(fixture); - const row = pivotRows[2].componentInstance; + const row = pivotRows[1].componentInstance; const rowHeaders = fixture.debugElement.queryAll( By.directive(IgxPivotRowDimensionHeaderComponent)); const secondDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'Clothing'); @@ -2177,13 +2177,17 @@ describe('IgxPivotGrid #pivotGrid', () => { [ { AllProducts: 'AllProducts', 'All cities': 'All Cities' - }, { - ProductCategory: 'Bikes', 'All cities': 'All Cities' - }, { + }, + { ProductCategory: 'Clothing', 'All cities': 'All Cities' - }, { + }, + { + ProductCategory: 'Bikes', 'All cities': 'All Cities' + }, + { ProductCategory: 'Accessories', 'All cities': 'All Cities' - }, { + }, + { ProductCategory: 'Components', 'All cities': 'All Cities' } ]; @@ -2233,38 +2237,38 @@ describe('IgxPivotGrid #pivotGrid', () => { columns: fixture.componentInstance.pivotConfigHierarchy.columns, rows: fixture.componentInstance.pivotConfigHierarchy.rows, values: [ - { - member: 'UnitsSold', - aggregate: { - aggregator: IgxPivotNumericAggregate.sum, - key: 'SUM', - label: 'Sum' - }, - enabled: true, - formatter: (value, row, column) => { - if (!column || !column.value || column.value.member !== 'UnitsSold') { - correctFirstColumnData = false; + { + member: 'UnitsSold', + aggregate: { + aggregator: IgxPivotNumericAggregate.sum, + key: 'SUM', + label: 'Sum' + }, + enabled: true, + formatter: (value, row, column) => { + if (!column || !column.value || column.value.member !== 'UnitsSold') { + correctFirstColumnData = false; + } + return value; } - return value; - } - }, - { - member: 'AmountOfSale', - displayName: 'Amount of Sale', - aggregate: { - aggregator: IgxTotalSaleAggregate.totalSale, - key: 'TOTAL', - label: 'Total' }, - enabled: true, - formatter: (value, row, column) => { - if (!column || !column.value || column.value.member !== 'AmountOfSale') { - correctSecondColumnData = false; + { + member: 'AmountOfSale', + displayName: 'Amount of Sale', + aggregate: { + aggregator: IgxTotalSaleAggregate.totalSale, + key: 'TOTAL', + label: 'Total' + }, + enabled: true, + formatter: (value, row, column) => { + if (!column || !column.value || column.value.member !== 'AmountOfSale') { + correctSecondColumnData = false; + } + return value; } - return value; } - } - ] + ] }; pivotGrid.width = '1500px'; @@ -2528,7 +2532,7 @@ describe('IgxPivotGrid #pivotGrid', () => { const dimensionContents = fixture.debugElement.queryAll(By.css('.igx-grid__tbody-pivot-dimension')); const rowHeaders = dimensionContents[1].queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)); const first = rowHeaders.map(x => x.componentInstance.column.header)[0]; - expect(first).toBe('Larry Lieb'); + expect(first).toBe('Stanley Brooker'); // insert in columns pivotGrid.insertDimensionAt({ memberName: 'SellerNameColumn', memberFunction: (rec) => rec.SellerName, enabled: true }, PivotDimensionType.Column, 0); @@ -2822,7 +2826,7 @@ describe('IgxPivotGrid #pivotGrid', () => { //check rows const rows = pivotGrid.rowList.toArray(); expect(rows.length).toBe(4); - const expectedHeaders = ['Accessories', 'Bikes', 'Clothing', 'Components']; + const expectedHeaders = ['Clothing', 'Bikes', 'Accessories', 'Components']; const rowHeaders = fixture.debugElement.queryAll( By.directive(IgxPivotRowDimensionHeaderComponent)); const rowDimensionHeaders = rowHeaders.map(x => x.componentInstance.column.header); @@ -2835,7 +2839,7 @@ describe('IgxPivotGrid #pivotGrid', () => { // check data const pivotRecord = (pivotGrid.rowList.first as IgxPivotRowComponent).data; - expect(pivotRecord.aggregationValues.get('London')).toBe(293); + expect(pivotRecord.aggregationValues.get('New York')).toBe(296); }); @@ -2868,8 +2872,8 @@ describe('IgxPivotGrid #pivotGrid', () => { ] }; fixture.detectChanges(); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 0).nativeElement.innerText).toBe('Accessories/Plovdiv:undefined'); - expect(pivotGrid.gridAPI.get_cell_by_index(0, 3).nativeElement.innerText).toBe('Accessories/London:293'); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 0).nativeElement.innerText).toBe('Clothing/Plovdiv:282'); + expect(pivotGrid.gridAPI.get_cell_by_index(0, 3).nativeElement.innerText).toBe('Clothing/London:undefined'); }); it('should allow filtering a dimension runtime.', () => { @@ -3029,7 +3033,7 @@ describe('IgxPivotGrid #pivotGrid', () => { const rowDimensionHeadersCol2 = rowDimensionCol2.map(x => x.componentInstance.rowDimensionColumn.header); dimensions = rowDimensionCol2.map(x => x.componentInstance.dimension); expect(dimensions.every(x => x.memberName === "City")).toBeTruthy(); - expect(rowDimensionHeadersCol2).toEqual(["Ciudad de la Costa", "London", "New York", "Plovdiv", "Sofia", "Yokohama"]); + expect(rowDimensionHeadersCol2).toEqual(['Plovdiv', 'New York', 'Ciudad de la Costa', 'London', 'Yokohama', 'Sofia']); const rowDimensionCol3 = contentRowHeaders.filter(y => y.componentInstance.layout.colStart === 3); const rowDimensionHeadersCol3 = rowDimensionCol3.map(x => x.componentInstance.rowDimensionColumn.header); @@ -3042,8 +3046,8 @@ describe('IgxPivotGrid #pivotGrid', () => { .map(x => x.componentInstance.rowDimensionColumn.header); dimensions = rowDimensionCol4.map(x => x.componentInstance.dimension); expect(dimensions.every(x => x.memberName === "ProductCategory")).toBeTruthy(); - expect(rowDimensionHeadersCol4).toEqual(["Bikes", "Clothing", "Accessories", - "Clothing", "Clothing", "Components", "Components"]); + expect(rowDimensionHeadersCol4).toEqual(["Clothing", "Clothing", "Bikes", + "Clothing", "Accessories", "Components", "Components"]); }); it("should horizontally expand/collapse on a single dimension hierarchy.", () => { @@ -3262,11 +3266,14 @@ describe('IgxPivotGrid #pivotGrid', () => { const allGroups = layoutContainer.queryAll( By.directive(IgxPivotRowDimensionHeaderComponent)); const row0Col0 = allGroups[0]; - const row0Col1 = allGroups.filter(x => x.componentInstance.column.header === "Ciudad de la Costa")[0]; + const row0Col1 = allGroups.filter(x => x.componentInstance.column.header === "Plovdiv")[0]; + const row1Col1 = allGroups.filter(x => x.componentInstance.column.header === "New York")[0]; + const row0Col2 = allGroups.filter(x => x.componentInstance.column.header === "AllProducts")[0]; - const row0Col3 = allGroups.filter(x => x.componentInstance.column.header === "Bikes")[0]; - const row1Col3 = allGroups.filter(x => x.componentInstance.column.header === "Clothing")[0]; - const row2Col3 = allGroups.filter(x => x.componentInstance.column.header === "Accessories")[0]; + const row1Col2 = allGroups.filter(x => x.componentInstance.column.header === "AllProducts")[1]; + const row0Col3 = allGroups.filter(x => x.componentInstance.column.header === "Clothing")[0]; + const row1Col3 = allGroups.filter(x => x.componentInstance.column.header === "Clothing")[1]; + const row2Col3 = allGroups.filter(x => x.componentInstance.column.header === "Bikes")[0]; UIInteractions.simulateClickAndSelectEvent(row0Col0); fixture.detectChanges(); @@ -3320,18 +3327,18 @@ describe('IgxPivotGrid #pivotGrid', () => { UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', row1Col3.nativeElement); tick(); fixture.detectChanges(); - GridFunctions.verifyHeaderIsFocused(row0Col2.parent); + GridFunctions.verifyHeaderIsFocused(row1Col2.parent); activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); expect(activeCells.length).toBe(1); - UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', row0Col2.nativeElement); + UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', row1Col2.nativeElement); tick(); fixture.detectChanges(); - GridFunctions.verifyHeaderIsFocused(row0Col1.parent); + GridFunctions.verifyHeaderIsFocused(row1Col1.parent); activeCells = fixture.debugElement.queryAll(By.css(`${ACTIVE_CELL_CSS_CLASS}`)); expect(activeCells.length).toBe(1); - UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', row0Col1.nativeElement); + UIInteractions.triggerKeyDownEvtUponElem('ArrowLeft', row1Col1.nativeElement); tick(); fixture.detectChanges(); GridFunctions.verifyHeaderIsFocused(row0Col0.parent); @@ -3385,7 +3392,7 @@ describe('IgxPivotGrid #pivotGrid', () => { const productRowContents = rowHeaders.filter(x => x.componentInstance.column.field === "ProductCategory"); const productRowContentsHeaders = productRowContents.map(x => x.componentInstance.column.header); - expect(productRowContentsHeaders).toEqual(['ProductCategory', 'Accessories', 'Bikes', 'Clothing', 'Components']); + expect(productRowContentsHeaders).toEqual(['ProductCategory', 'Clothing', 'Bikes', 'Accessories', 'Components']); const sortIcon = productsHeaderColumn.querySelectorAll('igx-icon')[0]; sortIcon.click(); @@ -3408,7 +3415,7 @@ describe('IgxPivotGrid #pivotGrid', () => { By.directive(IgxPivotRowDimensionHeaderComponent)); expect(pivotGrid.selectedRows).toEqual([]); const pivotRows = GridFunctions.getPivotRows(fixture); - const row = pivotRows[2].componentInstance; + const row = pivotRows[4].componentInstance; const secondDimCell = rowHeaders.find(x => x.componentInstance.column.header === 'Accessories'); secondDimCell.nativeElement.click(); fixture.detectChanges(); From 34acd1b858975f62ea77cb114e7c84e9a6bac575 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Wed, 15 Oct 2025 13:10:45 +0300 Subject: [PATCH 8/9] test(pivot): Update pivot state tests --- projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts index 7ef2e6779cc..bbc6bf04b4e 100644 --- a/projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.pivotgrid.spec.ts @@ -228,7 +228,7 @@ describe('IgxPivotGridState #pivotGrid :', () => { pivotGrid.rowSelection = 'single'; const state = fixture.componentInstance.state; expect(state).toBeDefined('IgxGridState directive is initialized'); - const headerRow = fixture.nativeElement.querySelector('igx-pivot-row-dimension-content'); + const headerRow = fixture.nativeElement.querySelectorAll('igx-pivot-row-dimension-content')[2]; const header = headerRow.querySelector('igx-pivot-row-dimension-header'); header.click(); fixture.detectChanges(); From 453d1fe69c82ab29c03c80af399be1d60310992f Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Wed, 15 Oct 2025 16:31:03 +0300 Subject: [PATCH 9/9] test(pivot): update pivot csv and excel export tests --- .../csv/csv-verification-wrapper.spec.ts | 4 +-- .../services/excel/test-data.service.spec.ts | 25 +++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/services/csv/csv-verification-wrapper.spec.ts b/projects/igniteui-angular/src/lib/services/csv/csv-verification-wrapper.spec.ts index 105cd01b132..65639433acc 100644 --- a/projects/igniteui-angular/src/lib/services/csv/csv-verification-wrapper.spec.ts +++ b/projects/igniteui-angular/src/lib/services/csv/csv-verification-wrapper.spec.ts @@ -291,9 +291,9 @@ export class CSVWrapper { public get pivotGridData() { return `ProductCategory${this._delimiter}Bulgaria${this._delimiter}USA${this._delimiter}Uruguay${this._eor}` + - `Accessories${this._delimiter}${this._delimiter}293${this._delimiter}${this._eor}` + - `Bikes${this._delimiter}${this._delimiter}${this._delimiter}68${this._eor}` + `Clothing${this._delimiter}774${this._delimiter}296${this._delimiter}456${this._eor}` + + `Bikes${this._delimiter}${this._delimiter}${this._delimiter}68${this._eor}` + + `Accessories${this._delimiter}${this._delimiter}293${this._delimiter}${this._eor}` + `Components${this._delimiter}${this._delimiter}240${this._delimiter}${this._eor}`; } } diff --git a/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts b/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts index 07322d9553e..97ed95aeb96 100644 --- a/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts +++ b/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts @@ -1292,7 +1292,6 @@ export class FileContentData { 012345657891011121311141524253637 `; - return this.createData(); } @@ -1658,7 +1657,7 @@ export class FileContentData { public get exportPivotGridData() { this._sharedStringsData = - `count="38" uniqueCount="23">AccessoriesBikesClothingComponentsUSAUruguayBulgaria04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; + `count="38" uniqueCount="23">ClothingBikesAccessoriesComponentsBulgariaUSAUruguay01/01/202102/19/202001/05/201905/12/202001/06/202004/07/202112/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; this._worksheetData = ` @@ -1666,38 +1665,44 @@ export class FileContentData { topLeftCell="D1" activePane="topRight" state="frozen"/> - 14151617181920212221222122212221222122212204729385.58158683.5626928212.811049216.0541129649.5751245668.33341324018.13 `; + 14151617181920212221222122212221222122212204728212.81849216.055929649.5761045668.331611683.56251229385.58351324018.13 `; return this.createData(); } public get exportPivotGridDataWithHeaders() { this._sharedStringsData = - `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; + `count="41" uniqueCount="26">ProductCategoryClothingBikesAccessoriesComponentsCountryBulgariaUSAUruguayDate01/01/202102/19/202001/05/201905/12/202001/06/202004/07/202112/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; this._worksheetData = ` + topLeftCell="D1" activePane="topRight" state="frozen"/> - 059171819202122232425242524252425242524252425161029385.582711683.56381228212.811349216.0561429649.5771545668.33461624018.13 `; + 059171819202122232425242524252425242524252425161028212.811149216.0571229649.5781345668.332814683.56371529385.58471624018.13 `; return this.createData(); } public get exportPivotGridDataHorizontal() { this._sharedStringsData = - `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; + `count="41" uniqueCount="26">ProductCategoryClothingBikesAccessoriesComponentsCountryBulgariaUSAUruguayDate01/01/202102/19/202001/05/201905/12/202001/06/202004/07/202112/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`; this._worksheetData = - `059171819202122232425242524252425242524252425161029385.582711683.56381228212.811349216.0561429649.5771545668.33461624018.13 `; + ` + + + + + 059171819202122232425242524252425242524252425161028212.811149216.0571229649.5781345668.332814683.56371529385.58471624018.13 `; return this.createData(); } public get exportPivotGridHierarchicalData() { this._sharedStringsData = - `count="40" uniqueCount="19">All CitiesCiudad de la CostaLondonNew YorkPlovdivSofiaYokohamaAllProductsBikesClothingAccessoriesComponentsBulgariaUSUruguayUKJapanUnitsSoldAmount of Sale`; + `count="40" uniqueCount="19">All CitiesPlovdivNew YorkCiudad de la CostaLondonYokohamaSofiaAllProductsClothingBikesAccessoriesComponentsBulgariaUSUruguayUKJapanUnitsSoldAmount of Sale`; this._worksheetData = ` @@ -1706,7 +1711,7 @@ export class FileContentData { topLeftCell="C1" activePane="topRight" state="frozen"/> - 1213141516171817181718171817180777411509.0229614672.7252431400.5629325074.942404351.2868242.0892823612.4229614672.7245631158.481029325074.94114927896.62404351.21752431400.56868242.08945631158.482729325074.941029325074.943729614672.72929614672.72472823612.4292823612.42574927896.6114927896.6672404351.2112404351.2 `; + 1213141516171817181718171817180777411509.0229614672.7252431400.5629325074.942404351.282823612.4229614672.7245631158.48968242.081029325074.94114927896.62404351.2172823612.4282823612.422729614672.72829614672.723752431400.56968242.08845631158.484729325074.941029325074.94572404351.2112404351.2674927896.6114927896.6 `; return this.createData(); }