Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
feat(quick-filter): Add a new quick filter component inside an experi…
Browse files Browse the repository at this point in the history
…mental package.

The quick filter component is used to provide a quick way to operate with the filter field inside a sidebar.
Inside the quick filter only an autocomplete with simple options can be displayed.

This is part of an experimental package and not meant to be used inside production.
The experimental package does not follow semantic versioning like the rest of the library does.

This means that we might break the api in every version. It is only meant to be used for testing and feedback purpose.

Fixes #453
Fixes #254
  • Loading branch information
Lukas Holzer committed Mar 3, 2020
1 parent a61c8d6 commit 3d37098
Show file tree
Hide file tree
Showing 47 changed files with 2,085 additions and 12 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"editor.tabSize": 2,
"files.eol": "\n",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"explorer.compactFolders": false
}
38 changes: 38 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -2524,6 +2524,44 @@
},
"schematics": {}
},
"quick-filter": {
"projectType": "library",
"root": "components/experimental/quick-filter",
"sourceRoot": "components/experimental/quick-filter/src",
"prefix": "dt",
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"components/experimental/quick-filter/tsconfig.lib.json",
"components/experimental/quick-filter/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**",
"!components/experimental/quick-filter/**"
]
}
},
"lint-styles": {
"builder": "@dynatrace/barista-builders:stylelint",
"options": {
"stylelintConfig": ".stylelintrc",
"reportFile": "dist/stylelint/report.xml",
"exclude": ["**/node_modules/**"],
"files": ["components/experimental/quick-filter/**/*.scss"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "components/experimental/quick-filter/jest.config.js",
"tsConfig": "components/experimental/quick-filter/tsconfig.spec.json",
"setupFile": "components/experimental/quick-filter/src/test-setup.ts"
}
}
}
},
"radial-chart": {
"projectType": "library",
"root": "components/radial-chart",
Expand Down
13 changes: 13 additions & 0 deletions apps/components-e2e/src/app/app.routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

export const routes: Routes = [
{
// TODO: remove only for testing
path: '',
pathMatch: 'full',
redirectTo: '/quick-filter',
},
{
path: 'autocomplete',
loadChildren: () =>
Expand Down Expand Up @@ -131,6 +137,13 @@ export const routes: Routes = [
module => module.DtE2EProgressBarModule,
),
},
{
path: 'quick-filter',
loadChildren: () =>
import('../components/quick-filter/quick-filter.module').then(
module => module.DtE2EQuickFilterModule,
),
},
{
path: 'radial-chart',
loadChildren: () =>
Expand Down
15 changes: 10 additions & 5 deletions apps/components-e2e/src/components/filter-field/filter-field.po.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ import { Selector, t, ClientFunction } from 'testcafe';

export const errorBox = Selector('.dt-filter-field-error');
export const filterField = Selector('#filter-field');
export const option = (nth: number) => Selector(`.dt-option:nth-child(${nth})`);
export const option = (nth: number) => Selector('.dt-option').nth(nth);
export const clearAll = Selector('.dt-filter-field-clear-all-button');
export const filterTags = Selector('dt-filter-field-tag');
export const tagOverlay = Selector('.dt-overlay-container');

/** Selector for the delete button (x) on a filter with a specific text */
export const tagDeleteButton = (filterText: string) =>
Selector('dt-filter-field-tag')
.withText(filterText)
.child('.dt-filter-field-tag-button');

export const input = Selector('input');

export const switchToFirstDatasource = Selector('#switchToFirstDatasource');
Expand All @@ -36,17 +42,16 @@ export function clickOption(
const controller = testController || t;

return controller
.click(filterField, { speed: 0.2 })
.wait(250)
.click(option(nth), { speed: 0.2 });
.click(filterField, { speed: 0.4 })
.click(option(nth), { speed: 0.4 });
}

/** Focus the input of the filter field to send key events to it. */
export const focusFilterFieldInput = ClientFunction(() => {
(document.querySelector('#filter-field input') as HTMLElement).focus();
});

/** Retreive all set tags in the filter field and their values. */
/** Retrieve all set tags in the filter field and their values. */
export const getFilterfieldTags = ClientFunction(() => {
const filterFieldTags: HTMLElement[] = [].slice.call(
document.querySelectorAll('.dt-filter-field-tag'),
Expand Down
10 changes: 9 additions & 1 deletion apps/components-e2e/src/components/filter-field/filter-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,18 @@ const TEST_DATA_2 = {
unit: 's',
},
},
{
name: 'Not in Quickfilter',
autocomplete: [
{ name: 'Option1' },
{ name: 'Option2' },
{ name: 'Option3' },
],
},
],
};

const DATA = [TEST_DATA, TEST_DATA_2];
export const DATA = [TEST_DATA, TEST_DATA_2];

@Component({
selector: 'dt-e2e-filter-field',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<dt-quick-filter [dataSource]="_dataSource" [filters]="_initialFilters">
<dt-quick-filter-title>Quick-filter</dt-quick-filter-title>
<dt-quick-filter-sub-title>
All options in the filter field above
</dt-quick-filter-sub-title>

my content
</dt-quick-filter>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @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 { DtFilterFieldDefaultDataSource } from '@dynatrace/barista-components/filter-field';
import { DATA } from '../../filter-field/filter-field';

@Component({
selector: 'dt-e2e-quick-filter',
templateUrl: 'quick-filter-initial-data.html',
})
export class DtE2EQuickFilterInitialData {
_dataSource = new DtFilterFieldDefaultDataSource(DATA[1]);

_initialFilters = [
[DATA[1].autocomplete[0], DATA[1].autocomplete[0].autocomplete![1]],
[DATA[1].autocomplete[1], DATA[1].autocomplete[1].autocomplete![2]],
];

constructor() {
console.log(this._initialFilters);
}
}
157 changes: 157 additions & 0 deletions apps/components-e2e/src/components/quick-filter/quick-filter.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* @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 { waitForAngular } from '../../utils/wait-for-angular';
import {
clearAll,
getFilterfieldTags,
tagDeleteButton,
} from '../filter-field/filter-field.po';
import {
getGroupItem,
getGroupItemInput,
getSelectedItem,
} from './quick-filter.po';

fixture('Quick Filter')
.page('http://localhost:4200/quick-filter')
.meta({
'filter-field': true,
'quick-filter': true,
drawer: true,
checkbox: true,
radio: true,
})
.beforeEach(async (testController: TestController) => {
await testController.resizeWindow(1200, 800);
await waitForAngular();
});

test('if nothing is selected the distinct should be set to all', async (testController: TestController) => {
await testController
.expect(getSelectedItem('AUT').textContent)
.match(/Any/)
.expect(getSelectedItem('USA').exists)
.notOk();
});

test('if nothing is selected the filter friedl should be empty', async (testController: TestController) => {
await testController.expect(getFilterfieldTags()).eql([]);
});

test('if distinct option gets updated it should update the filter field', async (testController: TestController) => {
await testController
.expect(getFilterfieldTags())
.eql([])
.click(getGroupItem('AUT', 'Linz'), { speed: 0.3 })
.expect(getFilterfieldTags())
.eql(['AUTLinz'])
.expect(getSelectedItem('AUT').textContent)
.match(/Linz/)
.click(getGroupItem('AUT', 'Graz'), { speed: 0.3 })
.expect(getFilterfieldTags())
.eql(['AUTGraz'])
.expect(getSelectedItem('AUT').textContent)
.match(/Graz/);
});

test('if it is possible to select multiple options', async (testController: TestController) => {
await testController
.click(getGroupItem('USA', 'San Francisco'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco'])
.click(getGroupItem('USA', 'Los Angeles'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco', 'USALos Angeles'])
.click(getGroupItem('USA', 'New York'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco', 'USALos Angeles', 'USANew York']);
});

test('if it is possible to select and deselect multiple options', async (testController: TestController) => {
await testController
.click(getGroupItem('USA', 'San Francisco'))
.click(getGroupItem('USA', 'Los Angeles'))
.click(getGroupItem('USA', 'New York'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco', 'USALos Angeles', 'USANew York'])
.click(getGroupItem('USA', 'San Francisco'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USALos Angeles', 'USANew York']);
});

test('if it is possible to reset all filters via the filter fields clearAll button', async (testController: TestController) => {
await testController
.click(getGroupItem('USA', 'San Francisco'))
.click(getGroupItem('USA', 'Los Angeles'))
.click(getGroupItem('USA', 'New York'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco', 'USALos Angeles', 'USANew York'])
.click(clearAll, { speed: 0.4 })
.expect(getFilterfieldTags())
.eql([])
.expect(getSelectedItem('USA').exists)
.notOk();
});

test('if it is possible to delete an option via the filter field', async (testController: TestController) => {
await testController
.click(getGroupItem('USA', 'San Francisco'))
.click(getGroupItem('USA', 'Los Angeles'))
.click(getGroupItem('USA', 'New York'), { speed: 0.4 })
.click(tagDeleteButton('Los Angeles'), { speed: 0.4 })
.expect(getFilterfieldTags())
.eql(['USASan Francisco', 'USANew York'])
.expect(getGroupItemInput('USA', 'New York').checked)
.ok()
.expect(getGroupItemInput('USA', 'Los Angeles').checked)
.notOk();
});

fixture('Quick Filter with initial Data')
.page('http://localhost:4200/quick-filter/initial-data')
.beforeEach(async (testController: TestController) => {
await testController.resizeWindow(1200, 800);
await waitForAngular();
});

test('if the initial filter in the filter field reflects the quick filter state', async (testController: TestController) => {
await testController
.expect(getSelectedItem('AUT').textContent)
.match(/Vienna/)
.expect(getSelectedItem('USA').textContent)
.match(/New York/);
});

test('if the initial filters are reflected in the filter field ', async (testController: TestController) => {
await testController
.expect(getFilterfieldTags())
.eql(['AUTVienna', 'USANew York']);
});

test('if a distinct group get set to any remove the group from the filter', async (testController: TestController) => {
await testController
.expect(getFilterfieldTags())
.eql(['AUTVienna', 'USANew York'])
.click(getGroupItem('AUT', 'Any'), { speed: 0.3 })
.expect(getFilterfieldTags())
.eql(['USANew York'])
.click(getGroupItem('AUT', 'Graz'), { speed: 0.3 })
.expect(getFilterfieldTags())
.eql(['USANew York', 'AUTGraz'])
.expect(getSelectedItem('AUT').textContent)
.match(/Graz/);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @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 { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
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';

const routes: Route[] = [
{ path: '', component: DtE2EQuickFilter },
{ path: 'initial-data', component: DtE2EQuickFilterInitialData },
];

@NgModule({
declarations: [DtE2EQuickFilter, DtE2EQuickFilterInitialData],
imports: [CommonModule, RouterModule.forChild(routes), DtQuickFilterModule],
exports: [],
providers: [],
})
export class DtE2EQuickFilterModule {}
Loading

0 comments on commit 3d37098

Please sign in to comment.