diff --git a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts index 9651d10376b..28aa1107db5 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts @@ -29,7 +29,7 @@ import { AbsoluteScrollStrategy } from '../../../services/overlay/scroll'; import { DisplayDensity } from '../../../core/displayDensity'; import { IgxDatePickerComponent } from '../../../date-picker/date-picker.component'; import { IgxTimePickerComponent } from '../../../time-picker/time-picker.component'; -import { PlatformUtil } from '../../../core/utils'; +import { isEqual, PlatformUtil } from '../../../core/utils'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -70,14 +70,23 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy { public set value(val) { if (!val && val !== 0) { this.expression.searchVal = null; - this.showHideArrowButtons(); + const index = this.expressionsList.findIndex(item => item.expression === this.expression); + if (index === 0 && this.expressionsList.length === 1) { + this.clearFiltering(); + return; + } } else { + const oldValue = this.expression.searchVal; + if (isEqual(oldValue, val)) { + return; + } + this.expression.searchVal = DataUtil.parseValue(this.column.dataType, val); if (this.expressionsList.find(item => item.expression === this.expression) === undefined) { this.addExpression(true); } + this.filter(); } - this.filter(); } public get displayDensity() { 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 39ca93a5c88..ecc082b1041 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 @@ -464,61 +464,6 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { verifyFilterRowUI(input, close, reset, false); })); - it('Should emit filteringDone when we clicked reset', fakeAsync(() => { - spyOn(grid.filtering, 'emit'); - spyOn(grid.filteringDone, 'emit'); - - const filterVal = 'search'; - const columnName = 'ProductName'; - - grid.filter(columnName, filterVal, IgxStringFilteringOperand.instance().condition('contains')); - tick(100); - fix.detectChanges(); - - const filteringExpressions = grid.filteringExpressionsTree.find(columnName) as FilteringExpressionsTree; - const args = { owner: grid, cancel: false, filteringExpressions }; - expect(grid.filtering.emit).toHaveBeenCalledWith(args); - expect(grid.filtering.emit).toHaveBeenCalledTimes(1); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(filteringExpressions); - expect(grid.filteringDone.emit).toHaveBeenCalledTimes(1); - - GridFunctions.clickFilterCellChip(fix, columnName); - GridFunctions.resetFilterRow(fix); - - const emptyFilter = new FilteringExpressionsTree(null, columnName); - expect(grid.filtering.emit).toHaveBeenCalledWith({ owner: grid, cancel: false, filteringExpressions: emptyFilter }); - expect(grid.filtering.emit).toHaveBeenCalledTimes(2); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(emptyFilter); - expect(grid.filteringDone.emit).toHaveBeenCalledTimes(2); - - const filterUiRow = fix.debugElement.query(By.css(FILTER_UI_ROW)); - const reset = filterUiRow.queryAll(By.css('button'))[0]; - expect(reset.nativeElement.classList.contains('igx-button--disabled')).toEqual(true); - })); - - it('Should emit filteringDone when clear the input of filteringUI', fakeAsync(() => { - spyOn(grid.filtering, 'emit'); - spyOn(grid.filteringDone, 'emit'); - const columnName = 'ProductName'; - const filterValue = 'search'; - grid.filter(columnName, filterValue, IgxStringFilteringOperand.instance().condition('contains')); - tick(100); - fix.detectChanges(); - - GridFunctions.clickFilterCellChip(fix, columnName); - - spyOn(grid.filteringDone, 'emit'); - - grid.filteringRow.onClearClick(); - tick(100); - fix.detectChanges(); - - const emptyFilter = new FilteringExpressionsTree(null, columnName); - const args = { owner: grid, cancel: false, filteringExpressions: emptyFilter }; - expect(grid.filtering.emit).toHaveBeenCalledWith(args); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(emptyFilter); - })); - it('Removing second condition removes the And/Or button', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { @@ -824,42 +769,6 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { expect(GridFunctions.getFilterRowRightArrowButton(fix)).not.toBe(null); })); - it('should update UI when chip is removed from header cell.', fakeAsync(() => { - spyOn(grid.filtering, 'emit'); - spyOn(grid.filteringDone, 'emit'); - - const columnName = 'ProductName'; - grid.filter(columnName, 'I', IgxStringFilteringOperand.instance().condition('startsWith')); - tick(30); - fix.detectChanges(); - - expect(grid.rowList.length).toEqual(2); - - const filteringExpressions = grid.filteringExpressionsTree.find(columnName) as FilteringExpressionsTree; - const args = { owner: grid, cancel: false, filteringExpressions }; - expect(grid.filtering.emit).toHaveBeenCalledWith(args); - expect(grid.filtering.emit).toHaveBeenCalledTimes(1); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(filteringExpressions); - expect(grid.filteringDone.emit).toHaveBeenCalledTimes(1); - - const filteringCells = GridFunctions.getFilteringCells(fix); - const stringCellChip = filteringCells[1].query(By.css('igx-chip')); - - // remove chip - ControlsFunction.clickChipRemoveButton(stringCellChip.nativeElement); - tick(30); - fix.detectChanges(); - - expect(grid.rowList.length).toEqual(8); - - const emptyFilter = new FilteringExpressionsTree(null, columnName); - args.filteringExpressions = emptyFilter; - expect(grid.filtering.emit).toHaveBeenCalledWith(args); - expect(grid.filtering.emit).toHaveBeenCalledTimes(2); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(emptyFilter); - expect(grid.filteringDone.emit).toHaveBeenCalledTimes(2); - })); - it('should update UI when chip is removed from filter row.', fakeAsync(() => { grid.filter('ProductName', 'I', IgxStringFilteringOperand.instance().condition('startsWith')); fix.detectChanges(); @@ -2794,6 +2703,239 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { expect(grid.rowList.length).toEqual(1); })); }); + + describe('Filtering events', () => { + let fix: ComponentFixture; + let grid: IgxGridComponent; + + const cal = SampleTestData.timeGenerator; + const today = SampleTestData.today; + + beforeEach(fakeAsync(() => { + fix = TestBed.createComponent(IgxGridDatesFilteringComponent); + fix.detectChanges(); + grid = fix.componentInstance.grid; + + spyOn(grid.filtering, 'emit'); + spyOn(grid.filteringDone, 'emit'); + })); + + it('Should emit filteringDone when we clicked reset - Date column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + cal.timedelta(today, 'day', 15), + 'ReleaseDate', + IgxDateFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when we clicked reset - String column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + 'search', + 'ProductName', + IgxStringFilteringOperand.instance().condition('contains') + ); + })); + + it('Should emit filteringDone when we clicked reset - Number column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + 100, + 'Downloads', + IgxNumberFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when we clicked reset - Currency column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + 100000, + 'Revenue', + IgxNumberFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when we clicked reset - Boolean column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + true, + 'Released', + IgxBooleanFilteringOperand.instance().condition('true') + ); + })); + + it('Should emit filteringDone when we clicked reset - DateTime column type', fakeAsync(() => { + emitFilteringDoneOnResetClick( + fix, + grid, + cal.timedelta(today, 'hour', 1), + 'ReleaseTime', + IgxDateTimeFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - Date column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + cal.timedelta(today, 'day', 15), + 'ReleaseDate', + IgxDateFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - String column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + 'search', + 'ProductName', + IgxStringFilteringOperand.instance().condition('contains') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - Number column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + 3, + 'Downloads', + IgxNumberFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - Currency column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + 100000, + 'Revenue', + IgxNumberFilteringOperand.instance().condition('equals') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - Boolean column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + true, + 'Released', + IgxBooleanFilteringOperand.instance().condition('true') + ); + })); + + it('Should emit filteringDone when clear the input of filteringUI - DateTime column type', fakeAsync(() => { + emitFilteringDoneOnInputClear( + fix, + grid, + cal.timedelta(today, 'hour', 1), + 'ReleaseTime', + IgxDateTimeFilteringOperand.instance().condition('equals') + ); + })); + + it('should update UI when chip is removed from header cell - Date column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + cal.timedelta(today, 'day', 15), + 'ReleaseDate', + IgxDateFilteringOperand.instance().condition('equals'), + 1, + 4 + ); + })); + + it('should update UI when chip is removed from header cell - String column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + 'I', + 'ProductName', + IgxStringFilteringOperand.instance().condition('startsWith'), + 2, + 1 + ); + })); + + it('should update UI when chip is removed from header cell - Number column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + 100, + 'Downloads', + IgxNumberFilteringOperand.instance().condition('equals'), + 1, + 2 + ); + })); + + it('should update UI when chip is removed from header cell - Currency column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + 100000, + 'Revenue', + IgxNumberFilteringOperand.instance().condition('equals'), + 1, + 7 + ); + })); + + it('should update UI when chip is removed from header cell - Boolean column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + true, + 'Released', + IgxBooleanFilteringOperand.instance().condition('true'), + 3, + 3 + ); + })); + + it('should update UI when chip is removed from header cell - DateTime column type.', fakeAsync(() => { + verifyRemoveChipFromHeader( + fix, + grid, + cal.timedelta(today, 'hour', 1), + 'ReleaseTime', + IgxDateTimeFilteringOperand.instance().condition('equals'), + 3, + 6 + ); + })); + + it('should emit filteringDone after chip is close from filtering row - Date column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'ReleaseDate', 4); + })); + + it('should emit filteringDone after chip is close from filtering row - String column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'ProductName', 9); + })); + + it('should emit filteringDone after chip is close from filtering row - Number column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'Downloads', 7); + })); + + it('should emit filteringDone after chip is close from filtering row - Currency column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'Revenue', 7); + })); + + it('should emit filteringDone after chip is close from filtering row - Boolean column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'Released', 1); + })); + + it('should emit filteringDone after chip is close from filtering row - DateTime column type', fakeAsync(() => { + closeChipFromFiteringUIRow(fix, grid, 'ReleaseTime', 4); + })); + }); }); describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { @@ -5306,7 +5448,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { ReleaseDateTime: null, ReleaseTime: new Date(2010, 4, 27, 23, 0, 0), Released: false, - AnotherField: 'BWord' + AnotherField: 'BWord', + Revenue: 60000 }, { Downloads: 127, @@ -5316,7 +5459,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { ReleaseDateTime: null, ReleaseTime: new Date(2021, 4, 27, 1, 0, 0), Released: true, - AnotherField: 'bWord' + AnotherField: 'bWord', + Revenue: 50000 }, { Downloads: 20, @@ -5326,7 +5470,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { ReleaseDateTime: null, ReleaseTime: new Date(2015, 4, 27, 12, 0, 0), Released: null, - AnotherField: 'aWord' + AnotherField: 'aWord', + Revenue: 100000 } ]; fix.componentInstance.data = data; @@ -6460,3 +6605,85 @@ const verifyChipVisibility = (fix, index: number, shouldBeFullyVisible: boolean) expect(chipRect.left >= visibleChipAreaRect.left && chipRect.right <= visibleChipAreaRect.right) .toBe(shouldBeFullyVisible, 'chip[' + index + '] visibility is incorrect'); }; + +const emitFilteringDoneOnResetClick = (fix, grid, filterVal: any, columnName: string, condition) => { + filterGrid(fix, grid, columnName, filterVal, condition); + + const filteringExpressions = grid.filteringExpressionsTree.find(columnName) as FilteringExpressionsTree; + verifyEmitFilteringDone(grid, filteringExpressions, 1); + + GridFunctions.clickFilterCellChip(fix, columnName); + GridFunctions.resetFilterRow(fix); + + const emptyFilter = new FilteringExpressionsTree(null, columnName); + verifyEmitFilteringDone(grid, emptyFilter, 2); + + const filterUiRow = fix.debugElement.query(By.css(FILTER_UI_ROW)); + const reset = filterUiRow.queryAll(By.css('button'))[0]; + expect(reset.nativeElement.classList.contains('igx-button--disabled')).toEqual(true); +}; + +const emitFilteringDoneOnInputClear = (fix, grid, filterVal, columnName, condition) => { + filterGrid(fix, grid, columnName, filterVal, condition); + + GridFunctions.clickFilterCellChip(fix, columnName); + + // spyOn(grid.filteringDone, 'emit'); + + grid.filteringRow.onClearClick(); + tick(100); + fix.detectChanges(); + + const emptyFilter = new FilteringExpressionsTree(null, columnName); + verifyEmitFilteringDone(grid, emptyFilter, 2); +}; + +const verifyRemoveChipFromHeader = (fix, grid, filterVal, columnName, condition, rowListLength, cellIndex) => { + filterGrid(fix, grid, columnName, filterVal, condition); + + const filteringExpressions = grid.filteringExpressionsTree.find(columnName) as FilteringExpressionsTree; + verifyEmitFilteringDone(grid, filteringExpressions, 1); + + const filteringCells = GridFunctions.getFilteringCells(fix); + const stringCellChip = filteringCells[cellIndex].query(By.css('igx-chip')); + + // remove chip + ControlsFunction.clickChipRemoveButton(stringCellChip.nativeElement); + tick(30); + fix.detectChanges(); + + expect(grid.rowList.length).toEqual(8); + + const emptyFilter = new FilteringExpressionsTree(null, columnName); + verifyEmitFilteringDone(grid, emptyFilter, 2); +}; + +const closeChipFromFiteringUIRow = (fix, grid, columnName, index) => { + GridFunctions.clickFilterCellChip(fix, columnName); + + const filterUIRow = fix.debugElement.query(By.css(FILTER_UI_ROW)); + const close = filterUIRow.queryAll(By.css('button'))[1]; + + GridFunctions.openFilterDDAndSelectCondition(fix, index); + + close.triggerEventHandler('click', null); + tick(); + fix.detectChanges(); + + const filteringExpressions = grid.filteringExpressionsTree.find(columnName) as FilteringExpressionsTree; + verifyEmitFilteringDone(grid, filteringExpressions, 1); +}; + +const verifyEmitFilteringDone = (grid, filteringExpressions, calledTimes) => { + const args = { owner: grid, cancel: false, filteringExpressions }; + expect(grid.filtering.emit).toHaveBeenCalledWith(args); + expect(grid.filtering.emit).toHaveBeenCalledTimes(calledTimes); + expect(grid.filteringDone.emit).toHaveBeenCalledWith(filteringExpressions); + expect(grid.filteringDone.emit).toHaveBeenCalledTimes(calledTimes); +}; + +const filterGrid = (fix, grid, columnName, filterVal, condition) => { + grid.filter(columnName, filterVal, condition); + tick(100); + fix.detectChanges(); +}; 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 9d8e441a11f..350d02c1487 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 @@ -1091,6 +1091,12 @@ export class IgxGridFilteringComponent extends BasicGridComponent { + + + + ` }) export class IgxGridDatesFilteringComponent extends BasicGridComponent { diff --git a/projects/igniteui-angular/src/lib/test-utils/sample-test-data.spec.ts b/projects/igniteui-angular/src/lib/test-utils/sample-test-data.spec.ts index e716a120db8..9524a331236 100644 --- a/projects/igniteui-angular/src/lib/test-utils/sample-test-data.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/sample-test-data.spec.ts @@ -1726,7 +1726,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', 1), ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', 1), Released: false, - AnotherField: 'a' + AnotherField: 'a', + Revenue: 100000 }, { Downloads: 127, @@ -1736,7 +1737,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', -1), ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'hour', -1), Released: true, - AnotherField: 'a' + AnotherField: 'a', + Revenue: 40000 }, { Downloads: 20, @@ -1746,7 +1748,8 @@ export class SampleTestData { ReleaseDateTime: null, ReleaseTime: null, Released: null, - AnotherField: 'a' + AnotherField: 'a', + Revenue: 9000 }, { Downloads: null, @@ -1756,7 +1759,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'minute', -10), ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'minute', -10), Released: true, - AnotherField: 'a' + AnotherField: 'a', + Revenue: 10000 }, { Downloads: 100, @@ -1766,7 +1770,8 @@ export class SampleTestData { ReleaseDateTime: undefined, ReleaseTime: undefined, Released: false, - AnotherField: 'a' + AnotherField: 'a', + Revenue: 30000 }, { Downloads: 702, @@ -1776,7 +1781,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'second', 20), ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'second', 20), Released: null, - AnotherField: 'Custom' + AnotherField: 'Custom', + Revenue: 60000 }, { Downloads: 0, @@ -1786,7 +1792,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'minute', +10), ReleaseTime: SampleTestData.timeGenerator.timedelta(SampleTestData.todayFullDate, 'minute', +10), Released: true, - AnotherField: 'custoM' + AnotherField: 'custoM', + Revenue: 10000 }, { Downloads: 1000, @@ -1796,7 +1803,8 @@ export class SampleTestData { ReleaseDateTime: SampleTestData.todayFullDate, ReleaseTime: SampleTestData.todayFullDate, Released: undefined, - AnotherField: 'custom' + AnotherField: 'custom', + Revenue: 50000 } ]);