Skip to content

Commit 73f68c0

Browse files
committed
feat(grid): use suggest for multi filter dropdown
1 parent 8816ab4 commit 73f68c0

File tree

9 files changed

+491
-223
lines changed

9 files changed

+491
-223
lines changed

projects/angular/components/ui-grid/src/body/ui-grid-column.directive.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ export class UiGridColumnDirective<T> implements OnChanges, OnDestroy {
227227
@Input()
228228
refetch = false;
229229

230+
/**
231+
* Determines the message which appears in the tooltip of an info icon inside the column header.
232+
*
233+
*/
230234
@Input()
231235
description = '';
232236

projects/angular/components/ui-grid/src/filters/ui-grid-dropdown-filter.directive.ts

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
import { BehaviorSubject } from 'rxjs';
1+
import { isArray } from 'lodash-es';
2+
import {
3+
BehaviorSubject,
4+
map,
5+
} from 'rxjs';
26

37
import {
48
Directive,
59
Input,
610
OnDestroy,
711
} from '@angular/core';
12+
import { ISuggestValueData } from '@uipath/angular/components/ui-suggest';
813

914
import { UiGridFilterDirective } from './ui-grid-filter';
1015

16+
type Arrayify <T> = T extends T ? T[] : never;
17+
18+
export type FilterSingleValue = string | number | boolean;
19+
export type FilterMultiValue = Arrayify<FilterSingleValue>;
20+
1121
/**
1222
* Dropdown option schema.
1323
*
@@ -18,7 +28,7 @@ export interface IDropdownOption {
1828
* The current dropdown value.
1929
*
2030
*/
21-
value: string | number | boolean;
31+
value: FilterSingleValue | FilterMultiValue;
2232
/**
2333
* The dropdown option label.
2434
*
@@ -31,6 +41,8 @@ export interface IDropdownOption {
3141
translationKey?: string;
3242
}
3343

44+
export type ISuggestDropdownValueData = ISuggestValueData<IDropdownOption['value']>;
45+
3446
/**
3547
* The dropdown filter definition directive.
3648
*
@@ -45,7 +57,30 @@ export class UiGridDropdownFilterDirective<T> extends UiGridFilterDirective<T> i
4557
*
4658
*/
4759
@Input()
48-
items?: IDropdownOption[];
60+
set items(value: IDropdownOption[]) {
61+
this._items = value;
62+
this.suggestItems = value.map((item, idx) => ({
63+
id: idx + 1,
64+
text: item.label,
65+
data: item.value,
66+
}));
67+
}
68+
get items() { return this._items!; }
69+
70+
/**
71+
* If true multiple values can be selected in the dropdown filter.
72+
*
73+
*/
74+
@Input()
75+
set multi(value: boolean) {
76+
this._multi = value;
77+
if (value) {
78+
this.selectedFilters$.next([]);
79+
}
80+
}
81+
get multi() {
82+
return this._multi;
83+
}
4984

5085
/**
5186
* If it should display the `All` option.
@@ -79,6 +114,31 @@ export class UiGridDropdownFilterDirective<T> extends UiGridFilterDirective<T> i
79114
* @ignore
80115
*/
81116
visible$ = new BehaviorSubject(true);
117+
/**
118+
* Current filter value selection.
119+
*
120+
*/
121+
selectedFilters$ = new BehaviorSubject<IDropdownOption['value'] | undefined>(undefined);
122+
123+
/**
124+
* Current filter selection expressed as ISuggestValue.
125+
*
126+
*/
127+
suggestValue$ = this.selectedFilters$.pipe(
128+
map(selection => {
129+
const value = this.suggestItems?.filter(item =>
130+
(isArray(selection) && selection?.some(s => s === item?.data) || selection === item?.data));
131+
return value as ISuggestDropdownValueData[];
132+
}),
133+
);
134+
135+
/**
136+
* Dropdown items expressed as ISuggestDropdownValueData
137+
*/
138+
suggestItems: ISuggestDropdownValueData[] = [];
139+
140+
private _items?: IDropdownOption[];
141+
private _multi = false;
82142

83143
/**
84144
* Updates the dropdown value.

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
} from 'rxjs/operators';
1111

1212
import { TestBed } from '@angular/core/testing';
13-
import { UiGridFooterDirective } from '@uipath/angular/components/ui-grid';
13+
import {
14+
IDropdownOption,
15+
UiGridFooterDirective,
16+
} from '@uipath/angular/components/ui-grid';
1417
import { ISuggestValue } from '@uipath/angular/components/ui-suggest';
1518

1619
import { UiGridColumnDirective } from '../body/ui-grid-column.directive';
17-
import { IDropdownOption } from '../filters/ui-grid-dropdown-filter.directive';
1820
import { UiGridHeaderDirective } from '../header/ui-grid-header.directive';
1921
import {
2022
FilterManager,
@@ -200,7 +202,7 @@ describe('Component: UiGrid', () => {
200202
)
201203
.subscribe(filter => {
202204
expect(filter).toBeDefined();
203-
expect(filter!.value).toEqual(columnOptionDefinition.option.value);
205+
expect(filter!.value as IDropdownOption['value']).toEqual(columnOptionDefinition.option.value);
204206
expect(filter!.method).toBe(columnOptionDefinition.column.dropdown!.method!);
205207
});
206208

@@ -218,7 +220,7 @@ describe('Component: UiGrid', () => {
218220
const [filter] = filters;
219221

220222
expect(filter).toBeDefined();
221-
expect(filter.value).toEqual(columnOptionDefinition.option.value);
223+
expect(filter.value as IDropdownOption['value']).toEqual(columnOptionDefinition.option.value);
222224
expect(filter.method).toBe(columnOptionDefinition.column.dropdown!.method!);
223225
});
224226

@@ -245,7 +247,7 @@ describe('Component: UiGrid', () => {
245247
const columnOptionDefinition = columnOptionDefinitionList[idx];
246248

247249
expect(filter).toBeDefined();
248-
expect(filter.value).toEqual(columnOptionDefinition.option.value);
250+
expect(filter.value as IDropdownOption['value']).toEqual(columnOptionDefinition.option.value);
249251
expect(filter.method).toBe(columnOptionDefinition.column.dropdown!.method!);
250252
});
251253
});

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

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isArray } from 'lodash-es';
12
import isEqual from 'lodash-es/isEqual';
23
import {
34
BehaviorSubject,
@@ -12,7 +13,9 @@ import { ISuggestValue } from '@uipath/angular/components/ui-suggest';
1213

1314
import { UiGridColumnDirective } from '../body/ui-grid-column.directive';
1415
import {
16+
FilterMultiValue,
1517
IDropdownOption,
18+
ISuggestDropdownValueData,
1619
UiGridDropdownFilterDirective,
1720
} from '../filters/ui-grid-dropdown-filter.directive';
1821
import { UiGridSearchFilterDirective } from '../filters/ui-grid-search-filter.directive';
@@ -61,6 +64,19 @@ export class FilterManager<T> {
6164
distinctUntilChanged(),
6265
);
6366

67+
activeFilterValueCount$ = combineLatest([
68+
this.activeCount$,
69+
this.filter$,
70+
]).pipe(
71+
map(([activeCount, filters]) => {
72+
const activeFilterValueCount = activeCount + filters.filter(f => isArray(f?.value))
73+
.map(f => (f.value as FilterMultiValue).length - 1)
74+
.reduce((acc, cur) => acc + cur, 0);
75+
76+
return Math.max(0, activeFilterValueCount);
77+
}),
78+
);
79+
6480
private _initialFilters: IFilterModel<T>[] | null = null;
6581

6682
constructor(
@@ -80,11 +96,49 @@ export class FilterManager<T> {
8096
this.filter$.complete();
8197
}
8298

83-
searchableDropdownUpdate = (column?: UiGridColumnDirective<T>, value?: ISuggestValue, selected?: boolean) =>
99+
selectFilters(column: UiGridColumnDirective<T>, selection?: ISuggestDropdownValueData) {
100+
if (selection?.data === undefined) { return this.dropdownUpdate(column, undefined); }
101+
102+
let value = selection.data;
103+
const currentValue = column.dropdown?.selectedFilters$.value;
104+
105+
if (column.dropdown?.multi && isArray(currentValue)) {
106+
const valueAlreadySelected = currentValue.some(v => v === value);
107+
value = (valueAlreadySelected
108+
? (currentValue as []).filter(v => v !== value)
109+
: [...currentValue, value]) as FilterMultiValue;
110+
}
111+
112+
this.dropdownUpdate(column, { value } as IDropdownOption);
113+
}
114+
115+
searchableDropdownUpdate = (column?: UiGridColumnDirective<T>, value?: ISuggestValue | ISuggestValue[], selected?: boolean) => {
116+
if (isArray(value)) {
117+
if (column?.searchableDropdown) {
118+
value.forEach(filterValue => {
119+
column!.searchableDropdown!.updateValue(filterValue, true);
120+
});
121+
this._emitFilterOptions();
122+
}
123+
return;
124+
}
125+
84126
this._updateFilterValue(column, value, selected, this._mapSearchableDropdownItem);
127+
};
85128

86-
dropdownUpdate = (column?: UiGridColumnDirective<T>, value?: IDropdownOption) =>
87-
this._updateFilterValue(column, value, false, this._mapDropdownItem);
129+
dropdownUpdate = (column?: UiGridColumnDirective<T>, dropdownOption?: IDropdownOption) => {
130+
if (column?.dropdown) {
131+
const selectedFilterValue = ((dropdownOption == null || isArray(dropdownOption.value))
132+
? dropdownOption?.value
133+
: [dropdownOption.value]) as IDropdownOption['value'] | undefined;
134+
column.dropdown.selectedFilters$.next(selectedFilterValue);
135+
}
136+
137+
const updateValue = (dropdownOption == null)
138+
? undefined
139+
: dropdownOption;
140+
return this._updateFilterValue(column, updateValue, false, this._mapDropdownItem);
141+
};
88142

89143
searchChange(term: string | undefined, header: UiGridHeaderDirective<T>, footer?: UiGridFooterDirective) {
90144
if (term === header.searchValue) { return; }
@@ -152,7 +206,7 @@ export class FilterManager<T> {
152206

153207
private _emitFilterOptions = () => {
154208
this.defaultValueDropdownFilters = this._columns
155-
.filter(({ dropdown }) => this._hasFilterValue(dropdown))
209+
.filter(({ dropdown }) => this._hasFilterValue(dropdown) && this._dropdownFilterDirectiveHasValue(dropdown!))
156210
.map(this._mapDropdownItem);
157211

158212
const emptyStateDropdownFilters = this._columns
@@ -175,6 +229,10 @@ export class FilterManager<T> {
175229
);
176230
};
177231

232+
private _dropdownFilterDirectiveHasValue = (dropdown: UiGridDropdownFilterDirective<T>) =>
233+
dropdown.value?.value !== undefined &&
234+
(!isArray(dropdown.value.value) || dropdown.value.value.length);
235+
178236
private _hasFilterValue = (dropdown?: UiGridSearchFilterDirective<T> | UiGridDropdownFilterDirective<T>) =>
179237
!!dropdown &&
180238
dropdown.value;

0 commit comments

Comments
 (0)