From 0af5ddea4140e376e012aabb40d1f3a27a109f8f Mon Sep 17 00:00:00 2001 From: Catalin Date: Mon, 18 Jul 2022 09:11:15 +0300 Subject: [PATCH 1/6] fix(playground): convert filter value to string --- .../app/pages/grid/component/grid.component.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/projects/playground/src/app/pages/grid/component/grid.component.ts b/projects/playground/src/app/pages/grid/component/grid.component.ts index 8accc6be9..6b57e4858 100644 --- a/projects/playground/src/app/pages/grid/component/grid.component.ts +++ b/projects/playground/src/app/pages/grid/component/grid.component.ts @@ -26,6 +26,7 @@ import { ViewChild, } from '@angular/core'; import { + IFilterModel, PageChangeEvent, UiGridComponent, } from '@uipath/angular/components/ui-grid'; @@ -90,14 +91,15 @@ export class GridComponent implements OnInit, OnDestroy, AfterViewInit { takeUntil(this._destroyed$), ).subscribe(([searchFilters, filters]) => { this.filteredData = cloneDeep(this.allData); - - searchFilters.forEach(filter => { - this.filteredData = this.filteredData.filter((row: any) => row[filter.property].includes(filter.value)); - }); - - filters.forEach(filter => { - this.filteredData = this.filteredData.filter((row: any) => row[filter.property].includes(filter.value)); - }); + const filterValues = (filter: IFilterModel) => { + this.filteredData = this.filteredData.filter((row: any) => + row[filter.property] + .toString() + .includes(filter.value?.toString())); + }; + + searchFilters.forEach(filterValues); + filters.forEach(filterValues); this.total = this.filteredData.length; From 8c6cbb3cfeeb8d6e6740569b679f4ea98935d068 Mon Sep 17 00:00:00 2001 From: Catalin Date: Tue, 19 Jul 2022 12:40:28 +0300 Subject: [PATCH 2/6] feat(grid): add input for custom filter value --- .../ui-grid/src/managers/filter-manager.ts | 20 ++++- .../ui-grid/src/ui-grid.component.html | 86 +++++++++++-------- .../ui-grid/src/ui-grid.component.ts | 19 ++++ 3 files changed, 87 insertions(+), 38 deletions(-) diff --git a/projects/angular/components/ui-grid/src/managers/filter-manager.ts b/projects/angular/components/ui-grid/src/managers/filter-manager.ts index dcd081141..83301c221 100644 --- a/projects/angular/components/ui-grid/src/managers/filter-manager.ts +++ b/projects/angular/components/ui-grid/src/managers/filter-manager.ts @@ -28,6 +28,9 @@ import { IFilterModel } from '../models'; * @internal */ export class FilterManager { + hasCustomFilter$ = new BehaviorSubject(false); + customFilters?: IFilterModel[]; + filter$ = new BehaviorSubject[]>([]); dirty$ = this.filter$.pipe( @@ -105,6 +108,17 @@ export class FilterManager { } } + updateCustomFilters(customValue: IFilterModel[]) { + this.customFilters = customValue; + this.hasCustomFilter$.next(true); + this.filter$.next(customValue); + } + + clearCustomFilters() { + this.hasCustomFilter$.next(false); + this._emitFilterOptions(); + } + private _updateFilterValue = ( column: UiGridColumnDirective | undefined, value: ISuggestValue | IDropdownOption | undefined, @@ -142,7 +156,11 @@ export class FilterManager { : []; if (isEqual(this.filter$.getValue(), updatedFilters)) { return; } - this.filter$.next(updatedFilters); + this.filter$.next( + this.hasCustomFilter$.value + ? this.customFilters! + : updatedFilters, + ); }; private _hasFilterValue = (dropdown?: UiGridSearchFilterDirective | UiGridDropdownFilterDirective) => diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.html b/projects/angular/components/ui-grid/src/ui-grid.component.html index cd34d9a12..ae9c2229f 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.html +++ b/projects/angular/components/ui-grid/src/ui-grid.component.html @@ -13,52 +13,64 @@ class="ui-grid-filter-container">
- + + + + + + + - - - - - - - - - - - + + + + + + + -
- - - + - -
-
- - - +
+ + + + + - +
+ +
+ + + + + + +
-
+
extends Resizabl @Input() disableSelectionByEntry: (entry: T) => null | string; + @Input() + get customFilterValue() { + return this._customFilterValue; + } + set customFilterValue(customValue) { + if (!Array.isArray(customValue) || !customValue.length) { return; } + this.filterManager.updateCustomFilters(customValue); + } + /** * Emits an event with the sort model when a column sort changes. * @@ -368,6 +378,9 @@ export class UiGridComponent extends Resizabl @Output() resizeEnd = new EventEmitter(); + @Output() + removeCustomFilter = new EventEmitter(); + /** * Emits the column definitions when their definition changes. * @@ -604,6 +617,7 @@ export class UiGridComponent extends Resizabl private _isShiftPressed = false; private _lastCheckboxIdx = 0; private _resizeSubscription$: null | Subscription = null; + private _customFilterValue: IFilterModel[] = []; /** * @ignore @@ -893,6 +907,11 @@ export class UiGridComponent extends Resizabl this.gridActionButtons?.nativeElement.querySelector(FOCUSABLE_ELEMENTS_QUERY)?.focus(); } + clearCustomFilter() { + this.removeCustomFilter.emit(); + this.filterManager.clearCustomFilters(); + } + private _announceGridHeaderActions() { this._queuedAnnouncer.enqueue(this.intl.gridHeaderActionsNotice); } From a6c4534e39303eb5c72ae65db9e23bba8950c8f8 Mon Sep 17 00:00:00 2001 From: Catalin Date: Tue, 19 Jul 2022 12:43:08 +0300 Subject: [PATCH 3/6] feat(playground): add custom filter input --- .../angular/components/ui-grid/src/ui-grid.component.ts | 6 +----- projects/angular/components/ui-grid/src/ui-grid.intl.ts | 5 +++++ .../src/app/pages/grid/component/grid.component.html | 3 ++- projects/playground/src/app/pages/grid/grid.models.ts | 1 + projects/playground/src/app/pages/grid/grid.page.ts | 2 ++ 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.ts b/projects/angular/components/ui-grid/src/ui-grid.component.ts index 3ae4ffcfb..1067bf1ea 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.component.ts @@ -342,10 +342,7 @@ export class UiGridComponent extends Resizabl disableSelectionByEntry: (entry: T) => null | string; @Input() - get customFilterValue() { - return this._customFilterValue; - } - set customFilterValue(customValue) { + set customFilterValue(customValue: IFilterModel[]) { if (!Array.isArray(customValue) || !customValue.length) { return; } this.filterManager.updateCustomFilters(customValue); } @@ -617,7 +614,6 @@ export class UiGridComponent extends Resizabl private _isShiftPressed = false; private _lastCheckboxIdx = 0; private _resizeSubscription$: null | Subscription = null; - private _customFilterValue: IFilterModel[] = []; /** * @ignore diff --git a/projects/angular/components/ui-grid/src/ui-grid.intl.ts b/projects/angular/components/ui-grid/src/ui-grid.intl.ts index d11c8730e..d34be01cd 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.intl.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.intl.ts @@ -108,6 +108,11 @@ export class UiGridIntl implements OnDestroy { * */ gridHeaderActionsNotice = 'Grid header actions updated. Press Shift + Alt + Arrow Up to move there.'; + /** + * Message for the button that clears the applied custom filter. + * + */ + clearCustomFilter = 'Clear custom filter'; /** * No data row message alternative function. * diff --git a/projects/playground/src/app/pages/grid/component/grid.component.html b/projects/playground/src/app/pages/grid/component/grid.component.html index a648e276b..0c8490d1e 100644 --- a/projects/playground/src/app/pages/grid/component/grid.component.html +++ b/projects/playground/src/app/pages/grid/component/grid.component.html @@ -13,7 +13,8 @@ [showPaintTime]="inputs.showPaintTime" [showHeaderRow]="inputs.showHeaderRow" [expandedEntry]="editedEntity" - [expandMode]="'preserve'"> + [expandMode]="'preserve'" + [customFilterValue]="inputs.customFilter ? [{property: 'parity', method: 'eq', value: 'odd'}] : []"> Date: Tue, 19 Jul 2022 17:33:10 +0300 Subject: [PATCH 4/6] feat(grid): add tests for custom filter --- .../ui-grid/src/ui-grid.component.spec.ts | 109 +++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts b/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts index 6f877a65b..78cf3a97d 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.component.spec.ts @@ -33,7 +33,10 @@ import { MatMenuItem } from '@angular/material/menu'; import { PageEvent } from '@angular/material/paginator'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ResizeStrategy } from '@uipath/angular/components/ui-grid'; +import { + IFilterModel, + ResizeStrategy, +} from '@uipath/angular/components/ui-grid'; import { ISuggestValues, UiSuggestComponent, @@ -3630,4 +3633,108 @@ describe('Component: UiGrid', () => { expect(buttons.length).toEqual(2); }); }); + + describe('Behaviour: Clear custom filter', () => { + @Component({ + template: ` + + + + + + + + + `, + }) + class TestFixtureCustomFilterGridComponent { + @ViewChild(UiGridComponent, { + static: true, + }) + grid!: UiGridComponent; + + get filterItems(): IDropdownOption[] { + return [1, 2, 3].map(count => ({ + value: count, + label: count.toString(), + })); + } + customFilter: IFilterModel[] = []; + filterValue = { + value: '777', + label: 'the label', + }; + data: ITestEntity[] = []; + } + + let fixture: ComponentFixture; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [ + UiGridModule, + UiGridCustomPaginatorModule, + NoopAnimationsModule, + ], + providers: [ + UiMatPaginatorIntl, + { + provide: UI_GRID_OPTIONS, + useValue: { + useLegacyDesign: false, + }, + }, + ], + declarations: [ + TestFixtureCustomFilterGridComponent, + ], + }); + + fixture = TestBed.createComponent(TestFixtureCustomFilterGridComponent); + + fixture.detectChanges(); + await fixture.whenStable(); + fixture.detectChanges(); + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should show custom filter after setting the grid\'s custom filter input', () => { + expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeTruthy(); + fixture.componentInstance.customFilter = [{ property: 'myNumber1', + method: 'eq', + value: '2' }]; + fixture.detectChanges(); + expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeFalsy(); + expect(document.querySelector('[data-cy="clear-custom-filter"]')).toBeTruthy(); + }); + + it('should revert to old filter value after clearing the custom filter', fakeAsync(() => { + fixture.componentInstance.customFilter = [{ property: 'myNumber2', + method: 'eq', + value: '2' }]; + fixture.detectChanges(); + expect(JSON.stringify(fixture.componentInstance.grid.filterManager.filter$.value)) + .toEqual(JSON.stringify(fixture.componentInstance.customFilter)); + expect(fixture.componentInstance.grid.filterManager.hasCustomFilter$.value).toBeTrue(); + + const clearCustomFilterButton = fixture.debugElement.query(By.css('[data-cy="clear-custom-filter"]')); + clearCustomFilterButton.nativeElement.dispatchEvent(EventGenerator.click); + fixture.detectChanges(); + + expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeTruthy(); + expect(fixture.componentInstance.grid.filterManager.hasCustomFilter$.value).toBeFalse(); + expect(fixture.componentInstance.grid.filterManager.filter$.value[0].value) + .toEqual(fixture.componentInstance.filterValue.value); + })); + }); }); From 7c0cc45030553a156b38fb7c63b90d13f7c16d58 Mon Sep 17 00:00:00 2001 From: Catalin Date: Thu, 21 Jul 2022 10:12:00 +0300 Subject: [PATCH 5/6] chore(deps): add dom iterable in tsconfig --- projects/angular/testing/src/utilities/fake-file-list.ts | 4 ++++ tsconfig.json | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/projects/angular/testing/src/utilities/fake-file-list.ts b/projects/angular/testing/src/utilities/fake-file-list.ts index b8ef0bad1..668ba841f 100644 --- a/projects/angular/testing/src/utilities/fake-file-list.ts +++ b/projects/angular/testing/src/utilities/fake-file-list.ts @@ -27,6 +27,10 @@ export class FakeFileList implements FileList { }); } + [Symbol.iterator](): IterableIterator { + return this.files[Symbol.iterator](); + } + /** * Retrieve an item at the specified index. * diff --git a/tsconfig.json b/tsconfig.json index 0d0e39015..09d9052a0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ ], "lib": [ "es2018", - "dom" + "dom", + "dom.iterable" ], "paths": { "@uipath/angular/a11y": [ From 5770bd83f916b059d06af4342154c3a1cd07ca4f Mon Sep 17 00:00:00 2001 From: Catalin Date: Thu, 21 Jul 2022 10:28:25 +0300 Subject: [PATCH 6/6] chore: bump version --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- projects/angular/package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 351b5302d..f46b59a17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v13.5.0 (2022-07-21) +* **deps** add dom iterable in tsconfig +* **grid** add tests for custom filter +* **playground** add custom filter input +* **grid** add input for custom filter value +* **playground** convert filter value to string + # v13.4.1 (2022-06-22) * **suggest** hide no results when header items are available diff --git a/package-lock.json b/package-lock.json index 53ee7a54e..73409c380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "angular-components", - "version": "13.4.1", + "version": "13.5.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 1a269e0fd..681d55aa3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-components", - "version": "13.4.1", + "version": "13.5.0", "author": { "name": "UiPath Inc", "url": "https://uipath.com" diff --git a/projects/angular/package.json b/projects/angular/package.json index 643b665bd..82b1d2af9 100644 --- a/projects/angular/package.json +++ b/projects/angular/package.json @@ -1,6 +1,6 @@ { "name": "@uipath/angular", - "version": "13.4.1", + "version": "13.5.0", "license": "MIT", "author": { "name": "UiPath Inc",