From 249ac11f71555a5bdabd6a2452bdbb1dc7dbfc19 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 21 Dec 2021 17:35:16 +0200 Subject: [PATCH 01/63] feat(esf): partial implementation for tgrid search --- .../lib/grids/filtering/excel-style/common.ts | 1 + .../grid.excel-style-filtering.component.html | 5 +- .../grid.excel-style-filtering.component.ts | 11 +++ .../grid.excel-style-filtering.module.ts | 11 ++- ...ree-grid-excel-style-search.component.html | 80 +++++++++++++++++++ .../tree-grid-excel-style-search.component.ts | 32 ++++++++ .../lib/grids/tree-grid/tree-grid.module.ts | 3 +- 7 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html create mode 100644 projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts index 86e6dfc3ba4..3e6268b7beb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts @@ -12,6 +12,7 @@ export class FilterListItem { public isFiltered: boolean; public isSpecial = false; public isBlanks = false; + public children?: Array; } /** diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html index 72477854113..c569551b24b 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html @@ -29,8 +29,11 @@ - + + + +
(); generateExpressionsList(this.column.filteringExpressionsTree, this.grid.filteringLogic, this.expressionsList); @@ -486,6 +493,10 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent // const data = this.column.gridAPI.filterDataByExpressions(expressionsTree); const data = this.grid.gridAPI.filterDataByExpressions(expressionsTree); + if (this.grid.childDataKey !== undefined) { + // TODO: add property to FilterListItem (or create new class) and populate children (recursively) for data + } + const shouldFormatValues = this.shouldFormatValues(); const columnField = this.column.field; const columnValues = (this.column.dataType === GridColumnDataType.Date) ? diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts index 02daaefe3fe..4ef53cff1f0 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts @@ -38,6 +38,8 @@ import { IgxDatePickerModule } from '../../../date-picker/date-picker.module'; import { IgxTimePickerModule } from '../../../time-picker/time-picker.component'; import { IgxFocusModule } from '../../../directives/focus/focus.directive'; import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/date-time-editor.directive'; +import { IgxTreeGridExcelStyleSearchComponent } from './tree-grid-excel-style-search.component'; +import { IgxTreeModule } from '../../../tree/public_api'; /** * @hidden @@ -54,6 +56,7 @@ import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/da IgxExcelStyleConditionalFilterComponent, IgxExcelStyleMovingComponent, IgxExcelStyleSearchComponent, + IgxTreeGridExcelStyleSearchComponent, IgxExcelStyleCustomDialogComponent, IgxExcelStyleDefaultExpressionComponent, IgxExcelStyleDateExpressionComponent, @@ -76,6 +79,7 @@ import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/da IgxExcelStyleConditionalFilterComponent, IgxExcelStyleMovingComponent, IgxExcelStyleSearchComponent, + IgxTreeGridExcelStyleSearchComponent, IgxExcelStyleHeaderComponent ], imports: [ @@ -98,7 +102,8 @@ import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/da IgxProgressBarModule, IgxSelectModule, IgxFocusModule, - IgxDateTimeEditorModule + IgxDateTimeEditorModule, + IgxTreeModule ], entryComponents: [ IgxGridExcelStyleFilteringComponent @@ -107,6 +112,4 @@ import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/da IgxSelectionAPIService ] }) -export class IgxGridExcelStyleFilteringModule { - -} +export class IgxGridExcelStyleFilteringModule { } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html new file mode 100644 index 00000000000..7828fd531ef --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html @@ -0,0 +1,80 @@ + + search + + + clear + + + + + +
{{item.label}}
+ +
+
+ + + + + + + + +
+
+ +
+
+ +
+
diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts new file mode 100644 index 00000000000..11fb105a2bb --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts @@ -0,0 +1,32 @@ +import { + Component +} from '@angular/core'; +import { IgxExcelStyleSearchComponent } from './excel-style-search.component'; + +/** + * A component used for presenting Excel style search UI for hierarchical tree-view structured data. + */ +@Component({ + selector: 'igx-tree-grid-excel-style-search', + templateUrl: './tree-grid-excel-style-search.component.html' +}) +export class IgxTreeGridExcelStyleSearchComponent extends IgxExcelStyleSearchComponent { + /** + * @hidden @internal + */ + // public displayedTreeListData: FilterListItem[] = []; + + /** + * @hidden @internal + */ + public get containerSize() { + // if (this.esf.listData.length) { + // return this.list.element.nativeElement.offsetHeight; + // } + + // // GE Nov 1st, 2021 #10355 Return a numeric value, so the chunk size is calculated properly. + // // If we skip this branch, on applying the filter the _calculateChunkSize() method off the ForOfDirective receives + // // an igxForContainerSize = undefined, thus assigns the chunkSize to the igxForOf.length which leads to performance issues. + return 0; + } +} \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts index dd1cf80b610..5eab966d83b 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts @@ -10,6 +10,7 @@ import { IgxTreeGridSummaryPipe } from './tree-grid.summary.pipe'; import { IgxRowLoadingIndicatorTemplateDirective } from './tree-grid.directives'; import { IgxTreeGridGroupingPipe } from './tree-grid.grouping.pipe'; import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by-area.component'; +import { IgxTreeModule } from '../../tree/public_api'; /** * @hidden */ @@ -42,7 +43,7 @@ import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by- IgxTreeGridAddRowPipe ], imports: [ - IgxGridCommonModule, + IgxGridCommonModule ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) From b4f31334823062af0bc333df3553f5d99da55117 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 11 Jan 2022 12:47:20 +0200 Subject: [PATCH 02/63] feat(tree-esf-search): displaying tree data --- .../excel-style-search.component.ts | 14 +- .../grid.excel-style-filtering.component.ts | 123 ++++++++++++++---- ...ree-grid-excel-style-search.component.html | 17 ++- .../tree-grid-excel-style-search.component.ts | 29 ++--- src/app/tree-grid/tree-grid.sample.html | 10 +- src/app/tree-grid/tree-grid.sample.ts | 2 +- 6 files changed, 139 insertions(+), 56 deletions(-) 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 7425dce6067..8c6e5de3bff 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 @@ -152,7 +152,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { }); }); esf.columnChange.pipe(takeUntil(this.destroy$)).subscribe(() => { - this.virtDir.resetScrollPosition(); + this.virtDir?.resetScrollPosition(); }); esf.listDataLoaded.pipe(takeUntil(this.destroy$)).subscribe(() => { @@ -179,10 +179,12 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public refreshSize = () => { - this.virtDir.igxForContainerSize = this.containerSize; - this.virtDir.igxForItemSize = this.itemSize; - this.virtDir.recalcUpdateSizes(); - this.cdr.detectChanges(); + if (this.virtDir) { + this.virtDir.igxForContainerSize = this.containerSize; + this.virtDir.igxForItemSize = this.itemSize; + this.virtDir.recalcUpdateSizes(); + this.cdr.detectChanges(); + } } /** @@ -250,6 +252,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return this.list.element.nativeElement.offsetHeight; } + console.log('containerSize()'); + // GE Nov 1st, 2021 #10355 Return a numeric value, so the chunk size is calculated properly. // If we skip this branch, on applying the filter the _calculateChunkSize() method off the ForOfDirective receives // an igxForContainerSize = undefined, thus assigns the chunkSize to the igxForOf.length which leads to performance issues. diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index d6ba9de5ea8..4cc4eaeecde 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -487,47 +487,94 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.grid.filterStrategy.shouldApplyFormatter(this.column.field); } + private processedData: any[]; + private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); // TODO: Check why we access the API service through the column ?? // const data = this.column.gridAPI.filterDataByExpressions(expressionsTree); const data = this.grid.gridAPI.filterDataByExpressions(expressionsTree); - - if (this.grid.childDataKey !== undefined) { - // TODO: add property to FilterListItem (or create new class) and populate children (recursively) for data - } + this.processedData = []; const shouldFormatValues = this.shouldFormatValues(); const columnField = this.column.field; - const columnValues = (this.column.dataType === GridColumnDataType.Date) ? - data.map(record => { - const value = (resolveNestedPath(record, columnField)); - const label = this.getFilterItemLabel(value, true, record); - return { label, value }; - }) : data.map(record => { - const value = resolveNestedPath(record, columnField); - return shouldFormatValues ? this.column.formatter(value, record) : value; + let columnValues; + + if (this.grid.childDataKey !== undefined) { + // TODO: add check for DATE + columnValues = data.map(record => { + if (this.processedData.indexOf(record) < 0) { + let value = resolveNestedPath(record, columnField); + if (shouldFormatValues) { + value = this.column.formatter(value, record); + } + const childrenValues = this.getChildrenColumnValues(record, columnField) + return { value, childrenValues }; + } + }); + + columnValues = columnValues.filter(function(el) { + return el !== undefined }); + } else { + columnValues = (this.column.dataType === GridColumnDataType.Date) ? + data.map(record => { + const value = (resolveNestedPath(record, columnField)); + const label = this.getFilterItemLabel(value, true, record); + return { label, value }; + }) : data.map(record => { + const value = resolveNestedPath(record, columnField); + return shouldFormatValues ? this.column.formatter(value, record) : value; + }); + } this.renderValues(columnValues); } + private getChildrenColumnValues(record: any, columnField: string) { + this.processedData.push(record); + let childrenValues = []; + const children = record[this.grid.childDataKey]; + if (children) { + children.forEach(child => { + if (this.processedData.indexOf(child) < 0) { + let value = resolveNestedPath(child, columnField); + // if (shouldFormatValues) { + // value = this.column.formatter(value, record); + // } + childrenValues.push({ value, childrenValues: this.getChildrenColumnValues(child, columnField) }); + } + }); + } else { + // TODO: uniqueValues + } + + return childrenValues; + } + private renderValues(columnValues: any[]) { - this.generateUniqueValues(columnValues); - this.generateFilterValues(this.column.dataType === GridColumnDataType.Date || this.column.dataType === GridColumnDataType.DateTime); + if (this.grid.childDataKey !== undefined) { + this.uniqueValues = columnValues; + } else { + this.uniqueValues = this.generateUniqueValues(columnValues); + } + + this.filterValues = this.generateFilterValues(this.column.dataType === GridColumnDataType.Date || this.column.dataType === GridColumnDataType.DateTime); this.generateListData(); } private generateUniqueValues(columnValues: any[]) { + let uniqueValues; + if (this.column.dataType === GridColumnDataType.String && this.column.filteringIgnoreCase) { const filteredUniqueValues = columnValues.map(s => s?.toString().toLowerCase()) .reduce((map, val, i) => map.get(val) ? map : map.set(val, columnValues[i]), new Map()); - this.uniqueValues = Array.from(filteredUniqueValues.values()); + uniqueValues = Array.from(filteredUniqueValues.values()); } else if (this.column.dataType === GridColumnDataType.DateTime) { - this.uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); - this.uniqueValues.forEach((d, i) => this.uniqueValues[i] = d ? new Date(d) : d); + uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); + uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); } else if (this.column.dataType === GridColumnDataType.Time) { - this.uniqueValues = Array.from(new Set(columnValues.map(v => { + uniqueValues = Array.from(new Set(columnValues.map(v => { if (v) { v = new Date(v); return new Date().setHours(v.getHours(), v.getMinutes(), v.getSeconds()); @@ -535,16 +582,20 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return v; } }))); - this.uniqueValues.forEach((d, i) => this.uniqueValues[i] = d ? new Date(d) : d); + uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); } else { - this.uniqueValues = this.column.dataType === GridColumnDataType.Date ? + uniqueValues = this.column.dataType === GridColumnDataType.Date ? uniqueDates(columnValues) : Array.from(new Set(columnValues)); } + + return uniqueValues; } private generateFilterValues(isDateColumn: boolean = false) { + let filterValues; + if (isDateColumn) { - this.filterValues = new Set(this.expressionsList.reduce((arr, e) => { + filterValues = new Set(this.expressionsList.reduce((arr, e) => { if (e.expression.condition.name === 'in') { return [...arr, ...Array.from((e.expression.searchVal as Set).values()).map(v => new Date(v).toISOString())]; @@ -552,7 +603,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return [...arr, ...[e.expression.searchVal ? e.expression.searchVal.toISOString() : e.expression.searchVal]]; }, [])); } else if (this.column.dataType === GridColumnDataType.Time) { - this.filterValues = new Set(this.expressionsList.reduce((arr, e) => { + filterValues = new Set(this.expressionsList.reduce((arr, e) => { if (e.expression.condition.name === 'in') { return [...arr, ...Array.from((e.expression.searchVal as Set).values()).map(v => typeof v === 'string' ? v : new Date(v).toLocaleTimeString())]; @@ -560,13 +611,15 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return [...arr, ...[e.expression.searchVal ? e.expression.searchVal.toLocaleTimeString() : e.expression.searchVal]]; }, [])); } else { - this.filterValues = new Set(this.expressionsList.reduce((arr, e) => { + filterValues = new Set(this.expressionsList.reduce((arr, e) => { if (e.expression.condition.name === 'in') { return [...arr, ...Array.from((e.expression.searchVal as Set).values())]; } return [...arr, ...[e.expression.searchVal]]; }, [])); } + + return filterValues; } private generateListData() { @@ -664,9 +717,14 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.selectAllSelected = true; this.containsNullOrEmpty = false; this.selectAllIndeterminate = false; - const applyFormatter = !this.shouldFormatValues(); + this.listData = this.generateFilterListItems(this.uniqueValues, shouldUpdateSelection); + this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; + } - this.uniqueValues.forEach(element => { + private generateFilterListItems(values: any[], shouldUpdateSelection: boolean) { + let filterListItems = []; + const applyFormatter = !this.shouldFormatValues(); + values?.forEach(element => { const hasValue = (element !== undefined && element !== null && element !== '' && this.column.dataType !== GridColumnDataType.Date) || !!(element && element.label); @@ -694,10 +752,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.value = this.getFilterItemValue(element); filterListItem.label = this.getFilterItemLabel(element, applyFormatter); filterListItem.indeterminate = false; - this.listData.push(filterListItem); + filterListItem.children = this.generateFilterListItems(element.childrenValues, shouldUpdateSelection); + filterListItems.push(filterListItem); } }); - this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; + + return filterListItems; } private addSelectAllItem() { @@ -736,6 +796,10 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { + if (this.grid.childDataKey !== undefined) { + element = element.value; + } + if (element?.label) { return element.label; } @@ -767,9 +831,14 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemValue(element: any) { + if (this.grid.childDataKey !== undefined) { + element = element.value; + } + if (this.column.dataType === GridColumnDataType.Date) { element = parseDate(element.value); } + return element; } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html index 7828fd531ef..16718fa3edd 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html @@ -20,15 +20,22 @@ - - + +
{{item.label}}
- + +
{{childLevel1.label}}
+ +
{{childLevel2.label}}
+ +
{{childLevel3.label}}
+
+
+
+ ; public columns: Array; public selectionMode; - public density: DisplayDensity = 'comfortable'; + public density: DisplayDensity = 'compact'; public displayDensities; public selectionModes: GridSelectionMode[] = ['none', 'single', 'multiple', 'multipleCascade']; From eb1a22e4f8d170ceea02c67f5a18b879380d5f2b Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 13 Jan 2022 13:32:41 +0200 Subject: [PATCH 03/63] feat(hierarchical-filter): new filtering strategy --- .../lib/data-operations/filtering-strategy.ts | 6 ++ .../grid.excel-style-filtering.component.html | 4 +- .../grid.excel-style-filtering.component.ts | 15 ++--- src/app/tree-grid/tree-grid.sample.html | 23 +------- src/app/tree-grid/tree-grid.sample.ts | 59 ++----------------- 5 files changed, 22 insertions(+), 85 deletions(-) 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 2d297eb1467..007ec7afed0 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -139,3 +139,9 @@ export class FormattedValuesFilteringStrategy extends FilteringStrategy { return value; } } + +export class HierarchicalFilteringStrategy extends FilteringStrategy { + constructor(public hierarchicalFilterFields: string[]) { + super(); + } +} diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html index 56e28759532..85584557b97 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html @@ -29,10 +29,10 @@ - + - + diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 4cc4eaeecde..11d55d8868b 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -394,8 +394,9 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent /** * @hidden @internal */ - public isTreeGrid() { - return this.grid.records !== undefined; + public isHierarchical() { + return this.grid.filterStrategy.hasOwnProperty('hierarchicalFilterFields') + && this.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.column.field) >= 0; } private init() { @@ -500,7 +501,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent const columnField = this.column.field; let columnValues; - if (this.grid.childDataKey !== undefined) { + if (this.isHierarchical()) { // TODO: add check for DATE columnValues = data.map(record => { if (this.processedData.indexOf(record) < 0) { @@ -546,14 +547,14 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } }); } else { - // TODO: uniqueValues + // TODO: unique values on last level } return childrenValues; } private renderValues(columnValues: any[]) { - if (this.grid.childDataKey !== undefined) { + if (this.isHierarchical()) { this.uniqueValues = columnValues; } else { this.uniqueValues = this.generateUniqueValues(columnValues); @@ -796,7 +797,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { - if (this.grid.childDataKey !== undefined) { + if (this.isHierarchical()) { element = element.value; } @@ -831,7 +832,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemValue(element: any) { - if (this.grid.childDataKey !== undefined) { + if (this.isHierarchical()) { element = element.value; } diff --git a/src/app/tree-grid/tree-grid.sample.html b/src/app/tree-grid/tree-grid.sample.html index 3b036d365b2..f2edcf5a26e 100644 --- a/src/app/tree-grid/tree-grid.sample.html +++ b/src/app/tree-grid/tree-grid.sample.html @@ -6,7 +6,7 @@ + [allowFiltering]="true" [moving]="true" [filterMode]="'excelStyleFilter'" [filterStrategy]="hierarchicalFilterStrategy"> @@ -33,25 +33,4 @@ [minWidth]="c.minWidth" [maxWidth]="c.maxWidth"> - -
- Enable Paging - Enable RowEditing -
- - - - {{ item }} - - - - - - - - - - -
-
diff --git a/src/app/tree-grid/tree-grid.sample.ts b/src/app/tree-grid/tree-grid.sample.ts index 2b6a80defc8..f2c8355e20b 100644 --- a/src/app/tree-grid/tree-grid.sample.ts +++ b/src/app/tree-grid/tree-grid.sample.ts @@ -1,6 +1,6 @@ import { Component, ViewChild, OnInit } from '@angular/core'; import { IgxTreeGridComponent, IgxExcelExporterService, IgxCsvExporterService, - IgxCsvExporterOptions, IgxExcelExporterOptions, CsvFileTypes, GridSelectionMode, DisplayDensity } from 'igniteui-angular'; + IgxCsvExporterOptions, IgxExcelExporterOptions, CsvFileTypes, GridSelectionMode, DisplayDensity, HierarchicalFilteringStrategy } from 'igniteui-angular'; import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; @Component({ @@ -20,11 +20,13 @@ export class TreeGridSampleComponent implements OnInit { public displayDensities; public selectionModes: GridSelectionMode[] = ['none', 'single', 'multiple', 'multipleCascade']; - private nextRow = 1; + public hierarchicalFilterStrategy: HierarchicalFilteringStrategy; constructor(private excelExporterService: IgxExcelExporterService, private csvExporterService: IgxCsvExporterService) { -} + this.hierarchicalFilterStrategy = new HierarchicalFilteringStrategy(['ID']); + } + public ngOnInit(): void { this.selectionMode = GridSelectionMode.multiple; this.displayDensities = [ @@ -48,61 +50,10 @@ export class TreeGridSampleComponent implements OnInit { this.data = HIERARCHICAL_SAMPLE_DATA.slice(0); } - public addRow() { - this.grid1.addRow({ - ID: `ADD${this.nextRow++}`, - CompanyName: 'Around the Horn', - ContactName: 'Thomas Hardy', - ContactTitle: 'Sales Representative', - Address: '120 Hanover Sq.', - City: 'London', - Region: null, - PostalCode: 'WA1 1DP', - Country: 'UK', - Phone: '(171) 555-7788', - Fax: '(171) 555-6750' - }); - } - public selectDensity(event) { this.density = this.displayDensities[event.index].label; } - public addChildRow() { - const selectedRowId = this.grid1.selectedRows[0]; - this.grid1.addRow ( - { - ID: `ADD${this.nextRow++}`, - CompanyName: 'Around the Horn', - ContactName: 'Thomas Hardy', - ContactTitle: 'Sales Representative', - Address: '120 Hanover Sq.', - City: 'London', - Region: null, - PostalCode: 'WA1 1DP', - Country: 'UK', - Phone: '(171) 555-7788', - Fax: '(171) 555-6750' - }, - selectedRowId); - } - - public deleteRow() { - this.grid1.deleteRow(this.grid1.selectedRows[0]); - } - - public undo() { - this.grid1.transactions.undo(); - } - - public redo() { - this.grid1.transactions.redo(); - } - - public commit() { - this.grid1.transactions.commit(this.data, this.grid1.primaryKey, this.grid1.childDataKey); - } - public exportToExcel() { this.excelExporterService.export(this.grid1, new IgxExcelExporterOptions('TreeGrid')); } From 861c524c07a55285afcb8cf0540fbdd8c12f1523 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 19 Jan 2022 12:45:32 +0200 Subject: [PATCH 04/63] feat(tree-esf-search): new filtering strategy --- .../lib/data-operations/filtering-strategy.ts | 82 ++++++++++++++++- .../excel-style-search.component.html | 23 ++++- .../excel-style-search.component.ts | 12 ++- .../grid.excel-style-filtering.component.html | 5 +- .../grid.excel-style-filtering.component.ts | 88 ++++--------------- .../grid.excel-style-filtering.module.ts | 3 - ...ree-grid-excel-style-search.component.html | 87 ------------------ .../tree-grid-excel-style-search.component.ts | 27 ------ src/app/tree-grid/tree-grid.sample.html | 2 +- 9 files changed, 133 insertions(+), 196 deletions(-) delete mode 100644 projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html delete mode 100644 projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts 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 007ec7afed0..05645c9b251 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -1,7 +1,7 @@ import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree'; import { resolveNestedPath, parseDate } from '../core/utils'; -import { GridType } from '../grids/common/grid.interface'; +import { ColumnType, GridType } from '../grids/common/grid.interface'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -12,6 +12,11 @@ export interface IFilteringStrategy { grid?: GridType): any[]; } +export interface IHierarchicalItem { + value: any; + children?: IHierarchicalItem[]; +} + export class NoopFilteringStrategy implements IFilteringStrategy { private static _instance: NoopFilteringStrategy = null; @@ -28,6 +33,10 @@ export class NoopFilteringStrategy implements IFilteringStrategy { export abstract class BaseFilteringStrategy implements IFilteringStrategy { public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean { + if (rec.data) { + rec = rec.data; + } + const cond = expr.condition; const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid); return cond.logic(val, expr.searchVal, expr.ignoreCase); @@ -71,6 +80,19 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } + // move formatting from base implementation to formatted values strategies - grid and treegrid + public getColumnValues( + column: ColumnType, + tree: FilteringExpressionsTree, + done: (values: any[] | IHierarchicalItem[]) => void) { + + const data = column.grid.gridAPI.filterDataByExpressions(tree); + const columnField = column.field; + let columnValues; + columnValues = data.map(record => resolveNestedPath(record, columnField)); + done(columnValues); + } + public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[]; @@ -141,7 +163,65 @@ export class FormattedValuesFilteringStrategy extends FilteringStrategy { } export class HierarchicalFilteringStrategy extends FilteringStrategy { + private processedData: IHierarchicalItem[]; + private childDataKey; + constructor(public hierarchicalFilterFields: string[]) { super(); } + + public override getColumnValues( + column: ColumnType, + tree: FilteringExpressionsTree, + done: (values: any[] | IHierarchicalItem[]) => void): void { + + if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { + return super.getColumnValues(column, tree, done); + } + + this.processedData = []; + this.childDataKey = column.grid.childDataKey; + const data = column.grid.gridAPI.filterDataByExpressions(tree); + const columnField = column.field; + let columnValues = []; + columnValues = data.map(record => { + if (this.processedData.indexOf(record) < 0) { // TODO: add check for DATE + let hierarchicalItem: IHierarchicalItem; + hierarchicalItem = { value: resolveNestedPath(record, columnField) }; + // if (shouldFormatValues) { + // value = this.column.formatter(value, record); + // } + hierarchicalItem.children = this.getChildren(record, columnField) + return hierarchicalItem; + } + }); + columnValues = columnValues.filter(function(el) { + return el !== undefined + }); + + done(columnValues); + } + + private getChildren(record: any, columnField: string) { + this.processedData.push(record); + let childrenValues = []; + const children = record[this.childDataKey]; + if (children) { + children.forEach(child => { + if (this.processedData.indexOf(child) < 0) { + let hierarchicalItem: IHierarchicalItem; + hierarchicalItem = { value: resolveNestedPath(child, columnField) }; + // if (shouldFormatValues) { + // value = this.column.formatter(value, record); + // } + hierarchicalItem.children = this.getChildren(child, columnField) + childrenValues.push(hierarchicalItem); + } + }); + } else { + // TODO: unique values on last level + } + + return childrenValues; + } } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index 81832ccddcf..984b5f74cfb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -20,7 +20,7 @@ - +
@@ -45,6 +45,27 @@ + + +
{{item.label}}
+ +
{{childLevel1.label}}
+ +
{{childLevel2.label}}
+ +
{{childLevel3.label}}
+ +
{{childLevel4.label}}
+ +
{{childLevel5.label}}
+
+
+
+
+
+
+
+ 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 8c6e5de3bff..66b46957784 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 @@ -249,11 +249,9 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { */ public get containerSize() { if (this.esf.listData.length) { - return this.list.element.nativeElement.offsetHeight; + return this.list?.element.nativeElement.offsetHeight; } - console.log('containerSize()'); - // GE Nov 1st, 2021 #10355 Return a numeric value, so the chunk size is calculated properly. // If we skip this branch, on applying the filter the _calculateChunkSize() method off the ForOfDirective receives // an igxForContainerSize = undefined, thus assigns the chunkSize to the igxForOf.length which leads to performance issues. @@ -415,6 +413,14 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { this.esf.closeDropdown(); } + /** + * @hidden @internal + */ + public isHierarchical() { + return this.esf.grid.filterStrategy?.hasOwnProperty('hierarchicalFilterFields') + && this.esf.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.esf.column.field) >= 0; + } + private createCondition(conditionName: string) { switch (this.esf.column.dataType) { case GridColumnDataType.Boolean: diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html index 85584557b97..a96efcc0190 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.html @@ -29,11 +29,8 @@ - + - - -
= 0; - } - private init() { this.expressionsList = new Array(); generateExpressionsList(this.column.filteringExpressionsTree, this.grid.filteringLogic, this.expressionsList); @@ -466,6 +458,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent const prevColumn = this.column; this.grid.uniqueColumnValuesStrategy(this.column, expressionsTree, (colVals: any[]) => { + // check for 'value' property - if 'value' then item is IHierarchicalItem + if (!this.column || this.column !== prevColumn) { return; } @@ -488,72 +482,28 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.grid.filterStrategy.shouldApplyFormatter(this.column.field); } - private processedData: any[]; - private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - // TODO: Check why we access the API service through the column ?? - // const data = this.column.gridAPI.filterDataByExpressions(expressionsTree); - const data = this.grid.gridAPI.filterDataByExpressions(expressionsTree); - this.processedData = []; - - const shouldFormatValues = this.shouldFormatValues(); - const columnField = this.column.field; - let columnValues; - if (this.isHierarchical()) { - // TODO: add check for DATE - columnValues = data.map(record => { - if (this.processedData.indexOf(record) < 0) { - let value = resolveNestedPath(record, columnField); - if (shouldFormatValues) { - value = this.column.formatter(value, record); - } - const childrenValues = this.getChildrenColumnValues(record, columnField) - return { value, childrenValues }; - } - }); - - columnValues = columnValues.filter(function(el) { - return el !== undefined + if (this.grid.filterStrategy instanceof BaseFilteringStrategy) { + this.grid.filterStrategy.getColumnValues(this.column, expressionsTree, (colVals: any[]) => { + const columnValues = (this.column.dataType === GridColumnDataType.Date) ? + colVals.map(value => { + const label = this.getFilterItemLabel(value); + return { label, value }; + }) : colVals; // TODO: should formatter go here? + + this.renderValues(columnValues); }); - } else { - columnValues = (this.column.dataType === GridColumnDataType.Date) ? - data.map(record => { - const value = (resolveNestedPath(record, columnField)); - const label = this.getFilterItemLabel(value, true, record); - return { label, value }; - }) : data.map(record => { - const value = resolveNestedPath(record, columnField); - return shouldFormatValues ? this.column.formatter(value, record) : value; - }); } - - this.renderValues(columnValues); } - private getChildrenColumnValues(record: any, columnField: string) { - this.processedData.push(record); - let childrenValues = []; - const children = record[this.grid.childDataKey]; - if (children) { - children.forEach(child => { - if (this.processedData.indexOf(child) < 0) { - let value = resolveNestedPath(child, columnField); - // if (shouldFormatValues) { - // value = this.column.formatter(value, record); - // } - childrenValues.push({ value, childrenValues: this.getChildrenColumnValues(child, columnField) }); - } - }); - } else { - // TODO: unique values on last level - } - - return childrenValues; + private isHierarchical() { + return this.grid.filterStrategy.hasOwnProperty('hierarchicalFilterFields') + && this.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.column.field) >= 0; } - private renderValues(columnValues: any[]) { + private renderValues(columnValues: any[] | IHierarchicalItem[]) { if (this.isHierarchical()) { this.uniqueValues = columnValues; } else { @@ -753,7 +703,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.value = this.getFilterItemValue(element); filterListItem.label = this.getFilterItemLabel(element, applyFormatter); filterListItem.indeterminate = false; - filterListItem.children = this.generateFilterListItems(element.childrenValues, shouldUpdateSelection); + filterListItem.children = this.generateFilterListItems(element.children, shouldUpdateSelection); filterListItems.push(filterListItem); } }); @@ -797,7 +747,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { - if (this.isHierarchical()) { + if (element.value) { element = element.value; } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts index 4ef53cff1f0..cb3111c2d5f 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.module.ts @@ -38,7 +38,6 @@ import { IgxDatePickerModule } from '../../../date-picker/date-picker.module'; import { IgxTimePickerModule } from '../../../time-picker/time-picker.component'; import { IgxFocusModule } from '../../../directives/focus/focus.directive'; import { IgxDateTimeEditorModule } from '../../../directives/date-time-editor/date-time-editor.directive'; -import { IgxTreeGridExcelStyleSearchComponent } from './tree-grid-excel-style-search.component'; import { IgxTreeModule } from '../../../tree/public_api'; /** @@ -56,7 +55,6 @@ import { IgxTreeModule } from '../../../tree/public_api'; IgxExcelStyleConditionalFilterComponent, IgxExcelStyleMovingComponent, IgxExcelStyleSearchComponent, - IgxTreeGridExcelStyleSearchComponent, IgxExcelStyleCustomDialogComponent, IgxExcelStyleDefaultExpressionComponent, IgxExcelStyleDateExpressionComponent, @@ -79,7 +77,6 @@ import { IgxTreeModule } from '../../../tree/public_api'; IgxExcelStyleConditionalFilterComponent, IgxExcelStyleMovingComponent, IgxExcelStyleSearchComponent, - IgxTreeGridExcelStyleSearchComponent, IgxExcelStyleHeaderComponent ], imports: [ diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html deleted file mode 100644 index 16718fa3edd..00000000000 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.html +++ /dev/null @@ -1,87 +0,0 @@ - - search - - - clear - - - - - -
{{item.label}}
- -
{{childLevel1.label}}
- -
{{childLevel2.label}}
- -
{{childLevel3.label}}
-
-
-
-
-
- - - - - - - - - -
-
- -
-
- -
-
diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts deleted file mode 100644 index 0bd4688b789..00000000000 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/tree-grid-excel-style-search.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - Component -} from '@angular/core'; -import { IgxExcelStyleSearchComponent } from './excel-style-search.component'; - -/** - * A component used for presenting Excel style search UI for hierarchical tree-view structured data. - */ -@Component({ - selector: 'igx-tree-grid-excel-style-search', - templateUrl: './tree-grid-excel-style-search.component.html' -}) -export class IgxTreeGridExcelStyleSearchComponent extends IgxExcelStyleSearchComponent { - // /** - // * @hidden @internal - // */ - // public get containerSize() { - // if (this.esf.listData.length) { - // return this.list.element.nativeElement.offsetHeight; - // } - - // // // GE Nov 1st, 2021 #10355 Return a numeric value, so the chunk size is calculated properly. - // // // If we skip this branch, on applying the filter the _calculateChunkSize() method off the ForOfDirective receives - // // // an igxForContainerSize = undefined, thus assigns the chunkSize to the igxForOf.length which leads to performance issues. - // return 0; - // } -} \ No newline at end of file diff --git a/src/app/tree-grid/tree-grid.sample.html b/src/app/tree-grid/tree-grid.sample.html index f2edcf5a26e..4c329e58a5a 100644 --- a/src/app/tree-grid/tree-grid.sample.html +++ b/src/app/tree-grid/tree-grid.sample.html @@ -24,7 +24,7 @@
- + --> Date: Thu, 20 Jan 2022 10:12:37 +0200 Subject: [PATCH 05/63] feat(tree-esf-search): moved hier filter strategy --- .../lib/data-operations/filtering-strategy.ts | 76 +------------------ .../grid.excel-style-filtering.component.ts | 4 +- .../tree-grid/tree-grid.filtering.strategy.ts | 71 ++++++++++++++++- 3 files changed, 74 insertions(+), 77 deletions(-) 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 05645c9b251..41cbfc47964 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -2,6 +2,7 @@ import { FilteringLogic, IFilteringExpression } from './filtering-expression.int import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree'; import { resolveNestedPath, parseDate } from '../core/utils'; import { ColumnType, GridType } from '../grids/common/grid.interface'; +import { IHierarchicalItem } from '../grids/tree-grid/tree-grid.filtering.strategy'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -12,11 +13,6 @@ export interface IFilteringStrategy { grid?: GridType): any[]; } -export interface IHierarchicalItem { - value: any; - children?: IHierarchicalItem[]; -} - export class NoopFilteringStrategy implements IFilteringStrategy { private static _instance: NoopFilteringStrategy = null; @@ -33,10 +29,6 @@ export class NoopFilteringStrategy implements IFilteringStrategy { export abstract class BaseFilteringStrategy implements IFilteringStrategy { public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean { - if (rec.data) { - rec = rec.data; - } - const cond = expr.condition; const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid); return cond.logic(val, expr.searchVal, expr.ignoreCase); @@ -84,7 +76,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { public getColumnValues( column: ColumnType, tree: FilteringExpressionsTree, - done: (values: any[] | IHierarchicalItem[]) => void) { + done: (values: any[] | IHierarchicalItem[]) => void) { //TODO: Check import const data = column.grid.gridAPI.filterDataByExpressions(tree); const columnField = column.field; @@ -161,67 +153,3 @@ export class FormattedValuesFilteringStrategy extends FilteringStrategy { return value; } } - -export class HierarchicalFilteringStrategy extends FilteringStrategy { - private processedData: IHierarchicalItem[]; - private childDataKey; - - constructor(public hierarchicalFilterFields: string[]) { - super(); - } - - public override getColumnValues( - column: ColumnType, - tree: FilteringExpressionsTree, - done: (values: any[] | IHierarchicalItem[]) => void): void { - - if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { - return super.getColumnValues(column, tree, done); - } - - this.processedData = []; - this.childDataKey = column.grid.childDataKey; - const data = column.grid.gridAPI.filterDataByExpressions(tree); - const columnField = column.field; - let columnValues = []; - columnValues = data.map(record => { - if (this.processedData.indexOf(record) < 0) { // TODO: add check for DATE - let hierarchicalItem: IHierarchicalItem; - hierarchicalItem = { value: resolveNestedPath(record, columnField) }; - // if (shouldFormatValues) { - // value = this.column.formatter(value, record); - // } - hierarchicalItem.children = this.getChildren(record, columnField) - return hierarchicalItem; - } - }); - columnValues = columnValues.filter(function(el) { - return el !== undefined - }); - - done(columnValues); - } - - private getChildren(record: any, columnField: string) { - this.processedData.push(record); - let childrenValues = []; - const children = record[this.childDataKey]; - if (children) { - children.forEach(child => { - if (this.processedData.indexOf(child) < 0) { - let hierarchicalItem: IHierarchicalItem; - hierarchicalItem = { value: resolveNestedPath(child, columnField) }; - // if (shouldFormatValues) { - // value = this.column.formatter(value, record); - // } - hierarchicalItem.children = this.getChildren(child, columnField) - childrenValues.push(hierarchicalItem); - } - }); - } else { - // TODO: unique values on last level - } - - return childrenValues; - } -} diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index e6a1b7253c9..df2a2ec90ef 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -24,8 +24,8 @@ import { GridColumnDataType } from '../../../data-operations/data-util'; import { Subscription } from 'rxjs'; import { DisplayDensity } from '../../../core/density'; import { GridSelectionMode } from '../../common/enums'; -import { BaseFilteringStrategy, FormattedValuesFilteringStrategy, IHierarchicalItem } from '../../../data-operations/filtering-strategy'; -import { TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; +import { BaseFilteringStrategy, FormattedValuesFilteringStrategy } from '../../../data-operations/filtering-strategy'; +import { IHierarchicalItem, TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem, generateExpressionsList } from './common'; 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 8d05210b880..054c7091380 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 @@ -2,9 +2,14 @@ import { parseDate, resolveNestedPath } from '../../core/utils'; import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { BaseFilteringStrategy } from '../../data-operations/filtering-strategy'; -import { GridType } from '../common/grid.interface'; +import { ColumnType, GridType } from '../common/grid.interface'; import { ITreeGridRecord } from './tree-grid.interfaces'; +export interface IHierarchicalItem { + value: any; + children?: IHierarchicalItem[]; +} + export class TreeGridFilteringStrategy extends BaseFilteringStrategy { public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { @@ -117,3 +122,67 @@ export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilter return rec; } } + +export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { + private processedData: IHierarchicalItem[]; + private childDataKey; + + constructor(public hierarchicalFilterFields: string[]) { + super(); + } + + public override getColumnValues( + column: ColumnType, + tree: FilteringExpressionsTree, + done: (values: any[] | IHierarchicalItem[]) => void): void { + + if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { + return super.getColumnValues(column, tree, done); + } + + this.processedData = []; + this.childDataKey = column.grid.childDataKey; + const data = column.grid.gridAPI.filterDataByExpressions(tree); + const columnField = column.field; + let columnValues = []; + columnValues = data.map(record => { + if (this.processedData.indexOf(record) < 0) { // TODO: add check for DATE + let hierarchicalItem: IHierarchicalItem; + hierarchicalItem = { value: resolveNestedPath(record, columnField) }; + // if (shouldFormatValues) { + // value = this.column.formatter(value, record); + // } + hierarchicalItem.children = this.getChildren(record, columnField) + return hierarchicalItem; + } + }); + columnValues = columnValues.filter(function(el) { + return el !== undefined + }); + + done(columnValues); + } + + private getChildren(record: any, columnField: string) { + this.processedData.push(record); + let childrenValues = []; + const children = record[this.childDataKey]; + if (children) { + children.forEach(child => { + if (this.processedData.indexOf(child) < 0) { + let hierarchicalItem: IHierarchicalItem; + hierarchicalItem = { value: resolveNestedPath(child, columnField) }; + // if (shouldFormatValues) { + // value = this.column.formatter(value, record); + // } + hierarchicalItem.children = this.getChildren(child, columnField) + childrenValues.push(hierarchicalItem); + } + }); + } else { + // TODO: unique values on last level + } + + return childrenValues; + } +} \ No newline at end of file From f5c8d6369ee5a5d301f33003c20868884c346d2e Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 21 Jan 2022 14:18:54 +0200 Subject: [PATCH 06/63] feat(tree-esf-search): tree node add indeterminate --- .../excel-style-search.component.html | 14 ++--- .../excel-style-search.component.ts | 56 +++++++++++++++++++ .../lib/tree/tree-node/tree-node.component.ts | 7 +++ .../src/lib/tree/tree-selection.service.ts | 8 +++ 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index 984b5f74cfb..d31554113fb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -45,18 +45,18 @@
- - + +
{{item.label}}
- +
{{childLevel1.label}}
- +
{{childLevel2.label}}
- +
{{childLevel3.label}}
- +
{{childLevel4.label}}
- +
{{childLevel5.label}}
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 66b46957784..1fa09d954ae 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 @@ -25,6 +25,7 @@ import { takeUntil } from 'rxjs/operators'; import { PlatformUtil } from '../../../core/utils'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem } from './common'; +import { IgxTreeComponent, ITreeNodeSelectionEvent } from '../../../tree/public_api'; @Directive({ selector: '[igxExcelStyleLoading]' @@ -61,6 +62,12 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { @ViewChild('list', { read: IgxListComponent, static: true }) public list: IgxListComponent; + /** + * @hidden @internal + */ + @ViewChild('tree', { read: IgxTreeComponent, static: true }) + public tree: IgxTreeComponent; + /** * @hidden @internal */ @@ -231,6 +238,55 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { eventArgs.checkbox.nativeCheckbox.nativeElement.blur(); } + + /** + * @hidden @internal + */ + public onNodeSelectionChange(eventArgs: ITreeNodeSelectionEvent) { + const selectAllBtn = this.displayedListData[0]; + if (eventArgs.removed.length === 1 && eventArgs.removed[0].data === selectAllBtn) { + this.displayedListData.forEach(element => { + if (element === this.addToCurrentFilter) { + return; + } + element.isSelected = false; + }); + selectAllBtn.indeterminate = false; + } else if (eventArgs.added.length === 1 && eventArgs.added[0].data === selectAllBtn) { + this.displayedListData.forEach(element => { + if (element === this.addToCurrentFilter) { + return; + } + element.isSelected = true; + }); + selectAllBtn.indeterminate = false; + } else { + eventArgs.added.forEach(node => { + (node.data as FilterListItem).isSelected = true; + }); + eventArgs.removed.forEach(node => { + (node.data as FilterListItem).isSelected = false; + }); + + const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilter) > -1 ? 2 : 1; + + const slicedArray = + this.displayedListData.slice(indexToStartSlicing, this.displayedListData.length); + + if (!slicedArray.find(el => el.isSelected === false)) { + selectAllBtn.indeterminate = false; + selectAllBtn.isSelected = true; + } else if (!slicedArray.find(el => el.isSelected === true)) { + selectAllBtn.indeterminate = false; + selectAllBtn.isSelected = false; + } else { + selectAllBtn.indeterminate = true; + } + } + + eventArgs.owner.nodes.first.indeterminate = selectAllBtn.indeterminate; + } + /** * @hidden @internal */ diff --git a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts index 6d69107c4f7..f6786b2985e 100644 --- a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts +++ b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts @@ -403,6 +403,13 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig return this.selectionService.isNodeIndeterminate(this); } + /** + * @hidden @internal + */ + public set indeterminate(state: boolean) { + this.selectionService.setIndeterminate(this, state); + } + /** The depth of the node, relative to the root * * ```html diff --git a/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts b/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts index 1c9bbc748c0..b8e886127d9 100644 --- a/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts +++ b/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts @@ -123,6 +123,14 @@ export class IgxTreeSelectionService { }); } + public setIndeterminate(node: IgxTreeNode, shouldBeIndeterminate: boolean) { + if (shouldBeIndeterminate) { + this.nodesToBeIndeterminate.add(node); + } else { + this.nodesToBeIndeterminate.delete(node); + } + } + /** Retriggers a node's selection state */ private retriggerNodeState(node: IgxTreeNode): void { if (node.selected) { From 41c1ebce15ba5f6f35844f7602eeaec07c6febac Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 31 Jan 2022 16:38:23 +0200 Subject: [PATCH 07/63] feat(tree-esf-search): set default filter strategy --- .../src/lib/grids/common/grid.interface.ts | 4 ++-- .../excel-style/excel-style-search.component.ts | 4 ++-- .../src/lib/grids/grid-base.directive.ts | 12 ++++++------ .../src/lib/grids/tree-grid/tree-grid.component.ts | 2 ++ .../src/lib/tree/tree-node/tree-node.component.ts | 6 ------ .../src/lib/tree/tree-selection.service.ts | 8 -------- 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index eb78c3ded15..ae693b3dfd8 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -19,7 +19,7 @@ import { IgxPaginatorComponent } from '../../paginator/paginator.component'; import { IgxCell, IgxEditRow } from './crud.service'; import { GridSelectionRange } from './types'; import { FilteringLogic } from '../../data-operations/filtering-expression.interface'; -import { IFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { BaseFilteringStrategy } from '../../data-operations/filtering-strategy'; import { DropPosition, IgxColumnMovingService } from '../moving/moving.service'; import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive'; import { Observable, Subject } from 'rxjs'; @@ -488,7 +488,7 @@ export interface GridType extends IGridDataBindable { sortStrategy: IGridSortingStrategy; groupStrategy?: IGridGroupingStrategy; filteringLogic: FilteringLogic; - filterStrategy: IFilteringStrategy; + filterStrategy: BaseFilteringStrategy; allowAdvancedFiltering: boolean; sortingExpressions: ISortingExpression[]; sortingExpressionsChange: EventEmitter; 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 1fa09d954ae..405d8acbc3d 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 @@ -59,13 +59,13 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - @ViewChild('list', { read: IgxListComponent, static: true }) + @ViewChild('list', { read: IgxListComponent, static: false }) public list: IgxListComponent; /** * @hidden @internal */ - @ViewChild('tree', { read: IgxTreeComponent, static: true }) + @ViewChild('tree', { read: IgxTreeComponent, static: false }) public tree: IgxTreeComponent; /** 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 40dd7e1edd4..d74ae4302e8 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -77,7 +77,7 @@ import { IgxExcelStyleLoadingValuesTemplateDirective } from './filtering/excel-s import { IgxGridColumnResizerComponent } from './resizing/resizer.component'; import { CharSeparatedValueData } from '../services/csv/char-separated-value-data'; import { IgxColumnResizingService } from './resizing/resizing.service'; -import { IFilteringStrategy } from '../data-operations/filtering-strategy'; +import { BaseFilteringStrategy, FilteringStrategy } from '../data-operations/filtering-strategy'; import { IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective, @@ -2061,12 +2061,12 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ @Input() - public get filterStrategy(): IFilteringStrategy { - return this._filteringStrategy; + public get filterStrategy(): BaseFilteringStrategy { + return this._filterStrategy; } - public set filterStrategy(classRef: IFilteringStrategy) { - this._filteringStrategy = classRef; + public set filterStrategy(classRef: BaseFilteringStrategy) { + this._filterStrategy = classRef; } /** @@ -2729,6 +2729,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements protected _userOutletDirective: IgxOverlayOutletDirective; protected _transactions: TransactionService; protected _batchEditing = false; + protected _filterStrategy = new FilteringStrategy() as BaseFilteringStrategy; /** @hidden @internal */ public get paginator() { @@ -2754,7 +2755,6 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements private _isLoading = false; private _locale: string; private overlayIDs = []; - private _filteringStrategy: IFilteringStrategy; private _sortingStrategy: IGridSortingStrategy; private _pinning: IPinningConfig = { columns: ColumnPinningPosition.Start }; diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts index cb461a85083..0b0912048a4 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts @@ -58,6 +58,7 @@ import { DisplayDensityToken, IDisplayDensityOptions } from '../../core/density' import { HierarchicalTransactionService } from '../../services/transaction/hierarchical-transaction'; import { IgxOverlayService } from '../../services/overlay/overlay'; import { IgxGridTransaction } from '../common/types'; +import { TreeGridFilteringStrategy } from './tree-grid.filtering.strategy'; let NEXT_ID = 0; @@ -271,6 +272,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy */ public loadingRows = new Set(); + protected _filterStrategy = new TreeGridFilteringStrategy(); protected _transactions: HierarchicalTransactionService; private _data; private _rowLoadingIndicatorTemplate: TemplateRef; diff --git a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts index f6786b2985e..d93ad91e2c4 100644 --- a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts +++ b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts @@ -403,12 +403,6 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig return this.selectionService.isNodeIndeterminate(this); } - /** - * @hidden @internal - */ - public set indeterminate(state: boolean) { - this.selectionService.setIndeterminate(this, state); - } /** The depth of the node, relative to the root * diff --git a/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts b/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts index b8e886127d9..1c9bbc748c0 100644 --- a/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts +++ b/projects/igniteui-angular/src/lib/tree/tree-selection.service.ts @@ -123,14 +123,6 @@ export class IgxTreeSelectionService { }); } - public setIndeterminate(node: IgxTreeNode, shouldBeIndeterminate: boolean) { - if (shouldBeIndeterminate) { - this.nodesToBeIndeterminate.add(node); - } else { - this.nodesToBeIndeterminate.delete(node); - } - } - /** Retriggers a node's selection state */ private retriggerNodeState(node: IgxTreeNode): void { if (node.selected) { From 852ffe4c20e9bcd49b661de8ae70c26d634f64c2 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 1 Feb 2022 10:59:30 +0200 Subject: [PATCH 08/63] chore(*): checks for undefined --- .../filtering/excel-style/excel-style-search.component.ts | 4 ++-- .../excel-style/grid.excel-style-filtering.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 405d8acbc3d..7c10133889e 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 @@ -473,8 +473,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public isHierarchical() { - return this.esf.grid.filterStrategy?.hasOwnProperty('hierarchicalFilterFields') - && this.esf.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.esf.column.field) >= 0; + return this.esf.grid?.filterStrategy?.hasOwnProperty('hierarchicalFilterFields') + && this.esf.grid?.filterStrategy['hierarchicalFilterFields'].indexOf(this.esf.column.field) >= 0; } private createCondition(conditionName: string) { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index df2a2ec90ef..e97b94911d3 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -747,7 +747,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { - if (element.value) { + if (element?.value) { element = element.value; } From ac349d40566039eec4ab0cab1bed7dd85c5254fc Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 2 Feb 2022 15:23:27 +0200 Subject: [PATCH 09/63] feat(tree-esf-search): select and current checkbox --- .../excel-style-search.component.html | 31 +++- .../excel-style-search.component.ts | 140 ++++++++++++------ .../grid.excel-style-filtering.component.ts | 4 + 3 files changed, 125 insertions(+), 50 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index d31554113fb..e4e2c02ebfc 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -27,9 +27,9 @@ {{ item.label }} @@ -45,8 +45,30 @@
- - +
+ + {{ selectAllItem.label }} + + + + + +
{{item.label}}
{{childLevel1.label}}
@@ -65,6 +87,7 @@
+
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 7c10133889e..caa2ca72ca3 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 @@ -20,7 +20,7 @@ import { } from '../../../data-operations/filtering-condition'; import { Subject } from 'rxjs'; import { IgxListComponent } from '../../../list/public_api'; -import { IChangeCheckboxEventArgs } from '../../../checkbox/checkbox.component'; +import { IChangeCheckboxEventArgs, IgxCheckboxComponent } from '../../../checkbox/checkbox.component'; import { takeUntil } from 'rxjs/operators'; import { PlatformUtil } from '../../../core/utils'; import { BaseFilteringComponent } from './base-filtering.component'; @@ -62,6 +62,18 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { @ViewChild('list', { read: IgxListComponent, static: false }) public list: IgxListComponent; + /** + * @hidden @internal + */ + @ViewChild('selectAllCheckbox', { read: IgxCheckboxComponent, static: false }) + public selectAllCheckbox: IgxCheckboxComponent; + + /** + * @hidden @internal + */ + @ViewChild('addToCurrentFilterCheckbox', { read: IgxCheckboxComponent, static: false }) + public addToCurrentFilterCheckbox: IgxCheckboxComponent; + /** * @hidden @internal */ @@ -83,8 +95,29 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public get addToCurrentFilter(): FilterListItem { - if (!this._addToCurrentFilter) { + public get selectAllItem(): FilterListItem { + if (!this._selectAllItem) { + const selectAllItem = { + isSelected: false, + isFiltered: false, + indeterminate: false, + isSpecial: true, + isBlanks: false, + value: this.esf.grid.resourceStrings.igx_grid_excel_select_all, + label: this.esf.grid.resourceStrings.igx_grid_excel_select_all + }; + + this._selectAllItem = selectAllItem; + } + + return this._selectAllItem; + } + + /** + * @hidden @internal + */ + public get addToCurrentFilterItem(): FilterListItem { + if (!this._addToCurrentFilterItem) { const addToCurrentFilterItem = { isSelected: false, isFiltered: false, @@ -95,10 +128,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { label: this.esf.grid.resourceStrings.igx_grid_excel_add_to_filter }; - this._addToCurrentFilter = addToCurrentFilterItem; + this._addToCurrentFilterItem = addToCurrentFilterItem; } - return this._addToCurrentFilter; + return this._addToCurrentFilterItem; } /** @@ -140,7 +173,9 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } private _isLoading; - private _addToCurrentFilter: FilterListItem; + private _selectAllItem: FilterListItem; + private _addToCurrentFilterItem: FilterListItem; + private _hierarchicalSelectedItems: FilterListItem[]; private destroy$ = new Subject(); constructor(public cdr: ChangeDetectorRef, public esf: BaseFilteringComponent, protected platform: PlatformUtil) { @@ -174,6 +209,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } public ngAfterViewInit() { + if (this.isHierarchical()) { + this._selectAllItem = this.esf.listData[0]; + this.esf.listData.splice(0, 1); + } requestAnimationFrame(this.refreshSize); } @@ -211,7 +250,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { if (selectedIndex === 0) { this.displayedListData.forEach(element => { - if (element === this.addToCurrentFilter) { + if (element === this.addToCurrentFilterItem) { return; } element.isSelected = eventArgs.checked; @@ -220,7 +259,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { selectAllBtn.indeterminate = false; } else { eventArgs.checkbox.value.isSelected = eventArgs.checked; - const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilter) > -1 ? 2 : 1; + const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilterItem) > -1 ? 2 : 1; const slicedArray = this.displayedListData.slice(indexToStartSlicing, this.displayedListData.length); @@ -238,29 +277,24 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { eventArgs.checkbox.nativeCheckbox.nativeElement.blur(); } + /** + * @hidden @internal + */ + public onSelectAllCheckboxChange(eventArgs: IChangeCheckboxEventArgs) { + const treeNodes = this.tree.nodes; + treeNodes.forEach(node => (node.data as FilterListItem).isSelected = eventArgs.checked); + } + /** + * @hidden @internal + */ + public onAddToCurrentFilterCheckboxChange(eventArgs: IChangeCheckboxEventArgs) { + } + /** * @hidden @internal */ public onNodeSelectionChange(eventArgs: ITreeNodeSelectionEvent) { - const selectAllBtn = this.displayedListData[0]; - if (eventArgs.removed.length === 1 && eventArgs.removed[0].data === selectAllBtn) { - this.displayedListData.forEach(element => { - if (element === this.addToCurrentFilter) { - return; - } - element.isSelected = false; - }); - selectAllBtn.indeterminate = false; - } else if (eventArgs.added.length === 1 && eventArgs.added[0].data === selectAllBtn) { - this.displayedListData.forEach(element => { - if (element === this.addToCurrentFilter) { - return; - } - element.isSelected = true; - }); - selectAllBtn.indeterminate = false; - } else { eventArgs.added.forEach(node => { (node.data as FilterListItem).isSelected = true; }); @@ -268,25 +302,20 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { (node.data as FilterListItem).isSelected = false; }); - const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilter) > -1 ? 2 : 1; - - const slicedArray = - this.displayedListData.slice(indexToStartSlicing, this.displayedListData.length); - - if (!slicedArray.find(el => el.isSelected === false)) { - selectAllBtn.indeterminate = false; - selectAllBtn.isSelected = true; - } else if (!slicedArray.find(el => el.isSelected === true)) { + this._hierarchicalSelectedItems = eventArgs.newSelection.map(item => item.data as FilterListItem); + const selectAllBtn = this.selectAllItem; + if (this._hierarchicalSelectedItems.length === 0) { selectAllBtn.indeterminate = false; selectAllBtn.isSelected = false; + } else if (this._hierarchicalSelectedItems.length === this.tree.nodes.length) { + selectAllBtn.indeterminate = false; + selectAllBtn.isSelected = true; } else { selectAllBtn.indeterminate = true; + selectAllBtn.isSelected = false; } } - eventArgs.owner.nodes.first.indeterminate = selectAllBtn.indeterminate; - } - /** * @hidden @internal */ @@ -318,8 +347,12 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public get applyButtonDisabled(): boolean { - return this.esf.listData[0] && !this.esf.listData[0].isSelected && !this.esf.listData[0].indeterminate || - this.displayedListData && this.displayedListData.length === 0; + if (this.isHierarchical()) { + return (this._hierarchicalSelectedItems ? this._hierarchicalSelectedItems.length === 0 : false); + } else { + return (this.esf.listData[0] && !this.esf.listData[0].isSelected && !this.esf.listData[0].indeterminate) || + (this.displayedListData && this.displayedListData.length === 0); + } } /** @@ -358,19 +391,24 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return; } - const searchAllBtn = this.esf.listData[0]; + let selectAllBtn; + if (this._selectAllItem) { + selectAllBtn = this._selectAllItem; + } else { + selectAllBtn = this.esf.listData[0]; + } if (!this.searchValue) { const anyFiltered = this.esf.listData.some(i => i.isFiltered); const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); if (anyFiltered && anyUnfiltered) { - searchAllBtn.indeterminate = true; + selectAllBtn.indeterminate = true; } this.esf.listData.forEach(i => i.isSelected = i.isFiltered); this.displayedListData = this.esf.listData; - searchAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all; + selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all; return; } @@ -401,13 +439,23 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { const filterTree = new FilteringExpressionsTree(FilteringLogic.Or, this.esf.column.field); const item = this.displayedListData[1]; - const addToCurrentFilterOptionVisible = item === this.addToCurrentFilter; + const addToCurrentFilterOptionVisible = item === this.addToCurrentFilterItem; - const selectedItems = addToCurrentFilterOptionVisible && item.isSelected ? + let selectedItems = []; + if (this.isHierarchical()) { + selectedItems = this._hierarchicalSelectedItems; + } else { + selectedItems = addToCurrentFilterOptionVisible && item.isSelected ? this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) : this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected); + } - const unselectedItem = this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === false); + let unselectedItem; + if (this.isHierarchical()) { + unselectedItem = this.esf.listData.find(el => el.isSelected === false); + } else { + unselectedItem = this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === false); + } if (unselectedItem) { if (selectedItems.length <= IgxExcelStyleSearchComponent.filterOptimizationThreshold) { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index e97b94911d3..f18f3af658a 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -794,6 +794,10 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getExpressionValue(element: any): string { + if (this.isHierarchical()) { + element = element.value; + } + let value; if (this.column.dataType === GridColumnDataType.Date) { value = element && element.value ? new Date(element.value).toISOString() : element.value; From d94847f55e1620c1e9f9be7a65e6c70205fa7080 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 3 Feb 2022 18:36:45 +0200 Subject: [PATCH 10/63] feat(tree-esf-search): selection fixes --- .../lib/data-operations/filtering-strategy.ts | 4 +- .../excel-style-search.component.html | 42 +++--- .../excel-style-search.component.ts | 127 ++++++++++++++---- .../grid.excel-style-filtering.component.ts | 30 ++--- 4 files changed, 142 insertions(+), 61 deletions(-) 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 41cbfc47964..56dec7a161d 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -76,13 +76,13 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { public getColumnValues( column: ColumnType, tree: FilteringExpressionsTree, - done: (values: any[] | IHierarchicalItem[]) => void) { //TODO: Check import + done: (values: any[] | IHierarchicalItem[]) => void) { const data = column.grid.gridAPI.filterDataByExpressions(tree); const columnField = column.field; let columnValues; columnValues = data.map(record => resolveNestedPath(record, columnField)); - done(columnValues); + done(columnValues); // TODO: change to promise } public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree, diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index e4e2c02ebfc..d67fb7f271f 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -57,7 +57,7 @@ {{ selectAllItem.label }} - + -
{{item.label}}
- -
{{childLevel1.label}}
- -
{{childLevel2.label}}
- -
{{childLevel3.label}}
- -
{{childLevel4.label}}
- -
{{childLevel5.label}}
+
{{item.label}}
+ +
{{childLevel1.label}}
+ +
{{childLevel2.label}}
+ +
{{childLevel3.label}}
+ +
{{childLevel4.label}}
+ +
{{childLevel5.label}}
+ +
{{childLevel6.label}}
+ +
{{childLevel7.label}}
+ +
{{childLevel8.label}}
+ +
{{childLevel9.label}}
+
+
+
+
+
-
-
+
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 caa2ca72ca3..b915b10540f 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 @@ -22,7 +22,7 @@ import { Subject } from 'rxjs'; import { IgxListComponent } from '../../../list/public_api'; import { IChangeCheckboxEventArgs, IgxCheckboxComponent } from '../../../checkbox/checkbox.component'; import { takeUntil } from 'rxjs/operators'; -import { PlatformUtil } from '../../../core/utils'; +import { cloneHierarchicalArray, PlatformUtil } from '../../../core/utils'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem } from './common'; import { IgxTreeComponent, ITreeNodeSelectionEvent } from '../../../tree/public_api'; @@ -155,7 +155,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public searchValue: any; - + /** * @hidden @internal */ @@ -284,7 +284,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { const treeNodes = this.tree.nodes; treeNodes.forEach(node => (node.data as FilterListItem).isSelected = eventArgs.checked); } - + /** * @hidden @internal */ @@ -295,26 +295,26 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public onNodeSelectionChange(eventArgs: ITreeNodeSelectionEvent) { - eventArgs.added.forEach(node => { - (node.data as FilterListItem).isSelected = true; - }); - eventArgs.removed.forEach(node => { - (node.data as FilterListItem).isSelected = false; - }); + eventArgs.added.forEach(node => { + (node.data as FilterListItem).isSelected = true; + }); + eventArgs.removed.forEach(node => { + (node.data as FilterListItem).isSelected = false; + }); this._hierarchicalSelectedItems = eventArgs.newSelection.map(item => item.data as FilterListItem); const selectAllBtn = this.selectAllItem; if (this._hierarchicalSelectedItems.length === 0) { - selectAllBtn.indeterminate = false; - selectAllBtn.isSelected = false; + selectAllBtn.indeterminate = false; + selectAllBtn.isSelected = false; } else if (this._hierarchicalSelectedItems.length === this.tree.nodes.length) { selectAllBtn.indeterminate = false; selectAllBtn.isSelected = true; - } else { - selectAllBtn.indeterminate = true; + } else { + selectAllBtn.indeterminate = true; selectAllBtn.isSelected = false; - } } + } /** * @hidden @internal @@ -408,27 +408,91 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { this.esf.listData.forEach(i => i.isSelected = i.isFiltered); this.displayedListData = this.esf.listData; + this.esf.detectChanges(); selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all; return; } const searchVal = this.searchValue.toLowerCase(); - this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) || - (it.label !== null && it.label !== undefined) && - !it.isBlanks && - it.label.toString().toLowerCase().indexOf(searchVal) > -1); + if (this.isHierarchical()) { + // TODO: add to current filter + + this._hierarchicalSelectedItems = []; + this.esf.listData.forEach(i => i.isSelected = false); + const matchedData = cloneHierarchicalArray(this.esf.listData, 'children'); + this.displayedListData = this.selectMatches(matchedData, searchVal); + this.esf.detectChanges(); + this.tree.nodes.forEach(n => { + n.selected = true; + if ((n.data as FilterListItem).label.toString().toLowerCase().indexOf(searchVal) > -1) { + this.expandAllParentNodes(n); + } + }); - this.esf.listData.forEach(i => i.isSelected = false); - this.displayedListData.forEach(i => i.isSelected = true); + // set state for select all btn + } else { + this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) || + (it.label !== null && it.label !== undefined) && + !it.isBlanks && + it.label.toString().toLowerCase().indexOf(searchVal) > -1); + + this.esf.listData.forEach(i => i.isSelected = false); + this.displayedListData.forEach(i => i.isSelected = true); + this.displayedListData.splice(1, 0, this.addToCurrentFilterItem); + if (this.displayedListData.length === 2) { + this.displayedListData = []; + } + } + + selectAllBtn.indeterminate = false; + selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results; + this.esf.detectChanges(); + } - this.displayedListData.splice(1, 0, this.addToCurrentFilter); + private selectMatches(data: FilterListItem[], searchVal: string) { + data.forEach(element => { + element.indeterminate = false; + element.isSelected = false; + const node = this.tree.nodes.filter(n => (n.data as FilterListItem).label === element.label)[0]; + if (node) { + node.expanded = false; + } - searchAllBtn.indeterminate = false; - searchAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results; + if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) { + element.isSelected = true; + this.selectAllChildren(element); + this._hierarchicalSelectedItems.push(element); + } else if (element.children.length > 0) { + element.children = this.selectMatches(element.children, searchVal); + // this.selectMatches(element.children, searchVal); + if (element.children.length > 0) { + element.isSelected = true; + if (node) { + node.expanded = true; + } + } + } + }); - if (this.displayedListData.length === 2) { - this.displayedListData = []; + return data.filter(element => element.isSelected === true); + } + + private selectAllChildren(element: FilterListItem) { + element.children.forEach(child => { + child.indeterminate = false; + child.isSelected = true; + this._hierarchicalSelectedItems.push(child); + if (child.children) { + this.selectAllChildren(child); + } + }) + } + + private expandAllParentNodes(node: any) { + if (node.parentNode) { + node.parentNode.expanded = true; + this.expandAllParentNodes(node.parentNode); } } @@ -438,13 +502,16 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { public applyFilter() { const filterTree = new FilteringExpressionsTree(FilteringLogic.Or, this.esf.column.field); - const item = this.displayedListData[1]; - const addToCurrentFilterOptionVisible = item === this.addToCurrentFilterItem; - let selectedItems = []; if (this.isHierarchical()) { - selectedItems = this._hierarchicalSelectedItems; + if (this.addToCurrentFilterCheckbox && this.addToCurrentFilterCheckbox.checked) { + // TODO + } + + selectedItems = this._hierarchicalSelectedItems; // TODO: fix apply without modifying selection } else { + const item = this.displayedListData[1]; + const addToCurrentFilterOptionVisible = item === this.addToCurrentFilterItem; selectedItems = addToCurrentFilterOptionVisible && item.isSelected ? this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) : this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected); @@ -521,6 +588,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public isHierarchical() { + // TODO: remove and check the return array type from filterStrategy.getColumnValues method + return this.esf.grid?.filterStrategy?.hasOwnProperty('hierarchicalFilterFields') && this.esf.grid?.filterStrategy['hierarchicalFilterFields'].indexOf(this.esf.column.field) >= 0; } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index f18f3af658a..30ade671e94 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -24,7 +24,7 @@ import { GridColumnDataType } from '../../../data-operations/data-util'; import { Subscription } from 'rxjs'; import { DisplayDensity } from '../../../core/density'; import { GridSelectionMode } from '../../common/enums'; -import { BaseFilteringStrategy, FormattedValuesFilteringStrategy } from '../../../data-operations/filtering-strategy'; +import { FormattedValuesFilteringStrategy } from '../../../data-operations/filtering-strategy'; import { IHierarchicalItem, TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import { BaseFilteringComponent } from './base-filtering.component'; @@ -484,21 +484,21 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - - if (this.grid.filterStrategy instanceof BaseFilteringStrategy) { - this.grid.filterStrategy.getColumnValues(this.column, expressionsTree, (colVals: any[]) => { - const columnValues = (this.column.dataType === GridColumnDataType.Date) ? - colVals.map(value => { - const label = this.getFilterItemLabel(value); - return { label, value }; - }) : colVals; // TODO: should formatter go here? - - this.renderValues(columnValues); - }); - } + + this.grid.filterStrategy.getColumnValues(this.column, expressionsTree, (colVals: any[]) => { + const columnValues = (this.column.dataType === GridColumnDataType.Date) ? + colVals.map(value => { + const label = this.getFilterItemLabel(value); + return { label, value }; + }) : colVals; // TODO: should formatter go here? + + this.renderValues(columnValues); + }); } private isHierarchical() { + // TODO: remove and check the return array type from filterStrategy.getColumnValues method + return this.grid.filterStrategy.hasOwnProperty('hierarchicalFilterFields') && this.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.column.field) >= 0; } @@ -575,7 +575,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private generateListData() { this.listData = new Array(); - const shouldUpdateSelection = this.areExpressionsSelectable() && this.areExpressionsValuesInTheList(); + const shouldUpdateSelection = (this.areExpressionsSelectable() && this.areExpressionsValuesInTheList()) || this.isHierarchical(); if (this.column.dataType === GridColumnDataType.Boolean) { this.addBooleanItems(); @@ -666,8 +666,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private addItems(shouldUpdateSelection: boolean) { this.selectAllSelected = true; - this.containsNullOrEmpty = false; this.selectAllIndeterminate = false; + this.containsNullOrEmpty = false; this.listData = this.generateFilterListItems(this.uniqueValues, shouldUpdateSelection); this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; } From ef4f0438afa120f8ab4dda19215116c56eb81d90 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 10 Feb 2022 11:34:14 +0200 Subject: [PATCH 11/63] feat(tree-esf-search): fixes and refactoring --- .../lib/data-operations/filtering-strategy.ts | 16 +- .../excel-style/base-filtering.component.ts | 1 + .../excel-style-search.component.html | 3 +- .../excel-style-search.component.ts | 148 +++++++++--------- .../grid.excel-style-filtering.component.ts | 37 +++-- .../tree-grid/tree-grid.filtering.strategy.ts | 22 +-- 6 files changed, 114 insertions(+), 113 deletions(-) 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 c5922e80072..97f62ee307a 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -3,7 +3,6 @@ import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering import { resolveNestedPath, parseDate } from '../core/utils'; import { ColumnType, GridType, PivotGridType } from '../grids/common/grid.interface'; import { PivotUtil } from '../grids/pivot-grid/pivot-util'; -import { IHierarchicalItem } from '../grids/tree-grid/tree-grid.filtering.strategy'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -14,6 +13,11 @@ export interface IFilteringStrategy { grid?: GridType): any[]; } +export class HierarchicalColumnValue { + public value: any; + public children?: HierarchicalColumnValue[]; +} + export class NoopFilteringStrategy implements IFilteringStrategy { private static _instance: NoopFilteringStrategy = null; @@ -76,14 +80,12 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { // move formatting from base implementation to formatted values strategies - grid and treegrid public getColumnValues( column: ColumnType, - tree: FilteringExpressionsTree, - done: (values: any[] | IHierarchicalItem[]) => void) { - + tree: FilteringExpressionsTree) : Promise { const data = column.grid.gridAPI.filterDataByExpressions(tree); const columnField = column.field; let columnValues; columnValues = data.map(record => resolveNestedPath(record, columnField)); - done(columnValues); // TODO: change to promise + return Promise.resolve(columnValues); } public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree, @@ -93,14 +95,14 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { } export class FilteringStrategy extends BaseFilteringStrategy { - private static _instace: FilteringStrategy = null; + private static _instance: FilteringStrategy = null; constructor() { super(); } public static instance() { - return this._instace || (this._instace = new this()); + return this._instance || (this._instance = new this()); } public filter(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree, 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 9a8b3aa4c94..fe6f2daa571 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 @@ -17,6 +17,7 @@ export abstract class BaseFilteringComponent { public abstract mainDropdown: ElementRef; public abstract expressionsList: ExpressionUI[]; public abstract listData: FilterListItem[]; + public abstract isHierarchical: boolean; public abstract loadingStart: EventEmitter; public abstract loadingEnd: EventEmitter; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index d67fb7f271f..280a18b3900 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -62,8 +62,7 @@ [tabindex]="-1" [checked]="addToCurrentFilterItem.isSelected" [disableRipple]="true" - [disableTransitions]="true" - (change)="onAddToCurrentFilterCheckboxChange($event)"> + [disableTransitions]="true"> {{ addToCurrentFilterItem.label }} 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 2a6efa2941c..4f6509339d3 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 @@ -95,7 +95,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public get selectAllItem(): FilterListItem { + public get selectAllItem(): FilterListItem { if (!this._selectAllItem) { const selectAllItem = { isSelected: false, @@ -173,8 +173,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } private _isLoading; - private _selectAllItem: FilterListItem; private _addToCurrentFilterItem: FilterListItem; + private _selectAllItem: FilterListItem; private _hierarchicalSelectedItems: FilterListItem[]; private destroy$ = new Subject(); @@ -205,6 +205,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } this.cdr.detectChanges(); + requestAnimationFrame(() => { + this.refreshSize(); + this.searchInput.nativeElement.focus(); + }); }); } @@ -285,12 +289,6 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { treeNodes.forEach(node => (node.data as FilterListItem).isSelected = eventArgs.checked); } - /** - * @hidden @internal - */ - public onAddToCurrentFilterCheckboxChange(eventArgs: IChangeCheckboxEventArgs) { - } - /** * @hidden @internal */ @@ -406,31 +404,31 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { selectAllBtn.indeterminate = true; } + if (this.isHierarchical()) { + this._hierarchicalSelectedItems = this.tree.nodes.map(n => n.data as FilterListItem).filter(item => item.isFiltered); + } + this.esf.listData.forEach(i => i.isSelected = i.isFiltered); this.displayedListData = this.esf.listData; - this.esf.detectChanges(); selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all; + this.cdr.detectChanges(); return; } const searchVal = this.searchValue.toLowerCase(); if (this.isHierarchical()) { - // TODO: add to current filter - this._hierarchicalSelectedItems = []; this.esf.listData.forEach(i => i.isSelected = false); const matchedData = cloneHierarchicalArray(this.esf.listData, 'children'); - this.displayedListData = this.selectMatches(matchedData, searchVal); - this.esf.detectChanges(); + this.displayedListData = this.hierarchicalSelectMatches(matchedData, searchVal); + this.cdr.detectChanges(); this.tree.nodes.forEach(n => { n.selected = true; if ((n.data as FilterListItem).label.toString().toLowerCase().indexOf(searchVal) > -1) { this.expandAllParentNodes(n); } }); - - // set state for select all btn } else { this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) || (it.label !== null && it.label !== undefined) && @@ -447,53 +445,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { selectAllBtn.indeterminate = false; selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results; - this.esf.detectChanges(); - } - - private selectMatches(data: FilterListItem[], searchVal: string) { - data.forEach(element => { - element.indeterminate = false; - element.isSelected = false; - const node = this.tree.nodes.filter(n => (n.data as FilterListItem).label === element.label)[0]; - if (node) { - node.expanded = false; - } - - if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) { - element.isSelected = true; - this.selectAllChildren(element); - this._hierarchicalSelectedItems.push(element); - } else if (element.children.length > 0) { - element.children = this.selectMatches(element.children, searchVal); - // this.selectMatches(element.children, searchVal); - if (element.children.length > 0) { - element.isSelected = true; - if (node) { - node.expanded = true; - } - } - } - }); - - return data.filter(element => element.isSelected === true); - } - - private selectAllChildren(element: FilterListItem) { - element.children.forEach(child => { - child.indeterminate = false; - child.isSelected = true; - this._hierarchicalSelectedItems.push(child); - if (child.children) { - this.selectAllChildren(child); - } - }) - } - - private expandAllParentNodes(node: any) { - if (node.parentNode) { - node.parentNode.expanded = true; - this.expandAllParentNodes(node.parentNode); - } + this.cdr.detectChanges(); } /** @@ -505,16 +457,16 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { let selectedItems = []; if (this.isHierarchical()) { if (this.addToCurrentFilterCheckbox && this.addToCurrentFilterCheckbox.checked) { - // TODO - } - - selectedItems = this._hierarchicalSelectedItems; // TODO: fix apply without modifying selection + this.addFilteredToSelectedItems(this.esf.listData); + } + + selectedItems = this._hierarchicalSelectedItems; } else { const item = this.displayedListData[1]; const addToCurrentFilterOptionVisible = item === this.addToCurrentFilterItem; selectedItems = addToCurrentFilterOptionVisible && item.isSelected ? - this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) : - this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected); + this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) : + this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected); } let unselectedItem; @@ -589,10 +541,64 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public isHierarchical() { - // TODO: remove and check the return array type from filterStrategy.getColumnValues method + return this.esf.isHierarchical; + } - return this.esf.grid?.filterStrategy?.hasOwnProperty('hierarchicalFilterFields') - && this.esf.grid?.filterStrategy['hierarchicalFilterFields'].indexOf(this.esf.column.field) >= 0; + private hierarchicalSelectMatches(data: FilterListItem[], searchVal: string) { + data.forEach(element => { + element.indeterminate = false; + element.isSelected = false; + const node = this.tree.nodes.filter(n => (n.data as FilterListItem).label === element.label)[0]; + if (node) { + node.expanded = false; + } + + if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) { + element.isSelected = true; + this.hierarchicalSelectAllChildren(element); + this._hierarchicalSelectedItems.push(element); + } else if (element.children.length > 0) { + element.children = this.hierarchicalSelectMatches(element.children, searchVal); + if (element.children.length > 0) { + element.isSelected = true; + if (node) { + node.expanded = true; + } + } + } + }); + + return data.filter(element => element.isSelected === true); + } + + private hierarchicalSelectAllChildren(element: FilterListItem) { + element.children.forEach(child => { + child.indeterminate = false; + child.isSelected = true; + this._hierarchicalSelectedItems.push(child); + if (child.children) { + this.hierarchicalSelectAllChildren(child); + } + }) + } + + private expandAllParentNodes(node: any) { + if (node.parentNode) { + node.parentNode.expanded = true; + this.expandAllParentNodes(node.parentNode); + } + } + + private addFilteredToSelectedItems(records: FilterListItem[]) { + records.forEach(record => { + if (record.children) { + this.addFilteredToSelectedItems(record.children); + } + + if (record.isFiltered && this._hierarchicalSelectedItems.indexOf(record) < 0) { + this._hierarchicalSelectedItems.push(record); + } + }) } private createCondition(conditionName: string) { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 30ade671e94..9678f4b0df8 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -24,8 +24,8 @@ import { GridColumnDataType } from '../../../data-operations/data-util'; import { Subscription } from 'rxjs'; import { DisplayDensity } from '../../../core/density'; import { GridSelectionMode } from '../../common/enums'; -import { FormattedValuesFilteringStrategy } from '../../../data-operations/filtering-strategy'; -import { IHierarchicalItem, TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; +import { FormattedValuesFilteringStrategy, HierarchicalColumnValue } from '../../../data-operations/filtering-strategy'; +import { TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem, generateExpressionsList } from './common'; @@ -197,6 +197,10 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent * @hidden @internal */ public overlayComponentId: string; + /** + * @hidden @internal + */ + public isHierarchical = false; private _minHeight; @@ -485,26 +489,21 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - this.grid.filterStrategy.getColumnValues(this.column, expressionsTree, (colVals: any[]) => { + const promise = this.grid.filterStrategy.getColumnValues(this.column, expressionsTree); + promise.then((colVals) => { + this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; const columnValues = (this.column.dataType === GridColumnDataType.Date) ? - colVals.map(value => { - const label = this.getFilterItemLabel(value); - return { label, value }; - }) : colVals; // TODO: should formatter go here? + colVals.map(value => { + const label = this.getFilterItemLabel(value); + return { label, value }; + }) : colVals; // TODO: should formatter go here? this.renderValues(columnValues); }); } - private isHierarchical() { - // TODO: remove and check the return array type from filterStrategy.getColumnValues method - - return this.grid.filterStrategy.hasOwnProperty('hierarchicalFilterFields') - && this.grid.filterStrategy['hierarchicalFilterFields'].indexOf(this.column.field) >= 0; - } - - private renderValues(columnValues: any[] | IHierarchicalItem[]) { - if (this.isHierarchical()) { + private renderValues(columnValues: any[] | HierarchicalColumnValue[]) { + if (this.isHierarchical) { this.uniqueValues = columnValues; } else { this.uniqueValues = this.generateUniqueValues(columnValues); @@ -575,7 +574,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private generateListData() { this.listData = new Array(); - const shouldUpdateSelection = (this.areExpressionsSelectable() && this.areExpressionsValuesInTheList()) || this.isHierarchical(); + const shouldUpdateSelection = (this.areExpressionsSelectable() && this.areExpressionsValuesInTheList()) || this.isHierarchical; if (this.column.dataType === GridColumnDataType.Boolean) { this.addBooleanItems(); @@ -782,7 +781,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemValue(element: any) { - if (this.isHierarchical()) { + if (this.isHierarchical) { element = element.value; } @@ -794,7 +793,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getExpressionValue(element: any): string { - if (this.isHierarchical()) { + if (this.isHierarchical) { element = element.value; } 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 054c7091380..60cc7545613 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 @@ -1,15 +1,10 @@ import { parseDate, resolveNestedPath } from '../../core/utils'; import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; -import { BaseFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { BaseFilteringStrategy, HierarchicalColumnValue } from '../../data-operations/filtering-strategy'; import { ColumnType, GridType } from '../common/grid.interface'; import { ITreeGridRecord } from './tree-grid.interfaces'; -export interface IHierarchicalItem { - value: any; - children?: IHierarchicalItem[]; -} - export class TreeGridFilteringStrategy extends BaseFilteringStrategy { public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { @@ -124,7 +119,7 @@ export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilter } export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { - private processedData: IHierarchicalItem[]; + private processedData: HierarchicalColumnValue[]; private childDataKey; constructor(public hierarchicalFilterFields: string[]) { @@ -133,11 +128,10 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { public override getColumnValues( column: ColumnType, - tree: FilteringExpressionsTree, - done: (values: any[] | IHierarchicalItem[]) => void): void { + tree: FilteringExpressionsTree) : Promise { if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { - return super.getColumnValues(column, tree, done); + return super.getColumnValues(column, tree); } this.processedData = []; @@ -147,8 +141,8 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { let columnValues = []; columnValues = data.map(record => { if (this.processedData.indexOf(record) < 0) { // TODO: add check for DATE - let hierarchicalItem: IHierarchicalItem; - hierarchicalItem = { value: resolveNestedPath(record, columnField) }; + let hierarchicalItem = new HierarchicalColumnValue(); + hierarchicalItem.value = resolveNestedPath(record, columnField); // if (shouldFormatValues) { // value = this.column.formatter(value, record); // } @@ -160,7 +154,7 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { return el !== undefined }); - done(columnValues); + return Promise.resolve(columnValues); } private getChildren(record: any, columnField: string) { @@ -170,7 +164,7 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { if (children) { children.forEach(child => { if (this.processedData.indexOf(child) < 0) { - let hierarchicalItem: IHierarchicalItem; + let hierarchicalItem: HierarchicalColumnValue; hierarchicalItem = { value: resolveNestedPath(child, columnField) }; // if (shouldFormatValues) { // value = this.column.formatter(value, record); From f2c2376009310928e52e6cc804baf54e5a90e4ba Mon Sep 17 00:00:00 2001 From: IBarakov Date: Fri, 11 Feb 2022 11:50:03 +0200 Subject: [PATCH 12/63] chore(*): add tests covering new changes --- .../tree-grid/tree-grid-filtering.spec.ts | 216 +++++++++++++++++- .../src/lib/test-utils/grid-functions.spec.ts | 22 ++ 2 files changed, 235 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index a4fbacedbb5..e30960bd4e3 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -1,5 +1,5 @@ -import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick, waitForAsync, flush } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { IgxTreeGridModule, IgxTreeGridComponent } from './public_api'; import { IgxTreeGridFilteringComponent, IgxTreeGridFilteringRowEditingComponent } from '../../test-utils/tree-grid-components.spec'; @@ -7,11 +7,15 @@ import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxStringFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand } from '../../data-operations/filtering-condition'; import { FilteringStrategy } from '../../data-operations/filtering-strategy'; -import { TreeGridFormattedValuesFilteringStrategy, TreeGridMatchingRecordsOnlyFilteringStrategy } from './tree-grid.filtering.strategy'; +import { HierarchicalFilteringStrategy, TreeGridFormattedValuesFilteringStrategy, TreeGridMatchingRecordsOnlyFilteringStrategy } from './tree-grid.filtering.strategy'; import { FilterMode } from '../common/enums'; import { GridFunctions } from '../../test-utils/grid-functions.spec'; -import { wait } from '../../test-utils/ui-interactions.spec'; +import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; +import { TreeTestFunctions } from '../../tree/tree-functions.spec'; +import { By } from '@angular/platform-browser'; + +const IGX_CHECKBOX_LABEL = '.igx-checkbox__label'; describe('IgxTreeGrid - Filtering actions #tGrid', () => { configureTestSuite(); @@ -350,6 +354,212 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { expect(items[1].textContent).toBe(' (Blanks) '); })); + describe('Tree grid ESF', () => { + let tGrid: IgxTreeGridComponent; + + beforeEach(fakeAsync(() => { + fix = TestBed.createComponent(IgxTreeGridFilteringComponent); + fix.detectChanges(); + tick(16); + tGrid = fix.componentInstance.treeGrid; + + const hierarchicalFilterStrategy = new HierarchicalFilteringStrategy(['ID']); + tGrid.filterStrategy = hierarchicalFilterStrategy; + tGrid.allowFiltering = true; + tGrid.filterMode = FilterMode.excelStyleFilter; + fix.detectChanges(); + })); + + it('Should render and expand tree nodes correctly', (async () => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + await wait(100); + fix.detectChanges(); + + let listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); + expect(listItems.length).toBe(4, 'incorrect rendered tree node count'); + + GridFunctions.clickExcelTreeNodeExpandIcon(fix, 0); + await wait(100); + fix.detectChanges(); + + listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); + expect(listItems.length).toBe(6, 'incorrect rendered tree node count'); + })); + + it('Should change arrow icon on expand', fakeAsync (() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const icon = GridFunctions.getExcelFilterTreeNodeIcon(fix, 0); + let iconText = icon.children[0].innerText; + expect(iconText).toBe('keyboard_arrow_right', 'incorrect rendered icon'); + + GridFunctions.clickExcelTreeNodeExpandIcon(fix, 0); + tick(100); + fix.detectChanges(); + + iconText = icon.children[0].innerText; + expect(iconText).toBe('keyboard_arrow_down', 'incorrect rendered icon'); + })); + + it('Should display Select All item', (async () => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + await wait(100); + fix.detectChanges(); + + const label = fix.debugElement.queryAll(By.css(IGX_CHECKBOX_LABEL))[0].nativeElement; + expect(label.innerText).toBe('Select All'); + })); + + it('Should display "Add current selection to filter" item correctly', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '1', fix); + tick(100); + fix.detectChanges(); + + const label = fix.debugElement.queryAll(By.css(IGX_CHECKBOX_LABEL))[1].nativeElement; + expect(label.innerText).toBe('Add current selection to filter'); + })); + + it('Should set indeterminate state correctly', fakeAsync (() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + GridFunctions.clickExcelTreeNodeExpandIcon(fix, 0); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + let checkbox = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')[2]; + checkbox.click(); + tick(100); + fix.detectChanges(); + + let checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + expect(checkboxes[0].indeterminate && !checkboxes[0].checked).toBe(true); + expect(checkboxes[1].indeterminate && !checkboxes[1].checked).toBe(true); + + checkbox = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')[0]; + checkbox.click(); + tick(100); + fix.detectChanges(); + + checkboxes = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + expect(!checkboxes[0].indeterminate && checkboxes[0].checked).toBe(true); + })); + + it('Should filter items and clear the search component correctly', async () => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + await wait(100); + fix.detectChanges(); + + const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + + let listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(listItems.length).toBe(4, 'incorrect rendered list items count'); + + const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '6', fix); + await wait(100); + fix.detectChanges(); + + listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(listItems.length).toBe(2, 'incorrect rendered list items count'); + + const clearIcon: any = Array.from(searchComponent.querySelectorAll('igx-icon')) + .find((icon: any) => icon.innerText === 'clear'); + + clearIcon.click(); + fix.detectChanges(); + + listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(listItems.length).toBe(4, 'incorrect rendered list items count'); + }); + + it('Should correctly filter tree grid', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '6', fix); + tick(100); + fix.detectChanges(); + + const listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(listItems.length).toEqual(2); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + const gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(3); + })); + + it('Should add list items to current filtered items when "Add current selection to filter" is selected', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + let searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + let inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '6', fix); + tick(100); + fix.detectChanges(); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + let gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(3); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '15', fix); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + const checkbox = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')[1]; + checkbox.click(); + tick(100); + fix.detectChanges(); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(5); + })); + }); + describe('Filtering: Row editing', () => { let treeGrid: IgxTreeGridComponent; beforeEach(fakeAsync(/** height/width setter rAF */() => { diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index dd18c145267..35ffb5577ff 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -26,6 +26,7 @@ import { IgxPivotRowComponent } from '../grids/pivot-grid/pivot-row.component'; import { SortingDirection } from '../data-operations/sorting-strategy'; import { IgxRowDirective } from '../grids/row.directive'; import { GridType, RowType } from '../grids/common/grid.interface'; +import { IgxTreeNodeComponent } from '../tree/tree-node/tree-node.component'; const SUMMARY_LABEL_CLASS = '.igx-grid-summary__label'; @@ -100,6 +101,7 @@ export const PAGER_CLASS = '.igx-page-nav'; const RESIZE_LINE_CLASS = '.igx-grid-th__resize-line'; const RESIZE_AREA_CLASS = '.igx-grid-th__resize-handle'; const GRID_COL_THEAD_CLASS = '.igx-grid-th'; +const TREE_NODE_TOGGLE = '.igx-tree-node__toggle-button'; export class GridFunctions { @@ -698,6 +700,15 @@ export class GridFunctions { return columnHeader.querySelector(ESF_FILTER_ICON_FILTERED); } + /** + * Gets the ESF tree node icon + */ + public static getExcelFilterTreeNodeIcon(fix: ComponentFixture, index: number) { + const treeNodeEl = fix.debugElement.queryAll(By.directive(IgxTreeNodeComponent))[index]?.nativeElement; + const expandIcon = treeNodeEl.querySelector(TREE_NODE_TOGGLE); + return expandIcon; + } + public static clickExcelFilterIcon(fix: ComponentFixture, columnField: string) { const filterIcon = GridFunctions.getExcelFilterIcon(fix, columnField); const filterIconFiltered = GridFunctions.getExcelFilterIconFiltered(fix, columnField); @@ -705,6 +716,11 @@ export class GridFunctions { UIInteractions.simulateClickAndSelectEvent(icon); } + public static clickExcelTreeNodeExpandIcon(fix: ComponentFixture, index: number) { + const expandIcon = GridFunctions.getExcelFilterTreeNodeIcon(fix, index); + UIInteractions.simulateClickAndSelectEvent(expandIcon); + } + public static clickExcelFilterIconFromCode(fix: ComponentFixture, grid: IgxGridBaseDirective, columnField: string) { const event = { stopPropagation: () => { }, preventDefault: () => { } }; const header = grid.getColumnByName(columnField).headerCell; @@ -1047,6 +1063,11 @@ export class GridFunctions { return GridFunctions.sortNativeElementsVertically(Array.from(searchComponent.querySelectorAll('igx-list-item'))); } + public static getExcelStyleSearchComponentTreeNodes(fix, comp = null, grid = 'igx-tree-grid'): HTMLElement[] { + const searchComponent = comp ? comp : GridFunctions.getExcelStyleSearchComponent(fix, null, grid); + return GridFunctions.sortNativeElementsVertically(Array.from(searchComponent.querySelectorAll('igx-tree-node'))); + } + public static getColumnHeaders(fix: ComponentFixture): DebugElement[] { return fix.debugElement.queryAll(By.directive(IgxGridHeaderComponent)); } @@ -1200,6 +1221,7 @@ export class GridFunctions { } public static getColumnCells(fix, columnKey, gridCell = 'igx-grid-cell') { + debugger const allCells = fix.debugElement.queryAll(By.css(gridCell)); return allCells.filter((cell) => cell.componentInstance.column.field === columnKey); } From 50cd6053dc587bfac313dee340880ca730ae6655 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 11 Feb 2022 12:07:42 +0200 Subject: [PATCH 13/63] chore(*): fixed external esf tests --- .../src/lib/grids/grid/grid-filtering-ui.spec.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 327b953d351..c5567b25128 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -6126,7 +6126,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { expect(grid.columnList.get(1).hidden).toBeTruthy(); })); - it('Column selection button should be visible/hidden when column is selectable/not selectable', () => { + it('Column selection button should be visible/hidden when column is selectable/not selectable', fakeAsync(() => { grid.columnSelection = GridSelectionMode.multiple; fix.detectChanges(); @@ -6135,6 +6135,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const esf = fix.componentInstance.esf; esf.column = grid.getColumnByName('Downloads'); + tick(); fix.detectChanges(); columnSelectionContainer = GridFunctions.getExcelFilteringColumnSelectionContainer(fix); @@ -6146,9 +6147,9 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { columnSelectionContainer = GridFunctions.getExcelFilteringColumnSelectionContainer(fix); expect(columnSelectionContainer).toBeNull(); - }); + })); - it('should select/deselect column when interact with the column selection item through esf menu', () => { + it('should select/deselect column when interact with the column selection item through esf menu', fakeAsync(() => { // Test in single multiple mode grid.columnSelection = GridSelectionMode.multiple; fix.detectChanges(); @@ -6156,6 +6157,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { spyOn(grid.columnSelectionChanging, 'emit'); const column = grid.getColumnByName('Downloads'); fix.componentInstance.esf.column = column; + tick(); fix.detectChanges(); GridFunctions.clickColumnSelectionInExcelStyleFiltering(fix); @@ -6187,7 +6189,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { GridSelectionFunctions.verifyColumnAndCellsSelected(column, true); GridSelectionFunctions.verifyColumnAndCellsSelected(columnId, false); - }); + })); }); }); From d1607062db5da1d995ca7c238fa8911923e3ca7a Mon Sep 17 00:00:00 2001 From: IBarakov Date: Fri, 11 Feb 2022 14:49:19 +0200 Subject: [PATCH 14/63] chore(*): fix some of the failing tests --- .../src/lib/grids/grid/grid-filtering-ui.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 327b953d351..decac6862d9 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -3276,6 +3276,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { fix.detectChanges(); GridFunctions.clickExcelFilterIcon(fix, 'Downloads'); + tick(100); + fix.detectChanges(); expect(grid.filteredData.length).toEqual(2); @@ -3307,6 +3309,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { fix.detectChanges(); GridFunctions.clickExcelFilterIcon(fix, 'Downloads'); + tick(100); + fix.detectChanges(); expect(grid.filteredData.length).toEqual(2); @@ -3327,6 +3331,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { it('Should not be able to exit custom dialog when press tab on apply button', fakeAsync(() => { GridFunctions.clickExcelFilterIcon(fix, 'Downloads'); + tick(100); + fix.detectChanges(); GridFunctions.clickExcelFilterCascadeButton(fix); tick(); From 905fbbae7b56f5f74dc767fab111c5350c1821ba Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 11 Feb 2022 18:28:36 +0200 Subject: [PATCH 15/63] feat(tree-esf-search): added back formatting --- .../src/lib/data-operations/filtering-strategy.ts | 1 - .../grid.excel-style-filtering.component.ts | 15 +++++++++------ .../src/lib/grids/grid/grid-filtering-ui.spec.ts | 5 +++-- 3 files changed, 12 insertions(+), 9 deletions(-) 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 97f62ee307a..11d892877bc 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -77,7 +77,6 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } - // move formatting from base implementation to formatted values strategies - grid and treegrid public getColumnValues( column: ColumnType, tree: FilteringExpressionsTree) : Promise { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 9678f4b0df8..dd21a1817f0 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -488,15 +488,18 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - const promise = this.grid.filterStrategy.getColumnValues(this.column, expressionsTree); promise.then((colVals) => { this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; - const columnValues = (this.column.dataType === GridColumnDataType.Date) ? - colVals.map(value => { - const label = this.getFilterItemLabel(value); - return { label, value }; - }) : colVals; // TODO: should formatter go here? + const shouldFormatValues = this.shouldFormatValues(); + const columnValues = colVals.map(value => { + if (this.column.dataType === GridColumnDataType.Date){ + const label = this.getFilterItemLabel(value, true); + return { label, value } + } else { + return shouldFormatValues ? this.column.formatter(value) : value; + }; + }); this.renderValues(columnValues); }); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 825693e6571..da820ea934e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -46,7 +46,7 @@ import { } from '../../test-utils/grid-samples.spec'; import { GridSelectionMode, FilterMode } from '../common/enums'; import { ControlsFunction } from '../../test-utils/controls-functions.spec'; -import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { FilteringStrategy, FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy'; import { IgxCalendarComponent } from '../../calendar/calendar.component'; import { IgxInputGroupComponent } from '../../input-group/public_api'; import { formatDate } from '../../core/utils'; @@ -6221,7 +6221,8 @@ describe('IgxGrid - Custom Filtering Strategy #grid', () => { })); it('Should be able to set custom filtering strategy', () => { - expect(grid.filterStrategy).toBeUndefined(); + expect(grid.filterStrategy).toBeDefined(); + expect(grid.filterStrategy).toBeInstanceOf(FilteringStrategy); grid.filterStrategy = fix.componentInstance.strategy; fix.detectChanges(); From ea14f30732c079a8966445453fcc836ef75366c3 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 11 Feb 2022 18:57:40 +0200 Subject: [PATCH 16/63] chore(*): filtering test fixes --- .../src/lib/grids/tree-grid/tree-grid-filtering.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index e30960bd4e3..34ed6fb9ba4 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -672,7 +672,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { })); it('should be able to apply custom filter strategy', fakeAsync(() => { - expect(treeGrid.filterStrategy).toBeUndefined(); + expect(treeGrid.filterStrategy).toBeDefined(); treeGrid.filter('Name', 'd', IgxStringFilteringOperand.instance().condition('contains'), true); tick(30); fix.detectChanges(); @@ -696,7 +696,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { })); it('should display only the filtered records when using TreeGridMatchingRecordsOnlyFilteringStrategy', fakeAsync(() => { - expect(treeGrid.filterStrategy).toBeUndefined(); + expect(treeGrid.filterStrategy).toBeDefined(); treeGrid.filter('Name', 'Trevor', IgxStringFilteringOperand.instance().condition('contains'), true); tick(30); fix.detectChanges(); From 0cef7e20dd08b330f24b916def1ed4438d3dddff Mon Sep 17 00:00:00 2001 From: desig9stein Date: Mon, 14 Feb 2022 10:51:18 +0200 Subject: [PATCH 17/63] feat: ESF tree styles --- .../grid/_excel-filtering-component.scss | 8 ++ .../grid/_excel-filtering-theme.scss | 95 ++++++++++++++++++- .../excel-style-search.component.html | 44 +++++---- 3 files changed, 125 insertions(+), 22 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss index 49ce69b4d1c..24661ff62a3 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss @@ -17,6 +17,14 @@ @extend %igx-excel-filter__loading !optional; } + @include e(tree) { + @extend %igx-excel-filter__tree !optional; + } + + @include e(tree-checkboxes) { + @extend %igx-excel-filter__tree-checkboxes !optional; + } + @include e(menu) { @extend %grid-excel-menu !optional; } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss index e1d947d5b87..8d28525dbee 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss @@ -23,6 +23,17 @@ $variant: map.get($theme, 'variant'); $bootstrap-theme: $variant == 'bootstrap'; + $tree-node-indent: ( + comfortable: rem(16px), + cosy: rem(8px), + compact: rem(4px) + ); + + $tree-node-height: ( + comfortable: rem(40px), + cosy: rem(32px), + compact: rem(24px) + ); %grid-excel-filter { display: block; @@ -32,6 +43,10 @@ box-shadow: igx-elevation($elevation: 12); overflow: auto; min-width: rem(320px); + + .igx-tree-node__wrapper { + padding: 0; + } } %grid-excel-filter--inline { @@ -250,13 +265,16 @@ padding: rem(16px); gap: rem(16px); - igx-list { flex-grow: 1; overflow: hidden; margin-left: -#{rem(16px)}; margin-right: -#{rem(16px)}; border: 0; + } + + igx-list, + %igx-excel-filter__tree { @if $bootstrap-theme { border-top: 1px dashed igx-color($palette, 'grays', 100); border-bottom: 1px dashed igx-color($palette, 'grays', 100); @@ -265,6 +283,39 @@ border-bottom: 1px dashed igx-color($palette, 'grays', 300); } } + + %igx-excel-filter__tree { + overflow-y: auto; + margin: 0 rem(-16px); + padding: rem(8px) 0 0 0; + + igx-icon { + width: rem(20px); + height: rem(20px); + font-size: rem(20px); + } + + > igx-checkbox, + .igx-tree-node__wrapper { + height: map.get($tree-node-height, 'comfortable'); + min-height: map.get($tree-node-height, 'comfortable'); + } + + .igx-tree-node__toggle-button { + min-width: rem(20px); + margin-left: map.get($tree-node-indent, 'comfortable'); + } + } + + %igx-excel-filter__tree-checkboxes { + display: flex; + flex-direction: column; + margin-bottom: rem(4px); + + > igx-checkbox { + margin-left: map.get($tree-node-indent, 'comfortable'); + } + } } %grid-excel-menu--cosy { @@ -302,6 +353,27 @@ margin-left: -#{rem(8px)}; margin-right: -#{rem(8px)}; } + + %igx-excel-filter__tree { + margin: 0 rem(-8px); + padding: rem(4px) 0; + + > igx-checkbox, + .igx-tree-node__wrapper { + height: map.get($tree-node-height, 'cosy'); + min-height: map.get($tree-node-height, 'cosy'); + } + + .igx-tree-node__toggle-button { + margin-left: map.get($tree-node-indent, 'cosy'); + } + } + + %igx-excel-filter__tree-checkboxes { + > igx-checkbox { + margin-left: map.get($tree-node-indent, 'cosy'); + } + } } %grid-excel-menu--compact { @@ -348,6 +420,27 @@ margin-left: -#{rem(4px)}; margin-right: -#{rem(4px)}; } + + %igx-excel-filter__tree { + margin: 0 rem(-4px); + padding: rem(2px) 0; + + igx-checkbox, + .igx-tree-node__wrapper { + height: map.get($tree-node-height, 'compact'); + min-height: map.get($tree-node-height, 'compact'); + } + + .igx-tree-node__toggle-button { + margin: 0 map.get($tree-node-indent, 'compact'); + } + } + + %igx-excel-filter__tree-checkboxes { + > igx-checkbox { + margin-left: map.get($tree-node-indent, 'compact'); + } + } } %grid-excel-menu__secondary { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index d67fb7f271f..98b8120b2aa 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -45,27 +45,29 @@ -
- - {{ selectAllItem.label }} - - - - {{ addToCurrentFilterItem.label }} - +
+
+ + {{ selectAllItem.label }} + + + + {{ addToCurrentFilterItem.label }} + +
From 455e529a010d3267c6b5e9c6bfad6cadcc7b5ae0 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Tue, 15 Feb 2022 15:39:13 +0200 Subject: [PATCH 18/63] refactor(filtering): refactor filtering strategy --- .../lib/data-operations/filtering-strategy.ts | 112 ++++++++++++------ .../grid.excel-style-filtering.component.ts | 70 +++-------- .../tree-grid/tree-grid.filtering.strategy.ts | 27 ++--- 3 files changed, 105 insertions(+), 104 deletions(-) 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 11d892877bc..283ed584bfe 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -1,8 +1,9 @@ import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree'; -import { resolveNestedPath, parseDate } from '../core/utils'; +import { resolveNestedPath, parseDate, uniqueDates } from '../core/utils'; import { ColumnType, GridType, PivotGridType } from '../grids/common/grid.interface'; import { PivotUtil } from '../grids/pivot-grid/pivot-util'; +import { GridColumnDataType } from './data-util'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -11,6 +12,10 @@ const TimeType = 'time'; export interface IFilteringStrategy { filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[]; + getUniqueColumnValues( + column: ColumnType, + tree: FilteringExpressionsTree) : Promise; + shouldFormatFilterValues(column: ColumnType): boolean; } export class HierarchicalColumnValue { @@ -18,27 +23,15 @@ export class HierarchicalColumnValue { public children?: HierarchicalColumnValue[]; } -export class NoopFilteringStrategy implements IFilteringStrategy { - private static _instance: NoopFilteringStrategy = null; - - private constructor() { } - - public static instance() { - return this._instance || (this._instance = new NoopFilteringStrategy()); - } - - public filter(data: any[], _: IFilteringExpressionsTree, __?: IFilteringExpressionsTree): any[] { - return data; - } -} - export abstract class BaseFilteringStrategy implements IFilteringStrategy { + // protected public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean { const cond = expr.condition; const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid); return cond.logic(val, expr.searchVal, expr.ignoreCase); } + // protected public matchRecord(rec: any, expressions: IFilteringExpressionsTree | IFilteringExpression, grid?: GridType): boolean { if (expressions) { if (expressions instanceof FilteringExpressionsTree) { @@ -77,14 +70,55 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } - public getColumnValues( + public getUniqueColumnValues( column: ColumnType, tree: FilteringExpressionsTree) : Promise { const data = column.grid.gridAPI.filterDataByExpressions(tree); const columnField = column.field; - let columnValues; - columnValues = data.map(record => resolveNestedPath(record, columnField)); - return Promise.resolve(columnValues); + const columnValues = data.map(record => { + let value = resolveNestedPath(record, columnField); + + value = column.formatter && this.shouldFormatFilterValues(column) ? + column.formatter(value) : + value; + + return value; + }); + const uniqueValues = this.generateUniqueValues(column, columnValues); + + return Promise.resolve(uniqueValues); + } + + private generateUniqueValues(column: ColumnType, columnValues: any[]) { + let uniqueValues: any[]; + + if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { + const filteredUniqueValues = columnValues.map(s => s?.toString().toLowerCase()) + .reduce((map, val, i) => map.get(val) ? map : map.set(val, columnValues[i]), new Map()); + uniqueValues = Array.from(filteredUniqueValues.values()); + } else if (column.dataType === GridColumnDataType.DateTime) { + uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); + uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); + } else if (column.dataType === GridColumnDataType.Time) { + uniqueValues = Array.from(new Set(columnValues.map(v => { + if (v) { + v = new Date(v); + return new Date().setHours(v.getHours(), v.getMinutes(), v.getSeconds()); + } else { + return v; + } + }))); + uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); + } else { + uniqueValues = column.dataType === GridColumnDataType.Date ? + uniqueDates(columnValues) : Array.from(new Set(columnValues)); + } + + return uniqueValues; + } + + public shouldFormatFilterValues(_column: ColumnType): boolean { + return false; } public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree, @@ -93,6 +127,22 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { protected abstract getFieldValue(rec: any, fieldName: string, isDate?: boolean, isTime?: boolean, grid?: GridType): any; } +export class NoopFilteringStrategy extends BaseFilteringStrategy { + protected getFieldValue(rec: any, _fieldName: string) { + return rec; + } + private static _instance: NoopFilteringStrategy = null; + + public static instance() { + return this._instance || (this._instance = new NoopFilteringStrategy()); + } + + public filter(data: any[], _: IFilteringExpressionsTree, __?: IFilteringExpressionsTree): any[] { + return data; + } +} + + export class FilteringStrategy extends BaseFilteringStrategy { private static _instance: FilteringStrategy = null; @@ -123,9 +173,14 @@ export class FilteringStrategy extends BaseFilteringStrategy { return res; } - protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false): any { + protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { + const column = grid?.getColumnByName(fieldName); let value = resolveNestedPath(rec, fieldName); - value = value && (isDate || isTime) ? parseDate(value) : value; + + value = column?.formatter && this.shouldFormatFilterValues(column) ? + column.formatter(value, rec) : + value && (isDate || isTime) ? parseDate(value) : value; + return value; } } @@ -140,18 +195,7 @@ export class FormattedValuesFilteringStrategy extends FilteringStrategy { super(); } - /** @hidden */ - public shouldApplyFormatter(fieldName: string): boolean { - return !this.fields || this.fields.length === 0 || this.fields.some(f => f === fieldName); - } - - protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { - const column = grid.getColumnByName(fieldName); - let value = resolveNestedPath(rec, fieldName); - - value = column.formatter && this.shouldApplyFormatter(fieldName) ? - column.formatter(value, rec) : value && (isDate || isTime) ? parseDate(value) : value; - - return value; + public shouldFormatFilterValues(column: ColumnType): boolean { + return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); } } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index dd21a1817f0..59cca4596f1 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -468,82 +468,46 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return; } - const columnValues = (this.column.dataType === GridColumnDataType.Date) ? + this.uniqueValues = (this.column.dataType === GridColumnDataType.Date) ? colVals.map(value => { const label = this.getFilterItemLabel(value); return { label, value }; }) : colVals; - this.renderValues(columnValues); + this.renderValues(); this.loadingEnd.emit(); }); } private shouldFormatValues() { - return this.column.formatter && - (this.grid.filterStrategy instanceof FormattedValuesFilteringStrategy || - this.grid.filterStrategy instanceof TreeGridFormattedValuesFilteringStrategy) && - this.grid.filterStrategy.shouldApplyFormatter(this.column.field); + return this.column.formatter && this.grid.filterStrategy.shouldFormatFilterValues(this.column); } private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - const promise = this.grid.filterStrategy.getColumnValues(this.column, expressionsTree); + const promise = this.grid.filterStrategy.getUniqueColumnValues(this.column, expressionsTree); promise.then((colVals) => { this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; - const shouldFormatValues = this.shouldFormatValues(); - const columnValues = colVals.map(value => { - if (this.column.dataType === GridColumnDataType.Date){ - const label = this.getFilterItemLabel(value, true); - return { label, value } - } else { - return shouldFormatValues ? this.column.formatter(value) : value; - }; - }); - - this.renderValues(columnValues); + this.uniqueValues = colVals; + // .map((colVal: ColumnValue | HierarchicalColumnValue) => { + // const value = colVal.value; + // if (this.column.dataType === GridColumnDataType.Date){ + // const label = this.getFilterItemLabel(value, true); + // return { label, value } + // } else { + // return shouldFormatValues ? this.column.formatter(value) : value; + // }; + // }); + + this.renderValues(); }); } - private renderValues(columnValues: any[] | HierarchicalColumnValue[]) { - if (this.isHierarchical) { - this.uniqueValues = columnValues; - } else { - this.uniqueValues = this.generateUniqueValues(columnValues); - } - + private renderValues() { this.filterValues = this.generateFilterValues(this.column.dataType === GridColumnDataType.Date || this.column.dataType === GridColumnDataType.DateTime); this.generateListData(); } - private generateUniqueValues(columnValues: any[]) { - let uniqueValues; - - if (this.column.dataType === GridColumnDataType.String && this.column.filteringIgnoreCase) { - const filteredUniqueValues = columnValues.map(s => s?.toString().toLowerCase()) - .reduce((map, val, i) => map.get(val) ? map : map.set(val, columnValues[i]), new Map()); - uniqueValues = Array.from(filteredUniqueValues.values()); - } else if (this.column.dataType === GridColumnDataType.DateTime) { - uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); - uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); - } else if (this.column.dataType === GridColumnDataType.Time) { - uniqueValues = Array.from(new Set(columnValues.map(v => { - if (v) { - v = new Date(v); - return new Date().setHours(v.getHours(), v.getMinutes(), v.getSeconds()); - } else { - return v; - } - }))); - uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); - } else { - uniqueValues = this.column.dataType === GridColumnDataType.Date ? - uniqueDates(columnValues) : Array.from(new Set(columnValues)); - } - - return uniqueValues; - } - private generateFilterValues(isDateColumn: boolean = false) { let filterValues; 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 60cc7545613..fc721099342 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 @@ -11,10 +11,15 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return this.filterImpl(data, expressionsTree, advancedExpressionsTree, undefined, grid); } - protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false): any { + protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { + const column = grid?.getColumnByName(fieldName); const hierarchicalRecord = rec as ITreeGridRecord; let value = resolveNestedPath(hierarchicalRecord.data, fieldName); - value = value && isDate ? parseDate(value) : value; + + value = column?.formatter && this.shouldFormatFilterValues(column) ? + column.formatter(value) : + value && (isDate || isTime) ? parseDate(value) : value; + return value; } @@ -61,18 +66,6 @@ export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringS public shouldApplyFormatter(fieldName: string): boolean { return !this.fields || this.fields.length === 0 || this.fields.some(f => f === fieldName); } - - protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { - const column = grid.getColumnByName(fieldName); - const hierarchicalRecord = rec as ITreeGridRecord; - let value = resolveNestedPath(hierarchicalRecord.data, fieldName); - - value = column.formatter && this.shouldApplyFormatter(fieldName) ? - column.formatter(value) : - value && (isDate || isTime) ? parseDate(value) : value; - - return value; - } } export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilteringStrategy { @@ -126,12 +119,12 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { super(); } - public override getColumnValues( + public override getUniqueColumnValues( column: ColumnType, tree: FilteringExpressionsTree) : Promise { if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { - return super.getColumnValues(column, tree); + return super.getUniqueColumnValues(column, tree); } this.processedData = []; @@ -179,4 +172,4 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { return childrenValues; } -} \ No newline at end of file +} From fd6455e71b5f48dd7ffd19ffdac37e3617462bc7 Mon Sep 17 00:00:00 2001 From: IBarakov Date: Tue, 15 Feb 2022 16:49:54 +0200 Subject: [PATCH 19/63] chore(*): remove debugger + fix failing test --- .../excel-style/grid.excel-style-filtering.component.ts | 2 +- .../igniteui-angular/src/lib/test-utils/grid-functions.spec.ts | 1 - .../src/lib/tree/tree-node/tree-node.component.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index dd21a1817f0..ae268a918a9 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -448,8 +448,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private populateColumnData() { + this.cdr.detectChanges(); if (this.grid.uniqueColumnValuesStrategy) { - this.cdr.detectChanges(); this.renderColumnValuesRemotely(); } else { this.renderColumnValuesFromData(); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 35ffb5577ff..cd4a9d74aad 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -1221,7 +1221,6 @@ export class GridFunctions { } public static getColumnCells(fix, columnKey, gridCell = 'igx-grid-cell') { - debugger const allCells = fix.debugElement.queryAll(By.css(gridCell)); return allCells.filter((cell) => cell.componentInstance.column.field === columnKey); } diff --git a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts index d93ad91e2c4..6d69107c4f7 100644 --- a/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts +++ b/projects/igniteui-angular/src/lib/tree/tree-node/tree-node.component.ts @@ -403,7 +403,6 @@ export class IgxTreeNodeComponent extends ToggleAnimationPlayer implements Ig return this.selectionService.isNodeIndeterminate(this); } - /** The depth of the node, relative to the root * * ```html From 21df27cf4da0009a62752c17d4b7d7b690aaef2a Mon Sep 17 00:00:00 2001 From: desig9stein Date: Tue, 15 Feb 2022 17:15:08 +0200 Subject: [PATCH 20/63] feat: ESF tree styles - Modify the theme of the tree component to look more like the list --- .../grid/_excel-filtering-component.scss | 8 ++- .../grid/_excel-filtering-theme.scss | 70 ++++++++++++++----- .../excel-style-search.component.html | 41 +++++------ 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss index 24661ff62a3..f5b1d4d4f7d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-component.scss @@ -21,8 +21,12 @@ @extend %igx-excel-filter__tree !optional; } - @include e(tree-checkboxes) { - @extend %igx-excel-filter__tree-checkboxes !optional; + @include e(tree-alike) { + @extend %igx-excel-filter__tree-alike !optional; + } + + @include e(tree-alike-item) { + @extend %igx-excel-filter__tree-alike-item !optional; } @include e(menu) { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss index 8d28525dbee..9be41c5dd43 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss @@ -3,6 +3,7 @@ @use '../../themes/schemas/dark' as *; @use '../button-group/button-group-component' as *; @use '../button-group/button-group-theme' as *; +@use '../tree/tree-theme' as *; @use 'sass:map'; //// @@ -29,12 +30,19 @@ compact: rem(4px) ); + $tree-node-expander-size: rem(20px); $tree-node-height: ( comfortable: rem(40px), cosy: rem(32px), compact: rem(24px) ); + $checkbox-indent: ( + comfortable: calc(#{map.get($tree-node-indent, 'comfortable')} + #{$tree-node-expander-size} + #{rem(8px)}), + cosy: calc((#{map.get($tree-node-indent, 'cosy')} * 2) + #{$tree-node-expander-size}), + compact: calc((#{map.get($tree-node-indent, 'compact')} * 2) + #{$tree-node-expander-size}), + ); + %grid-excel-filter { display: block; width: rem(320px); @@ -43,10 +51,6 @@ box-shadow: igx-elevation($elevation: 12); overflow: auto; min-width: rem(320px); - - .igx-tree-node__wrapper { - padding: 0; - } } %grid-excel-filter--inline { @@ -106,6 +110,22 @@ $item-selected-border-color: transparent )); + + @include igx-tree(igx-tree-theme( + $background: igx-color($palette, 'surface'), + $background-selected: igx-color($palette, 'surface'), + $background-active: igx-color($palette, 'surface'), + $background-active-selected: igx-color($palette, 'surface'), + $foreground: igx-contrast-color($palette, 'surface'), + $foreground-selected: igx-contrast-color($palette, 'surface'), + $foreground-active: igx-contrast-color($palette, 'surface'), + $foreground-active-selected: igx-contrast-color($palette, 'surface'), + )); + + .igx-tree-node__wrapper { + padding: 0; + } + igx-chips-area { padding: rem(16px) rem(16px) 0 rem(16px); gap: rem(8px); @@ -285,14 +305,15 @@ } %igx-excel-filter__tree { + background: igx-color($palette, 'surface'); overflow-y: auto; margin: 0 rem(-16px); - padding: rem(8px) 0 0 0; + flex: 1; igx-icon { - width: rem(20px); - height: rem(20px); - font-size: rem(20px); + width: $tree-node-expander-size; + height: $tree-node-expander-size; + font-size: $tree-node-expander-size; } > igx-checkbox, @@ -307,13 +328,26 @@ } } - %igx-excel-filter__tree-checkboxes { + %igx-excel-filter__tree-alike { + background: igx-color($palette, 'surface'); display: flex; flex-direction: column; - margin-bottom: rem(4px); + z-index: 1; + } + + %igx-excel-filter__tree-alike-item { + display: flex; + align-items: center; + height: map.get($tree-node-height, 'comfortable'); + background: igx-color($palette, 'surface'); + + &:hover, + &:focus { + background: igx-color($palette, 'grays', 200); + } > igx-checkbox { - margin-left: map.get($tree-node-indent, 'comfortable'); + margin-left: map.get($checkbox-indent, 'comfortable'); } } } @@ -356,7 +390,6 @@ %igx-excel-filter__tree { margin: 0 rem(-8px); - padding: rem(4px) 0; > igx-checkbox, .igx-tree-node__wrapper { @@ -369,9 +402,11 @@ } } - %igx-excel-filter__tree-checkboxes { + %igx-excel-filter__tree-alike-item { + height: map.get($tree-node-height, 'cosy'); + > igx-checkbox { - margin-left: map.get($tree-node-indent, 'cosy'); + margin-left: map.get($checkbox-indent, 'cosy'); } } } @@ -423,7 +458,6 @@ %igx-excel-filter__tree { margin: 0 rem(-4px); - padding: rem(2px) 0; igx-checkbox, .igx-tree-node__wrapper { @@ -436,9 +470,11 @@ } } - %igx-excel-filter__tree-checkboxes { + %igx-excel-filter__tree-alike-item { + height: map.get($tree-node-height, 'compact'); + > igx-checkbox { - margin-left: map.get($tree-node-indent, 'compact'); + margin-left: map.get($checkbox-indent, 'compact'); } } } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index c40b5b64165..f852bb9add2 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -46,27 +46,28 @@
-
- - {{ selectAllItem.label }} - - - +
+
+ + {{ selectAllItem.label }} + +
+
+ - {{ addToCurrentFilterItem.label }} - + {{ addToCurrentFilterItem.label }} + +
From 2b221cd03de79d1ab48e69ab95b53efe874625a1 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Tue, 15 Feb 2022 17:57:30 +0200 Subject: [PATCH 21/63] fix(filtering): fix unique values for Date column --- .../src/lib/data-operations/filtering-strategy.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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 283ed584bfe..7bf313092f5 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -94,7 +94,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { const filteredUniqueValues = columnValues.map(s => s?.toString().toLowerCase()) - .reduce((map, val, i) => map.get(val) ? map : map.set(val, columnValues[i]), new Map()); + .reduce((map, val, i) => map.has(val) ? map : map.set(val, columnValues[i]), new Map()); uniqueValues = Array.from(filteredUniqueValues.values()); } else if (column.dataType === GridColumnDataType.DateTime) { uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); @@ -109,9 +109,15 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { } }))); uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); + } else if (column.dataType === GridColumnDataType.Date) { + const valuesMap = columnValues.reduce((map: Map, val) => { + const date = new Date(val); + const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString(); + return map.has(key) ? map : map.set(key, val) + }, new Map()); + uniqueValues = Array.from(valuesMap.values()); } else { - uniqueValues = column.dataType === GridColumnDataType.Date ? - uniqueDates(columnValues) : Array.from(new Set(columnValues)); + uniqueValues = Array.from(new Set(columnValues)); } return uniqueValues; From 594549e8fa3f7694d2e47fb739b4e5bcfaf9e0d4 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Tue, 15 Feb 2022 18:18:44 +0200 Subject: [PATCH 22/63] fix(filtering): fix esf date handling --- .../lib/data-operations/filtering-strategy.ts | 4 ++ .../grid.excel-style-filtering.component.ts | 38 ++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) 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 7bf313092f5..bdc852a0b33 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -111,6 +111,10 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); } else if (column.dataType === GridColumnDataType.Date) { const valuesMap = columnValues.reduce((map: Map, val) => { + if (!val) { + return map.set(val, val); + } + const date = new Date(val); const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString(); return map.has(key) ? map : map.set(key, val) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 59cca4596f1..8809284ff72 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -468,11 +468,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return; } - this.uniqueValues = (this.column.dataType === GridColumnDataType.Date) ? - colVals.map(value => { - const label = this.getFilterItemLabel(value); - return { label, value }; - }) : colVals; + this.uniqueValues = colVals; this.renderValues(); this.loadingEnd.emit(); @@ -642,9 +638,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent let filterListItems = []; const applyFormatter = !this.shouldFormatValues(); values?.forEach(element => { - const hasValue = (element !== undefined && element !== null && element !== '' - && this.column.dataType !== GridColumnDataType.Date) - || !!(element && element.label); + const hasValue = element !== undefined && element !== null && element !== ''; if (hasValue) { const filterListItem = new FilterListItem(); @@ -712,20 +706,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.listData.unshift(blanks); } - private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { - if (element?.value) { - element = element.value; - } - - if (element?.label) { - return element.label; - } - + private getFilterItemLabel(value: any, applyFormatter: boolean = true, data?: any) { if (this.column.formatter) { if (applyFormatter) { - return this.column.formatter(element, data); + return this.column.formatter(value, data); } - return element; + return value; } const { display, format, digitsInfo, currencyCode, timezone } = this.column.pipeArgs; @@ -735,15 +721,15 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent case GridColumnDataType.Date: case GridColumnDataType.DateTime: case GridColumnDataType.Time: - return formatDate(element, format, locale, timezone); + return formatDate(value, format, locale, timezone); case GridColumnDataType.Currency: - return formatCurrency(element, currencyCode || getLocaleCurrencyCode(locale), display, digitsInfo, locale); + return formatCurrency(value, currencyCode || getLocaleCurrencyCode(locale), display, digitsInfo, locale); case GridColumnDataType.Number: - return formatNumber(element, locale, digitsInfo); + return formatNumber(value, locale, digitsInfo); case GridColumnDataType.Percent: - return formatPercent(element, locale, digitsInfo); + return formatPercent(value, locale, digitsInfo); default: - return element; + return value; } } @@ -753,7 +739,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } if (this.column.dataType === GridColumnDataType.Date) { - element = parseDate(element.value); + element = parseDate(element); } return element; @@ -766,7 +752,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent let value; if (this.column.dataType === GridColumnDataType.Date) { - value = element && element.value ? new Date(element.value).toISOString() : element.value; + value = element ? new Date(element).toISOString() : element; } else if (this.column.dataType === GridColumnDataType.DateTime) { value = element ? new Date(element).toISOString() : element; } else if (this.column.dataType === GridColumnDataType.Time) { From ac47fc8e18c62f65576b1f4af6f4af866c1d1e9f Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Tue, 15 Feb 2022 18:59:49 +0200 Subject: [PATCH 23/63] fix(filtering): fix tree grid formatted strategy --- .../src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 fc721099342..fc059f0c5b3 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 @@ -62,9 +62,8 @@ export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringS super(); } - /** @hidden */ - public shouldApplyFormatter(fieldName: string): boolean { - return !this.fields || this.fields.length === 0 || this.fields.some(f => f === fieldName); + public shouldFormatFilterValues(column: ColumnType): boolean { + return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); } } From bf576fb2aab99ef7654ecbb70b09d675c42a0cd8 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 16 Feb 2022 10:11:05 +0200 Subject: [PATCH 24/63] feat(tree-esf-search): tree unique values --- .../grid.excel-style-filtering.component.ts | 73 ++++++++++++++----- .../tree-grid/tree-grid.filtering.strategy.ts | 10 +-- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index ae268a918a9..45b41201ed2 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -462,8 +462,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent const prevColumn = this.column; this.grid.uniqueColumnValuesStrategy(this.column, expressionsTree, (colVals: any[]) => { - // check for 'value' property - if 'value' then item is IHierarchicalItem - if (!this.column || this.column !== prevColumn) { return; } @@ -492,12 +490,13 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent promise.then((colVals) => { this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; const shouldFormatValues = this.shouldFormatValues(); - const columnValues = colVals.map(value => { - if (this.column.dataType === GridColumnDataType.Date){ - const label = this.getFilterItemLabel(value, true); - return { label, value } + const columnValues = colVals.map(colVal => { + if (this.column.dataType === GridColumnDataType.Date) { + const label = this.getFilterItemLabel(colVal, true); + colVal.children = this.setLabelForHierarchicalDates(colVal.children); + return { label, value: colVal } } else { - return shouldFormatValues ? this.column.formatter(value) : value; + return shouldFormatValues ? this.column.formatter(colVal) : colVal; }; }); @@ -505,9 +504,22 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent }); } + private setLabelForHierarchicalDates(colVals: HierarchicalColumnValue[]) { + const labeledVals = colVals.map((colVal) => { + if (colVal.children?.length > 0) { + colVal.children = this.setLabelForHierarchicalDates(colVal.children); + } + // const label = colVal.value ? this.getFilterItemLabel(colVal, true) : this.grid.resourceStrings.igx_grid_excel_blanks; + const label = this.getFilterItemLabel(colVal, true); + return { label, value: colVal }; + }); + + return labeledVals; + } + private renderValues(columnValues: any[] | HierarchicalColumnValue[]) { if (this.isHierarchical) { - this.uniqueValues = columnValues; + this.uniqueValues = this.generateHierarchicalUniqueValues(columnValues); } else { this.uniqueValues = this.generateUniqueValues(columnValues); } @@ -516,6 +528,31 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.generateListData(); } + private generateHierarchicalUniqueValues (columnValues: any[]) { + if (columnValues.some(colVal => ( + (colVal.children && colVal.children.length > 0) || + (colVal.value.children && colVal.value.children.length > 0) + ))) { + columnValues.forEach(colVal => { + if (colVal.children && colVal.children.length > 0) { + colVal.children = this.generateHierarchicalUniqueValues(colVal.children); + } else if (colVal.value.children && colVal.value.children.length > 0) { + colVal.value.children = this.generateHierarchicalUniqueValues(colVal.value.children); + } + }); + } else { + columnValues = columnValues.map(colVal => colVal.value); + const uniqueValues = this.generateUniqueValues(columnValues); + columnValues = uniqueValues.map(value => { + const hierarchicalColumnValue = new HierarchicalColumnValue(); + hierarchicalColumnValue.value = value; + return hierarchicalColumnValue; + }) + } + + return columnValues; + } + private generateUniqueValues(columnValues: any[]) { let uniqueValues; @@ -599,8 +636,9 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return resolvedValue; }); - if (this.containsNullOrEmpty) { - this.addBlanksItem(shouldUpdateSelection); + if (!this.isHierarchical && this.containsNullOrEmpty) { + const blanksItem = this.generateBlanksItem(shouldUpdateSelection); + this.listData.unshift(blanksItem); } if (this.listData.length > 0) { @@ -705,7 +743,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.value = this.getFilterItemValue(element); filterListItem.label = this.getFilterItemLabel(element, applyFormatter); filterListItem.indeterminate = false; - filterListItem.children = this.generateFilterListItems(element.children, shouldUpdateSelection); + filterListItem.children = this.generateFilterListItems(element.children ?? element.value?.children, shouldUpdateSelection); filterListItems.push(filterListItem); } }); @@ -724,7 +762,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.listData.unshift(selectAll); } - private addBlanksItem(shouldUpdateSelection) { + private generateBlanksItem(shouldUpdateSelection) { const blanks = new FilterListItem(); if (this.column.filteringExpressionsTree) { if (shouldUpdateSelection) { @@ -745,17 +783,18 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent blanks.indeterminate = false; blanks.isSpecial = true; blanks.isBlanks = true; - this.listData.unshift(blanks); + + return blanks; } private getFilterItemLabel(element: any, applyFormatter: boolean = true, data?: any) { - if (element?.value) { - element = element.value; - } - if (element?.label) { return element.label; } + + if (this.isHierarchical) { + element = element.value; + } if (this.column.formatter) { if (applyFormatter) { 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 60cc7545613..77d9d6c820f 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 @@ -140,12 +140,9 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { const columnField = column.field; let columnValues = []; columnValues = data.map(record => { - if (this.processedData.indexOf(record) < 0) { // TODO: add check for DATE + if (this.processedData.indexOf(record) < 0) { let hierarchicalItem = new HierarchicalColumnValue(); hierarchicalItem.value = resolveNestedPath(record, columnField); - // if (shouldFormatValues) { - // value = this.column.formatter(value, record); - // } hierarchicalItem.children = this.getChildren(record, columnField) return hierarchicalItem; } @@ -166,15 +163,10 @@ export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { if (this.processedData.indexOf(child) < 0) { let hierarchicalItem: HierarchicalColumnValue; hierarchicalItem = { value: resolveNestedPath(child, columnField) }; - // if (shouldFormatValues) { - // value = this.column.formatter(value, record); - // } hierarchicalItem.children = this.getChildren(child, columnField) childrenValues.push(hierarchicalItem); } }); - } else { - // TODO: unique values on last level } return childrenValues; From 785487c2ba2f6523e5e9511825b6c551a3d7c1da Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Wed, 16 Feb 2022 11:46:52 +0200 Subject: [PATCH 25/63] fix(filtering): fix esf for hierarchical items --- .../grid.excel-style-filtering.component.ts | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 8809284ff72..23041f18e35 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -188,7 +188,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent /** * @hidden @internal */ - public uniqueValues = []; + public uniqueValues: any[] | HierarchicalColumnValue[] = []; /** * @hidden @internal */ @@ -634,11 +634,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; } - private generateFilterListItems(values: any[], shouldUpdateSelection: boolean) { + private generateFilterListItems(values: any[] | HierarchicalColumnValue[], shouldUpdateSelection: boolean) { let filterListItems = []; const applyFormatter = !this.shouldFormatValues(); values?.forEach(element => { - const hasValue = element !== undefined && element !== null && element !== ''; + const value = this.isHierarchical ? element.value : element; + const hasValue = value !== undefined && value !== null && value !== ''; if (hasValue) { const filterListItem = new FilterListItem(); @@ -650,8 +651,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.isFiltered = false; if (shouldUpdateSelection) { - const value = this.getExpressionValue(element); - if (this.filterValues.has(value)) { + const exprValue = this.getExpressionValue(value); + if (this.filterValues.has(exprValue)) { filterListItem.isSelected = true; filterListItem.isFiltered = true; } @@ -660,8 +661,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.selectAllSelected = false; } } - filterListItem.value = this.getFilterItemValue(element); - filterListItem.label = this.getFilterItemLabel(element, applyFormatter); + filterListItem.value = this.getFilterItemValue(value); + filterListItem.label = this.getFilterItemLabel(value, applyFormatter); filterListItem.indeterminate = false; filterListItem.children = this.generateFilterListItems(element.children, shouldUpdateSelection); filterListItems.push(filterListItem); @@ -734,10 +735,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getFilterItemValue(element: any) { - if (this.isHierarchical) { - element = element.value; - } - if (this.column.dataType === GridColumnDataType.Date) { element = parseDate(element); } @@ -746,10 +743,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private getExpressionValue(element: any): string { - if (this.isHierarchical) { - element = element.value; - } - let value; if (this.column.dataType === GridColumnDataType.Date) { value = element ? new Date(element).toISOString() : element; From cff83fc91eff8eb054f815a1d361e8a2b72f804d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 16 Feb 2022 14:04:56 +0200 Subject: [PATCH 26/63] chore(*): revert change and emit sorting changed --- .../excel-style/grid.excel-style-filtering.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 45b41201ed2..29552aa8786 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -448,8 +448,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private populateColumnData() { - this.cdr.detectChanges(); if (this.grid.uniqueColumnValuesStrategy) { + this.cdr.detectChanges(); this.renderColumnValuesRemotely(); } else { this.renderColumnValuesFromData(); @@ -501,6 +501,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent }); this.renderValues(columnValues); + this.sortingChanged.emit(); }); } From f710cb6a2e30c05b4ac1018ee7e439ef6e95371d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 16 Feb 2022 15:08:14 +0200 Subject: [PATCH 27/63] chore(*): added check for hierarchical --- .../excel-style/grid.excel-style-filtering.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 29552aa8786..5c729c09b5a 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -493,7 +493,9 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent const columnValues = colVals.map(colVal => { if (this.column.dataType === GridColumnDataType.Date) { const label = this.getFilterItemLabel(colVal, true); - colVal.children = this.setLabelForHierarchicalDates(colVal.children); + if (this.isHierarchical && colVal.children) { + colVal.children = this.setLabelForHierarchicalDates(colVal.children); + } return { label, value: colVal } } else { return shouldFormatValues ? this.column.formatter(colVal) : colVal; From 5413dd16ff64d66e5bb9977dda036091286f4636 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Wed, 16 Feb 2022 17:33:18 +0200 Subject: [PATCH 28/63] test(filtering): fix 2 and remove 1 tests --- .../lib/data-operations/filtering-strategy.ts | 2 +- .../lib/grids/grid/grid-filtering-ui.spec.ts | 47 +------------------ 2 files changed, 3 insertions(+), 46 deletions(-) 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 bdc852a0b33..cf5b19d0972 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -89,7 +89,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return Promise.resolve(uniqueValues); } - private generateUniqueValues(column: ColumnType, columnValues: any[]) { + protected generateUniqueValues(column: ColumnType, columnValues: any[]) { let uniqueValues: any[]; if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index da820ea934e..d605fb58744 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -5909,7 +5909,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix); expect(listItems.length).toBe(8, 'incorrect rendered list items count'); - expect(listItems[1].innerText).toBe('No value!'); + expect(listItems[1].innerText).toBe('(Blanks)'); for (let i = 2; i < listItems.length; i++) { const date = dates[i]; const label = date !== null && date !== undefined && date !== '' ? datePipe.transform(date, 'longDate') : 'No value!'; @@ -5959,7 +5959,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix); expect(listItems.length).toBe(8, 'incorrect rendered list items count'); - expect(listItems[1].innerText).toBe('No value!'); + expect(listItems[1].innerText).toBe('(Blanks)'); for (let i = 2; i < listItems.length; i++) { const date = dates[i - 2]; const label = date !== null && date !== undefined && date !== '' ? datePipe.transform(date, 'longDate') : 'No value!'; @@ -5970,49 +5970,6 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { expect(loadingIndicator).toBeNull('esf loading indicator is visible'); })); - it('Verify ESF list items when different date values produce same value label in the ESF list', fakeAsync(() => { - const grid = fix.componentInstance.grid; - grid.getColumnByName('ReleaseDate').formatter = ((value: any) => { - const pipe = new DatePipe(grid.locale); - const val = value !== null && value !== undefined && value !== '' ? pipe.transform(value, 'longTime') : 'No value!'; - return val; - }); - fix.detectChanges(); - - // Open excel style custom filtering dialog and wait a bit. - GridFunctions.clickExcelFilterIcon(fix, 'ReleaseDate'); - tick(1050); - fix.detectChanges(); - - // Verify items in search have loaded and that the loading indicator is not visible. - const listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix); - const uniqueValues = new Set(); - grid.data.forEach(r => { - const val = grid.getColumnByName('ReleaseDate').formatter(r.ReleaseDate); - uniqueValues.add(val); - }); - // +1 stands for the Select All option - expect(listItems.length).toBe(uniqueValues.size + 1, 'incorrect rendered list items count'); - - const checkboxElements = GridFunctions.getExcelStyleFilteringCheckboxes(fix); - checkboxElements[0].click(); - tick(); - fix.detectChanges(); - - checkboxElements[2].click(); - tick(); - fix.detectChanges(); - - GridFunctions.clickApplyExcelStyleFiltering(fix); - fix.detectChanges(); - - expect(grid.filteredData.length).toEqual(1); - expect(grid.filteredData[0].Downloads).toEqual(254); - - tick(1050); - fix.detectChanges(); - })); - it('Done callback should be executed only once per column', fakeAsync(() => { const compInstance = fix.componentInstance as IgxGridFilteringESFLoadOnDemandComponent; // Open excel style custom filtering dialog and wait a bit. From 65c47111ba1da900de9723e59b788acd50634da1 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Wed, 16 Feb 2022 18:34:14 +0200 Subject: [PATCH 29/63] chore(filtering): clean up some code --- .../src/lib/data-operations/filtering-strategy.ts | 5 ++--- .../grid.excel-style-filtering.component.ts | 15 ++------------- 2 files changed, 4 insertions(+), 16 deletions(-) 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 cf5b19d0972..7f82b2d44b5 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -1,8 +1,7 @@ import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree'; -import { resolveNestedPath, parseDate, uniqueDates } from '../core/utils'; -import { ColumnType, GridType, PivotGridType } from '../grids/common/grid.interface'; -import { PivotUtil } from '../grids/pivot-grid/pivot-util'; +import { resolveNestedPath, parseDate } from '../core/utils'; +import { ColumnType, GridType } from '../grids/common/grid.interface'; import { GridColumnDataType } from './data-util'; const DateType = 'date'; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index a865db29ea0..0c09e72946c 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -19,13 +19,12 @@ import { ViewRef } from '@angular/core'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree'; -import { parseDate, uniqueDates, PlatformUtil, formatDate, formatCurrency } from '../../../core/utils'; +import { parseDate, PlatformUtil, formatDate, formatCurrency } from '../../../core/utils'; import { GridColumnDataType } from '../../../data-operations/data-util'; import { Subscription } from 'rxjs'; import { DisplayDensity } from '../../../core/density'; import { GridSelectionMode } from '../../common/enums'; -import { FormattedValuesFilteringStrategy, HierarchicalColumnValue } from '../../../data-operations/filtering-strategy'; -import { TreeGridFormattedValuesFilteringStrategy } from '../../tree-grid/tree-grid.filtering.strategy'; +import { HierarchicalColumnValue } from '../../../data-operations/filtering-strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem, generateExpressionsList } from './common'; @@ -483,16 +482,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent promise.then((colVals) => { this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; this.uniqueValues = colVals; - // .map((colVal: ColumnValue | HierarchicalColumnValue) => { - // const value = colVal.value; - // if (this.column.dataType === GridColumnDataType.Date){ - // const label = this.getFilterItemLabel(value, true); - // return { label, value } - // } else { - // return shouldFormatValues ? this.column.formatter(value) : value; - // }; - // }); - this.renderValues(); this.sortingChanged.emit(); }); From defd888b23fb9c87d4d26ee5987bb764fc5ffe83 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 17 Feb 2022 12:09:34 +0200 Subject: [PATCH 30/63] fix(filtering): fix date values --- .../lib/data-operations/filtering-strategy.ts | 2 +- .../grid.excel-style-filtering.component.ts | 35 +------------------ 2 files changed, 2 insertions(+), 35 deletions(-) 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 7f82b2d44b5..789b9ae23e7 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -116,7 +116,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { const date = new Date(val); const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString(); - return map.has(key) ? map : map.set(key, val) + return map.has(key) ? map : map.set(key, date); }, new Map()); uniqueValues = Array.from(valuesMap.values()); } else { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 0c09e72946c..361022a59c9 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -492,31 +492,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.generateListData(); } - // private generateHierarchicalUniqueValues (columnValues: any[]) { - // if (columnValues.some(colVal => ( - // (colVal.children && colVal.children.length > 0) || - // (colVal.value.children && colVal.value.children.length > 0) - // ))) { - // columnValues.forEach(colVal => { - // if (colVal.children && colVal.children.length > 0) { - // colVal.children = this.generateHierarchicalUniqueValues(colVal.children); - // } else if (colVal.value.children && colVal.value.children.length > 0) { - // colVal.value.children = this.generateHierarchicalUniqueValues(colVal.value.children); - // } - // }); - // } else { - // columnValues = columnValues.map(colVal => colVal.value); - // const uniqueValues = this.generateUniqueValues(columnValues); - // columnValues = uniqueValues.map(value => { - // const hierarchicalColumnValue = new HierarchicalColumnValue(); - // hierarchicalColumnValue.value = value; - // return hierarchicalColumnValue; - // }) - // } - - // return columnValues; - // } - private generateFilterValues(isDateColumn: boolean = false) { let filterValues; @@ -675,7 +650,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.selectAllSelected = false; } } - filterListItem.value = this.getFilterItemValue(value); + filterListItem.value = value; filterListItem.label = this.getFilterItemLabel(value, applyFormatter); filterListItem.indeterminate = false; filterListItem.children = this.generateFilterListItems(element.children ?? element.value?.children, shouldUpdateSelection); @@ -749,14 +724,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } } - private getFilterItemValue(element: any) { - if (this.column.dataType === GridColumnDataType.Date) { - element = parseDate(element); - } - - return element; - } - private getExpressionValue(element: any): string { let value; if (this.column.dataType === GridColumnDataType.Date) { From e86f746382d45fae6b1fd84eb5aa8de3127269f9 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 17 Feb 2022 13:58:39 +0200 Subject: [PATCH 31/63] feat(filtering): fix hierarchical items sorting --- .../grid.excel-style-filtering.component.ts | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 361022a59c9..a36e2caf06f 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -533,19 +533,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.addItems(shouldUpdateSelection); } - this.listData = this.column.sortStrategy.sort(this.listData, 'value', SortingDirection.Asc, this.column.sortingIgnoreCase, - (obj, key) => { - let resolvedValue = obj[key]; - if (this.column.dataType === GridColumnDataType.Time) { - resolvedValue = new Date().setHours( - resolvedValue.getHours(), - resolvedValue.getMinutes(), - resolvedValue.getSeconds(), - resolvedValue.getMilliseconds()); - } - - return resolvedValue; - }); + this.listData = this.sortFilterItems(this.listData); if (!this.isHierarchical && this.containsNullOrEmpty) { const blanksItem = this.generateBlanksItem(shouldUpdateSelection); @@ -563,6 +551,29 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.listDataLoaded.emit(); } + private sortFilterItems(items: FilterListItem[]) { + if (!items) { + return undefined; + } + + items.forEach(item => + item.children = this.sortFilterItems(item.children)); + + return this.column.sortStrategy.sort(items, 'value', SortingDirection.Asc, this.column.sortingIgnoreCase, + (obj, key) => { + let resolvedValue = obj[key]; + if (this.column.dataType === GridColumnDataType.Time) { + resolvedValue = new Date().setHours( + resolvedValue.getHours(), + resolvedValue.getMinutes(), + resolvedValue.getSeconds(), + resolvedValue.getMilliseconds()); + } + + return resolvedValue; + }); + } + private getColumnFilterExpressionsTree() { const gridExpressionsTree: IFilteringExpressionsTree = this.grid.filteringExpressionsTree; const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.fieldName); From 1c160703f09cf66854736b9978d08cab05a1149f Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 17 Feb 2022 20:37:23 +0200 Subject: [PATCH 32/63] feat(tree-esf-search): fix selection and empty --- .../excel-style-search.component.html | 6 +++- .../excel-style-search.component.ts | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index f852bb9add2..6cd95a49445 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -46,7 +46,7 @@
-
+
+ +
+ {{resourceStrings.igx_list_no_items}} +
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 4f6509339d3..b34faddaf27 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 @@ -26,6 +26,8 @@ import { cloneHierarchicalArray, PlatformUtil } from '../../../core/utils'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem } from './common'; import { IgxTreeComponent, ITreeNodeSelectionEvent } from '../../../tree/public_api'; +import { CurrentResourceStrings } from '../../../core/i18n/resources'; +import { IListResourceStrings } from '../../../core/i18n/list-resources'; @Directive({ selector: '[igxExcelStyleLoading]' @@ -171,11 +173,19 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return this.defaultExcelStyleLoadingValuesTemplate; } } + + /** + * @hidden @internal + */ + public get resourceStrings(): IListResourceStrings { + return this._resourceStrings; + } private _isLoading; private _addToCurrentFilterItem: FilterListItem; private _selectAllItem: FilterListItem; private _hierarchicalSelectedItems: FilterListItem[]; + private _resourceStrings = CurrentResourceStrings.ListResStrings; private destroy$ = new Subject(); constructor(public cdr: ChangeDetectorRef, public esf: BaseFilteringComponent, protected platform: PlatformUtil) { @@ -409,12 +419,20 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } this.esf.listData.forEach(i => i.isSelected = i.isFiltered); - this.displayedListData = this.esf.listData; + if (this.displayedListData !== this.esf.listData) { + this.displayedListData = this.esf.listData; + if (this.isHierarchical()) { + this.cdr.detectChanges(); + this.tree.nodes.forEach(n => { + n.selected = true; + }); + } + } selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all; this.cdr.detectChanges(); return; - } + } const searchVal = this.searchValue.toLowerCase(); if (this.isHierarchical()) { @@ -544,6 +562,13 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return this.esf.isHierarchical; } + /** + * @hidden @internal + */ + public isTreeEmpty() { + return this.esf.isHierarchical && this.displayedListData.length === 0; + } + private hierarchicalSelectMatches(data: FilterListItem[], searchVal: string) { data.forEach(element => { element.indeterminate = false; From dbdf37538f8f098d55d8d9349b8c99075021794e Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Fri, 18 Feb 2022 11:05:29 +0200 Subject: [PATCH 33/63] fix(filtering): fix hierarchical filter for pk/fk data --- .../grids/tree-grid/tree-grid-api.service.ts | 14 ++- .../tree-grid/tree-grid-filtering.spec.ts | 5 +- .../tree-grid/tree-grid.filtering.strategy.ts | 90 ++++++++----------- 3 files changed, 47 insertions(+), 62 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index 86cb92fd210..ae7ba728341 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -225,6 +225,15 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { } public filterDataByExpressions(expressionsTree: IFilteringExpressionsTree): any[] { + let records = this.filterTreeDataByExpressions(expressionsTree); + const data = []; + + this.getFlatDataFromFilteredRecords(records, data); + + return data; + } + + public filterTreeDataByExpressions(expressionsTree: IFilteringExpressionsTree): ITreeGridRecord[] { let records = this.grid.rootRecords; if (expressionsTree.filteringOperands.length) { @@ -235,10 +244,7 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { records = DataUtil.filter(cloneArray(records), state, this.grid); } - const data = []; - this.getFlatDataFromFilteredRecords(records, data); - - return data; + return records; } protected update_row_in_array(value: any, rowID: any, index: number) { diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index 34ed6fb9ba4..6af9fcaaefb 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -7,12 +7,11 @@ import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxStringFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand } from '../../data-operations/filtering-condition'; import { FilteringStrategy } from '../../data-operations/filtering-strategy'; -import { HierarchicalFilteringStrategy, TreeGridFormattedValuesFilteringStrategy, TreeGridMatchingRecordsOnlyFilteringStrategy } from './tree-grid.filtering.strategy'; +import { TreeGridFilteringStrategy, TreeGridFormattedValuesFilteringStrategy, TreeGridMatchingRecordsOnlyFilteringStrategy } from './tree-grid.filtering.strategy'; import { FilterMode } from '../common/enums'; import { GridFunctions } from '../../test-utils/grid-functions.spec'; import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; -import { TreeTestFunctions } from '../../tree/tree-functions.spec'; import { By } from '@angular/platform-browser'; const IGX_CHECKBOX_LABEL = '.igx-checkbox__label'; @@ -363,7 +362,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { tick(16); tGrid = fix.componentInstance.treeGrid; - const hierarchicalFilterStrategy = new HierarchicalFilteringStrategy(['ID']); + const hierarchicalFilterStrategy = new TreeGridFilteringStrategy(['ID']); tGrid.filterStrategy = hierarchicalFilterStrategy; tGrid.allowFiltering = true; tGrid.filterMode = FilterMode.excelStyleFilter; 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 a77d8e14697..bfd2c89146b 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 @@ -3,9 +3,15 @@ import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { BaseFilteringStrategy, HierarchicalColumnValue } from '../../data-operations/filtering-strategy'; import { ColumnType, GridType } from '../common/grid.interface'; +import { IgxTreeGridAPIService } from './tree-grid-api.service'; import { ITreeGridRecord } from './tree-grid.interfaces'; export class TreeGridFilteringStrategy extends BaseFilteringStrategy { + + constructor(public hierarchicalFilterFields?: string[]) { + super(); + } + public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] { return this.filterImpl(data, expressionsTree, advancedExpressionsTree, undefined, grid); @@ -49,6 +55,35 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { } return res; } + + public override getUniqueColumnValues( + column: ColumnType, + tree: FilteringExpressionsTree) : Promise { + + if (!this.hierarchicalFilterFields || this.hierarchicalFilterFields.indexOf(column.field) < 0) { + return super.getUniqueColumnValues(column, tree); + } + + const data = (column.grid.gridAPI as IgxTreeGridAPIService).filterTreeDataByExpressions(tree); + const columnValues = this.getHierarchicalColumnValues(data, column); + + return Promise.resolve(columnValues); + } + + private getHierarchicalColumnValues(records: ITreeGridRecord[], column: ColumnType) { + return records?.map(record => { + let value = resolveNestedPath(record.data, column.field); + + value = column.formatter && this.shouldFormatFilterValues(column) ? + column.formatter(value) : + value; + + const hierarchicalItem = new HierarchicalColumnValue(); + hierarchicalItem.value = value; + hierarchicalItem.children = this.getHierarchicalColumnValues(record.children, column) + return hierarchicalItem; + }); + } } export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringStrategy { @@ -109,58 +144,3 @@ export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilter return rec; } } - -export class HierarchicalFilteringStrategy extends TreeGridFilteringStrategy { - private processedData: HierarchicalColumnValue[]; - private childDataKey; - - constructor(public hierarchicalFilterFields: string[]) { - super(); - } - - public override getUniqueColumnValues( - column: ColumnType, - tree: FilteringExpressionsTree) : Promise { - - if (this.hierarchicalFilterFields.indexOf(column.field) < 0) { - return super.getUniqueColumnValues(column, tree); - } - - this.processedData = []; - this.childDataKey = column.grid.childDataKey; - const data = column.grid.gridAPI.filterDataByExpressions(tree); - const columnField = column.field; - let columnValues = []; - columnValues = data.map(record => { - if (this.processedData.indexOf(record) < 0) { - let hierarchicalItem = new HierarchicalColumnValue(); - hierarchicalItem.value = resolveNestedPath(record, columnField); - hierarchicalItem.children = this.getChildren(record, columnField) - return hierarchicalItem; - } - }); - columnValues = columnValues.filter(function(el) { - return el !== undefined - }); - - return Promise.resolve(columnValues); - } - - private getChildren(record: any, columnField: string) { - this.processedData.push(record); - let childrenValues = []; - const children = record[this.childDataKey]; - if (children) { - children.forEach(child => { - if (this.processedData.indexOf(child) < 0) { - let hierarchicalItem: HierarchicalColumnValue; - hierarchicalItem = { value: resolveNestedPath(child, columnField) }; - hierarchicalItem.children = this.getChildren(child, columnField) - childrenValues.push(hierarchicalItem); - } - }); - } - - return childrenValues; - } -} From 956894f817c337abfd81a94aae74f1e3326e60de Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 18 Feb 2022 16:49:26 +0200 Subject: [PATCH 34/63] chore(*): fixed selection --- .../filtering/excel-style/excel-style-search.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 5a868a83aac..6e1e011e4e4 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 @@ -420,7 +420,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { if (this.isHierarchical()) { this.cdr.detectChanges(); this.tree.nodes.forEach(n => { - n.selected = true; + const item = n.data as FilterListItem; + n.selected = item.isSelected || item.isFiltered; }); } } From 5bd9b1f3b270cb06a24136682193f2e13eaf3b1d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 21 Feb 2022 11:39:07 +0200 Subject: [PATCH 35/63] chore(*): renamed filtering strategy in sample --- src/app/tree-grid/tree-grid.sample.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/tree-grid/tree-grid.sample.ts b/src/app/tree-grid/tree-grid.sample.ts index f2c8355e20b..dcd3ecbee99 100644 --- a/src/app/tree-grid/tree-grid.sample.ts +++ b/src/app/tree-grid/tree-grid.sample.ts @@ -1,6 +1,6 @@ import { Component, ViewChild, OnInit } from '@angular/core'; import { IgxTreeGridComponent, IgxExcelExporterService, IgxCsvExporterService, - IgxCsvExporterOptions, IgxExcelExporterOptions, CsvFileTypes, GridSelectionMode, DisplayDensity, HierarchicalFilteringStrategy } from 'igniteui-angular'; + IgxCsvExporterOptions, IgxExcelExporterOptions, CsvFileTypes, GridSelectionMode, DisplayDensity, TreeGridFilteringStrategy } from 'igniteui-angular'; import { HIERARCHICAL_SAMPLE_DATA } from '../shared/sample-data'; @Component({ @@ -20,11 +20,11 @@ export class TreeGridSampleComponent implements OnInit { public displayDensities; public selectionModes: GridSelectionMode[] = ['none', 'single', 'multiple', 'multipleCascade']; - public hierarchicalFilterStrategy: HierarchicalFilteringStrategy; + public hierarchicalFilterStrategy: TreeGridFilteringStrategy; constructor(private excelExporterService: IgxExcelExporterService, private csvExporterService: IgxCsvExporterService) { - this.hierarchicalFilterStrategy = new HierarchicalFilteringStrategy(['ID']); + this.hierarchicalFilterStrategy = new TreeGridFilteringStrategy(['ID']); } public ngOnInit(): void { From 7d52b3df041c8cf630a7ec7878499b70b4b63847 Mon Sep 17 00:00:00 2001 From: IBarakov Date: Mon, 21 Feb 2022 13:48:32 +0200 Subject: [PATCH 36/63] chore(*): add 'clear filter' tests --- .../tree-grid/tree-grid-filtering.spec.ts | 147 +++++++++++++++--- .../test-utils/tree-grid-components.spec.ts | 2 +- 2 files changed, 125 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index 6af9fcaaefb..c1c29f0b2c3 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -374,15 +374,15 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { await wait(100); fix.detectChanges(); - let listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); - expect(listItems.length).toBe(4, 'incorrect rendered tree node count'); + let treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); + expect(treeItems.length).toBe(4, 'incorrect rendered tree node count'); GridFunctions.clickExcelTreeNodeExpandIcon(fix, 0); await wait(100); fix.detectChanges(); - listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); - expect(listItems.length).toBe(6, 'incorrect rendered tree node count'); + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, null); + expect(treeItems.length).toBe(6, 'incorrect rendered tree node count'); })); it('Should change arrow icon on expand', fakeAsync (() => { @@ -436,22 +436,16 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { fix.detectChanges(); const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); - let checkbox = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')[2]; - checkbox.click(); - tick(100); - fix.detectChanges(); - - let checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); - expect(checkboxes[0].indeterminate && !checkboxes[0].checked).toBe(true); - expect(checkboxes[1].indeterminate && !checkboxes[1].checked).toBe(true); - checkbox = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')[0]; - checkbox.click(); + let checkboxes: any[] = GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid'); + checkboxes[2].parentElement.click(); tick(100); fix.detectChanges(); checkboxes = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); - expect(!checkboxes[0].indeterminate && checkboxes[0].checked).toBe(true); + expect(checkboxes[0].indeterminate && !checkboxes[0].checked).toBe(true); + expect(checkboxes[1].indeterminate && !checkboxes[1].checked).toBe(true); + expect(checkboxes[2].checked).toBe(false); })); it('Should filter items and clear the search component correctly', async () => { @@ -461,16 +455,16 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); - let listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); - expect(listItems.length).toBe(4, 'incorrect rendered list items count'); + let treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(4, 'incorrect rendered items count'); const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); UIInteractions.clickAndSendInputElementValue(inputNativeElement, '6', fix); await wait(100); fix.detectChanges(); - listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); - expect(listItems.length).toBe(2, 'incorrect rendered list items count'); + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(2, 'incorrect rendered items count'); const clearIcon: any = Array.from(searchComponent.querySelectorAll('igx-icon')) .find((icon: any) => icon.innerText === 'clear'); @@ -478,10 +472,109 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { clearIcon.click(); fix.detectChanges(); - listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); - expect(listItems.length).toBe(4, 'incorrect rendered list items count'); + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(4, 'incorrect rendered items count'); }); + it('Should filter items and clear filters correctly', fakeAsync(() => { + let gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(18); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + let searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + let treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(4, 'incorrect rendered items count'); + + const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '8', fix); + tick(100); + fix.detectChanges(); + + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(4, 'incorrect rendered items count'); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(7); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + const btn = GridFunctions.getExcelFilteringClearFiltersComponent(fix, excelMenu); + const clearIcon: any = btn.querySelector('igx-icon'); + clearIcon.click(); + tick(100); + fix.detectChanges(); + + searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toBe(4, 'incorrect rendered tree node items count'); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(18, 'incorrect rendered grid items count'); + })); + + it('Should correctly update checkboxes after clearing column filters', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + let searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + + let inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '8', fix); + tick(100); + fix.detectChanges(); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + const btn = GridFunctions.getExcelFilteringClearFiltersComponent(fix, excelMenu); + const clearIcon: any = btn.querySelector('igx-icon'); + clearIcon.click(); + tick(100); + fix.detectChanges(); + + let checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + checkboxes.forEach(ch => expect(ch.checked).toBe(true, 'incorrect checkbox state')); + + searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '8', fix); + tick(100); + fix.detectChanges(); + + checkboxes = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + checkboxes.forEach(ch => expect(ch.checked).toBe(true, 'incorrect checkbox state')); + })); + it('Should correctly filter tree grid', fakeAsync(() => { GridFunctions.clickExcelFilterIcon(fix, 'ID'); tick(100); @@ -494,8 +587,8 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { tick(100); fix.detectChanges(); - const listItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); - expect(listItems.length).toEqual(2); + const treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toEqual(2, 'incorrect rendered items count'); GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); tick(100); @@ -506,6 +599,14 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { .sort(); expect(gridCellValues.length).toEqual(3); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + let checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + expect(!checkboxes[1].checked && !checkboxes[2].checked && !checkboxes[3].checked && checkboxes[4].indeterminate).toBe(true); })); it('Should add list items to current filtered items when "Add current selection to filter" is selected', fakeAsync(() => { diff --git a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts index 7c470a6f443..4b453e26cf3 100644 --- a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts @@ -25,7 +25,7 @@ export class IgxTreeGridSortingComponent { @Component({ template: ` - + From 36a84b14ec508b741b6f7f8d4a1f17a8c0017c45 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Mon, 21 Feb 2022 15:55:56 +0200 Subject: [PATCH 37/63] feat(filtering): generate hierarchical filter values --- .../excel-style/base-filtering.component.ts | 1 + .../lib/grids/filtering/excel-style/common.ts | 1 + .../excel-style-search.component.ts | 18 ++++---- .../grid.excel-style-filtering.component.ts | 41 +++++++++++++------ .../tree-grid/tree-grid.filtering.strategy.ts | 18 +++++++- 5 files changed, 55 insertions(+), 24 deletions(-) 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 fe6f2daa571..194f35836b8 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 @@ -41,5 +41,6 @@ export abstract class BaseFilteringComponent { public abstract onPin(): void; public abstract onHideToggle(): void; public abstract cancel(): void; + public abstract getFilterItemValue(item: FilterListItem): string; } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts index 3e6268b7beb..724df63cefb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts @@ -13,6 +13,7 @@ export class FilterListItem { public isSpecial = false; public isBlanks = false; public children?: Array; + public parent?: FilterListItem; } /** 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 6e1e011e4e4..18e5b1cd9c3 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 @@ -69,7 +69,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { */ @ViewChild('selectAllCheckbox', { read: IgxCheckboxComponent, static: false }) public selectAllCheckbox: IgxCheckboxComponent; - + /** * @hidden @internal */ @@ -157,7 +157,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public searchValue: any; - + /** * @hidden @internal */ @@ -173,7 +173,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return this.defaultExcelStyleLoadingValuesTemplate; } } - + /** * @hidden @internal */ @@ -429,7 +429,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { this.cdr.detectChanges(); return; - } + } const searchVal = this.searchValue.toLowerCase(); if (this.isHierarchical()) { @@ -473,8 +473,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { if (this.isHierarchical()) { if (this.addToCurrentFilterCheckbox && this.addToCurrentFilterCheckbox.checked) { this.addFilteredToSelectedItems(this.esf.listData); - } - + } + selectedItems = this._hierarchicalSelectedItems; } else { const item = this.displayedListData[1]; @@ -509,7 +509,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { condition, fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, - searchVal: element.value + searchVal: this.esf.getFilterItemValue(element) }); }); } else { @@ -527,7 +527,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { 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)) + selectedItems.map(e => this.esf.getFilterItemValue(e))) }); if (blanksItem) { @@ -587,7 +587,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { node.expanded = true; } } - } + } }); return data.filter(element => element.isSelected === true); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index d318ff56708..2673d14c303 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -634,7 +634,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; } - private generateFilterListItems(values: any[] | HierarchicalColumnValue[], shouldUpdateSelection: boolean) { + private generateFilterListItems(values: any[] | HierarchicalColumnValue[], shouldUpdateSelection: boolean, parent?: FilterListItem) { let filterListItems = []; const applyFormatter = !this.shouldFormatValues(); values?.forEach(element => { @@ -643,6 +643,10 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent if (hasValue) { const filterListItem = new FilterListItem(); + filterListItem.parent = parent; + filterListItem.value = value; + filterListItem.label = this.getFilterItemLabel(value, applyFormatter); + filterListItem.indeterminate = false; filterListItem.isSelected = true; filterListItem.isFiltered = true; @@ -651,7 +655,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.isFiltered = false; if (shouldUpdateSelection) { - const exprValue = this.getExpressionValue(value); + const exprValue = this.getExpressionValue(filterListItem); if (this.filterValues.has(exprValue)) { filterListItem.isSelected = true; filterListItem.isFiltered = true; @@ -661,10 +665,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.selectAllSelected = false; } } - filterListItem.value = value; - filterListItem.label = this.getFilterItemLabel(value, applyFormatter); - filterListItem.indeterminate = false; - filterListItem.children = this.generateFilterListItems(element.children ?? element.value?.children, shouldUpdateSelection); + + filterListItem.children = this.generateFilterListItems(element.children ?? element.value?.children, shouldUpdateSelection, filterListItem); filterListItems.push(filterListItem); } }); @@ -735,17 +737,30 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } } - private getExpressionValue(element: any): string { - let value; + private getExpressionValue(element: FilterListItem): string { + let value = this.getFilterItemValue(element); + if (this.column.dataType === GridColumnDataType.Date) { - value = element ? new Date(element).toISOString() : element; + value = value ? new Date(value).toISOString() : value; } else if (this.column.dataType === GridColumnDataType.DateTime) { - value = element ? new Date(element).toISOString() : element; + value = value ? new Date(value).toISOString() : value; } else if (this.column.dataType === GridColumnDataType.Time) { - value = element ? new Date(element).toLocaleTimeString() : element; - } else { - value = element; + value = value ? new Date(value).toLocaleTimeString() : value; } + return value; } + + /** + * @hidden @internal + */ + public getFilterItemValue(item: FilterListItem) { + if (this.isHierarchical) { + return item.parent ? + `${this.getFilterItemValue(item.parent)}.[${item.value}]` : + `[${item.value}]`; + } else { + return item.value; + } + } } 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 bfd2c89146b..7bb87378315 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 @@ -20,7 +20,9 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { protected getFieldValue(rec: any, fieldName: string, isDate: boolean = false, isTime: boolean = false, grid?: GridType): any { const column = grid?.getColumnByName(fieldName); const hierarchicalRecord = rec as ITreeGridRecord; - let value = resolveNestedPath(hierarchicalRecord.data, fieldName); + let value = this.isHierarchicalFilterField(fieldName) ? + this.getHierarchicalFieldValue(hierarchicalRecord, fieldName) : + resolveNestedPath(hierarchicalRecord.data, fieldName); value = column?.formatter && this.shouldFormatFilterValues(column) ? column.formatter(value) : @@ -29,6 +31,14 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return value; } + private getHierarchicalFieldValue(record: ITreeGridRecord, field: string) { + const value = resolveNestedPath(record.data, field); + + return record.parent ? + `${this.getHierarchicalFieldValue(record.parent, field)}${value ? `.[${value}]` : ''}` : + `[${value}]`; + } + private filterImpl(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] { let i: number; @@ -56,11 +66,15 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return res; } + private isHierarchicalFilterField(field: string) { + return this.hierarchicalFilterFields && this.hierarchicalFilterFields.indexOf(field) !== -1; + } + public override getUniqueColumnValues( column: ColumnType, tree: FilteringExpressionsTree) : Promise { - if (!this.hierarchicalFilterFields || this.hierarchicalFilterFields.indexOf(column.field) < 0) { + if (!this.isHierarchicalFilterField(column.field)) { return super.getUniqueColumnValues(column, tree); } From 4815ac0df6d0a6401ff25cdc58bccf4cc13de0e7 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Mon, 21 Feb 2022 16:18:59 +0200 Subject: [PATCH 38/63] fix(filtering): fix expressions selectable check --- .../grid.excel-style-filtering.component.ts | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 2673d14c303..f332e3876be 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -423,28 +423,29 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return selectableExpressionsCount === this.expressionsList.length; } - private areExpressionsValuesInTheList() { - if (this.column.dataType === GridColumnDataType.Boolean) { - return true; - } - - if (this.filterValues.size === 1) { - const firstValue = this.filterValues.values().next().value; - - if (!firstValue && firstValue !== 0) { - return true; - } - } - - for (const expression of this.uniqueValues) { - const value = this.getExpressionValue(expression); - if (this.filterValues.has(value)) { - return true; - } - } - - return false; - } + // TODO Check if this is necessary + // private areExpressionsValuesInTheList() { + // if (this.column.dataType === GridColumnDataType.Boolean) { + // return true; + // } + + // if (this.filterValues.size === 1) { + // const firstValue = this.filterValues.values().next().value; + + // if (!firstValue && firstValue !== 0) { + // return true; + // } + // } + + // for (const expression of this.uniqueValues) { + // const value = this.getExpressionValue(expression); + // if (this.filterValues.has(value)) { + // return true; + // } + // } + + // return false; + // } private populateColumnData() { if (this.grid.uniqueColumnValuesStrategy) { @@ -525,7 +526,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private generateListData() { this.listData = new Array(); - const shouldUpdateSelection = (this.areExpressionsSelectable() && this.areExpressionsValuesInTheList()) || this.isHierarchical; + const shouldUpdateSelection = this.areExpressionsSelectable(); if (this.column.dataType === GridColumnDataType.Boolean) { this.addBooleanItems(); From b510a05c2bf60c7af45a977ccaff34ec80823117 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Mon, 21 Feb 2022 17:54:23 +0200 Subject: [PATCH 39/63] feat(filtering): switch back to strategy interface --- .../src/lib/grids/common/grid.interface.ts | 4 ++-- .../src/lib/grids/grid-base.directive.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 5413ef6db58..f555aa05ac5 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -19,7 +19,7 @@ import { IgxPaginatorComponent } from '../../paginator/paginator.component'; import { IgxCell, IgxEditRow } from './crud.service'; import { GridSelectionRange } from './types'; import { FilteringLogic } from '../../data-operations/filtering-expression.interface'; -import { BaseFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { IFilteringStrategy } from '../../data-operations/filtering-strategy'; import { DropPosition, IgxColumnMovingService } from '../moving/moving.service'; import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive'; import { Observable, Subject } from 'rxjs'; @@ -492,7 +492,7 @@ export interface GridType extends IGridDataBindable { sortStrategy: IGridSortingStrategy; groupStrategy?: IGridGroupingStrategy; filteringLogic: FilteringLogic; - filterStrategy: BaseFilteringStrategy; + filterStrategy: IFilteringStrategy; allowAdvancedFiltering: boolean; sortingExpressions: ISortingExpression[]; sortingExpressionsChange: EventEmitter; 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 b0dbc540493..19d4d9a6420 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -77,7 +77,7 @@ import { IgxExcelStyleLoadingValuesTemplateDirective } from './filtering/excel-s import { IgxGridColumnResizerComponent } from './resizing/resizer.component'; import { CharSeparatedValueData } from '../services/csv/char-separated-value-data'; import { IgxColumnResizingService } from './resizing/resizing.service'; -import { BaseFilteringStrategy, FilteringStrategy } from '../data-operations/filtering-strategy'; +import { FilteringStrategy, IFilteringStrategy } from '../data-operations/filtering-strategy'; import { IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective, @@ -1062,7 +1062,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements /** * Emitted before the grid's data view is changed because of a data operation, rebinding, etc. - * + * * @example * ```typescript * @@ -1073,7 +1073,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements /** * Emitted after the grid's data view is changed because of a data operation, rebinding, etc. - * + * * @example * ```typescript * @@ -1081,7 +1081,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements */ @Output() public dataChanged = new EventEmitter(); - + /** * @hidden @internal @@ -2084,11 +2084,11 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ @Input() - public get filterStrategy(): BaseFilteringStrategy { + public get filterStrategy(): IFilteringStrategy { return this._filterStrategy; } - public set filterStrategy(classRef: BaseFilteringStrategy) { + public set filterStrategy(classRef: IFilteringStrategy) { this._filterStrategy = classRef; } @@ -2754,7 +2754,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements protected _userOutletDirective: IgxOverlayOutletDirective; protected _transactions: TransactionService; protected _batchEditing = false; - protected _filterStrategy = new FilteringStrategy() as BaseFilteringStrategy; + protected _filterStrategy: IFilteringStrategy = new FilteringStrategy(); protected _autoGeneratedCols = []; protected _dataView = []; From 12cf155da303a6e91d8ecd249d1d5a572160ec2c Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 21 Feb 2022 19:57:40 +0200 Subject: [PATCH 40/63] feat(tree-esf-search): template for empty --- .../src/i18n/BG/grid-resources.ts | 1 + .../src/i18n/CS/grid-resources.ts | 1 + .../src/i18n/DA/grid-resources.ts | 1 + .../src/i18n/DE/grid-resources.ts | 1 + .../src/i18n/ES/grid-resources.ts | 1 + .../src/i18n/FR/grid-resources.ts | 1 + .../src/i18n/HU/grid-resources.ts | 1 + .../src/i18n/IT/grid-resources.ts | 1 + .../src/i18n/JA/grid-resources.ts | 1 + .../src/i18n/KO/grid-resources.ts | 1 + .../src/i18n/NB/grid-resources.ts | 1 + .../src/i18n/NL/grid-resources.ts | 1 + .../src/i18n/PL/grid-resources.ts | 1 + .../src/i18n/PT/grid-resources.ts | 1 + .../src/i18n/RO/grid-resources.ts | 1 + .../src/i18n/SV/grid-resources.ts | 1 + .../src/i18n/TR/grid-resources.ts | 1 + .../src/i18n/ZH-HANS/grid-resources.ts | 1 + .../src/i18n/ZH-HANT/grid-resources.ts | 1 + .../src/lib/core/i18n/grid-resources.ts | 2 + .../excel-style-search.component.html | 23 +++++++- .../excel-style-search.component.ts | 26 ++++----- .../lib/grids/tree-grid/tree-grid.module.ts | 1 - src/app/tree-grid/tree-grid.sample.html | 29 +++++++--- src/app/tree-grid/tree-grid.sample.ts | 56 ++++++++++++++++++- 25 files changed, 128 insertions(+), 28 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts index b962412b3b6..f760efbd355 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsBG_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Булев филтър', igx_grid_excel_currency_filter: 'Филтър по валута', igx_grid_excel_custom_filter: 'Персонализиран филтър...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Разширено филтриране', igx_grid_advanced_filter_and_group: '"And" група', igx_grid_advanced_filter_or_group: '"Or" група', diff --git a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts index 13c41d5d7a0..129dcc00215 100644 --- a/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/CS/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsCS_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Booleovský filtr', igx_grid_excel_currency_filter: 'Filtr měn', igx_grid_excel_custom_filter: 'Vlastní filtr ...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Pokročilé filtrování', igx_grid_advanced_filter_and_group: '"A" skupina', igx_grid_advanced_filter_or_group: '"Nebo" skupina', diff --git a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts index b69f3921cc0..41cdf64f7e6 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DA/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsDA_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Boolsk filter', igx_grid_excel_currency_filter: 'Valutafilter', igx_grid_excel_custom_filter: 'Brugerdefineret filter', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Avanceret filtrering', igx_grid_advanced_filter_and_group: '"Og" gruppe', igx_grid_advanced_filter_or_group: '"Eller" gruppe', diff --git a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts index 697fefb1f9b..aeedfcc9e49 100644 --- a/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/DE/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsDE_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Logischer Filter', igx_grid_excel_currency_filter: 'Währungs Filter', igx_grid_excel_custom_filter: 'Benutzerdefinierter Filter...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Erweiterte Filterung', igx_grid_advanced_filter_and_group: '"Und" Gruppe', igx_grid_advanced_filter_or_group: '"Oder" Gruppe', diff --git a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts index 141f43624df..86f78e5e2dd 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ES/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsES_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtro booleano', igx_grid_excel_currency_filter: 'Filtro divisa', igx_grid_excel_custom_filter: 'Filtro personalizado...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Filtro Avanzado', igx_grid_advanced_filter_and_group: 'Grupo "Y"', igx_grid_advanced_filter_or_group: '"Grupo "O"', diff --git a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts index 6318fa37bec..036a12a1003 100644 --- a/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/FR/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsFR_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtre booléen', igx_grid_excel_currency_filter: 'Filtre devise', igx_grid_excel_custom_filter: 'Filtre personnalisé...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Filtrage avancé', igx_grid_advanced_filter_and_group: 'Groupe "Et"', igx_grid_advanced_filter_or_group: 'Groupe "Ou"', diff --git a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts index 433e8c35955..3e358e9bddc 100644 --- a/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/HU/grid-resources.ts @@ -90,6 +90,7 @@ const GridResourceStringsHU_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Logikai szűrő', igx_grid_excel_currency_filter: 'Pénznemszűrő', igx_grid_excel_custom_filter: 'Egyéni szűrő...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Speciális szűrés', igx_grid_advanced_filter_and_group: '"És" csoport', igx_grid_advanced_filter_or_group: '"Vagy" Csoport', diff --git a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts index f8d7f7a4a92..1c27e425c5f 100644 --- a/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/IT/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsIT_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtro booleano', igx_grid_excel_currency_filter: 'Filtro valuta', igx_grid_excel_custom_filter: 'Filtro personalizzato…', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Filtro avanzato', igx_grid_advanced_filter_and_group: 'Gruppo "And"', igx_grid_advanced_filter_or_group: 'Gruppo "Or"', diff --git a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts index cd3a2e8056f..d9d581db415 100644 --- a/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/JA/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsJA_: ExpandRequire = { igx_grid_excel_boolean_filter: 'ブール値フィルター', igx_grid_excel_currency_filter: '通貨フィルター', igx_grid_excel_custom_filter: 'カスタム フィルター...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: '高度なフィルター', igx_grid_advanced_filter_and_group: '"And" グループ', igx_grid_advanced_filter_or_group: '"Or" グループ', diff --git a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts index a181899d28b..420b0d1e057 100644 --- a/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/KO/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsKO_: ExpandRequire = { igx_grid_excel_boolean_filter: '불린 필터', igx_grid_excel_currency_filter: '통화 필터', igx_grid_excel_custom_filter: '사용자 필터...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: '고급 필터링', igx_grid_advanced_filter_and_group: '그룹 "그리고"', igx_grid_advanced_filter_or_group: '그룹 "또는"', diff --git a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts index 8c2bbeeaaa0..8c51b9f9740 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NB/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsNB_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Boolsk filter', igx_grid_excel_currency_filter: 'Valutafilter', igx_grid_excel_custom_filter: 'Tilpasset filter...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Avansert filtrering', igx_grid_advanced_filter_and_group: '"Og"-gruppe', igx_grid_advanced_filter_or_group: '"Eller"-gruppe', diff --git a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts index 70c87ade8a7..9873f5d9f53 100644 --- a/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/NL/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsNL_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Booleaans filter', igx_grid_excel_currency_filter: 'Valutafilter', igx_grid_excel_custom_filter: 'Aangepast filter ...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Geavanceerd filteren', igx_grid_advanced_filter_and_group: 'En-groep', igx_grid_advanced_filter_or_group: 'Of-groep', diff --git a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts index 9a181b3694f..58024ad3c65 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PL/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsPL_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtr logiczny', igx_grid_excel_currency_filter: 'Filtr walutowy', igx_grid_excel_custom_filter: 'Filtr niestandardowy...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Zaawansowane filtrowanie', igx_grid_advanced_filter_and_group: '"I" grupuj', igx_grid_advanced_filter_or_group: '"Lub" grupuj', diff --git a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts index 44208c0ff49..2523c2cd6a5 100644 --- a/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/PT/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsPT_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtro booleano', igx_grid_excel_currency_filter: 'Filtro de moeda', igx_grid_excel_custom_filter: 'Filtro personalizado...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Filtragem avançada', igx_grid_advanced_filter_and_group: '"E" grupo', igx_grid_advanced_filter_or_group: '"Ou" grupo', diff --git a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts index a30c71cc2cb..6af7e368e91 100644 --- a/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/RO/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsRO_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Filtru boolean', igx_grid_excel_currency_filter: 'Filtru valutar', igx_grid_excel_custom_filter: 'Filtru customizat...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Filtrare avansată', igx_grid_advanced_filter_and_group: '"Și" Grup', igx_grid_advanced_filter_or_group: '"Sau" grup', diff --git a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts index 24f72c0a06a..d5dc7b18c65 100644 --- a/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/SV/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsSV_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Booleskt filter', igx_grid_excel_currency_filter: 'Valutafilter', igx_grid_excel_custom_filter: 'Anpassat filter ...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Avancerad filtrering', igx_grid_advanced_filter_and_group: '"Och" -grupp', igx_grid_advanced_filter_or_group: '"Eller" -grupp', diff --git a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts index 41d16711ff2..64bc33df683 100644 --- a/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/TR/grid-resources.ts @@ -89,6 +89,7 @@ const GridResourceStringsTR_: ExpandRequire = { igx_grid_excel_boolean_filter: 'Boole filtresi', igx_grid_excel_currency_filter: 'Para birimi filtresi', igx_grid_excel_custom_filter: 'Özel filtre...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Gelişmiş Filtreleme', igx_grid_advanced_filter_and_group: '"Ve" Grubu', igx_grid_advanced_filter_or_group: '"Veya" Grubu', diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts index 35933f955e1..a3921250eb6 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANS/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsZHHANS_: ExpandRequire = { igx_grid_excel_boolean_filter: '布尔筛选器', igx_grid_excel_currency_filter: '货币筛选器', igx_grid_excel_custom_filter: '自定义筛选器...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: '高级筛选', igx_grid_advanced_filter_and_group: '"And" 组', igx_grid_advanced_filter_or_group: '"Or" 组', diff --git a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts index b343e4d633f..dc233d98546 100644 --- a/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/ZH-HANT/grid-resources.ts @@ -87,6 +87,7 @@ const GridResourceStringsZHHANT_: ExpandRequire = { igx_grid_excel_boolean_filter: '布林值篩選條件', igx_grid_excel_currency_filter: '貨幣篩選條件', igx_grid_excel_custom_filter: '自訂篩選條件...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: '進階篩選', igx_grid_advanced_filter_and_group: '“And” 群組', igx_grid_advanced_filter_or_group: '“Or” 群組', diff --git a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts index 4581e0a530b..e3fce5f7bfd 100644 --- a/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts +++ b/projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts @@ -86,6 +86,7 @@ export interface IGridResourceStrings { igx_grid_excel_boolean_filter?: string; igx_grid_excel_currency_filter?: string; igx_grid_excel_custom_filter?: string; + igx_grid_excel_no_matches?: string; igx_grid_advanced_filter_title?: string; igx_grid_advanced_filter_and_group?: string; igx_grid_advanced_filter_or_group?: string; @@ -244,6 +245,7 @@ export const GridResourceStringsEN: IGridResourceStrings = { igx_grid_excel_boolean_filter: 'Boolean filter', igx_grid_excel_currency_filter: 'Currency filter', igx_grid_excel_custom_filter: 'Custom filter...', + igx_grid_excel_no_matches: 'No matches', igx_grid_advanced_filter_title: 'Advanced Filtering', igx_grid_advanced_filter_and_group: '"And" Group', igx_grid_advanced_filter_or_group: '"Or" Group', diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index 6cd95a49445..ad371ec8ddb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -43,6 +43,10 @@
+ + + +
@@ -103,11 +107,24 @@ -
- {{resourceStrings.igx_list_no_items}} -
+ +
+ + +
+
+ + + +
+ +
+ {{esf.grid.resourceStrings.igx_grid_excel_no_matches}} +
+
+ 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 6e1e011e4e4..0ae91c98481 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 @@ -26,8 +26,6 @@ import { cloneHierarchicalArray, PlatformUtil } from '../../../core/utils'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem } from './common'; import { IgxTreeComponent, ITreeNodeSelectionEvent } from '../../../tree/public_api'; -import { CurrentResourceStrings } from '../../../core/i18n/resources'; -import { IListResourceStrings } from '../../../core/i18n/list-resources'; @Directive({ selector: '[igxExcelStyleLoading]' @@ -94,6 +92,13 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { @ViewChild('defaultExcelStyleLoadingValuesTemplate', { read: TemplateRef }) protected defaultExcelStyleLoadingValuesTemplate: TemplateRef; + /** + * @hidden + * @internal + */ + @ViewChild('defaultEmptySearch', { read: TemplateRef, static: false }) + protected defaultEmptySearchTemplate: TemplateRef; + /** * @hidden @internal */ @@ -173,19 +178,11 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { return this.defaultExcelStyleLoadingValuesTemplate; } } - - /** - * @hidden @internal - */ - public get resourceStrings(): IListResourceStrings { - return this._resourceStrings; - } private _isLoading; private _addToCurrentFilterItem: FilterListItem; private _selectAllItem: FilterListItem; private _hierarchicalSelectedItems: FilterListItem[]; - private _resourceStrings = CurrentResourceStrings.ListResStrings; private destroy$ = new Subject(); constructor(public cdr: ChangeDetectorRef, public esf: BaseFilteringComponent, protected platform: PlatformUtil) { @@ -208,6 +205,11 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { }); esf.listDataLoaded.pipe(takeUntil(this.destroy$)).subscribe(() => { + if (this.isHierarchical() && this.esf.listData[0].isSpecial) { + this._selectAllItem = this.esf.listData[0]; + this.esf.listData.splice(0, 1); + } + if (this.searchValue) { this.clearInput(); } else { @@ -223,10 +225,6 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } public ngAfterViewInit() { - if (this.isHierarchical()) { - this._selectAllItem = this.esf.listData[0]; - this.esf.listData.splice(0, 1); - } requestAnimationFrame(this.refreshSize); } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts index 5eab966d83b..9333642d432 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.module.ts @@ -10,7 +10,6 @@ import { IgxTreeGridSummaryPipe } from './tree-grid.summary.pipe'; import { IgxRowLoadingIndicatorTemplateDirective } from './tree-grid.directives'; import { IgxTreeGridGroupingPipe } from './tree-grid.grouping.pipe'; import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by-area.component'; -import { IgxTreeModule } from '../../tree/public_api'; /** * @hidden */ diff --git a/src/app/tree-grid/tree-grid.sample.html b/src/app/tree-grid/tree-grid.sample.html index 4c329e58a5a..6c5709807cf 100644 --- a/src/app/tree-grid/tree-grid.sample.html +++ b/src/app/tree-grid/tree-grid.sample.html @@ -19,18 +19,31 @@ - + +
+ Enable Paging + Enable RowEditing +
+ + + + {{ item }} + + + + + + + + + + +
+
diff --git a/src/app/tree-grid/tree-grid.sample.ts b/src/app/tree-grid/tree-grid.sample.ts index dcd3ecbee99..b4894f5b384 100644 --- a/src/app/tree-grid/tree-grid.sample.ts +++ b/src/app/tree-grid/tree-grid.sample.ts @@ -16,12 +16,13 @@ export class TreeGridSampleComponent implements OnInit { public data: Array; public columns: Array; public selectionMode; - public density: DisplayDensity = 'compact'; + public density: DisplayDensity = 'comfortable'; public displayDensities; public selectionModes: GridSelectionMode[] = ['none', 'single', 'multiple', 'multipleCascade']; - public hierarchicalFilterStrategy: TreeGridFilteringStrategy; + private nextRow = 1; + constructor(private excelExporterService: IgxExcelExporterService, private csvExporterService: IgxCsvExporterService) { this.hierarchicalFilterStrategy = new TreeGridFilteringStrategy(['ID']); @@ -50,10 +51,61 @@ export class TreeGridSampleComponent implements OnInit { this.data = HIERARCHICAL_SAMPLE_DATA.slice(0); } + public addRow() { + this.grid1.addRow({ + ID: `ADD${this.nextRow++}`, + CompanyName: 'Around the Horn', + ContactName: 'Thomas Hardy', + ContactTitle: 'Sales Representative', + Address: '120 Hanover Sq.', + City: 'London', + Region: null, + PostalCode: 'WA1 1DP', + Country: 'UK', + Phone: '(171) 555-7788', + Fax: '(171) 555-6750' + }); + } + public selectDensity(event) { this.density = this.displayDensities[event.index].label; } + public addChildRow() { + const selectedRowId = this.grid1.selectedRows[0]; + this.grid1.addRow ( + { + ID: `ADD${this.nextRow++}`, + CompanyName: 'Around the Horn', + ContactName: 'Thomas Hardy', + ContactTitle: 'Sales Representative', + Address: '120 Hanover Sq.', + City: 'London', + Region: null, + PostalCode: 'WA1 1DP', + Country: 'UK', + Phone: '(171) 555-7788', + Fax: '(171) 555-6750' + }, + selectedRowId); + } + + public deleteRow() { + this.grid1.deleteRow(this.grid1.selectedRows[0]); + } + + public undo() { + this.grid1.transactions.undo(); + } + + public redo() { + this.grid1.transactions.redo(); + } + + public commit() { + this.grid1.transactions.commit(this.data, this.grid1.primaryKey, this.grid1.childDataKey); + } + public exportToExcel() { this.excelExporterService.export(this.grid1, new IgxExcelExporterOptions('TreeGrid')); } From 2ecfdfa13c58b6028bdcdd0053f5a427be9e710d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 22 Feb 2022 11:09:38 +0200 Subject: [PATCH 41/63] chore(*): fixed mixins names --- .../lib/core/styles/components/grid/_excel-filtering-theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss index d5a7246b5f6..90e1bd315b5 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss @@ -111,7 +111,7 @@ )); - @include igx-tree(igx-tree-theme( + @include tree(tree-theme( $background: igx-color($palette, 'surface'), $background-selected: igx-color($palette, 'surface'), $background-active: igx-color($palette, 'surface'), From d6d9a48a6e77ddbea83e6ba8c8d180712031490d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 22 Feb 2022 11:50:20 +0200 Subject: [PATCH 42/63] chore(*): check if grid is defined --- .../filtering/excel-style/excel-style-search.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index ad371ec8ddb..9b4292007c0 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -121,7 +121,7 @@
- {{esf.grid.resourceStrings.igx_grid_excel_no_matches}} + {{esf.grid?.resourceStrings.igx_grid_excel_no_matches}}
From d5b66970de2b50f26a36b9d81d29a18504433294 Mon Sep 17 00:00:00 2001 From: IBarakov Date: Tue, 22 Feb 2022 15:16:25 +0200 Subject: [PATCH 43/63] chore(*): fix failing test + add 'no matches' test --- .../tree-grid/tree-grid-filtering.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index c1c29f0b2c3..d8f6c1581e6 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -572,6 +572,9 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { fix.detectChanges(); checkboxes = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + const addToFilterCheckbox = checkboxes.splice(1,1)[0]; + + expect(addToFilterCheckbox.checked).toBe(false, 'incorrect checkbox state') checkboxes.forEach(ch => expect(ch.checked).toBe(true, 'incorrect checkbox state')); })); @@ -658,6 +661,22 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { expect(gridCellValues.length).toEqual(5); })); + + it('Should display message when search results are empty', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + let searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + let inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '77', fix); + tick(100); + fix.detectChanges(); + + const emptyTextEl = searchComponent.querySelector('article'); + expect(emptyTextEl.innerText).toEqual('No matches'); + })); }); describe('Filtering: Row editing', () => { From 27facac6c4bc791e416a9689a57298cc98f0a790 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 22 Feb 2022 16:47:39 +0200 Subject: [PATCH 44/63] feat(tree-esf-search): fixed apply button disabled --- .../excel-style-search.component.html | 6 +++--- .../excel-style/excel-style-search.component.ts | 17 ++++------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html index 9b4292007c0..b384cb72ad4 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.html @@ -45,7 +45,7 @@ - + @@ -115,11 +115,11 @@ - +
- +
{{esf.grid?.resourceStrings.igx_grid_excel_no_matches}}
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 89d3e745403..479b47aea5a 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 @@ -92,13 +92,6 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { @ViewChild('defaultExcelStyleLoadingValuesTemplate', { read: TemplateRef }) protected defaultExcelStyleLoadingValuesTemplate: TemplateRef; - /** - * @hidden - * @internal - */ - @ViewChild('defaultEmptySearch', { read: TemplateRef, static: false }) - protected defaultEmptySearchTemplate: TemplateRef; - /** * @hidden @internal */ @@ -205,8 +198,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { }); esf.listDataLoaded.pipe(takeUntil(this.destroy$)).subscribe(() => { + this._selectAllItem = this.esf.listData[0]; if (this.isHierarchical() && this.esf.listData[0].isSpecial) { - this._selectAllItem = this.esf.listData[0]; this.esf.listData.splice(0, 1); } @@ -293,6 +286,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public onSelectAllCheckboxChange(eventArgs: IChangeCheckboxEventArgs) { + this._selectAllItem.isSelected = eventArgs.checked; + this._selectAllItem.indeterminate = false; const treeNodes = this.tree.nodes; treeNodes.forEach(node => (node.data as FilterListItem).isSelected = eventArgs.checked); } @@ -353,12 +348,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public get applyButtonDisabled(): boolean { - if (this.isHierarchical()) { - return (this._hierarchicalSelectedItems ? this._hierarchicalSelectedItems.length === 0 : false); - } else { - return (this.esf.listData[0] && !this.esf.listData[0].isSelected && !this.esf.listData[0].indeterminate) || + return (this._selectAllItem && !this._selectAllItem.isSelected && !this._selectAllItem.indeterminate) || (this.displayedListData && this.displayedListData.length === 0); - } } /** From 33d2fd4e1a8d45d1a80a3504b2e4eee76d5014a6 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 23 Feb 2022 11:18:44 +0200 Subject: [PATCH 45/63] chore(*): check if tree is initialized --- .../grids/filtering/excel-style/excel-style-search.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 479b47aea5a..78c74b2ed2a 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 @@ -399,7 +399,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { const anyFiltered = this.esf.listData.some(i => i.isFiltered); const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); selectAllBtn.indeterminate = anyFiltered && anyUnfiltered; - if (this.isHierarchical()) { + if (this.isHierarchical() && this.tree) { this._hierarchicalSelectedItems = this.tree.nodes.map(n => n.data as FilterListItem).filter(item => item.isFiltered); } From 56949348984a0af2fc0e5be700cbf7e7bcbc6d20 Mon Sep 17 00:00:00 2001 From: IBarakov Date: Wed, 23 Feb 2022 13:40:20 +0200 Subject: [PATCH 46/63] chore(*): add tests for tree grid esf with custom templates --- .../tree-grid/tree-grid-filtering.spec.ts | 95 +++++++++++++++++-- .../test-utils/tree-grid-components.spec.ts | 28 ++++++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index d8f6c1581e6..3188f4d3c5c 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -1,8 +1,8 @@ -import { TestBed, fakeAsync, tick, waitForAsync, flush } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { IgxTreeGridModule, IgxTreeGridComponent } from './public_api'; -import { IgxTreeGridFilteringComponent, IgxTreeGridFilteringRowEditingComponent } from '../../test-utils/tree-grid-components.spec'; +import { IgxTreeGridFilteringComponent, IgxTreeGridFilteringESFTemplatesComponent, IgxTreeGridFilteringRowEditingComponent } from '../../test-utils/tree-grid-components.spec'; import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxStringFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand } from '../../data-operations/filtering-condition'; @@ -24,7 +24,9 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ - IgxTreeGridFilteringComponent, IgxTreeGridFilteringRowEditingComponent + IgxTreeGridFilteringComponent, + IgxTreeGridFilteringRowEditingComponent, + IgxTreeGridFilteringESFTemplatesComponent ], imports: [ BrowserAnimationsModule, @@ -299,7 +301,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { grid.filter('ID', 1, IgxNumberFilteringOperand.instance().condition('equals')); fix.detectChanges(); rows = TreeGridFunctions.getAllRows(fix); - expect(rows.length).toEqual(16, 'Wrong rows count'); + expect(rows.length).toEqual(17, 'Wrong rows count'); }); it('\'Blanks\' should be always visible', (async () => { @@ -535,7 +537,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { expect(gridCellValues.length).toEqual(18, 'incorrect rendered grid items count'); })); - it('Should correctly update checkboxes after clearing column filters', fakeAsync(() => { + it('Should update checkboxes after clearing column filters correctly', fakeAsync(() => { GridFunctions.clickExcelFilterIcon(fix, 'ID'); tick(100); fix.detectChanges(); @@ -578,7 +580,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { checkboxes.forEach(ch => expect(ch.checked).toBe(true, 'incorrect checkbox state')); })); - it('Should correctly filter tree grid', fakeAsync(() => { + it('Should filter tree grid correctly', fakeAsync(() => { GridFunctions.clickExcelFilterIcon(fix, 'ID'); tick(100); fix.detectChanges(); @@ -679,6 +681,87 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { })); }); + describe('Tree grid ESF templates', () => { + let tGrid: IgxTreeGridComponent; + + beforeEach(fakeAsync(() => { + fix = TestBed.createComponent(IgxTreeGridFilteringESFTemplatesComponent); + fix.detectChanges(); + tick(16); + tGrid = fix.componentInstance.treeGrid; + + const hierarchicalFilterStrategy = new TreeGridFilteringStrategy(['ID']); + tGrid.filterStrategy = hierarchicalFilterStrategy; + tGrid.allowFiltering = true; + tGrid.filterMode = FilterMode.excelStyleFilter; + fix.detectChanges(); + })); + + it('Should use custom templates for ESF components instead of default ones.', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + + expect(excelMenu.querySelector('igx-excel-style-column-operations')).not.toBeNull(); + expect(excelMenu.querySelector('igx-excel-style-filter-operations')).not.toBeNull(); + expect(GridFunctions.getExcelFilteringSortComponent(fix, excelMenu)).not.toBeNull(); + expect(GridFunctions.getExcelFilteringSearchComponent(fix, excelMenu)).not.toBeNull(); + + expect(GridFunctions.getExcelFilteringHeaderComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringMoveComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringPinComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringHideComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringColumnSelectionComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringClearFiltersComponent(fix, excelMenu)).toBeNull(); + expect(GridFunctions.getExcelFilteringConditionalFilterComponent(fix, excelMenu)).toBeNull(); + })); + + it('Should filter tree grid with templates correctly', fakeAsync(() => { + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix, null, 'igx-tree-grid'); + const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent, 'igx-tree-grid'); + + UIInteractions.clickAndSendInputElementValue(inputNativeElement, '6', fix); + tick(100); + fix.detectChanges(); + + const treeItems = GridFunctions.getExcelStyleSearchComponentTreeNodes(fix, searchComponent); + expect(treeItems.length).toEqual(2, 'incorrect rendered items count'); + + GridFunctions.clickApplyExcelStyleFiltering(fix, null, 'igx-tree-grid'); + tick(100); + fix.detectChanges(); + + const gridCellValues = GridFunctions.getColumnCells(fix, 'ID', 'igx-tree-grid-cell') + .map(c => c.nativeElement.innerText) + .sort(); + + expect(gridCellValues.length).toEqual(3); + + GridFunctions.clickExcelFilterIcon(fix, 'ID'); + tick(100); + fix.detectChanges(); + + const excelMenu = GridFunctions.getExcelStyleFilteringComponent(fix, 'igx-tree-grid'); + let checkboxes: any[] = Array.from(GridFunctions.getExcelStyleFilteringCheckboxes(fix, excelMenu, 'igx-tree-grid')); + expect(!checkboxes[1].checked && !checkboxes[2].checked && !checkboxes[3].checked && checkboxes[4].indeterminate).toBe(true); + })); + + it('Should use custom excel style filter icon instead of default one.', fakeAsync(() => { + const header = GridFunctions.getColumnHeader('ID', fix); + fix.detectChanges(); + const icon = GridFunctions.getHeaderFilterIcon(header); + fix.detectChanges(); + expect(icon).not.toBeNull(); + expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('filter_alt'); + })); + }); + describe('Filtering: Row editing', () => { let treeGrid: IgxTreeGridComponent; beforeEach(fakeAsync(/** height/width setter rAF */() => { diff --git a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts index 4b453e26cf3..c37f80385da 100644 --- a/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/tree-grid-components.spec.ts @@ -38,6 +38,34 @@ export class IgxTreeGridFilteringComponent { public data = SampleTestData.employeeTreeData(); } +@Component({ + template: ` + + + + + + + + filter_alt + + + + + + + + + + + + ` +}) +export class IgxTreeGridFilteringESFTemplatesComponent { + @ViewChild(IgxTreeGridComponent, { static: true }) public treeGrid: IgxTreeGridComponent; + public data = SampleTestData.employeeTreeData(); +} + @Component({ template: ` Date: Wed, 23 Feb 2022 18:19:31 +0200 Subject: [PATCH 47/63] feat(filtering): move esf logic to the strategy --- .../lib/data-operations/filtering-strategy.ts | 121 ++++++++++-------- .../src/lib/grids/api.service.ts | 4 + .../src/lib/grids/common/grid.interface.ts | 1 + .../excel-style/base-filtering.component.ts | 2 - .../excel-style-search.component.ts | 4 +- .../grid.excel-style-filtering.component.ts | 115 ++++------------- .../grids/tree-grid/tree-grid-api.service.ts | 13 +- .../tree-grid/tree-grid.filtering.strategy.ts | 45 ++++--- .../tree-grid/tree-grid.grouping.pipe.ts | 10 +- 9 files changed, 150 insertions(+), 165 deletions(-) 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 789b9ae23e7..292724323c6 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -1,8 +1,10 @@ import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree'; -import { resolveNestedPath, parseDate } from '../core/utils'; +import { resolveNestedPath, parseDate, formatDate, formatCurrency } from '../core/utils'; import { ColumnType, GridType } from '../grids/common/grid.interface'; import { GridColumnDataType } from './data-util'; +import { SortingDirection } from './sorting-strategy'; +import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; const DateType = 'date'; const DateTimeType = 'dateTime'; @@ -11,15 +13,13 @@ const TimeType = 'time'; export interface IFilteringStrategy { filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[]; - getUniqueColumnValues( - column: ColumnType, - tree: FilteringExpressionsTree) : Promise; - shouldFormatFilterValues(column: ColumnType): boolean; + getFilterItems(column: ColumnType, tree: FilteringExpressionsTree) : Promise; } -export class HierarchicalColumnValue { - public value: any; - public children?: HierarchicalColumnValue[]; +export interface IgxFilterItem { + value: any; + label?: string; + children?: IgxFilterItem[]; } export abstract class BaseFilteringStrategy implements IFilteringStrategy { @@ -69,59 +69,80 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } - public getUniqueColumnValues( - column: ColumnType, - tree: FilteringExpressionsTree) : Promise { - const data = column.grid.gridAPI.filterDataByExpressions(tree); + public getFilterItems(column: ColumnType, tree: FilteringExpressionsTree): Promise { + + let data = column.grid.gridAPI.filterDataByExpressions(tree); + data = column.grid.gridAPI.sortDataByExpressions(data, + [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }]); + const columnField = column.field; - const columnValues = data.map(record => { + let filterItems: IgxFilterItem[] = data.map(record => { let value = resolveNestedPath(record, columnField); + const applyFormatter = column.formatter && this.shouldFormatFilterValues(column); - value = column.formatter && this.shouldFormatFilterValues(column) ? - column.formatter(value) : + value = applyFormatter ? + column.formatter(value, record) : value; - return value; + return { + value, + label: this.getFilterItemLabel(column, value, !applyFormatter, record) + }; }); - const uniqueValues = this.generateUniqueValues(column, columnValues); + filterItems = this.getUniqueFilterItems(column, filterItems); - return Promise.resolve(uniqueValues); + return Promise.resolve(filterItems); } - protected generateUniqueValues(column: ColumnType, columnValues: any[]) { - let uniqueValues: any[]; - - if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) { - const filteredUniqueValues = columnValues.map(s => s?.toString().toLowerCase()) - .reduce((map, val, i) => map.has(val) ? map : map.set(val, columnValues[i]), new Map()); - uniqueValues = Array.from(filteredUniqueValues.values()); - } else if (column.dataType === GridColumnDataType.DateTime) { - uniqueValues = Array.from(new Set(columnValues.map(v => v?.toLocaleString()))); - uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); - } else if (column.dataType === GridColumnDataType.Time) { - uniqueValues = Array.from(new Set(columnValues.map(v => { - if (v) { - v = new Date(v); - return new Date().setHours(v.getHours(), v.getMinutes(), v.getSeconds()); - } else { - return v; - } - }))); - uniqueValues.forEach((d, i) => uniqueValues[i] = d ? new Date(d) : d); - } else if (column.dataType === GridColumnDataType.Date) { - const valuesMap = columnValues.reduce((map: Map, val) => { - if (!val) { - return map.set(val, val); - } + protected getFilterItemLabel(column: ColumnType, value: any, applyFormatter: boolean, data: any) { + if (column.formatter) { + if (applyFormatter) { + return column.formatter(value, data); + } + return value; + } - const date = new Date(val); - const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString(); - return map.has(key) ? map : map.set(key, date); - }, new Map()); - uniqueValues = Array.from(valuesMap.values()); - } else { - uniqueValues = Array.from(new Set(columnValues)); + const { display, format, digitsInfo, currencyCode, timezone } = column.pipeArgs; + const locale = column.grid.locale; + + switch (column.dataType) { + case GridColumnDataType.Date: + case GridColumnDataType.DateTime: + case GridColumnDataType.Time: + return formatDate(value, format, locale, timezone); + case GridColumnDataType.Currency: + return formatCurrency(value, currencyCode || getLocaleCurrencyCode(locale), display, digitsInfo, locale); + case GridColumnDataType.Number: + return formatNumber(value, locale, digitsInfo); + case GridColumnDataType.Percent: + return formatPercent(value, locale, digitsInfo); + default: + return value; } + } + + 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?.toLocaleString(); + 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()) : 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; } diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 09c2cc8cbc1..145cf943a08 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -479,6 +479,10 @@ export class GridBaseAPIService implements GridServiceType { return data; } + public sortDataByExpressions(data: any[], expressions: ISortingExpression[]) { + return DataUtil.sort(cloneArray(data), expressions, this.grid.sortStrategy, this.grid); + } + /** * Updates related row of provided grid's data source with provided new row value * diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index f555aa05ac5..d33ed136e7f 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -234,6 +234,7 @@ export interface GridServiceType { clear_sort(fieldName: string): void; filterDataByExpressions(expressionsTree: IFilteringExpressionsTree): any[]; + sortDataByExpressions(data: any[], expressions: ISortingExpression[]): any[]; update_cell(cell: IgxCell): IGridEditEventArgs; update_row(row: IgxEditRow, value: any, event?: Event): IGridEditEventArgs; 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 194f35836b8..3cdf3c2966a 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 @@ -41,6 +41,4 @@ export abstract class BaseFilteringComponent { public abstract onPin(): void; public abstract onHideToggle(): void; public abstract cancel(): void; - public abstract getFilterItemValue(item: FilterListItem): string; - } 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 479b47aea5a..667a8186598 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 @@ -498,7 +498,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { condition, fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, - searchVal: this.esf.getFilterItemValue(element) + searchVal: element.value }); }); } else { @@ -516,7 +516,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { 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 => this.esf.getFilterItemValue(e))) + selectedItems.map(e => e.value)) }); if (blanksItem) { diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index f332e3876be..12a0cae3890 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -19,19 +19,17 @@ import { ViewRef } from '@angular/core'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree'; -import { parseDate, PlatformUtil, formatDate, formatCurrency } from '../../../core/utils'; +import { PlatformUtil, formatDate, formatCurrency } from '../../../core/utils'; import { GridColumnDataType } from '../../../data-operations/data-util'; import { Subscription } from 'rxjs'; import { DisplayDensity } from '../../../core/density'; import { GridSelectionMode } from '../../common/enums'; -import { HierarchicalColumnValue } from '../../../data-operations/filtering-strategy'; +import { IgxFilterItem } from '../../../data-operations/filtering-strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem, generateExpressionsList } from './common'; import { ColumnType, GridType, IGX_GRID_BASE } from '../../common/grid.interface'; import { IgxOverlayService } from '../../../services/overlay/overlay'; -import { SortingDirection } from '../../../data-operations/sorting-strategy'; - @Directive({ selector: 'igx-excel-style-column-operations,[igxExcelStyleColumnOperations]' @@ -187,7 +185,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent /** * @hidden @internal */ - public uniqueValues: any[] | HierarchicalColumnValue[] = []; + public uniqueValues: IgxFilterItem[] = []; /** * @hidden @internal */ @@ -423,30 +421,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return selectableExpressionsCount === this.expressionsList.length; } - // TODO Check if this is necessary - // private areExpressionsValuesInTheList() { - // if (this.column.dataType === GridColumnDataType.Boolean) { - // return true; - // } - - // if (this.filterValues.size === 1) { - // const firstValue = this.filterValues.values().next().value; - - // if (!firstValue && firstValue !== 0) { - // return true; - // } - // } - - // for (const expression of this.uniqueValues) { - // const value = this.getExpressionValue(expression); - // if (this.filterValues.has(value)) { - // return true; - // } - // } - - // return false; - // } - private populateColumnData() { if (this.grid.uniqueColumnValuesStrategy) { this.cdr.detectChanges(); @@ -461,28 +435,27 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent const expressionsTree: FilteringExpressionsTree = this.getColumnFilterExpressionsTree(); const prevColumn = this.column; - this.grid.uniqueColumnValuesStrategy(this.column, expressionsTree, (colVals: any[]) => { + this.grid.uniqueColumnValuesStrategy(this.column, expressionsTree, (values: any[]) => { if (!this.column || this.column !== prevColumn) { return; } - this.uniqueValues = colVals; + this.uniqueValues = values.map(v => ({ + value: v, + label: this.getFilterItemLabel(v) + })); this.renderValues(); this.loadingEnd.emit(); }); } - private shouldFormatValues() { - return this.column.formatter && this.grid.filterStrategy.shouldFormatFilterValues(this.column); - } - private renderColumnValuesFromData() { const expressionsTree = this.getColumnFilterExpressionsTree(); - const promise = this.grid.filterStrategy.getUniqueColumnValues(this.column, expressionsTree); - promise.then((colVals) => { - this.isHierarchical = colVals.length > 0 && colVals[0] instanceof HierarchicalColumnValue; - this.uniqueValues = colVals; + const promise = this.grid.filterStrategy.getFilterItems(this.column, expressionsTree); + promise.then((items) => { + this.isHierarchical = items.length > 0 && items.some(i => i.children && i.children.length > 0); + this.uniqueValues = items; this.renderValues(); this.sortingChanged.emit(); }); @@ -534,8 +507,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.addItems(shouldUpdateSelection); } - this.listData = this.sortFilterItems(this.listData); - if (!this.isHierarchical && this.containsNullOrEmpty) { const blanksItem = this.generateBlanksItem(shouldUpdateSelection); this.listData.unshift(blanksItem); @@ -552,29 +523,6 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.listDataLoaded.emit(); } - private sortFilterItems(items: FilterListItem[]) { - if (!items) { - return undefined; - } - - items.forEach(item => - item.children = this.sortFilterItems(item.children)); - - return this.column.sortStrategy.sort(items, 'value', SortingDirection.Asc, this.column.sortingIgnoreCase, - (obj, key) => { - let resolvedValue = obj[key]; - if (this.column.dataType === GridColumnDataType.Time) { - resolvedValue = new Date().setHours( - resolvedValue.getHours(), - resolvedValue.getMinutes(), - resolvedValue.getSeconds(), - resolvedValue.getMilliseconds()); - } - - return resolvedValue; - }); - } - private getColumnFilterExpressionsTree() { const gridExpressionsTree: IFilteringExpressionsTree = this.grid.filteringExpressionsTree; const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.fieldName); @@ -596,14 +544,15 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.selectAllSelected = true; this.selectAllIndeterminate = false; this.uniqueValues.forEach(element => { + const value = element.value; const filterListItem = new FilterListItem(); - if (element !== undefined && element !== null && element !== '') { + if (value !== undefined && value !== null && value !== '') { if (this.column.filteringExpressionsTree) { - if (element === true && this.expressionsList.find(exp => exp.expression.condition.name === 'true')) { + if (value === true && this.expressionsList.find(exp => exp.expression.condition.name === 'true')) { filterListItem.isSelected = true; filterListItem.isFiltered = true; this.selectAllIndeterminate = true; - } else if (element === false && this.expressionsList.find(exp => exp.expression.condition.name === 'false')) { + } else if (value === false && this.expressionsList.find(exp => exp.expression.condition.name === 'false')) { filterListItem.isSelected = true; filterListItem.isFiltered = true; this.selectAllIndeterminate = true; @@ -615,8 +564,8 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.isSelected = true; filterListItem.isFiltered = true; } - filterListItem.value = element; - filterListItem.label = element ? + filterListItem.value = value; + filterListItem.label = value ? this.grid.resourceStrings.igx_grid_filter_true : this.grid.resourceStrings.igx_grid_filter_false; filterListItem.indeterminate = false; @@ -635,18 +584,19 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent this.containsNullOrEmpty = this.uniqueValues.length > this.listData.length; } - private generateFilterListItems(values: any[] | HierarchicalColumnValue[], shouldUpdateSelection: boolean, parent?: FilterListItem) { + private generateFilterListItems(values: IgxFilterItem[], shouldUpdateSelection: boolean, parent?: FilterListItem) { let filterListItems = []; - const applyFormatter = !this.shouldFormatValues(); values?.forEach(element => { - const value = this.isHierarchical ? element.value : element; + const value = element.value; const hasValue = value !== undefined && value !== null && value !== ''; if (hasValue) { const filterListItem = new FilterListItem(); filterListItem.parent = parent; filterListItem.value = value; - filterListItem.label = this.getFilterItemLabel(value, applyFormatter); + filterListItem.label = element.label !== undefined ? + element.label : + this.getFilterItemLabel(value); filterListItem.indeterminate = false; filterListItem.isSelected = true; filterListItem.isFiltered = true; @@ -656,7 +606,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent filterListItem.isFiltered = false; if (shouldUpdateSelection) { - const exprValue = this.getExpressionValue(filterListItem); + const exprValue = this.getExpressionValue(value); if (this.filterValues.has(exprValue)) { filterListItem.isSelected = true; filterListItem.isFiltered = true; @@ -738,9 +688,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } } - private getExpressionValue(element: FilterListItem): string { - let value = this.getFilterItemValue(element); - + private getExpressionValue(value: any): string { if (this.column.dataType === GridColumnDataType.Date) { value = value ? new Date(value).toISOString() : value; } else if (this.column.dataType === GridColumnDataType.DateTime) { @@ -751,17 +699,4 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return value; } - - /** - * @hidden @internal - */ - public getFilterItemValue(item: FilterListItem) { - if (this.isHierarchical) { - return item.parent ? - `${this.getFilterItemValue(item.parent)}.[${item.value}]` : - `[${item.value}]`; - } else { - return item.value; - } - } } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index ae7ba728341..7c8829f89df 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -7,6 +7,8 @@ import { cloneArray, mergeObjects } from '../../core/utils'; import { IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { TreeGridFilteringStrategy } from './tree-grid.filtering.strategy'; import { ColumnType, GridType } from '../common/grid.interface'; +import { ISortingExpression } from '../../data-operations/sorting-strategy'; +import { IgxDataRecordSorting } from '../common/strategy'; @Injectable() export class IgxTreeGridAPIService extends GridBaseAPIService { @@ -233,6 +235,15 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { return data; } + public sortDataByExpressions(data: ITreeGridRecord[], expressions: ISortingExpression[]) { + const records: ITreeGridRecord[] = DataUtil.sort( + cloneArray(data), + expressions, + this.grid.sortStrategy ?? new IgxDataRecordSorting(), + this.grid); + return records.map(r => r.data); + } + public filterTreeDataByExpressions(expressionsTree: IFilteringExpressionsTree): ITreeGridRecord[] { let records = this.grid.rootRecords; @@ -314,7 +325,7 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { for (const record of records) { if (!record.isFilteredOutParent) { - data.push(record.data); + data.push(record); } this.getFlatDataFromFilteredRecords(record.children, data); } 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 7bb87378315..073080f91c3 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 @@ -1,7 +1,8 @@ import { parseDate, resolveNestedPath } from '../../core/utils'; import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; -import { BaseFilteringStrategy, HierarchicalColumnValue } from '../../data-operations/filtering-strategy'; +import { BaseFilteringStrategy, IgxFilterItem } from '../../data-operations/filtering-strategy'; +import { SortingDirection } from '../../data-operations/sorting-strategy'; import { ColumnType, GridType } from '../common/grid.interface'; import { IgxTreeGridAPIService } from './tree-grid-api.service'; import { ITreeGridRecord } from './tree-grid.interfaces'; @@ -25,7 +26,7 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { resolveNestedPath(hierarchicalRecord.data, fieldName); value = column?.formatter && this.shouldFormatFilterValues(column) ? - column.formatter(value) : + column.formatter(value, rec.data) : value && (isDate || isTime) ? parseDate(value) : value; return value; @@ -70,32 +71,42 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return this.hierarchicalFilterFields && this.hierarchicalFilterFields.indexOf(field) !== -1; } - public override getUniqueColumnValues( - column: ColumnType, - tree: FilteringExpressionsTree) : Promise { - + public getFilterItems(column: ColumnType, tree: FilteringExpressionsTree): Promise { if (!this.isHierarchicalFilterField(column.field)) { - return super.getUniqueColumnValues(column, tree); + return super.getFilterItems(column, tree); } - const data = (column.grid.gridAPI as IgxTreeGridAPIService).filterTreeDataByExpressions(tree); - const columnValues = this.getHierarchicalColumnValues(data, column); + let data = (column.grid.gridAPI as IgxTreeGridAPIService).filterTreeDataByExpressions(tree); + data = DataUtil.treeGridSort( + data, + [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }], + column.grid.sortStrategy, + null, + column.grid); + + const items = this.getHierarchicalFilterItems(data, column); - return Promise.resolve(columnValues); + + return Promise.resolve(items); } - private getHierarchicalColumnValues(records: ITreeGridRecord[], column: ColumnType) { + private getHierarchicalFilterItems(records: ITreeGridRecord[], column: ColumnType, parent?: IgxFilterItem): IgxFilterItem[] { return records?.map(record => { let value = resolveNestedPath(record.data, column.field); + const applyFormatter = column.formatter && this.shouldFormatFilterValues(column); - value = column.formatter && this.shouldFormatFilterValues(column) ? - column.formatter(value) : + value = applyFormatter ? + column.formatter(value, record.data) : value; - const hierarchicalItem = new HierarchicalColumnValue(); - hierarchicalItem.value = value; - hierarchicalItem.children = this.getHierarchicalColumnValues(record.children, column) - return hierarchicalItem; + const hierarchicalValue = parent ? + `${parent.value}${value ? `.[${value}]` : ''}` : + `[${value}]` + + const filterItem: IgxFilterItem = { value: hierarchicalValue }; + filterItem.label = this.getFilterItemLabel(column, value, !applyFormatter, record.data); + filterItem.children = this.getHierarchicalFilterItems(record.children, column, filterItem); + return filterItem; }); } } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.grouping.pipe.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.grouping.pipe.ts index 109ecb789dd..554ee8b9b92 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.grouping.pipe.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.grouping.pipe.ts @@ -31,9 +31,13 @@ export class IgxGroupedTreeGridSorting extends IgxSorting { } protected getFieldValue(obj: any, key: string, isDate: boolean = false, isTime: boolean = false): any { - return obj.data[HIDDEN_FIELD_NAME] ? - super.getFieldValue(obj.data[HIDDEN_FIELD_NAME], key, isDate, isTime) : - super.getFieldValue(obj.data, key, isDate, isTime); + const data = obj.data[HIDDEN_FIELD_NAME] ? + obj.data.hasOwnProperty(key) ? + obj.data : + obj.data[HIDDEN_FIELD_NAME] : + obj.data; + + return super.getFieldValue(data, key, isDate, isTime); } } From 4a7343356c6a4b9cc5bf2474737a25f932770b6f Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Wed, 23 Feb 2022 21:35:59 +0200 Subject: [PATCH 48/63] test(filtering): sort values in strategy --- .../igniteui-angular/src/lib/test-utils/grid-samples.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) 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 2292f4ba8cf..bc8a45df298 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 @@ -20,6 +20,7 @@ import { ISortingStrategy, SortingDirection } from '../data-operations/sorting-s import { IgxActionStripComponent } from '../action-strip/action-strip.component'; import { ExpressionUI } from '../grids/filtering/excel-style/common'; import { IDataCloneStrategy } from '../data-operations/data-clone-strategy'; +import { GridColumnDataType } from '../data-operations/data-util'; @Component({ template: `
@@ -1215,6 +1216,9 @@ export class IgxGridFilteringESFLoadOnDemandComponent extends BasicGridComponent setTimeout(() => { const filteredData = this._filteringStrategy.filter(this.data, columnExprTree, null, null); const columnValues = filteredData.map(record => record[column.field]); + columnValues.sort(column.dataType !== GridColumnDataType.String ? + (a, b) => a - b : + undefined); done(columnValues); this.doneCallbackCounter++; }, 1000); From dae95ee71b6e35465bb829e7647f4027c4ad9557 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Wed, 23 Feb 2022 21:54:17 +0200 Subject: [PATCH 49/63] feat(filtering): sort strategy result --- .../grid.excel-style-filtering.component.ts | 20 ++++++++++++++++--- .../src/lib/test-utils/grid-samples.spec.ts | 3 --- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 12a0cae3890..9fa867c417d 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -30,6 +30,7 @@ import { BaseFilteringComponent } from './base-filtering.component'; import { ExpressionUI, FilterListItem, generateExpressionsList } from './common'; import { ColumnType, GridType, IGX_GRID_BASE } from '../../common/grid.interface'; import { IgxOverlayService } from '../../../services/overlay/overlay'; +import { SortingDirection } from '../../../data-operations/sorting-strategy'; @Directive({ selector: 'igx-excel-style-column-operations,[igxExcelStyleColumnOperations]' @@ -440,11 +441,24 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return; } - this.uniqueValues = values.map(v => ({ - value: v, - label: this.getFilterItemLabel(v) + const items = values.map(v => ({ + value: v })); + this.uniqueValues = this.column.sortStrategy.sort(items, 'value', SortingDirection.Asc, this.column.sortingIgnoreCase, + (obj, key) => { + let resolvedValue = obj[key]; + if (this.column.dataType === GridColumnDataType.Time) { + resolvedValue = new Date().setHours( + resolvedValue.getHours(), + resolvedValue.getMinutes(), + resolvedValue.getSeconds(), + resolvedValue.getMilliseconds()); + } + + return resolvedValue; + }); + this.renderValues(); this.loadingEnd.emit(); }); 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 bc8a45df298..f745ea04899 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 @@ -1216,9 +1216,6 @@ export class IgxGridFilteringESFLoadOnDemandComponent extends BasicGridComponent setTimeout(() => { const filteredData = this._filteringStrategy.filter(this.data, columnExprTree, null, null); const columnValues = filteredData.map(record => record[column.field]); - columnValues.sort(column.dataType !== GridColumnDataType.String ? - (a, b) => a - b : - undefined); done(columnValues); this.doneCallbackCounter++; }, 1000); From 40146f958044e593e459a03e6d4a080503a4887c Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 24 Feb 2022 11:23:59 +0200 Subject: [PATCH 50/63] fix(flitering): fix circular dependency --- .../src/lib/data-operations/data-util.spec.ts | 14 +++++++------- .../src/lib/data-operations/data-util.ts | 7 ------- .../src/lib/data-operations/filtering-strategy.ts | 14 ++++++++++++-- .../igniteui-angular/src/lib/grids/api.service.ts | 3 ++- .../src/lib/grids/grid/grid.pipes.ts | 4 ++-- .../lib/grids/pivot-grid/pivot-grid.component.ts | 5 +++-- .../src/lib/grids/pivot-grid/pivot-grid.pipes.ts | 4 ++-- .../lib/grids/tree-grid/tree-grid-api.service.ts | 3 ++- .../tree-grid/tree-grid.filtering.strategy.ts | 2 +- .../exporter-common/base-export-service.ts | 9 +++++---- 10 files changed, 36 insertions(+), 29 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts index 6dcf68d6b30..d6813674dee 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts @@ -7,7 +7,7 @@ import { DataUtil } from './data-util'; import { IGroupByResult } from './grouping-result.interface'; import { IGroupingState } from './groupby-state.interface'; import { IGroupByRecord } from './groupby-record.interface'; -import { FilteringStrategy } from './filtering-strategy'; +import { FilteringStrategy, FilterUtil } from './filtering-strategy'; import { IFilteringExpressionsTree, FilteringExpressionsTree } from './filtering-expressions-tree'; import { IFilteringState } from './filtering-state.interface'; import { FilteringLogic } from './filtering-expression.interface'; @@ -355,7 +355,7 @@ const testFilter = () => { searchVal: 3 } ]; - const res = DataUtil.filter(data, state); + const res = FilterUtil.filter(data, state); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual([4]); }); @@ -384,12 +384,12 @@ const testFilter = () => { } ]; - let res = DataUtil.filter(data, state); + let res = FilterUtil.filter(data, state); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual(dataGenerator.getValuesForColumn(data, 'number')); (res[0] as { string: string }).string = 'ROW'; // case-sensitive - res = DataUtil.filter(res, stateIgnoreCase); + res = FilterUtil.filter(res, stateIgnoreCase); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual([0]); }); @@ -405,7 +405,7 @@ const testFilter = () => { searchVal: new Date() } ]; - const res = DataUtil.filter(data, state); + const res = FilterUtil.filter(data, state); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual([1, 2, 3, 4]); }); @@ -419,7 +419,7 @@ const testFilter = () => { fieldName: 'boolean' } ]; - const res = DataUtil.filter(data, state); + const res = FilterUtil.filter(data, state); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual([0, 2, 4]); }); @@ -434,7 +434,7 @@ const testFilter = () => { fieldName: 'boolean' } ]; - const res = DataUtil.filter(data, state); + const res = FilterUtil.filter(data, state); expect(dataGenerator.getValuesForColumn(res, 'number')) .toEqual([0, 2]); }); 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 c5421f3e705..cbad97fd4d3 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.ts @@ -117,13 +117,6 @@ export class DataUtil { return data.slice(index * recordsPerPage, (index + 1) * recordsPerPage); } - public static filter(data: T[], state: IFilteringState, grid?: GridType): T[] { - if (!state.strategy) { - state.strategy = new FilteringStrategy(); - } - return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid); - } - public static correctPagingState(state: IPagingState, length: number) { const maxPage = Math.ceil(length / state.recordsPerPage) - 1; if (!isNaN(maxPage) && state.index > maxPage) { 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 292724323c6..41a1524e042 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -5,11 +5,21 @@ import { ColumnType, GridType } from '../grids/common/grid.interface'; import { GridColumnDataType } from './data-util'; import { SortingDirection } from './sorting-strategy'; import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common'; +import { IFilteringState } from './filtering-state.interface'; const DateType = 'date'; const DateTimeType = 'dateTime'; const TimeType = 'time'; +export class FilterUtil { + public static filter(data: T[], state: IFilteringState, grid?: GridType): T[] { + if (!state.strategy) { + state.strategy = new FilteringStrategy(); + } + return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid); + } +} + export interface IFilteringStrategy { filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[]; @@ -147,7 +157,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return uniqueValues; } - public shouldFormatFilterValues(_column: ColumnType): boolean { + protected shouldFormatFilterValues(_column: ColumnType): boolean { return false; } @@ -225,7 +235,7 @@ export class FormattedValuesFilteringStrategy extends FilteringStrategy { super(); } - public shouldFormatFilterValues(column: ColumnType): boolean { + protected shouldFormatFilterValues(column: ColumnType): boolean { return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); } } diff --git a/projects/igniteui-angular/src/lib/grids/api.service.ts b/projects/igniteui-angular/src/lib/grids/api.service.ts index 75fddca5bde..e762ab9ba47 100644 --- a/projects/igniteui-angular/src/lib/grids/api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/api.service.ts @@ -9,6 +9,7 @@ import { CellType, ColumnType, GridServiceType, GridType, RowType } from './comm import { IGridEditEventArgs, IRowToggleEventArgs } from './common/events'; import { IgxColumnMovingService } from './moving/moving.service'; import { ISortingExpression, SortingDirection } from '../data-operations/sorting-strategy'; +import { FilterUtil } from '../data-operations/filtering-strategy'; /** * @hidden @@ -466,7 +467,7 @@ export class GridBaseAPIService implements GridServiceType { if (expressionsTree.filteringOperands.length) { const state = { expressionsTree, strategy: this.grid.filterStrategy }; - data = DataUtil.filter(cloneArray(data), state, this.grid); + data = FilterUtil.filter(cloneArray(data), state, this.grid); } return data; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts index 3ef96af89da..0b02d8ddb85 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts @@ -6,7 +6,7 @@ import { IGroupByResult } from '../../data-operations/grouping-result.interface' import { IFilteringExpressionsTree, FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { IGroupingExpression } from '../../data-operations/grouping-expression.interface'; import { GridType, IGX_GRID_BASE } from '../common/grid.interface'; -import { IFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { FilterUtil, IFilteringStrategy } from '../../data-operations/filtering-strategy'; import { GridPagingMode } from '../common/enums'; import { ISortingExpression } from '../../data-operations/sorting-strategy'; import { IGridSortingStrategy, IGridGroupingStrategy } from '../common/strategy'; @@ -123,7 +123,7 @@ export class IgxGridFilteringPipe implements PipeTransform { return collection; } - const result = DataUtil.filter(cloneArray(collection), state, this.grid); + const result = FilterUtil.filter(cloneArray(collection), state, this.grid); this.grid.setFilteredData(result, pinned); return result; } 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 0029575a64c..dcb8262867d 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 @@ -68,6 +68,7 @@ import { IgxPivotGridColumnResizerComponent } from '../resizing/pivot-grid/pivot import { IgxActionStripComponent } from '../../action-strip/action-strip.component'; import { IPageEventArgs } from '../../paginator/paginator-interfaces'; import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy'; +import { FilterUtil } from '../../data-operations/filtering-strategy'; let NEXT_ID = 0; const MINIMUM_COLUMN_WIDTH = 200; @@ -956,7 +957,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni strategy: this.filterStrategy || new DimensionValuesFilteringStrategy(), advancedFilteringExpressionsTree: this.advancedFilteringExpressionsTree }; - const filtered = DataUtil.filter(data, state, this); + const filtered = FilterUtil.filter(data, state, this); const allValuesHierarchy = PivotUtil.getFieldsHierarchy( filtered, [dim], @@ -1957,7 +1958,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni strategy: this.filterStrategy || new DimensionValuesFilteringStrategy(), advancedFilteringExpressionsTree: this.advancedFilteringExpressionsTree }; - const filtered = DataUtil.filter(cloneArray(value.records), state, this); + const filtered = FilterUtil.filter(cloneArray(value.records), state, this); if (filtered.length === 0) { shouldGenerate = false; } diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts index 7be6de4fb1d..e497b265951 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts @@ -2,7 +2,7 @@ import { Inject, Pipe, PipeTransform } from '@angular/core'; import { cloneArray } from '../../core/utils'; import { DataUtil } from '../../data-operations/data-util'; import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; -import { IFilteringStrategy } from '../../data-operations/filtering-strategy'; +import { FilterUtil, IFilteringStrategy } from '../../data-operations/filtering-strategy'; import { DefaultPivotGridRecordSortingStrategy, DefaultPivotSortingStrategy, DimensionValuesFilteringStrategy, PivotColumnDimensionsStrategy, @@ -255,7 +255,7 @@ export class IgxPivotGridFilterPipe implements PipeTransform { return collection; } - const result = DataUtil.filter(cloneArray(collection, true), state, this.gridAPI.grid); + const result = FilterUtil.filter(cloneArray(collection, true), state, this.gridAPI.grid); return result; } diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts index 7c8829f89df..fd7277bac99 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-api.service.ts @@ -9,6 +9,7 @@ import { TreeGridFilteringStrategy } from './tree-grid.filtering.strategy'; import { ColumnType, GridType } from '../common/grid.interface'; import { ISortingExpression } from '../../data-operations/sorting-strategy'; import { IgxDataRecordSorting } from '../common/strategy'; +import { FilterUtil } from '../../data-operations/filtering-strategy'; @Injectable() export class IgxTreeGridAPIService extends GridBaseAPIService { @@ -252,7 +253,7 @@ export class IgxTreeGridAPIService extends GridBaseAPIService { expressionsTree, strategy: this.grid.filterStrategy ?? new TreeGridFilteringStrategy() }; - records = DataUtil.filter(cloneArray(records), state, this.grid); + records = FilterUtil.filter(cloneArray(records), state, this.grid); } return records; 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 073080f91c3..001776bf978 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 @@ -122,7 +122,7 @@ export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringS super(); } - public shouldFormatFilterValues(column: ColumnType): boolean { + protected shouldFormatFilterValues(column: ColumnType): boolean { return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field); } } diff --git a/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts b/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts index d2baa8b52e0..ef18d3bdac5 100644 --- a/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts +++ b/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts @@ -12,6 +12,7 @@ import { IFilteringState } from '../../data-operations/filtering-state.interface import { DatePipe } from '@angular/common'; import { IGroupByRecord } from '../../data-operations/groupby-record.interface'; import { ColumnType, GridType, IPathSegment } from '../../grids/common/grid.interface'; +import { FilterUtil } from '../../data-operations/filtering-strategy'; export enum ExportRecordType { GroupedRecord = 'GroupedRecord', @@ -520,7 +521,7 @@ export abstract class IgxBaseExporter { strategy: grid.filterStrategy }; - data = DataUtil.filter(data, filteringState, grid); + data = FilterUtil.filter(data, filteringState, grid); } if (hasSorting && !this.options.ignoreSorting) { @@ -594,7 +595,7 @@ export abstract class IgxBaseExporter { strategy: islandGrid.filterStrategy }; - data = DataUtil.filter(data, filteringState, islandGrid); + data = FilterUtil.filter(data, filteringState, islandGrid); } if (hasSorting && !this.options.ignoreSorting) { @@ -624,7 +625,7 @@ export abstract class IgxBaseExporter { strategy: island.filterStrategy }; - data = DataUtil.filter(data, filteringState, island); + data = FilterUtil.filter(data, filteringState, island); } if (hasSorting && !this.options.ignoreSorting) { @@ -721,7 +722,7 @@ export abstract class IgxBaseExporter { strategy: grid.filterStrategy }; - gridData = DataUtil.filter(gridData, filteringState, grid); + gridData = FilterUtil.filter(gridData, filteringState, grid); } if (hasSorting && !this.options.ignoreSorting) { From 0b20008e23650d88c97779b5abd3cfaaedbadd52 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 24 Feb 2022 14:50:53 +0200 Subject: [PATCH 51/63] feat(filtering): fix loading for getFilterItems --- .../src/lib/data-operations/filtering-strategy.ts | 4 ++-- .../excel-style/grid.excel-style-filtering.component.ts | 6 +++++- .../src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) 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 41a1524e042..ec976e90008 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -23,7 +23,7 @@ export class FilterUtil { export interface IFilteringStrategy { filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[]; - getFilterItems(column: ColumnType, tree: FilteringExpressionsTree) : Promise; + getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree) : Promise; } export interface IgxFilterItem { @@ -79,7 +79,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } - public getFilterItems(column: ColumnType, tree: FilteringExpressionsTree): Promise { + public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { let data = column.grid.gridAPI.filterDataByExpressions(tree); data = column.grid.gridAPI.sortDataByExpressions(data, diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts index 9fa867c417d..64eec9b34e6 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/grid.excel-style-filtering.component.ts @@ -423,8 +423,9 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private populateColumnData() { + this.cdr.detectChanges(); + if (this.grid.uniqueColumnValuesStrategy) { - this.cdr.detectChanges(); this.renderColumnValuesRemotely(); } else { this.renderColumnValuesFromData(); @@ -465,12 +466,15 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent } private renderColumnValuesFromData() { + this.loadingStart.emit(); + const expressionsTree = this.getColumnFilterExpressionsTree(); const promise = this.grid.filterStrategy.getFilterItems(this.column, expressionsTree); promise.then((items) => { this.isHierarchical = items.length > 0 && items.some(i => i.children && i.children.length > 0); this.uniqueValues = items; this.renderValues(); + this.loadingEnd.emit(); this.sortingChanged.emit(); }); } 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 001776bf978..3bac94b8d26 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 @@ -71,7 +71,7 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { return this.hierarchicalFilterFields && this.hierarchicalFilterFields.indexOf(field) !== -1; } - public getFilterItems(column: ColumnType, tree: FilteringExpressionsTree): Promise { + public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { if (!this.isHierarchicalFilterField(column.field)) { return super.getFilterItems(column, tree); } From c04ca0f2e8eaf87cc58f876ef7509de2858b68ce Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 24 Feb 2022 16:16:45 +0200 Subject: [PATCH 52/63] fix(style): fixed mixin names --- .../grid/_excel-filtering-theme.scss | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss index 90e1bd315b5..b5779d260dc 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_excel-filtering-theme.scss @@ -112,14 +112,14 @@ @include tree(tree-theme( - $background: igx-color($palette, 'surface'), - $background-selected: igx-color($palette, 'surface'), - $background-active: igx-color($palette, 'surface'), - $background-active-selected: igx-color($palette, 'surface'), - $foreground: igx-contrast-color($palette, 'surface'), - $foreground-selected: igx-contrast-color($palette, 'surface'), - $foreground-active: igx-contrast-color($palette, 'surface'), - $foreground-active-selected: igx-contrast-color($palette, 'surface'), + $background: color($palette, 'surface'), + $background-selected: color($palette, 'surface'), + $background-active: color($palette, 'surface'), + $background-active-selected: color($palette, 'surface'), + $foreground: contrast-color($palette, 'surface'), + $foreground-selected: contrast-color($palette, 'surface'), + $foreground-active: contrast-color($palette, 'surface'), + $foreground-active-selected: contrast-color($palette, 'surface'), )); .igx-tree-node__wrapper { @@ -305,7 +305,7 @@ } %igx-excel-filter__tree { - background: igx-color($palette, 'surface'); + background: color($palette, 'surface'); overflow-y: auto; margin: 0 rem(-16px); flex: 1; @@ -329,7 +329,7 @@ } %igx-excel-filter__tree-alike { - background: igx-color($palette, 'surface'); + background: color($palette, 'surface'); display: flex; flex-direction: column; z-index: 1; @@ -339,11 +339,11 @@ display: flex; align-items: center; height: map.get($tree-node-height, 'comfortable'); - background: igx-color($palette, 'surface'); + background: color($palette, 'surface'); &:hover, &:focus { - background: igx-color($palette, 'grays', 200); + background: color($palette, 'grays', 200); } > igx-checkbox { From e0a17ac31e5d32ec59f5ec4dc6e285ec8ed6d36e Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 24 Feb 2022 17:04:08 +0200 Subject: [PATCH 53/63] test(filtering): add lod test with filter stategy --- .../lib/grids/grid/grid-filtering-ui.spec.ts | 31 ++++++++++++++++++- .../src/lib/test-utils/grid-samples.spec.ts | 11 ++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 1a1895b6c9f..6707e6e3e77 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -42,7 +42,8 @@ import { CustomFilteringStrategyComponent, IgxGridExternalESFComponent, IgxGridExternalESFTemplateComponent, - IgxGridDatesFilteringComponent + IgxGridDatesFilteringComponent, + LoadOnDemandFilterStrategy } from '../../test-utils/grid-samples.spec'; import { GridSelectionMode, FilterMode } from '../common/enums'; import { ControlsFunction } from '../../test-utils/controls-functions.spec'; @@ -5725,8 +5726,10 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { describe('Load values on demand', () => { let fix; + let grid; beforeEach(fakeAsync(() => { fix = TestBed.createComponent(IgxGridFilteringESFLoadOnDemandComponent); + grid = fix.componentInstance.grid; fix.detectChanges(); })); @@ -5752,6 +5755,32 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { expect(loadingIndicator).toBeNull('esf loading indicator is visible'); })); + it('Verify unique values are loaded correctly in ESF search component when using filtering strategy.', fakeAsync(() => { + grid.uniqueColumnValuesStrategy = undefined; + grid.filterStrategy = new LoadOnDemandFilterStrategy(); + fix.detectChanges(); + + // Open excel style custom filtering dialog and wait a bit. + GridFunctions.clickExcelFilterIcon(fix, 'ProductName'); + tick(400); + fix.detectChanges(); + + // Verify items in search have not loaded yet and that the loading indicator is visible. + let listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix); + expect(listItems.length).toBe(0, 'incorrect rendered list items count'); + let loadingIndicator = GridFunctions.getExcelFilteringLoadingIndicator(fix); + expect(loadingIndicator).not.toBeNull('esf loading indicator is not visible'); + + // Wait for items to load. + tick(650); + + // Verify items in search have loaded and that the loading indicator is not visible. + listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix); + expect(listItems.length).toBe(6, 'incorrect rendered list items count'); + loadingIndicator = GridFunctions.getExcelFilteringLoadingIndicator(fix); + expect(loadingIndicator).toBeNull('esf loading indicator is visible'); + })); + it('Verify unique date values are loaded correctly in ESF search component.', fakeAsync(() => { // Open excel style custom filtering dialog and wait a bit. GridFunctions.clickExcelFilterIcon(fix, 'ReleaseDate'); 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 f745ea04899..2805b82f5b9 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 @@ -11,7 +11,7 @@ import { ColumnDefinitions, GridTemplateStrings, EventSubscriptions, TemplateDef import { IgxColumnComponent } from '../grids/columns/column.component'; import { IgxFilteringOperand, IgxNumberFilteringOperand } from '../data-operations/filtering-condition'; import { IFilteringExpressionsTree, FilteringExpressionsTree } from '../data-operations/filtering-expressions-tree'; -import { FilteringStrategy } from '../data-operations/filtering-strategy'; +import { FilteringStrategy, IgxFilterItem } from '../data-operations/filtering-strategy'; import { CellType, IgxGridComponent } from '../grids/grid/public_api'; import { IgxRowEditTabStopDirective } from '../grids/grid.rowEdit.directive'; import { IgxGridExcelStyleFilteringComponent } from '../grids/filtering/excel-style/grid.excel-style-filtering.component'; @@ -21,6 +21,7 @@ import { IgxActionStripComponent } from '../action-strip/action-strip.component' import { ExpressionUI } from '../grids/filtering/excel-style/common'; import { IDataCloneStrategy } from '../data-operations/data-clone-strategy'; import { GridColumnDataType } from '../data-operations/data-util'; +import { ColumnType } from '../grids/common/grid.interface'; @Component({ template: `
@@ -1193,6 +1194,14 @@ export class CustomFilteringStrategyComponent extends BasicGridComponent { public data = SampleTestData.personNameObjectJobCompany(); } + +export class LoadOnDemandFilterStrategy extends FilteringStrategy { + public override getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise { + return new Promise(resolve => setTimeout(() => + resolve(super.getFilterItems(column, tree)), 1000)); + } +} + @Component({ template: ` From 0423a3ab4f945988b27b3feadbddec8126b50ce5 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 24 Feb 2022 17:22:24 +0200 Subject: [PATCH 54/63] chore(*): fix lint errors --- .../src/lib/grids/grid/grid-filtering-ui.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 6707e6e3e77..2f4988df272 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -5726,7 +5726,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { describe('Load values on demand', () => { let fix; - let grid; + let grid: IgxGridComponent; beforeEach(fakeAsync(() => { fix = TestBed.createComponent(IgxGridFilteringESFLoadOnDemandComponent); grid = fix.componentInstance.grid; @@ -5869,7 +5869,6 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { return newRec; }); const dates = fix.componentInstance.data.filter(el => el.ReleaseDate).map(el => new Date(el.ReleaseDate)).sort((a, b) => a - b); - const grid = fix.componentInstance.grid; grid.locale = 'fr-FR'; const datePipe = new DatePipe(grid.locale); const formatOptions = { @@ -5917,7 +5916,6 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { newRec.ReleaseDate = rec.ReleaseDate ? rec.ReleaseDate.getTime() : null; return newRec; }); - const grid = fix.componentInstance.grid; grid.locale = 'fr-FR'; const datePipe = new DatePipe(grid.locale); grid.getColumnByName('ReleaseDate').formatter = ((value: any) => { @@ -5950,7 +5948,6 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { })); it('Verify date values are displayed in correct format according to column formatter after filtering', fakeAsync(() => { - const grid = fix.componentInstance.grid; grid.locale = 'fr-FR'; const datePipe = new DatePipe(grid.locale); grid.getColumnByName('ReleaseDate').formatter = ((value: any) => { From 4c63bb2042f392aa7059f598c1433dcdf8ba82e9 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Thu, 24 Feb 2022 17:52:21 +0200 Subject: [PATCH 55/63] chore(*): update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aad053d3b20..561715afb14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ All notable changes for each version of this project will be documented in this - For more information, check out the [README](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/grids/pivot-grid/README.md), [specification](https://github.com/IgniteUI/igniteui-angular/wiki/igxPivotGrid-Specification) and [official documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/pivotgrid). +- `IgxTreeGrid` + - Added support for tree filter items in the Excel Style filtering UI. Use the `TreeGridFilteringStrategy` constructor to specify which columns should display tree filter items. + - `igxTooltipTarget` directive now allows specifying a plain text tooltip without adding an additional DOM element decorated with the `igxTooltip` directive. This is achieved via the newly introduced `tooltip` string input. ```html
-
+
{{esf.grid?.resourceStrings.igx_grid_excel_no_matches}} -
+
From 8ec1f919567e299f498ed454193a74c836b1652c Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Fri, 25 Feb 2022 10:41:11 +0200 Subject: [PATCH 57/63] feat(filtering): add tree filter items in group by sample --- src/app/tree-grid-groupby/tree-grid-groupby.sample.html | 1 + src/app/tree-grid-groupby/tree-grid-groupby.sample.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/tree-grid-groupby/tree-grid-groupby.sample.html b/src/app/tree-grid-groupby/tree-grid-groupby.sample.html index 609728a924f..4e67c029cab 100644 --- a/src/app/tree-grid-groupby/tree-grid-groupby.sample.html +++ b/src/app/tree-grid-groupby/tree-grid-groupby.sample.html @@ -14,6 +14,7 @@ [width]="'1200px'" [height]="'700px'" [sortStrategy]="sorting" + [filterStrategy]="filteringStrategy" [allowFiltering]="true" [filterMode]="'excelStyleFilter'" (cellEditDone)="cellEditDone()"> diff --git a/src/app/tree-grid-groupby/tree-grid-groupby.sample.ts b/src/app/tree-grid-groupby/tree-grid-groupby.sample.ts index e0c84c53120..2b809a51069 100644 --- a/src/app/tree-grid-groupby/tree-grid-groupby.sample.ts +++ b/src/app/tree-grid-groupby/tree-grid-groupby.sample.ts @@ -1,6 +1,6 @@ import { Component, ViewChild, OnInit } from '@angular/core'; import { IgxTreeGridComponent, IGroupingExpression, GridSelectionMode, - DisplayDensity, DefaultSortingStrategy, ITreeGridRecord } from 'igniteui-angular'; + DisplayDensity, DefaultSortingStrategy, ITreeGridRecord, TreeGridFilteringStrategy } from 'igniteui-angular'; import { IgxGroupedTreeGridSorting, ITreeGridAggregation } from 'projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.grouping.pipe'; import { SAMPLE_DATA } from '../shared/sample-data'; @@ -26,6 +26,7 @@ export class TreeGridGroupBySampleComponent implements OnInit { public groupKey = 'Groups'; public childDataKey = 'ChildCompanies'; public sorting = IgxGroupedTreeGridSorting.instance(); + public filteringStrategy = new TreeGridFilteringStrategy([this.groupKey]); public employeeAggregations: ITreeGridAggregation[] = [{ field: 'Employees', From b4073136c6d1d2e57c5d28dea4a3c03d2bbd7718 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Fri, 25 Feb 2022 10:55:45 +0200 Subject: [PATCH 58/63] feat(filtering): fix issue with tree grid grouping --- .../src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3bac94b8d26..2e17f18db5c 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 @@ -100,7 +100,7 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { value; const hierarchicalValue = parent ? - `${parent.value}${value ? `.[${value}]` : ''}` : + (value || value === 0) ? `${parent.value}.[${value}]` : value : `[${value}]` const filterItem: IgxFilterItem = { value: hierarchicalValue }; From d7f3e45e248680f39aaf5d4ee16df85f117656a3 Mon Sep 17 00:00:00 2001 From: IBarakov Date: Fri, 25 Feb 2022 12:43:34 +0200 Subject: [PATCH 59/63] chore(*): article -> igx-excel-filter__empty --- .../src/lib/grids/tree-grid/tree-grid-filtering.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts index 3188f4d3c5c..c04d643d430 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts @@ -676,7 +676,7 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => { tick(100); fix.detectChanges(); - const emptyTextEl = searchComponent.querySelector('article'); + const emptyTextEl = searchComponent.querySelector('.igx-excel-filter__empty'); expect(emptyTextEl.innerText).toEqual('No matches'); })); }); From 78d25bab3304f60ae72fda5c1846f0c6c84657b2 Mon Sep 17 00:00:00 2001 From: Diyan Dimitrov Date: Fri, 25 Feb 2022 14:07:54 +0200 Subject: [PATCH 60/63] chore(*): add missing semicolon --- .../src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e17f18db5c..53bb92a75f7 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 @@ -101,7 +101,7 @@ export class TreeGridFilteringStrategy extends BaseFilteringStrategy { const hierarchicalValue = parent ? (value || value === 0) ? `${parent.value}.[${value}]` : value : - `[${value}]` + `[${value}]`; const filterItem: IgxFilterItem = { value: hierarchicalValue }; filterItem.label = this.getFilterItemLabel(column, value, !applyFormatter, record.data); From 8fca4ecbee80abfb9cec02f19af24f4f2eedd243 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 25 Feb 2022 15:15:02 +0200 Subject: [PATCH 61/63] chore(*): fix select all button state --- .../filtering/excel-style/excel-style-search.component.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 63908c0a19a..5cd06da7bd2 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 @@ -395,10 +395,12 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { selectAllBtn = this.esf.listData[0]; } + const anyFiltered = this.esf.listData.some(i => i.isFiltered); + const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); + selectAllBtn.indeterminate = anyFiltered && anyUnfiltered; + selectAllBtn.isSelected = anyFiltered && !anyUnfiltered; + if (!this.searchValue) { - const anyFiltered = this.esf.listData.some(i => i.isFiltered); - const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); - selectAllBtn.indeterminate = anyFiltered && anyUnfiltered; if (this.isHierarchical() && this.tree) { this._hierarchicalSelectedItems = this.tree.nodes.map(n => n.data as FilterListItem).filter(item => item.isFiltered); } From debfb1ea101d80c9519dde00dc123ea91a7bf848 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 25 Feb 2022 16:32:53 +0200 Subject: [PATCH 62/63] chore(*): fixed select all button state --- .../excel-style/excel-style-search.component.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 5cd06da7bd2..c0a4ddf2834 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 @@ -395,12 +395,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { selectAllBtn = this.esf.listData[0]; } - const anyFiltered = this.esf.listData.some(i => i.isFiltered); - const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); - selectAllBtn.indeterminate = anyFiltered && anyUnfiltered; - selectAllBtn.isSelected = anyFiltered && !anyUnfiltered; - if (!this.searchValue) { + const anyFiltered = this.esf.listData.some(i => i.isFiltered); + const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered); + selectAllBtn.indeterminate = anyFiltered && anyUnfiltered; if (this.isHierarchical() && this.tree) { this._hierarchicalSelectedItems = this.tree.nodes.map(n => n.data as FilterListItem).filter(item => item.isFiltered); } @@ -450,6 +448,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } selectAllBtn.indeterminate = false; + selectAllBtn.isSelected = true; selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results; this.cdr.detectChanges(); } From fa2eec6b3db549f2b2a9b1ec33a3b91da1d91f47 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 25 Feb 2022 17:15:02 +0200 Subject: [PATCH 63/63] chore(*): sample improvements --- src/app/tree-grid/tree-grid.sample.html | 2 +- src/app/tree-grid/tree-grid.sample.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app/tree-grid/tree-grid.sample.html b/src/app/tree-grid/tree-grid.sample.html index 6c5709807cf..af7c19c1707 100644 --- a/src/app/tree-grid/tree-grid.sample.html +++ b/src/app/tree-grid/tree-grid.sample.html @@ -6,7 +6,7 @@ + [allowFiltering]="true" [moving]="true" [filterMode]="'excelStyleFilter'" [filterStrategy]="filterStrategy"> diff --git a/src/app/tree-grid/tree-grid.sample.ts b/src/app/tree-grid/tree-grid.sample.ts index b4894f5b384..8f7407e72dd 100644 --- a/src/app/tree-grid/tree-grid.sample.ts +++ b/src/app/tree-grid/tree-grid.sample.ts @@ -19,14 +19,12 @@ export class TreeGridSampleComponent implements OnInit { public density: DisplayDensity = 'comfortable'; public displayDensities; public selectionModes: GridSelectionMode[] = ['none', 'single', 'multiple', 'multipleCascade']; - public hierarchicalFilterStrategy: TreeGridFilteringStrategy; + public filterStrategy = new TreeGridFilteringStrategy(['ID']); private nextRow = 1; constructor(private excelExporterService: IgxExcelExporterService, - private csvExporterService: IgxCsvExporterService) { - this.hierarchicalFilterStrategy = new TreeGridFilteringStrategy(['ID']); - } + private csvExporterService: IgxCsvExporterService) { } public ngOnInit(): void { this.selectionMode = GridSelectionMode.multiple;