Skip to content

Commit f89669c

Browse files
authored
Merge pull request #238 from UiPath/feat/grid_custom_filter
Feat: grid custom filter value
2 parents 23fd4e4 + 5770bd8 commit f89669c

File tree

15 files changed

+227
-52
lines changed

15 files changed

+227
-52
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# v13.5.0 (2022-07-21)
2+
* **deps** add dom iterable in tsconfig
3+
* **grid** add tests for custom filter
4+
* **playground** add custom filter input
5+
* **grid** add input for custom filter value
6+
* **playground** convert filter value to string
7+
18
# v13.4.1 (2022-06-22)
29
* **suggest** hide no results when header items are available
310

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-components",
3-
"version": "13.4.1",
3+
"version": "13.5.0",
44
"author": {
55
"name": "UiPath Inc",
66
"url": "https://uipath.com"

projects/angular/components/ui-grid/src/managers/filter-manager.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ import { IFilterModel } from '../models';
2828
* @internal
2929
*/
3030
export class FilterManager<T> {
31+
hasCustomFilter$ = new BehaviorSubject(false);
32+
customFilters?: IFilterModel<T>[];
33+
3134
filter$ = new BehaviorSubject<IFilterModel<T>[]>([]);
3235

3336
dirty$ = this.filter$.pipe(
@@ -105,6 +108,17 @@ export class FilterManager<T> {
105108
}
106109
}
107110

111+
updateCustomFilters(customValue: IFilterModel<T>[]) {
112+
this.customFilters = customValue;
113+
this.hasCustomFilter$.next(true);
114+
this.filter$.next(customValue);
115+
}
116+
117+
clearCustomFilters() {
118+
this.hasCustomFilter$.next(false);
119+
this._emitFilterOptions();
120+
}
121+
108122
private _updateFilterValue = (
109123
column: UiGridColumnDirective<T> | undefined,
110124
value: ISuggestValue | IDropdownOption | undefined,
@@ -142,7 +156,11 @@ export class FilterManager<T> {
142156
: [];
143157
if (isEqual(this.filter$.getValue(), updatedFilters)) { return; }
144158

145-
this.filter$.next(updatedFilters);
159+
this.filter$.next(
160+
this.hasCustomFilter$.value
161+
? this.customFilters!
162+
: updatedFilters,
163+
);
146164
};
147165

148166
private _hasFilterValue = (dropdown?: UiGridSearchFilterDirective<T> | UiGridDropdownFilterDirective<T>) =>

projects/angular/components/ui-grid/src/ui-grid.component.html

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,64 @@
1313
class="ui-grid-filter-container">
1414
<div class="ui-grid-filter-container-lhs-group">
1515
<div class="ui-grid-filter-container-lhs-group-actions">
16-
<ng-container *ngIf="useLegacyDesign">
16+
<ng-container *ngIf="filterManager.hasCustomFilter$ | async; else noCustomFilter">
1717
<ng-container *ngTemplateOutlet="toggleColumnsTmpl"></ng-container>
18+
<button (click)="clearCustomFilter()"
19+
mat-flat-button type="button"
20+
data-cy="clear-custom-filter">
21+
{{ intl.clearCustomFilter }}
22+
</button>
1823
</ng-container>
24+
<ng-template #noCustomFilter>
25+
<ng-container>
26+
<ng-container *ngIf="useLegacyDesign">
27+
<ng-container *ngTemplateOutlet="toggleColumnsTmpl"></ng-container>
28+
</ng-container>
1929

20-
<ng-container *ngIf="!(hasSelection$ | async) ||
21-
!header?.actionButtons?.length">
22-
<ui-grid-search *ngIf="header?.search"
23-
[debounce]="header!.searchDebounce"
24-
[maxLength]="header!.searchMaxLength"
25-
[placeholder]="intl.searchPlaceholder"
26-
[searchTooltip]="intl.searchTooltip"
27-
[clearTooltip]="intl.clearTooltip"
28-
[tooltipDisabled]="resizeManager.isResizing"
29-
[value]="header!.searchValue!"
30-
(searchChange)="filterManager.searchChange($event, header!, footer)"
31-
class="ui-grid-search ui-grid-filter-option">
32-
</ui-grid-search>
33-
34-
<ng-container *ngIf="!useLegacyDesign">
35-
<ng-container *ngTemplateOutlet="toggleColumnsTmpl"></ng-container>
36-
</ng-container>
37-
38-
<ng-container *ngTemplateOutlet="filtersTmpl">
39-
</ng-container>
40-
</ng-container>
30+
<ng-container *ngIf="!(hasSelection$ | async) ||
31+
!header?.actionButtons?.length">
32+
<ui-grid-search *ngIf="header?.search"
33+
[debounce]="header!.searchDebounce"
34+
[maxLength]="header!.searchMaxLength"
35+
[placeholder]="intl.searchPlaceholder"
36+
[searchTooltip]="intl.searchTooltip"
37+
[clearTooltip]="intl.clearTooltip"
38+
[tooltipDisabled]="resizeManager.isResizing"
39+
[value]="header!.searchValue!"
40+
(searchChange)="filterManager.searchChange($event, header!, footer)"
41+
class="ui-grid-search ui-grid-filter-option">
42+
</ui-grid-search>
43+
44+
<ng-container *ngIf="!useLegacyDesign">
45+
<ng-container *ngTemplateOutlet="toggleColumnsTmpl"></ng-container>
46+
</ng-container>
4147

42-
<div *ngIf="!(hasSelection$ | async)"
43-
class="ui-grid-action-buttons ui-grid-action-buttons-inline">
44-
<ng-container *ngFor="let button of header?.inlineButtons">
45-
<ng-container *ngIf="button.visible">
46-
<ng-container *ngTemplateOutlet="button.html ?? null">
48+
<ng-container *ngTemplateOutlet="filtersTmpl">
4749
</ng-container>
4850
</ng-container>
49-
</ng-container>
50-
</div>
5151

52-
<div #gridActionButtons
53-
*ngIf="hasSelection$ | async"
54-
class="ui-grid-action-buttons ui-grid-action-buttons-selection">
55-
<ng-container *ngFor="let button of header?.actionButtons">
56-
<ng-container *ngIf="button.visible">
57-
<ng-container *ngTemplateOutlet="button.html?? null">
52+
<div *ngIf="!(hasSelection$ | async)"
53+
class="ui-grid-action-buttons ui-grid-action-buttons-inline">
54+
<ng-container *ngFor="let button of header?.inlineButtons">
55+
<ng-container *ngIf="button.visible">
56+
<ng-container *ngTemplateOutlet="button.html ?? null">
57+
</ng-container>
58+
</ng-container>
5859
</ng-container>
59-
</ng-container>
60+
</div>
61+
62+
<div #gridActionButtons
63+
*ngIf="hasSelection$ | async"
64+
class="ui-grid-action-buttons ui-grid-action-buttons-selection">
65+
<ng-container *ngFor="let button of header?.actionButtons">
66+
<ng-container *ngIf="button.visible">
67+
<ng-container *ngTemplateOutlet="button.html?? null">
68+
</ng-container>
69+
</ng-container>
70+
</ng-container>
71+
</div>
6072
</ng-container>
61-
</div>
73+
</ng-template>
6274
</div>
6375
<div *ngIf="showFilters && (hasAnyFiltersVisible$ | async)"
6476
[@filters-container]

projects/angular/components/ui-grid/src/ui-grid.component.spec.ts

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ import { MatMenuItem } from '@angular/material/menu';
3333
import { PageEvent } from '@angular/material/paginator';
3434
import { By } from '@angular/platform-browser';
3535
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
36-
import { ResizeStrategy } from '@uipath/angular/components/ui-grid';
36+
import {
37+
IFilterModel,
38+
ResizeStrategy,
39+
} from '@uipath/angular/components/ui-grid';
3740
import {
3841
ISuggestValues,
3942
UiSuggestComponent,
@@ -3630,4 +3633,108 @@ describe('Component: UiGrid', () => {
36303633
expect(buttons.length).toEqual(2);
36313634
});
36323635
});
3636+
3637+
describe('Behaviour: Clear custom filter', () => {
3638+
@Component({
3639+
template: `
3640+
<ui-grid [data]="data"
3641+
[customFilterValue]="customFilter">
3642+
<ui-grid-header [search]="search">
3643+
</ui-grid-header>
3644+
<ui-grid-column [property]="'myNumber'"
3645+
[searchable]="true"
3646+
[sortable]="true"
3647+
title="Number Header"
3648+
width="50%">
3649+
<ui-grid-dropdown-filter
3650+
[value]="filterValue"
3651+
[items]="filterItems">
3652+
</ui-grid-dropdown-filter>
3653+
</ui-grid-column>
3654+
</ui-grid>
3655+
`,
3656+
})
3657+
class TestFixtureCustomFilterGridComponent {
3658+
@ViewChild(UiGridComponent, {
3659+
static: true,
3660+
})
3661+
grid!: UiGridComponent<ITestEntity>;
3662+
3663+
get filterItems(): IDropdownOption[] {
3664+
return [1, 2, 3].map(count => ({
3665+
value: count,
3666+
label: count.toString(),
3667+
}));
3668+
}
3669+
customFilter: IFilterModel<any>[] = [];
3670+
filterValue = {
3671+
value: '777',
3672+
label: 'the label',
3673+
};
3674+
data: ITestEntity[] = [];
3675+
}
3676+
3677+
let fixture: ComponentFixture<TestFixtureCustomFilterGridComponent>;
3678+
3679+
beforeEach(async () => {
3680+
TestBed.configureTestingModule({
3681+
imports: [
3682+
UiGridModule,
3683+
UiGridCustomPaginatorModule,
3684+
NoopAnimationsModule,
3685+
],
3686+
providers: [
3687+
UiMatPaginatorIntl,
3688+
{
3689+
provide: UI_GRID_OPTIONS,
3690+
useValue: {
3691+
useLegacyDesign: false,
3692+
},
3693+
},
3694+
],
3695+
declarations: [
3696+
TestFixtureCustomFilterGridComponent,
3697+
],
3698+
});
3699+
3700+
fixture = TestBed.createComponent(TestFixtureCustomFilterGridComponent);
3701+
3702+
fixture.detectChanges();
3703+
await fixture.whenStable();
3704+
fixture.detectChanges();
3705+
});
3706+
3707+
afterEach(() => {
3708+
fixture.destroy();
3709+
});
3710+
3711+
it('should show custom filter after setting the grid\'s custom filter input', () => {
3712+
expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeTruthy();
3713+
fixture.componentInstance.customFilter = [{ property: 'myNumber1',
3714+
method: 'eq',
3715+
value: '2' }];
3716+
fixture.detectChanges();
3717+
expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeFalsy();
3718+
expect(document.querySelector('[data-cy="clear-custom-filter"]')).toBeTruthy();
3719+
});
3720+
3721+
it('should revert to old filter value after clearing the custom filter', fakeAsync(() => {
3722+
fixture.componentInstance.customFilter = [{ property: 'myNumber2',
3723+
method: 'eq',
3724+
value: '2' }];
3725+
fixture.detectChanges();
3726+
expect(JSON.stringify(fixture.componentInstance.grid.filterManager.filter$.value))
3727+
.toEqual(JSON.stringify(fixture.componentInstance.customFilter));
3728+
expect(fixture.componentInstance.grid.filterManager.hasCustomFilter$.value).toBeTrue();
3729+
3730+
const clearCustomFilterButton = fixture.debugElement.query(By.css('[data-cy="clear-custom-filter"]'));
3731+
clearCustomFilterButton.nativeElement.dispatchEvent(EventGenerator.click);
3732+
fixture.detectChanges();
3733+
3734+
expect(document.querySelector('.ui-grid-dropdown-filter-button')).toBeTruthy();
3735+
expect(fixture.componentInstance.grid.filterManager.hasCustomFilter$.value).toBeFalse();
3736+
expect(fixture.componentInstance.grid.filterManager.filter$.value[0].value)
3737+
.toEqual(fixture.componentInstance.filterValue.value);
3738+
}));
3739+
});
36333740
});

projects/angular/components/ui-grid/src/ui-grid.component.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import {
7979
import { ResizableGrid } from './managers/resize/types';
8080
import {
8181
GridOptions,
82+
IFilterModel,
8283
ISortModel,
8384
} from './models';
8485
import { UiGridIntl } from './ui-grid.intl';
@@ -340,6 +341,12 @@ export class UiGridComponent<T extends { id: number | string }> extends Resizabl
340341
@Input()
341342
disableSelectionByEntry: (entry: T) => null | string;
342343

344+
@Input()
345+
set customFilterValue(customValue: IFilterModel<T>[]) {
346+
if (!Array.isArray(customValue) || !customValue.length) { return; }
347+
this.filterManager.updateCustomFilters(customValue);
348+
}
349+
343350
/**
344351
* Emits an event with the sort model when a column sort changes.
345352
*
@@ -368,6 +375,9 @@ export class UiGridComponent<T extends { id: number | string }> extends Resizabl
368375
@Output()
369376
resizeEnd = new EventEmitter<void>();
370377

378+
@Output()
379+
removeCustomFilter = new EventEmitter<void>();
380+
371381
/**
372382
* Emits the column definitions when their definition changes.
373383
*
@@ -893,6 +903,11 @@ export class UiGridComponent<T extends { id: number | string }> extends Resizabl
893903
this.gridActionButtons?.nativeElement.querySelector(FOCUSABLE_ELEMENTS_QUERY)?.focus();
894904
}
895905

906+
clearCustomFilter() {
907+
this.removeCustomFilter.emit();
908+
this.filterManager.clearCustomFilters();
909+
}
910+
896911
private _announceGridHeaderActions() {
897912
this._queuedAnnouncer.enqueue(this.intl.gridHeaderActionsNotice);
898913
}

projects/angular/components/ui-grid/src/ui-grid.intl.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ export class UiGridIntl implements OnDestroy {
108108
*
109109
*/
110110
gridHeaderActionsNotice = 'Grid header actions updated. Press Shift + Alt + Arrow Up to move there.';
111+
/**
112+
* Message for the button that clears the applied custom filter.
113+
*
114+
*/
115+
clearCustomFilter = 'Clear custom filter';
111116
/**
112117
* No data row message alternative function.
113118
*

projects/angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@uipath/angular",
3-
"version": "13.4.1",
3+
"version": "13.5.0",
44
"license": "MIT",
55
"author": {
66
"name": "UiPath Inc",

projects/angular/testing/src/utilities/fake-file-list.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export class FakeFileList implements FileList {
2727
});
2828
}
2929

30+
[Symbol.iterator](): IterableIterator<File> {
31+
return this.files[Symbol.iterator]();
32+
}
33+
3034
/**
3135
* Retrieve an item at the specified index.
3236
*

0 commit comments

Comments
 (0)