Skip to content

Commit 77c79e5

Browse files
Explorer subpath support (#1368)
* feat: add support for attribute expressions * test: update tests * feat: add support for filtering with attribute expressions * fix: caught a couple bugs while updating tests
1 parent 90b7cba commit 77c79e5

34 files changed

+520
-652
lines changed

projects/common/src/utilities/types/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export type DistributiveOmit<T, K extends UnionKeys<T>> = T extends any ? Omit<T
77

88
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
99
export type RequireBy<T, K extends keyof T> = T & Required<Pick<T, K>>;
10+
export type KeysWithType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
11+
1012
export interface Dictionary<T> {
1113
[key: string]: T;
1214
}

projects/components/src/filtering/filter-bar/filter-bar.service.test.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ describe('Filter Bar service', () => {
3535
),
3636
new StringMapFilterBuilder().buildFilter(
3737
getTestFilterAttribute(FilterAttributeType.StringMap),
38-
FilterOperator.ContainsKeyValue,
39-
['myKey', 'myValue']
38+
FilterOperator.Equals,
39+
'myValue',
40+
'myKey'
4041
)
4142
];
4243

@@ -184,18 +185,33 @@ describe('Filter Bar service', () => {
184185
expect(testFilters).toEqual([stringFilter, inNumberFilter]);
185186

186187
/*
187-
* Add a StringMap CONTAINS_KEY_VALUE that should not replace any existing filters
188+
* Add a StringMap EQUALS should not replace any existing filters
188189
*/
189190

190-
const ckvStringMapFilter = new StringMapFilterBuilder().buildFilter(
191+
const firstStringMapFilter = new StringMapFilterBuilder().buildFilter(
191192
getTestFilterAttribute(FilterAttributeType.StringMap),
192-
FilterOperator.ContainsKeyValue,
193-
['myKey', 'myValue']
193+
FilterOperator.Equals,
194+
'myValue',
195+
'myKey'
194196
);
195197

196-
testFilters = spectator.service.addFilter(testFilters, ckvStringMapFilter);
198+
testFilters = spectator.service.addFilter(testFilters, firstStringMapFilter);
199+
200+
expect(testFilters).toEqual([stringFilter, inNumberFilter, firstStringMapFilter]);
201+
/*
202+
* Add a second StringMap EQUALS filters with a different key should not replace any existing filters
203+
*/
197204

198-
expect(testFilters).toEqual([stringFilter, inNumberFilter, ckvStringMapFilter]);
205+
const secondStringMapFilter = new StringMapFilterBuilder().buildFilter(
206+
getTestFilterAttribute(FilterAttributeType.StringMap),
207+
FilterOperator.Equals,
208+
'myValue',
209+
'mySecondKey'
210+
);
211+
212+
testFilters = spectator.service.addFilter(testFilters, secondStringMapFilter);
213+
214+
expect(testFilters).toEqual([stringFilter, inNumberFilter, firstStringMapFilter, secondStringMapFilter]);
199215

200216
/*
201217
* Add a StringMap CONTAINS_KEY that should not replace any existing filters
@@ -209,7 +225,13 @@ describe('Filter Bar service', () => {
209225

210226
testFilters = spectator.service.addFilter(testFilters, ckStringMapFilter);
211227

212-
expect(testFilters).toEqual([stringFilter, inNumberFilter, ckvStringMapFilter, ckStringMapFilter]);
228+
expect(testFilters).toEqual([
229+
stringFilter,
230+
inNumberFilter,
231+
firstStringMapFilter,
232+
secondStringMapFilter,
233+
ckStringMapFilter
234+
]);
213235
});
214236

215237
test('correctly updates filters', () => {
@@ -221,8 +243,9 @@ describe('Filter Bar service', () => {
221243

222244
const testStringMapFilter = new StringMapFilterBuilder().buildFilter(
223245
getTestFilterAttribute(FilterAttributeType.StringMap),
224-
FilterOperator.ContainsKeyValue,
225-
['myTestKey', 'myTestValue']
246+
FilterOperator.Equals,
247+
'myTestValue',
248+
'myTestKey'
226249
);
227250

228251
const testNumberFilter = new NumberFilterBuilder().buildFilter(

projects/components/src/filtering/filter-bar/filter-bar.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Injectable } from '@angular/core';
2-
import { areCompatibleFilters, areEqualFilters, Filter } from '../filter/filter';
2+
import { isEqual } from 'lodash-es';
3+
import { areCompatibleFilters, Filter } from '../filter/filter';
34

45
@Injectable({
56
providedIn: 'root'
@@ -14,18 +15,18 @@ export class FilterBarService {
1415
public updateFilter(filters: Filter[], oldFilter: Filter, newFilter: Filter): Filter[] {
1516
const clonedFilters = [...filters];
1617

17-
const index = filters.findIndex(f => areEqualFilters(f, oldFilter));
18+
const index = filters.findIndex(f => isEqual(f, oldFilter));
1819

1920
if (index < 0) {
2021
throw new Error(`Unable to update filter. Filter for '${oldFilter.field}' not found.`);
2122
}
2223

2324
clonedFilters.splice(index, 1, newFilter);
2425

25-
return clonedFilters.filter(f => areEqualFilters(f, newFilter) || areCompatibleFilters(f, newFilter));
26+
return clonedFilters.filter(f => isEqual(f, newFilter) || areCompatibleFilters(f, newFilter));
2627
}
2728

2829
public deleteFilter(filters: Filter[], filter: Filter): Filter[] {
29-
return filters.filter(f => !areEqualFilters(f, filter));
30+
return filters.filter(f => !isEqual(f, filter));
3031
}
3132
}

projects/components/src/filtering/filter-bar/filter-chip/filter-chip.component.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ export class FilterChipComponent implements OnInit, OnChanges {
120120
private mapToComboBoxOption(filter: IncompleteFilter): ComboBoxOption<IncompleteFilter> {
121121
return {
122122
text: filter.userString,
123-
value: filter,
124-
tooltip: `${filter.userString} (${filter.field})`
123+
value: filter
125124
};
126125
}
127126
}

projects/components/src/filtering/filter-bar/filter-chip/filter-chip.service.test.ts

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ describe('Filter Chip service', () => {
6262
case FilterOperator.In:
6363
return new InFilterParser();
6464
case FilterOperator.ContainsKey:
65-
case FilterOperator.ContainsKeyValue:
6665
return new ContainsFilterParser();
6766
default:
6867
assertUnreachable(operator);
@@ -246,21 +245,79 @@ describe('Filter Chip service', () => {
246245
]);
247246
});
248247

249-
test('correctly autocompletes operator filters for string map attribute once space key is entered', () => {
248+
test('correctly autocompletes operator filters for string map attribute', () => {
250249
const attribute = getTestFilterAttribute(FilterAttributeType.StringMap);
251250

252-
expect(spectator.service.autocompleteFilters(attributes, 'String Map Attribute.testKey ')).toEqual([
251+
// Contains key or subpath operators if no subpath specified but could be
252+
expect(spectator.service.autocompleteFilters(attributes, 'String Map Attribute')).toEqual([
253253
{
254254
metadata: attribute,
255255
field: attribute.name,
256256
operator: FilterOperator.ContainsKey,
257-
userString: `${attribute.displayName} ${FilterOperator.ContainsKey} testKey`
257+
userString: `${attribute.displayName} ${FilterOperator.ContainsKey}`
258258
},
259259
{
260260
metadata: attribute,
261261
field: attribute.name,
262-
operator: FilterOperator.ContainsKeyValue,
263-
userString: `${attribute.displayName}.testKey ${FilterOperator.ContainsKeyValue}`
262+
operator: FilterOperator.Equals,
263+
userString: `${attribute.displayName}.example ${FilterOperator.Equals}`
264+
},
265+
{
266+
metadata: attribute,
267+
field: attribute.name,
268+
operator: FilterOperator.NotEquals,
269+
userString: `${attribute.displayName}.example ${FilterOperator.NotEquals}`
270+
},
271+
{
272+
metadata: attribute,
273+
field: attribute.name,
274+
operator: FilterOperator.In,
275+
userString: `${attribute.displayName}.example ${FilterOperator.In}`
276+
},
277+
{
278+
metadata: attribute,
279+
field: attribute.name,
280+
operator: FilterOperator.Like,
281+
userString: `${attribute.displayName}.example ${FilterOperator.Like}`
282+
}
283+
]);
284+
285+
// Regular operators only once subpath included
286+
expect(spectator.service.autocompleteFilters(attributes, 'String Map Attribute.testKey')).toEqual([
287+
expect.objectContaining({
288+
metadata: attribute,
289+
field: attribute.name,
290+
operator: FilterOperator.ContainsKey,
291+
// This operator isn't actually eligible but filtering operators is done by the chip/combobox, so just make sure the string doesn't match
292+
userString: expect.not.stringMatching(`${attribute.displayName}.testKey`)
293+
}),
294+
{
295+
metadata: attribute,
296+
field: attribute.name,
297+
subpath: 'testKey',
298+
operator: FilterOperator.Equals,
299+
userString: `${attribute.displayName}.testKey ${FilterOperator.Equals}`
300+
},
301+
{
302+
metadata: attribute,
303+
field: attribute.name,
304+
subpath: 'testKey',
305+
operator: FilterOperator.NotEquals,
306+
userString: `${attribute.displayName}.testKey ${FilterOperator.NotEquals}`
307+
},
308+
{
309+
metadata: attribute,
310+
field: attribute.name,
311+
subpath: 'testKey',
312+
operator: FilterOperator.In,
313+
userString: `${attribute.displayName}.testKey ${FilterOperator.In}`
314+
},
315+
{
316+
metadata: attribute,
317+
field: attribute.name,
318+
subpath: 'testKey',
319+
operator: FilterOperator.Like,
320+
userString: `${attribute.displayName}.testKey ${FilterOperator.Like}`
264321
}
265322
]);
266323
});

0 commit comments

Comments
 (0)