diff --git a/apps/components-e2e/src/components/filter-field/filter-field.module.ts b/apps/components-e2e/src/components/filter-field/filter-field.module.ts
index e2be60b4f0..052c459374 100644
--- a/apps/components-e2e/src/components/filter-field/filter-field.module.ts
+++ b/apps/components-e2e/src/components/filter-field/filter-field.module.ts
@@ -19,12 +19,24 @@ import { NgModule } from '@angular/core';
import { Route, RouterModule } from '@angular/router';
import { DtFilterFieldModule } from '@dynatrace/barista-components/filter-field';
import { DtE2EFilterField } from './filter-field';
+import {
+ DtExampleFilterFieldAsync,
+ DtFilterFieldExamplesModule,
+} from '@dynatrace/examples/filter-field';
-const routes: Route[] = [{ path: '', component: DtE2EFilterField }];
+const routes: Route[] = [
+ { path: '', component: DtE2EFilterField },
+ { path: 'async', component: DtExampleFilterFieldAsync },
+];
@NgModule({
declarations: [DtE2EFilterField],
- imports: [CommonModule, RouterModule.forChild(routes), DtFilterFieldModule],
+ imports: [
+ CommonModule,
+ DtFilterFieldExamplesModule,
+ RouterModule.forChild(routes),
+ DtFilterFieldModule,
+ ],
exports: [],
providers: [],
})
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.html b/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.html
new file mode 100644
index 0000000000..04fd3e8ecc
--- /dev/null
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.html
@@ -0,0 +1,14 @@
+
+ Quick-filter
+
+ All options in the filter field above
+
+
+ Quick-filter async example
+
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.ts b/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.ts
new file mode 100644
index 0000000000..4415936339
--- /dev/null
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter-async/quick-filter-async.ts
@@ -0,0 +1,77 @@
+/**
+ * @license
+ * Copyright 2020 Dynatrace LLC
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component } from '@angular/core';
+import { isObject } from '@dynatrace/barista-components/core';
+import {
+ DtQuickFilterDefaultDataSource,
+ DtQuickFilterDefaultDataSourceConfig,
+ DtQuickFilterCurrentFilterChangeEvent,
+ DtQuickFilterDefaultDataSourceType,
+} from '@dynatrace/barista-components/experimental/quick-filter';
+
+const filterFieldData = {
+ autocomplete: [
+ {
+ name: 'AUT (async)',
+ async: true,
+ autocomplete: [],
+ },
+ {
+ name: 'USA',
+ autocomplete: [
+ { name: 'San Francisco' },
+ { name: 'Los Angeles' },
+ { name: 'New York' },
+ ],
+ },
+ ],
+};
+
+const asyncData = {
+ name: 'AUT (async)',
+ autocomplete: [{ name: 'Linz' }, { name: 'Vienna' }, { name: 'Graz' }],
+};
+
+@Component({
+ selector: 'dt-e2e-quick-filter-async',
+ templateUrl: './quick-filter-async.html',
+ // template: ''
+})
+export class DtE2EQuickFilterAsync {
+ /** configuration for the quick filter */
+ private _config: DtQuickFilterDefaultDataSourceConfig = {
+ // Method to decide if a node should be displayed in the quick filter
+ showInSidebar: (node) =>
+ isObject(node) && node.name && node.name !== 'AUT (async)',
+ };
+
+ _dataSource = new DtQuickFilterDefaultDataSource<
+ DtQuickFilterDefaultDataSourceType
+ >(filterFieldData, this._config);
+
+ currentFilterChanges(
+ event: DtQuickFilterCurrentFilterChangeEvent<
+ DtQuickFilterDefaultDataSourceType
+ >,
+ ): void {
+ if (event.added[0] === filterFieldData.autocomplete[0]) {
+ setTimeout(() => {
+ this._dataSource.data = asyncData;
+ }, 1000);
+ }
+ }
+}
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.html b/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.html
index 64d492a50e..f60de008be 100644
--- a/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.html
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.html
@@ -6,3 +6,7 @@
my content
+
+
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.ts b/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.ts
index 64f90d7db7..b730ad316a 100644
--- a/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.ts
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter-initial-data/quick-filter-initial-data.ts
@@ -37,4 +37,13 @@ export class DtE2EQuickFilterInitialData {
FILTER_FIELD_TEST_DATA.autocomplete[1].autocomplete![2],
],
];
+
+ changeInitialFilters(): void {
+ this._initialFilters = [
+ [
+ FILTER_FIELD_TEST_DATA.autocomplete[1],
+ FILTER_FIELD_TEST_DATA.autocomplete[1].autocomplete![2],
+ ],
+ ];
+ }
}
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter.e2e.ts b/apps/components-e2e/src/components/quick-filter/quick-filter.e2e.ts
index e3f09ec6a6..bfb004db0b 100644
--- a/apps/components-e2e/src/components/quick-filter/quick-filter.e2e.ts
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter.e2e.ts
@@ -26,6 +26,7 @@ import {
getSelectedItem,
} from './quick-filter.po';
import { resetWindowSizeToDefault } from '../../utils';
+import { Selector } from 'testcafe';
fixture('Quick Filter')
.page('http://localhost:4200/quick-filter')
@@ -156,3 +157,19 @@ test('when the distinct group get set to the any option, then remove the group f
.expect(getSelectedItem('AUT').textContent)
.match(/Graz/);
});
+
+test('should be possible to change the filters via binding on the quick-filter', async (testController: TestController) => {
+ await testController
+ .expect(getFilterfieldTags())
+ .eql(['AUTVienna', 'USANew York'])
+ .click(tagDeleteButton('New York'), { speed: 0.4 })
+ .expect(getFilterfieldTags())
+ .eql(['AUTVienna'])
+ .click(Selector('.change-filter-binding'))
+ .expect(getFilterfieldTags())
+ .eql(['USANew York'])
+ .expect(getSelectedItem('USA').textContent)
+ .match(/New York/)
+ .expect(getSelectedItem('AUT').textContent)
+ .match(/Any/);
+});
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter.module.ts b/apps/components-e2e/src/components/quick-filter/quick-filter.module.ts
index 9949a31d66..c34239b836 100644
--- a/apps/components-e2e/src/components/quick-filter/quick-filter.module.ts
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter.module.ts
@@ -20,15 +20,21 @@ import { Route, RouterModule } from '@angular/router';
import { DtQuickFilterModule } from '@dynatrace/barista-components/experimental/quick-filter';
import { DtE2EQuickFilter } from './quick-filter/quick-filter';
import { DtE2EQuickFilterInitialData } from './quick-filter-initial-data/quick-filter-initial-data';
+import { DtE2EQuickFilterAsync } from './quick-filter-async/quick-filter-async';
const routes: Route[] = [
{ path: '', component: DtE2EQuickFilter },
{ path: 'initial-data', component: DtE2EQuickFilterInitialData },
+ { path: 'async', component: DtE2EQuickFilterAsync },
];
@NgModule({
- declarations: [DtE2EQuickFilter, DtE2EQuickFilterInitialData],
- imports: [CommonModule, RouterModule.forChild(routes), DtQuickFilterModule],
+ declarations: [
+ DtE2EQuickFilter,
+ DtE2EQuickFilterInitialData,
+ DtE2EQuickFilterAsync,
+ ],
+ imports: [CommonModule, DtQuickFilterModule, RouterModule.forChild(routes)],
exports: [],
providers: [],
})
diff --git a/apps/components-e2e/src/components/quick-filter/quick-filter/quick-filter.ts b/apps/components-e2e/src/components/quick-filter/quick-filter/quick-filter.ts
index 5d1358955f..02310cdac2 100644
--- a/apps/components-e2e/src/components/quick-filter/quick-filter/quick-filter.ts
+++ b/apps/components-e2e/src/components/quick-filter/quick-filter/quick-filter.ts
@@ -24,13 +24,13 @@ import {
FILTER_FIELD_TEST_DATA,
FILTER_FIELD_TEST_DATA_VALIDATORS,
} from '@dynatrace/testing/fixtures';
-import { isObject } from 'util';
+import { isObject } from 'lodash-es';
const DATA = [FILTER_FIELD_TEST_DATA_VALIDATORS, FILTER_FIELD_TEST_DATA];
const config: DtQuickFilterDefaultDataSourceConfig = {
showInSidebar: (node) =>
- isObject(node) && node.name && node.name !== 'Not in Quickfilter',
+ (isObject(node) as any) && node.name && node.name !== 'Not in Quickfilter',
};
@Component({
diff --git a/apps/components-e2e/src/components/quick-filter/util.ts b/apps/components-e2e/src/components/quick-filter/util.ts
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/libs/barista-components/experimental/quick-filter/BUILD.bazel b/libs/barista-components/experimental/quick-filter/BUILD.bazel
index 65d7010fb1..24b4bb46a3 100644
--- a/libs/barista-components/experimental/quick-filter/BUILD.bazel
+++ b/libs/barista-components/experimental/quick-filter/BUILD.bazel
@@ -22,16 +22,17 @@ ng_module_view_engine(
module_name = "@dynatrace/barista-components/experimental/quick-filter",
tsconfig = "tsconfig_lib",
deps = [
- "@npm//@angular/cdk",
- "@npm//@angular/common",
- "@npm//@angular/core",
- "@npm//rxjs",
- "//libs/barista-components/core:compile",
"//libs/barista-components/checkbox:compile",
+ "//libs/barista-components/core:compile",
"//libs/barista-components/drawer:compile",
"//libs/barista-components/filter-field:compile",
"//libs/barista-components/icon:compile",
"//libs/barista-components/radio:compile",
+ "@npm//@angular/cdk",
+ "@npm//@angular/common",
+ "@npm//@angular/core",
+ "@npm//lodash-es",
+ "@npm//rxjs",
],
)
@@ -72,19 +73,19 @@ jest(
ts_config = ":tsconfig_test",
deps = [
":compile",
+ "//libs/barista-components/checkbox:compile",
+ "//libs/barista-components/core:compile",
+ "//libs/barista-components/drawer:compile",
+ "//libs/barista-components/filter-field:compile",
+ "//libs/barista-components/icon:compile",
+ "//libs/barista-components/radio:compile",
"//libs/testing/browser",
"//libs/testing/fixtures",
"@npm//@angular/cdk",
"@npm//@angular/common",
"@npm//@angular/core",
- "@npm//rxjs",
"@npm//@angular/platform-browser",
- "//libs/barista-components/core:compile",
- "//libs/barista-components/checkbox:compile",
- "//libs/barista-components/drawer:compile",
- "//libs/barista-components/filter-field:compile",
- "//libs/barista-components/icon:compile",
- "//libs/barista-components/radio:compile",
+ "@npm//rxjs",
],
)
diff --git a/libs/barista-components/experimental/quick-filter/src/quick-filter-group.html b/libs/barista-components/experimental/quick-filter/src/quick-filter-group.html
index a755e18d9a..22d397af93 100644
--- a/libs/barista-components/experimental/quick-filter/src/quick-filter-group.html
+++ b/libs/barista-components/experimental/quick-filter/src/quick-filter-group.html
@@ -9,7 +9,7 @@
Any
@@ -30,7 +30,7 @@
*ngFor="let item of _getOptions()"
[value]="item"
[checked]="_isActive(item)"
- (change)="_selectCheckBox($event)"
+ (change)="_selectCheckBox($event, _nodeDef)"
>
{{ _getViewValue(item) }}
diff --git a/libs/barista-components/experimental/quick-filter/src/quick-filter-group.ts b/libs/barista-components/experimental/quick-filter/src/quick-filter-group.ts
index ac13c0868d..ec0b98898a 100644
--- a/libs/barista-components/experimental/quick-filter/src/quick-filter-group.ts
+++ b/libs/barista-components/experimental/quick-filter/src/quick-filter-group.ts
@@ -25,6 +25,7 @@ import {
} from '@angular/core';
import { DtCheckboxChange } from '@dynatrace/barista-components/checkbox';
import {
+ DtAutocompleteValue,
DtNodeDef,
isDtOptionDef,
isDtRenderType,
@@ -51,7 +52,7 @@ import {
class: 'dt-quick-filter-group',
},
})
-export class DtQuickFilterGroup {
+export class DtQuickFilterGroup {
/**
* @internal
* The aria-level of the group headlines for the document outline.
@@ -63,7 +64,7 @@ export class DtQuickFilterGroup {
/** @internal The list of all active filters */
@Input()
- set activeFilters(filters: any[][]) {
+ set activeFilters(filters: DtAutocompleteValue[][]) {
this._activeFilterPaths = buildIdPathsFromFilters(filters || []);
this._changeDetectorRef.markForCheck();
}
@@ -82,17 +83,17 @@ export class DtQuickFilterGroup {
}
/** @internal Updates a radio box */
- _selectOption(change: DtRadioChange): void {
+ _selectOption(change: DtRadioChange, group: DtNodeDef): void {
if (change.value) {
- this.filterChange.emit(updateFilter(change.value));
+ this.filterChange.emit(updateFilter([group, change.value]));
}
}
/** @internal Select or deselect a checkbox */
- _selectCheckBox(change: DtCheckboxChange): void {
+ _selectCheckBox(change: DtCheckboxChange, group: DtNodeDef): void {
const action = change.checked
- ? addFilter(change.source.value)
- : removeFilter(change.source.value);
+ ? addFilter([group, change.source.value])
+ : removeFilter(change.source.value.option!.uid!);
this.filterChange.emit(action);
}
diff --git a/libs/barista-components/experimental/quick-filter/src/quick-filter-utils.ts b/libs/barista-components/experimental/quick-filter/src/quick-filter-utils.ts
index 9e635ad8a4..1df2d1ef36 100644
--- a/libs/barista-components/experimental/quick-filter/src/quick-filter-utils.ts
+++ b/libs/barista-components/experimental/quick-filter/src/quick-filter-utils.ts
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-import { DELIMITER } from '@dynatrace/barista-components/filter-field';
+import { DtAutocompleteValue } from '@dynatrace/barista-components/filter-field';
-export function buildIdPathsFromFilters(filters: any[][]): string[] {
- return filters.map((path) =>
- path.reduce(
- (previousValue, currentValue) =>
- `${previousValue.name}${DELIMITER}${currentValue.name}${DELIMITER}`,
- ),
- );
+/** @internal Build an array of uids from the options without the groups */
+export function buildIdPathsFromFilters(
+ filters: DtAutocompleteValue[][],
+): string[] {
+ return filters.map((group) => group[group.length - 1].option?.uid || '');
}
diff --git a/libs/barista-components/experimental/quick-filter/src/quick-filter.spec.ts b/libs/barista-components/experimental/quick-filter/src/quick-filter.spec.ts
index 9df20a47f1..1d5cd10346 100644
--- a/libs/barista-components/experimental/quick-filter/src/quick-filter.spec.ts
+++ b/libs/barista-components/experimental/quick-filter/src/quick-filter.spec.ts
@@ -123,6 +123,7 @@ describe('dt-quick-filter', () => {
FILTER_FIELD_TEST_DATA.autocomplete[0].autocomplete![0],
],
];
+ zone.simulateZoneExit();
fixture.detectChanges();
fixture.componentInstance._dataSource = new DtQuickFilterDefaultDataSource(
@@ -131,8 +132,8 @@ describe('dt-quick-filter', () => {
showInSidebar: () => true,
},
);
+ zone.simulateZoneExit();
fixture.detectChanges();
-
expect(filterFieldInstance.filters).toMatchObject([]);
expect(quickFilterInstance.filters).toMatchObject([]);
});
diff --git a/libs/barista-components/experimental/quick-filter/src/quick-filter.ts b/libs/barista-components/experimental/quick-filter/src/quick-filter.ts
index 94d943c9e9..90f032a1ea 100644
--- a/libs/barista-components/experimental/quick-filter/src/quick-filter.ts
+++ b/libs/barista-components/experimental/quick-filter/src/quick-filter.ts
@@ -31,13 +31,26 @@ import {
DtFilterField,
DtFilterFieldChangeEvent,
DtFilterFieldCurrentFilterChangeEvent,
+ DtFilterValue,
+ isDtAutocompleteValue,
+ _getSourcesOfDtFilterValues,
} from '@dynatrace/barista-components/filter-field';
-import { BehaviorSubject, Subject, Observable } from 'rxjs';
-import { switchMap, take, takeUntil } from 'rxjs/operators';
+import { BehaviorSubject, Observable, Subject } from 'rxjs';
+import { filter, map, switchMap, take, takeUntil } from 'rxjs/operators';
import { DtQuickFilterDataSource } from './quick-filter-data-source';
-import { Action, setFilters, switchDataSource } from './state/actions';
+import {
+ Action,
+ addInitialFilters,
+ setFilters,
+ switchDataSource,
+} from './state/actions';
import { quickFilterReducer } from './state/reducer';
-import { getAutocompletes, getDataSource, getFilters } from './state/selectors';
+import {
+ getAutocompletes,
+ getDataSource,
+ getFilters,
+ getInitialFilters,
+} from './state/selectors';
import { createQuickFilterStore, QuickFilterState } from './state/store';
/** Directive that is used to place a title inside the quick filters sidebar */
@@ -143,7 +156,7 @@ export class DtQuickFilter implements AfterViewInit, OnDestroy {
return this._filterField.filters;
}
set filters(filters: T[][]) {
- this._store.dispatch(setFilters(filters));
+ this._store.dispatch(addInitialFilters(filters));
}
/**
@@ -169,21 +182,33 @@ export class DtQuickFilter implements AfterViewInit, OnDestroy {
/** Angular life-cycle hook that will be called after the view is initialized */
ngAfterViewInit(): void {
// We need to wait for the first on stable call, otherwise the
- // underlying filterfield will thow an expression changed after checked
+ // underlying filter field will throw an expression changed after checked
// error. Deferring the first filter setting.
// Relates to a very weird and hard to reproduce bug described in
// https://github.com/dynatrace-oss/barista/issues/1305
- this._zone.onStable
+ const stable$ = this._zone.onStable.pipe(take(1));
+
+ stable$
.pipe(
- take(1),
switchMap(() => this._activeFilters$),
+ map((filters) =>
+ filters.map((values) => _getSourcesOfDtFilterValues(values)),
+ ),
+ takeUntil(this._destroy$),
+ )
+ .subscribe((filters) => {
+ this._filterField.filters = filters;
+ });
+
+ stable$
+ .pipe(
+ switchMap(() => this._store.select(getInitialFilters)),
+ filter(Boolean),
takeUntil(this._destroy$),
)
- // When the filters changes apply them to the filter field
.subscribe((filters) => {
- if (this._filterField.filters !== filters) {
- this._filterField.filters = filters;
- }
+ this._filterField.filters = filters;
+ this._store.dispatch(setFilters(this._getFilteredValues()));
});
}
@@ -212,7 +237,15 @@ export class DtQuickFilter implements AfterViewInit, OnDestroy {
/** @internal Bubble the filter field change event through */
_filterFieldChanged(change: DtFilterFieldChangeEvent): void {
- this._store.dispatch(setFilters(change.filters));
+ // Filter only autocomplete filters as we don't use free-text and range in the quick-filter
+ this._store.dispatch(setFilters(this._getFilteredValues()));
this.filterChanges.emit(change);
}
+
+ /** Get the filter Values from the Filter Field with only the displayable autocompletes */
+ private _getFilteredValues(): DtFilterValue[][] {
+ return this._filterField._filterValues.filter((group) =>
+ group.every((value) => isDtAutocompleteValue(value)),
+ );
+ }
}
diff --git a/libs/barista-components/experimental/quick-filter/src/state/actions.ts b/libs/barista-components/experimental/quick-filter/src/state/actions.ts
index 75301aac02..b73d7b1c8e 100644
--- a/libs/barista-components/experimental/quick-filter/src/state/actions.ts
+++ b/libs/barista-components/experimental/quick-filter/src/state/actions.ts
@@ -15,56 +15,62 @@
*/
import {
DtFilterFieldDataSource,
+ DtFilterValue,
DtNodeDef,
} from '@dynatrace/barista-components/filter-field';
-/** Enum for all the possible action types */
+/** @internal Enum for all the possible action types */
export enum ActionType {
INIT = '@@actions init',
ADD_FILTER = '@@actions add filter',
REMOVE_FILTER = '@@actions remove filter',
UPDATE_FILTER = '@@actions update filter',
SET_FILTERS = '@@actions set filters',
+ ADD_INITIAL_FILTERS = '@@actions add initial filters',
UNSET_FILTER_GROUP = '@@actions unset filter group',
SWITCH_DATA_SOURCE = '@@actions switch dataSource',
UPDATE_DATA_SOURCE = '@@actions update dataSource',
}
-/** Interface for an action */
+/** @internal Interface for an action */
export interface Action {
readonly type: ActionType;
payload?: T;
}
-/** Function which helps to create actions without mistakes */
+/** @internal Function which helps to create actions without mistakes */
export const action = (type: ActionType, payload?: T): Action => ({
type,
payload,
});
-/** Action that sets filters (Bulk operation for addFilter) */
-export const setFilters = (filters: any[][]) =>
- action(ActionType.SET_FILTERS, filters);
+/** @internal Action that sets filters (Bulk operation for addFilter) */
+export const setFilters = (filters: DtFilterValue[][]) =>
+ action(ActionType.SET_FILTERS, filters);
-/** Action that unsets a filter group */
+/** @internal Initial filters are set via binding on the quick filter so they are not in the value data format. */
+export const addInitialFilters = (filters: any[][]) =>
+ action(ActionType.ADD_INITIAL_FILTERS, filters);
+
+/** @internal Action that unsets a filter group */
export const unsetFilterGroup = (group: DtNodeDef) =>
action(ActionType.UNSET_FILTER_GROUP, group);
-/** Action that adds a filter */
-export const addFilter = (item: DtNodeDef) =>
- action(ActionType.ADD_FILTER, item);
+/** @internal Action that adds a filter */
+export const addFilter = (filter: DtNodeDef[]) =>
+ action(ActionType.ADD_FILTER, filter);
-/** Action that removes a filter */
-export const removeFilter = (item: DtNodeDef) =>
- action(ActionType.REMOVE_FILTER, item);
+/** @internal Action that removes a filter */
+export const removeFilter = (uid: string) =>
+ action(ActionType.REMOVE_FILTER, uid);
-/** Action that updates a filter */
-export const updateFilter = (item: DtNodeDef) =>
- action(ActionType.UPDATE_FILTER, item);
+/** @internal Action that updates a filter */
+export const updateFilter = (filter: DtNodeDef[]) =>
+ action(ActionType.UPDATE_FILTER, filter);
-/** Action that subscribes to a new data source */
+/** @internal Action that subscribes to a new data source */
export const switchDataSource = (item: DtFilterFieldDataSource) =>
action>(ActionType.SWITCH_DATA_SOURCE, item);
-/** Action that updates the data source */
+/** @internal Action that updates the data source */
export const updateDataSource = (nodeDef: DtNodeDef) =>
action(ActionType.UPDATE_DATA_SOURCE, nodeDef);
diff --git a/libs/barista-components/experimental/quick-filter/src/state/effects.ts b/libs/barista-components/experimental/quick-filter/src/state/effects.ts
index 6219d31479..9daa685703 100644
--- a/libs/barista-components/experimental/quick-filter/src/state/effects.ts
+++ b/libs/barista-components/experimental/quick-filter/src/state/effects.ts
@@ -19,26 +19,26 @@ import {
DtNodeDef,
} from '@dynatrace/barista-components/filter-field';
import { MonoTypeOperatorFunction, Observable } from 'rxjs';
-import { filter, map, switchMap } from 'rxjs/operators';
+import { filter, map, switchMap, take } from 'rxjs/operators';
import { Action, ActionType, updateDataSource } from './actions';
import { QuickFilterState } from './store';
-/** Type for an effect */
+/** @internal Type for an effect */
export type Effect = (
action$: Observable,
state$?: Observable,
) => Observable;
-/** Operator to filter actions */
+/** @internal Operator to filter actions */
export const ofType = (
...types: ActionType[]
): MonoTypeOperatorFunction> =>
filter((action: Action) => types.indexOf(action.type) > -1);
-/** Connects to a new Data dataSource */
+/** @internal Connects to a new Data dataSource */
export const switchDataSourceEffect: Effect = (action$: Observable) =>
action$.pipe(
ofType>(ActionType.SWITCH_DATA_SOURCE),
- switchMap((action) => action.payload!.connect()),
+ switchMap((action) => action.payload!.connect().pipe(take(1))),
map((nodeDef: DtNodeDef) => updateDataSource(nodeDef)),
);
diff --git a/libs/barista-components/experimental/quick-filter/src/state/reducer.spec.ts b/libs/barista-components/experimental/quick-filter/src/state/reducer.spec.ts
deleted file mode 100644
index e72639e769..0000000000
--- a/libs/barista-components/experimental/quick-filter/src/state/reducer.spec.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * @license
- * Copyright 2020 Dynatrace LLC
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { dtAutocompleteDef } from '@dynatrace/barista-components/filter-field';
-import { buildData } from './reducer';
-
-test('should build the data with an object', () => {
- const data = { name: 'Linz' };
- const autocomplete = dtAutocompleteDef(data, null, [], false, false, false);
- expect(buildData(autocomplete)).toMatchObject([data]);
-});
-
-test('should build the data with undefined', () => {
- const data = undefined;
- const autocomplete = dtAutocompleteDef(data, null, [], false, false, false);
-
- expect(buildData(autocomplete)).toMatchObject([data]);
-});
diff --git a/libs/barista-components/experimental/quick-filter/src/state/reducer.ts b/libs/barista-components/experimental/quick-filter/src/state/reducer.ts
index 761d82eb4d..8ff2e6bc61 100644
--- a/libs/barista-components/experimental/quick-filter/src/state/reducer.ts
+++ b/libs/barista-components/experimental/quick-filter/src/state/reducer.ts
@@ -14,10 +14,7 @@
* limitations under the License.
*/
import { DtLogger, DtLoggerFactory } from '@dynatrace/barista-components/core';
-import {
- DELIMITER,
- DtNodeDef,
-} from '@dynatrace/barista-components/filter-field';
+import { DtAutocompleteValue } from '@dynatrace/barista-components/filter-field';
import { Action, ActionType } from './actions';
import { initialState, QuickFilterState } from './store';
@@ -50,9 +47,16 @@ export function quickFilterReducer(
}
return { ...initialState, dataSource: action.payload };
case ActionType.UPDATE_DATA_SOURCE:
- return { ...state, nodeDef: action.payload };
+ return {
+ ...state,
+ filters: [],
+ nodeDef: action.payload,
+ };
case ActionType.SET_FILTERS:
- return { ...state, filters: action.payload };
+ // reset the initial filters as we have already applied them if there are one.
+ return { ...state, filters: action.payload, initialFilters: undefined };
+ case ActionType.ADD_INITIAL_FILTERS:
+ return { ...state, initialFilters: action.payload };
case ActionType.UNSET_FILTER_GROUP:
return {
...state,
@@ -77,13 +81,19 @@ export function quickFilterReducer(
// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
/** @internal Add a filter to the filters array */
-export function addFilter(filters: any[][], item: DtNodeDef): any[][] {
- return [...filters, buildData(item)];
+export function addFilter(
+ filters: DtAutocompleteValue[][],
+ filter: DtAutocompleteValue[],
+): DtAutocompleteValue[][] {
+ return [...filters, filter];
}
/** @internal Remove a filter from the filters array */
-export function removeFilter(filters: any[][], item: DtNodeDef): any[][] {
- const index = findSelectedOption(filters, item, false);
+export function removeFilter(
+ filters: DtAutocompleteValue[][],
+ uid: string,
+): DtAutocompleteValue[][] {
+ const index = findSelectedOption(filters, uid, false);
const updatedState = [...filters];
if (index > -1) {
@@ -94,70 +104,48 @@ export function removeFilter(filters: any[][], item: DtNodeDef): any[][] {
}
/** @internal Update a filter inside the filters array */
-export function updateFilter(filters: any[][], item: DtNodeDef): any[][] {
- const index = findSelectedOption(filters, item, true);
-
+export function updateFilter(
+ filters: DtAutocompleteValue[][],
+ filter: DtAutocompleteValue[],
+): DtAutocompleteValue[][] {
+ const uid = filter[filter.length - 1].option.uid;
+ const index = findSelectedOption(filters, uid, true);
+ // if the filter is not in the filters list add it
if (index < 0) {
- return addFilter(filters, item);
+ return addFilter(filters, filter);
}
-
- filters[index] = buildData(item);
-
+ // replace the existing filter
+ filters[index] = filter;
return filters;
}
/** @internal Remove a group from the filters array */
-export function unsetFilterGroup(filters: any[][], group: DtNodeDef): any[][] {
- if (group.option && group.option.viewValue) {
- return filters.filter(
- (filter) => filter[0].name !== group.option!.viewValue,
- );
- }
- return filters;
-}
-
-/** @internal Build the quick filter data out of a node definition */
-export function buildData(item: DtNodeDef): any[] {
- const data = [item.data];
-
- if (item.option && item.option.parentAutocomplete) {
- data.unshift(item.option.parentAutocomplete.data);
- }
- return data;
+export function unsetFilterGroup(
+ filters: DtAutocompleteValue[][],
+ group: DtAutocompleteValue,
+): DtAutocompleteValue[][] {
+ const index = findSelectedOption(filters, group.option.uid, true);
+ return filters.filter((_, i) => i !== index);
}
/** @internal Find a filter inside the filters array based on a NodeDef */
export function findSelectedOption(
- filters: any[][],
- item: DtNodeDef,
+ filters: DtAutocompleteValue[][],
+ uid: string | null,
distinct: boolean = false,
): number {
return filters.findIndex((path) => {
- if (item.option && item.option.uid) {
- // split the uid in the parts that define the path
- // (like the user clicked through in the filter field)
- const parts = item.option.uid.split(DELIMITER);
-
- // if the option is distinct we only have to check for the groups name because
- // there can only be one distinct option selected so we know immediately if it is selected
- if (distinct && parts[0] === path[0].name) {
- return true;
- }
-
- // if it is not distinct we have to build the full path out of the current filters to check
- // wether the path matches them provided node definition
- const dataPath = path.reduce(
- (previousValue, currentValue) =>
- `${previousValue.name}${DELIMITER}${currentValue.name}${DELIMITER}`,
- );
-
- // if the built path for the filters inside the array is equal to the option
- // in the provided nodeDef then we found our selected option
- if (item.option.uid === dataPath) {
- return true;
- }
+ if (!uid) {
+ return false;
+ }
+ // if the option is distinct we only have to check for the groups name because
+ // there can only be one distinct option selected so we know immediately if it is selected
+ if (distinct && uid.startsWith(path[0].option.uid!)) {
+ return true;
+ }
+ // if the last items uid in the path is equal to the uid, then we found our option.
+ if (uid === path[path.length - 1].option.uid) {
+ return true;
}
-
- return false;
});
}
diff --git a/libs/barista-components/experimental/quick-filter/src/state/selectors.ts b/libs/barista-components/experimental/quick-filter/src/state/selectors.ts
index 5f90dad464..f2a2d9bb83 100644
--- a/libs/barista-components/experimental/quick-filter/src/state/selectors.ts
+++ b/libs/barista-components/experimental/quick-filter/src/state/selectors.ts
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { isDefined } from '@dynatrace/barista-components/core';
import {
applyDtOptionIds,
DtNodeDef,
isDtAutocompleteDef,
} from '@dynatrace/barista-components/filter-field';
import { Observable } from 'rxjs';
-import { filter, map, pluck, tap, withLatestFrom } from 'rxjs/operators';
+import { filter, map, tap, withLatestFrom } from 'rxjs/operators';
import { DtQuickFilterDataSource } from '../quick-filter-data-source';
import { QuickFilterState } from './store';
-import { isDefined } from '@dynatrace/barista-components/core';
/** @internal Select all autocompletes from the root Node Def from the store */
export const getAutocompletes = (
@@ -35,7 +35,7 @@ export const getAutocompletes = (
applyDtOptionIds(state.nodeDef);
}
}),
- pluck('nodeDef'),
+ map(({ nodeDef }) => nodeDef),
filter((state) => isDefined(state) && isDtAutocompleteDef(state)),
withLatestFrom(
getDataSource(state$).pipe(filter(Boolean)),
@@ -51,8 +51,12 @@ export const getAutocompletes = (
/** @internal Select the data Source from the store */
export const getDataSource = (state$: Observable) =>
- state$.pipe(pluck('dataSource'));
+ state$.pipe(map((state) => state?.dataSource));
/** @internal Select the actual applied filters */
export const getFilters = (state$: Observable) =>
- state$.pipe(pluck('filters'));
+ state$.pipe(map(({ filters }) => filters));
+
+/** @internal Get the initial filters */
+export const getInitialFilters = (state$: Observable) =>
+ state$.pipe(map(({ initialFilters }) => initialFilters));
diff --git a/libs/barista-components/experimental/quick-filter/src/state/store.ts b/libs/barista-components/experimental/quick-filter/src/state/store.ts
index 723e10cc3f..95c70ff365 100644
--- a/libs/barista-components/experimental/quick-filter/src/state/store.ts
+++ b/libs/barista-components/experimental/quick-filter/src/state/store.ts
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-import { DtNodeDef } from '@dynatrace/barista-components/filter-field';
+import {
+ DtAutocompleteValue,
+ DtNodeDef,
+} from '@dynatrace/barista-components/filter-field';
import { BehaviorSubject, merge, Observable } from 'rxjs';
import { map, shareReplay, withLatestFrom } from 'rxjs/operators';
import { DtQuickFilterDataSource } from '../quick-filter-data-source';
@@ -28,8 +31,10 @@ export interface QuickFilterState {
nodeDef?: DtNodeDef;
/** The dataSource that is connected with the QuickFilter */
dataSource?: DtQuickFilterDataSource;
- /** Array of all active filters */
- filters: any[][];
+ /** Array of all active filter values (internal filter representation of the filter field) */
+ filters: DtAutocompleteValue[][];
+ /** Initial Filter array that might be added via a binding to the quick filter */
+ initialFilters?: any[][];
}
/** @internal The initial QuickFilter state */
diff --git a/libs/barista-components/filter-field/index.ts b/libs/barista-components/filter-field/index.ts
index 01c80ff7e2..fd59ed5790 100644
--- a/libs/barista-components/filter-field/index.ts
+++ b/libs/barista-components/filter-field/index.ts
@@ -53,5 +53,7 @@ export {
isDtAutocompleteValue,
isDtFreeTextValue,
isDtRangeValue,
+ DtAutocompleteValue,
+ _getSourcesOfDtFilterValues,
} from './src/types';
export { DT_FILTER_VALUES_PARSER_CONFIG } from './src/filter-field-config';
diff --git a/libs/barista-components/filter-field/src/filter-field.ts b/libs/barista-components/filter-field/src/filter-field.ts
index d4c5afbc1a..763a7e377b 100644
--- a/libs/barista-components/filter-field/src/filter-field.ts
+++ b/libs/barista-components/filter-field/src/filter-field.ts
@@ -261,6 +261,10 @@ export class DtFilterField
this._tryApplyFilters(value);
this._changeDetectorRef.markForCheck();
}
+ /** @internal */
+ get _filterValues(): DtFilterValue[][] {
+ return this._filters;
+ }
private _filters: DtFilterValue[][] = [];
/** Set the Aria-Label attribute */