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/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">
- + + + + + + + - - - - - - - - - - - + + + + + + + -
- - - + - -
-
- - - +
+ + + + + - +
+ +
+ + + + + + +
-
+
{ 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); + })); + }); }); 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 add0cf606..1067bf1ea 100644 --- a/projects/angular/components/ui-grid/src/ui-grid.component.ts +++ b/projects/angular/components/ui-grid/src/ui-grid.component.ts @@ -79,6 +79,7 @@ import { import { ResizableGrid } from './managers/resize/types'; import { GridOptions, + IFilterModel, ISortModel, } from './models'; import { UiGridIntl } from './ui-grid.intl'; @@ -340,6 +341,12 @@ export class UiGridComponent extends Resizabl @Input() disableSelectionByEntry: (entry: T) => null | string; + @Input() + set customFilterValue(customValue: IFilterModel[]) { + 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 +375,9 @@ export class UiGridComponent extends Resizabl @Output() resizeEnd = new EventEmitter(); + @Output() + removeCustomFilter = new EventEmitter(); + /** * Emits the column definitions when their definition changes. * @@ -893,6 +903,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); } 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/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", 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/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'}] : []"> { 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; diff --git a/projects/playground/src/app/pages/grid/grid.models.ts b/projects/playground/src/app/pages/grid/grid.models.ts index e8bdcc65c..ad4ccf8cd 100644 --- a/projects/playground/src/app/pages/grid/grid.models.ts +++ b/projects/playground/src/app/pages/grid/grid.models.ts @@ -27,4 +27,5 @@ export interface IInputs { virtualScroll: boolean; showPaintTime: boolean; showHeaderRow: boolean; + customFilter: boolean; } diff --git a/projects/playground/src/app/pages/grid/grid.page.ts b/projects/playground/src/app/pages/grid/grid.page.ts index d8c30d177..e2e4c8118 100644 --- a/projects/playground/src/app/pages/grid/grid.page.ts +++ b/projects/playground/src/app/pages/grid/grid.page.ts @@ -52,6 +52,7 @@ export class GridPageComponent implements AfterViewInit { 'virtualScroll', 'showPaintTime', 'showHeaderRow', + 'customFilter', ]; buttonKeys = [ @@ -101,6 +102,7 @@ export class GridPageComponent implements AfterViewInit { virtualScroll: [false], showPaintTime: [false], showHeaderRow: [true], + customFilter: [false], }), header: this._fb.group({ searchable: [true], 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": [