Skip to content

Commit 697cfa0

Browse files
authored
Merge branch 'master' into nrobakova/fix-issue-5763
2 parents d3346a9 + f7d79c7 commit 697cfa0

13 files changed

+240
-13
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
All notable changes for each version of this project will be documented in this file.
44

55
## 8.2.0
6-
76
### New theme
87
Ignite UI for angular now have a new theme that mimics Microsoft "Fluent" design system.
98
Depending on your use case you can use one of the following mixins:
@@ -31,6 +30,7 @@ For more information about the theming please read our [documentation](https://w
3130
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
3231
- Advanced Filtering functionality is added. In the advanced filtering dialog, you could create groups of conditions across all grid columns. The advanced filtering button is shown in the grid's toolbar when `allowAdvancedFiltering` and `showToolbar` properties are set to `true`. You could also open/close the advanced filtering dialog using the `openAdvancedFilteringDialog` and `closeAdvancedFilteringDialog` methods.
3332
- `uniqueColumnValuesStrategy` input is added. This property provides a callback for loading unique column values on demand. If this property is provided, the unique values it generates will be used by the Excel Style Filtering (instead of using the unique values from the data that is bound to the grid).
33+
- `[filterStrategy] - input that allows you to override the default filtering strategy`
3434
- `igxExcelStyleLoading` directive is added, which can be used to provide a custom loading template for the Excel Style Filtering. If this property is not provided, a default loading template will be used instead.
3535
- introduced new properties `cellSelection` and `rowSelection` which accept GridSelection mode enumeration. Grid selection mode could be none, single or multiple. Also `hideRowSelectors` property is added, which allows you to show and hide row selectors when row selection is enabled.
3636
- introduced functionality for templating row and header selectors - [spec](https://github.com/IgniteUI/igniteui-angular/wiki/Row-Selection-Templating-(Grid-feature))

projects/igniteui-angular/src/lib/core/styles/components/chip/_chip-theme.scss

+5-4
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@
215215
@include igx-root-css-vars($theme);
216216

217217
$transition: all 120ms $ease-in-out-quad;
218-
$chip-max-width: 24ch;
218+
$chip-max-width: 32ch;
219219

220220
$chip-height-material: (
221221
comfortable: rem(32px),
@@ -338,9 +338,10 @@
338338

339339
%igx-chip__prefix,
340340
%igx-chip__suffix {
341-
display: inline-flex;
342-
align-items: center;
343-
justify-content: center;
341+
@include ellipsis();
342+
display: inline-block;
343+
vertical-align: middle;
344+
max-width: $chip-max-width;
344345
}
345346

346347
%igx-chip__suffix {

projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy {
5555
}
5656

5757
export class FilteringStrategy extends BaseFilteringStrategy {
58+
private static _instace: FilteringStrategy = null;
59+
60+
public constructor() { super(); }
61+
62+
public static instance() {
63+
return this._instace || (this._instace = new this());
64+
}
65+
5866
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree): T[] {
5967
let i;
6068
let rec;

projects/igniteui-angular/src/lib/grids/grid-base.component.ts

+23
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filter
9999
import { IgxColumnResizingService } from './grid-column-resizing.service';
100100
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './igx-row-selectors.module';
101101
import { DeprecateProperty } from '../core/deprecateDecorators';
102+
import { IFilteringStrategy } from '../data-operations/filtering-strategy';
102103
import { IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective,
103104
IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective } from './grid/grid.directives';
104105
import { GridKeydownTargetType, GridSelectionMode, GridSummaryPosition, GridSummaryCalculationMode, FilterMode } from './common/enums';
@@ -242,6 +243,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
242243
private _locale = null;
243244
public _destroyed = false;
244245
private overlayIDs = [];
246+
private _filteringStrategy: IFilteringStrategy;
245247

246248
private _hostWidth;
247249
private _advancedFilteringOverlayId: string;
@@ -1082,6 +1084,27 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
10821084
}
10831085
}
10841086

1087+
/**
1088+
* Gets the filtering strategy of the grid.
1089+
* ```typescript
1090+
* let filterStrategy = this.grid.filterStrategy
1091+
* ```
1092+
*/
1093+
@Input()
1094+
get filterStrategy(): IFilteringStrategy {
1095+
return this._filteringStrategy;
1096+
}
1097+
1098+
/**
1099+
* Sets the filtering strategy of the grid.
1100+
* ```html
1101+
* <igx-grid #grid [data]="localData" [filterStrategy]="filterStrategy"></igx-grid>
1102+
* ```
1103+
*/
1104+
set filterStrategy(classRef: IFilteringStrategy) {
1105+
this._filteringStrategy = classRef;
1106+
}
1107+
10851108
/**
10861109
* An @Input property that provides a callback for loading unique column values on demand.
10871110
* If this property is provided, the unique values it generates will be used by the Excel Style Filtering.

projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts

+95-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
IgxTestExcelFilteringDatePickerComponent,
4141
IgxGridFilteringTemplateComponent,
4242
IgxGridFilteringESFTemplatesComponent,
43-
IgxGridFilteringESFLoadOnDemandComponent
43+
IgxGridFilteringESFLoadOnDemandComponent,
44+
CustomFilteringStrategyComponent
4445
} from '../../test-utils/grid-samples.spec';
4546
import { HelperUtils, resizeObserverIgnoreError } from '../../test-utils/helper-utils.spec';
4647
import { GridSelectionMode, FilterMode } from '../common/enums';
@@ -3601,7 +3602,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => {
36013602
IgxTestExcelFilteringDatePickerComponent,
36023603
IgxGridFilteringESFTemplatesComponent,
36033604
IgxGridFilteringESFLoadOnDemandComponent,
3604-
IgxGridFilteringMCHComponent
3605+
IgxGridFilteringMCHComponent,
3606+
CustomFilteringStrategyComponent
36053607
],
36063608
imports: [
36073609
NoopAnimationsModule,
@@ -5843,6 +5845,97 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => {
58435845
GridFunctions.verifyColumnIsPinned(column, true, 8);
58445846
}));
58455847
});
5848+
describe('IgxGrid - Custom Filtering Strategy #grid', () => {
5849+
let fix;
5850+
let grid;
5851+
5852+
beforeEach(async(() => {
5853+
resizeObserverIgnoreError();
5854+
fix = TestBed.createComponent(CustomFilteringStrategyComponent);
5855+
grid = fix.componentInstance.grid;
5856+
fix.detectChanges();
5857+
}));
5858+
5859+
it('Should be able to set custom filtering strategy', () => {
5860+
expect(grid.filterStrategy).toBeUndefined();
5861+
grid.filterStrategy = fix.componentInstance.strategy;
5862+
fix.detectChanges();
5863+
5864+
expect(grid.filterStrategy).toEqual(fix.componentInstance.strategy);
5865+
});
5866+
5867+
it('Should be able to override getFieldValue method', fakeAsync(() => {
5868+
GridFunctions.clickFilterCellChip(fix, 'Name'); // Name column contains nasted object as a vulue
5869+
fix.detectChanges();
5870+
GridFunctions.typeValueInFilterRowInput('ca', fix);
5871+
tick(50);
5872+
GridFunctions.submitFilterRowInput(fix);
5873+
tick(50);
5874+
fix.detectChanges();
5875+
5876+
expect(grid.filteredData).toEqual([]);
5877+
GridFunctions.resetFilterRow(fix);
5878+
GridFunctions.closeFilterRow(fix);
5879+
fix.detectChanges();
5880+
5881+
// Apply the custom strategy and perform the same filter
5882+
grid.filterStrategy = fix.componentInstance.strategy;
5883+
fix.detectChanges();
5884+
GridFunctions.clickFilterCellChip(fix, 'Name');
5885+
fix.detectChanges();
5886+
GridFunctions.typeValueInFilterRowInput('ca', fix);
5887+
tick(50);
5888+
GridFunctions.submitFilterRowInput(fix);
5889+
tick(50);
5890+
fix.detectChanges();
5891+
5892+
expect(grid.filteredData).toEqual(
5893+
[{ ID: 1, Name: { FirstName: 'Casey', LastName: 'Houston' }, JobTitle: 'Vice President', Company: 'Company A' }]);
5894+
}));
5895+
5896+
it('Should be able to override findMatchByExpression method', fakeAsync(() => {
5897+
GridFunctions.clickFilterCellChip(fix, 'JobTitle'); // Default strategy is case not sensitive
5898+
fix.detectChanges();
5899+
GridFunctions.typeValueInFilterRowInput('direct', fix);
5900+
tick(50);
5901+
GridFunctions.submitFilterRowInput(fix);
5902+
tick(50);
5903+
fix.detectChanges();
5904+
5905+
expect(grid.filteredData).toEqual([
5906+
{ ID: 2, Name: { FirstName: 'Gilberto', LastName: 'Todd' } , JobTitle: 'Director', Company: 'Company C' },
5907+
{ ID: 3, Name: { FirstName: 'Tanya', LastName: 'Bennett' } , JobTitle: 'Director', Company: 'Company A' }]);
5908+
GridFunctions.resetFilterRow(fix);
5909+
GridFunctions.closeFilterRow(fix);
5910+
fix.detectChanges();
5911+
5912+
// Apply the custom strategy and perform the same filter
5913+
grid.filterStrategy = fix.componentInstance.strategy;
5914+
fix.detectChanges();
5915+
GridFunctions.clickFilterCellChip(fix, 'JobTitle');
5916+
fix.detectChanges();
5917+
GridFunctions.typeValueInFilterRowInput('direct', fix);
5918+
tick(50);
5919+
GridFunctions.submitFilterRowInput(fix);
5920+
tick(50);
5921+
fix.detectChanges();
5922+
5923+
expect(grid.filteredData).toEqual([]);
5924+
}));
5925+
5926+
it('should use the custom filtering stategy when filter the grid through API method', fakeAsync(() => {
5927+
grid.filterStrategy = fix.componentInstance.strategy;
5928+
fix.detectChanges();
5929+
grid.filter('Name', 'D', IgxStringFilteringOperand.instance().condition('contains'));
5930+
tick(30);
5931+
fix.detectChanges();
5932+
5933+
expect(grid.filteredData).toEqual([
5934+
{ ID: 7, Name: { FirstName: 'Debra', LastName: 'Morton' } ,
5935+
JobTitle: 'Associate Software Developer', Company: 'Company B' },
5936+
{ ID: 10, Name: { FirstName: 'Eduardo', LastName: 'Ramirez' }, JobTitle: 'Manager', Company: 'Company E' }]);
5937+
}));
5938+
});
58465939
});
58475940

58485941
const expectedResults = [];

projects/igniteui-angular/src/lib/grids/grid/grid.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
<ng-template igxGridFor let-rowData [igxGridForOf]="data
108108
| gridTransaction:id:pipeTrigger
109109
| visibleColumns:hasVisibleColumns
110-
| gridFiltering:filteringExpressionsTree:advancedFilteringExpressionsTree:id:pipeTrigger
110+
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger
111111
| gridSort:sortingExpressions:id:pipeTrigger
112112
| gridGroupBy:groupingExpressions:groupingExpansionState:groupsExpanded:id:groupsRecords:pipeTrigger
113113
| gridPaging:page:perPage:id:pipeTrigger

projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { IgxGridComponent } from './grid.component';
1010
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
1111
import { GridBaseAPIService } from '../api.service';
1212
import { IgxGridBaseComponent, IGridDataBindable } from '../grid-base.component';
13+
import { IFilteringStrategy } from '../../data-operations/filtering-strategy';
1314

1415
/**
1516
*@hidden
@@ -130,10 +131,12 @@ export class IgxGridFilteringPipe implements PipeTransform {
130131
constructor(private gridAPI: GridBaseAPIService<IgxGridBaseComponent & IGridDataBindable>) { }
131132

132133
public transform(collection: any[], expressionsTree: IFilteringExpressionsTree,
134+
filterStrategy: IFilteringStrategy,
133135
advancedExpressionsTree: IFilteringExpressionsTree, id: string, pipeTrigger: number) {
134136
const grid = this.gridAPI.grid;
135137
const state = {
136138
expressionsTree: expressionsTree,
139+
strategy: filterStrategy,
137140
advancedExpressionsTree: advancedExpressionsTree
138141
};
139142

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
<ng-template igxGridFor let-rowData [igxGridForOf]="data
8080
| gridTransaction:id:pipeTrigger
8181
| visibleColumns:hasVisibleColumns
82-
| gridFiltering:filteringExpressionsTree:advancedFilteringExpressionsTree:id:pipeTrigger
82+
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger
8383
| gridSort:sortingExpressions:id:pipeTrigger
8484
| gridHierarchicalPaging:page:perPage:id:pipeTrigger
8585
| gridHierarchical:hierarchicalState:id:primaryKey:childLayoutKeys:pipeTrigger" let-rowIndex="index"

projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts

+41-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { IgxTreeGridFilteringComponent, IgxTreeGridFilteringRowEditingComponent
66
import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec';
77
import { configureTestSuite } from '../../test-utils/configure-suite';
88
import { IgxStringFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand } from '../../data-operations/filtering-condition';
9+
import { FilteringStrategy } from '../../data-operations/filtering-strategy';
910

1011
describe('IgxTreeGrid - Filtering actions #tGrid', () => {
1112
configureTestSuite();
@@ -389,6 +390,45 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => {
389390
// if there are any parent nodes in this collection then the changes were preserved
390391
expect(filteredParentNodes.length).toBeGreaterThan(0);
391392
}));
392-
});
393393

394+
it('should be able to apply custom filter strategy', fakeAsync(() => {
395+
expect(treeGrid.filterStrategy).toBeUndefined();
396+
treeGrid.filter('Name', 'd', IgxStringFilteringOperand.instance().condition('contains'), true);
397+
tick(30);
398+
fix.detectChanges();
399+
400+
expect(treeGrid.rowList.length).toBe(9);
401+
402+
treeGrid.clearFilter();
403+
fix.detectChanges();
404+
// tslint:disable-next-line: no-use-before-declare
405+
const customFilter = new CustomTreeGridFilterStrategy();
406+
// apply the same filter condition but with custu
407+
treeGrid.filterStrategy = customFilter;
408+
fix.detectChanges();
409+
410+
treeGrid.filter('Name', 'd', IgxStringFilteringOperand.instance().condition('contains'), true);
411+
tick(30);
412+
fix.detectChanges();
413+
414+
expect(treeGrid.rowList.length).toBe(4);
415+
expect(treeGrid.filteredData.map(rec => rec.ID)).toEqual([ 847, 225, 663, 141]);
416+
}));
417+
});
418+
class CustomTreeGridFilterStrategy extends FilteringStrategy {
419+
420+
public filter(data: [], expressionsTree): any[] {
421+
const result = [];
422+
if (!expressionsTree || !expressionsTree.filteringOperands ||
423+
expressionsTree.filteringOperands.length === 0 || !data.length) {
424+
return data;
425+
}
426+
data.forEach((rec: any) => {
427+
if (this.matchRecord(rec.data, expressionsTree)) {
428+
result.push(rec);
429+
}
430+
});
431+
return result;
432+
}
433+
}
394434
});

projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
| treeGridTransaction:id:pipeTrigger
7171
| visibleColumns:hasVisibleColumns
7272
| treeGridHierarchizing:primaryKey:foreignKey:childDataKey:id:pipeTrigger
73-
| treeGridFiltering:filteringExpressionsTree:advancedFilteringExpressionsTree:id:pipeTrigger
73+
| treeGridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger
7474
| treeGridSorting:sortingExpressions:id:pipeTrigger
7575
| treeGridFlattening:id:expansionDepth:expansionStates:pipeTrigger
7676
| treeGridPaging:page:perPage:id:pipeTrigger

projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.pipe.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { Pipe, PipeTransform } from '@angular/core';
22
import { DataUtil } from '../../data-operations/data-util';
33
import { GridBaseAPIService } from '../api.service';
44
import { IgxTreeGridComponent } from './tree-grid.component';
5+
import { BaseFilteringStrategy, IFilteringStrategy } from '../../data-operations/filtering-strategy';
56
import { IFilteringExpressionsTree, FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
6-
import { BaseFilteringStrategy } from '../../data-operations/filtering-strategy';
77
import { IFilteringState } from '../../data-operations/filtering-state.interface';
88
import { ITreeGridRecord } from './tree-grid.interfaces';
99
import { IgxTreeGridAPIService } from './tree-grid-api.service';
@@ -62,14 +62,19 @@ export class IgxTreeGridFilteringPipe implements PipeTransform {
6262
}
6363

6464
public transform(hierarchyData: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree,
65+
filterStrategy: IFilteringStrategy,
6566
advancedFilteringExpressionsTree: IFilteringExpressionsTree, id: string, pipeTrigger: number): ITreeGridRecord[] {
6667
const grid: IgxTreeGridComponent = this.gridAPI.grid;
67-
const state = {
68+
const state: IFilteringState = {
6869
expressionsTree: expressionsTree,
6970
advancedExpressionsTree: advancedFilteringExpressionsTree,
7071
strategy: new TreeGridFilteringStrategy()
7172
};
7273

74+
if (filterStrategy) {
75+
state.strategy = filterStrategy;
76+
}
77+
7378
this.resetFilteredOutProperty(grid.records);
7479

7580
if (FilteringExpressionsTree.empty(state.expressionsTree) && FilteringExpressionsTree.empty(state.advancedExpressionsTree)) {

projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts

+40
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,46 @@ export class IgxGridFilteringComponent extends BasicGridComponent {
958958
}
959959
}
960960

961+
export class CustomFilterStrategy extends FilteringStrategy {
962+
public constructor() { super(); }
963+
964+
public findMatchByExpression(rec: object, expr): boolean {
965+
const cond = expr.condition;
966+
const val = this.getFieldValue(rec, expr.fieldName);
967+
const ignoreCase = expr.fieldName === 'JobTitle' ? false : true;
968+
return cond.logic(val, expr.searchVal, ignoreCase);
969+
}
970+
971+
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree): T[] {
972+
return super.filter(data, expressionsTree);
973+
}
974+
975+
public getFieldValue(rec: object, fieldName: string): any {
976+
return fieldName === 'Name' ? rec[fieldName]['FirstName'] : rec[fieldName];
977+
}
978+
}
979+
980+
@Component({
981+
template: `<igx-grid [data]="data" height="500px" width="600px" [allowFiltering]='true'>
982+
<igx-column [field]="'ID'" [header]="'ID'" [filterable]="false"></igx-column>
983+
<igx-column width="100px" [field]="'Name'" [filterable]="filterable">
984+
<ng-template igxCell let-val>
985+
<span>{{val.FirstName}}</span>
986+
</ng-template>
987+
</igx-column>
988+
<igx-column [field]="'JobTitle'" [filterable]="filterable" ></igx-column>
989+
<igx-column [field]="'Company'" [filterable]="filterable" ></igx-column>
990+
</igx-grid>`
991+
})
992+
export class CustomFilteringStrategyComponent extends BasicGridComponent {
993+
public strategy = new CustomFilterStrategy();
994+
public filterable = true;
995+
996+
public data = SampleTestData.personNameObjectJobCompany();
997+
}
998+
999+
1000+
9611001
@Component({
9621002
template: `<igx-grid [data]="data" height="500px" [allowFiltering]='true'
9631003
[filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy">

0 commit comments

Comments
 (0)