TSK-1473: Always display classification and workbasket filter (#1357)

* TSK-1473: Always display classification and workbasket filter

* TSK-1473: fix jest test

Co-authored-by: Chi Nguyen <c.nguyen.prog@gmail.com>
This commit is contained in:
Sofie Hofmann 2020-12-08 18:05:38 +01:00 committed by GitHub
parent fc6f420b96
commit f76b0c4522
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 161 additions and 116 deletions

View File

@ -3,28 +3,28 @@
<!-- ACTION TOOLBAR -->
<section class="classification-list__action-toolbar">
<div class="classification-list__action-buttons">
<!-- ADD BUTTON -->
<button mat-flat-button class="action-toolbar__add-button mr-1" matTooltip="Create new classification"
(click)="addClassification()">
Add
<mat-icon class="md-20">add</mat-icon>
</button>
<!-- IMPORT EXPORT BUTTONS -->
<taskana-administration-import-export
class="classification-list__import-export" [currentSelection]="taskanaType.CLASSIFICATIONS" [parentComponent]="'classifications'">
</taskana-administration-import-export>
<span class="workbasket-details__spacer" style="flex: 1 1 auto"> </span>
<button mat-stroked-button matTooltip="Display filter options" (click)="displayFilter()" style="color: #555">
<mat-icon *ngIf="!showFilter">filter_list</mat-icon>
<mat-icon *ngIf="showFilter">keyboard_arrow_up</mat-icon>
</button>
</div>
<!-- FILTER -->
<div class="classification-list__filter" *ngIf="showFilter">
<div class="classification-list__filter">
<!-- CATEGORY FILTER -->
<div class="classification-list__category-filter">
<button mat-icon-button [matMenuTriggerFor]="menu"
matTooltip="Filter Category" class="category-filter__filter-button">
<button mat-stroked-button class="category-filter__filter-button" [matMenuTriggerFor]="menu"
matTooltip="Filter Category">
<mat-icon *ngIf="selectedCategory == ''">filter_list</mat-icon>
<svg-icon class="category-filter__icons" [src]="(getCategoryIcon(selectedCategory) | async)?.name"
[title]="(getCategoryIcon(selectedCategory) | async)?.text"
@ -46,6 +46,7 @@
</mat-menu>
</div>
<!-- FILTER INPUT FIELD -->
<div class="filter__input">
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
<mat-label>Filter classification</mat-label>
@ -53,9 +54,11 @@
</mat-form-field>
</div>
<!-- TYPE FILTER -->
<taskana-administration-classification-types-selector
class="pull-right">
</taskana-administration-classification-types-selector>
</div>
</section>

View File

@ -4,16 +4,16 @@
height: calc(100vh - 55px);
}
.classification-list__action-toolbar {
padding: 0 16px;
min-height: 68px;
padding: 16px 16px 0;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.classification-list__action-buttons {
display: flex;
border: none;
margin-bottom: 0;
padding: 16px 4px 8px 4px;
padding: 0 4px;
}
.action-toolbar__add-button {
background-color: $aquamarine;
color: white;
@ -24,11 +24,12 @@
}
.classification-list__filter {
display: flex;
padding: 7px 4px 0;
}
.classification-list__category-filter {
padding-top: 7px;
}
.category-filter__icons {
height: 33px;
width: 16px;
@ -39,6 +40,11 @@
margin: 0;
top: -2px;
}
.category-filter__filter-button {
color: #555;
top: 3px;
margin-right: 12px;
}
.filter__input {
width: 100%;

View File

@ -144,9 +144,7 @@ describe('ClassificationListComponent', () => {
expect(debugElement.nativeElement.querySelector('taskana-administration-import-export')).toBeTruthy();
});
it('should display classification-types-selector component when showFilter is true', () => {
component.showFilter = true;
fixture.detectChanges();
it('should display classification-types-selector component', () => {
const typesSelectorComponent = debugElement.nativeElement.querySelector(
'taskana-administration-classification-types-selector'
);
@ -154,25 +152,19 @@ describe('ClassificationListComponent', () => {
});
/* HTML: FILTER */
it('should display filter input field when showFilter is true', () => {
component.showFilter = true;
fixture.detectChanges();
it('should display filter input field', () => {
const button = debugElement.nativeElement.querySelector('.filter__input-field');
expect(button).toBeTruthy();
expect(button.textContent).toBe('Filter classification');
});
it('should display filter button when showFilter is true', () => {
component.showFilter = true;
fixture.detectChanges();
it('should display filter button', () => {
const button = debugElement.nativeElement.querySelector('.category-filter__filter-button');
expect(button).toBeTruthy();
expect(button.textContent).toBe('filter_list');
});
it('should change selectedCategory property when button is clicked', () => {
component.showFilter = true;
fixture.detectChanges();
const filterButton = debugElement.nativeElement.querySelector('.category-filter__filter-button');
filterButton.click();
fixture.detectChanges();
@ -184,8 +176,6 @@ describe('ClassificationListComponent', () => {
});
it('should display list of categories which can be selected', () => {
component.showFilter = true;
fixture.detectChanges();
const filterButton = debugElement.nativeElement.querySelector('.category-filter__filter-button');
filterButton.click();
fixture.detectChanges();

View File

@ -30,7 +30,6 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
requestInProgress = true;
inputValue: string;
selectedCategory = '';
showFilter = false;
@Select(ClassificationSelectors.classificationTypes) classificationTypes$: Observable<string[]>;
@Select(ClassificationSelectors.selectedClassificationType) classificationTypeSelected$: Observable<string>;
@ -112,10 +111,6 @@ export class ClassificationListComponent implements OnInit, OnDestroy {
this.selectedCategory = category;
}
displayFilter() {
this.showFilter = !this.showFilter;
}
setRequestInProgress(value: boolean) {
this.requestInProgressService.setRequestInProgress(value);
}

View File

@ -19,14 +19,19 @@
style="margin-right: 4px;" [sortingFields]="sortingFields" (performSorting)="sorting($event)" [defaultSortBy]="workbasketDefaultSortBy">
</taskana-shared-sort>
<!-- FILTER -->
<button mat-stroked-button class="workbasket-list-toolbar__filter-button" matTooltip="Display filter options" (click)="onClickFilter()">
<mat-icon *ngIf="!showFilter">filter_list</mat-icon>
<mat-icon *ngIf="showFilter">keyboard_arrow_up</mat-icon>
</div>
<!-- FILTER -->
<div class="workbasket-list-toolbar__filter">
<button mat-stroked-button class="filter__filter-button" matTooltip="Display more filter options" (click)="onClickFilter()">
<mat-icon *ngIf="!isExpanded">keyboard_arrow_down</mat-icon>
<mat-icon *ngIf="isExpanded">keyboard_arrow_up</mat-icon>
</button>
<div class="filter__filter-component-wrapper">
<taskana-shared-filter [isExpanded]="isExpanded" (performFilter)="filtering($event)"></taskana-shared-filter>
</div>
</div>
<taskana-shared-filter *ngIf="showFilter" (performFilter)="filtering($event)" component="workbasket-list" (inputComponent)="setComponent($event)"></taskana-shared-filter>
</div>

View File

@ -1,9 +1,8 @@
@import 'src/theme/_colors.scss';
.workbasket-list-toolbar {
padding: 16px 16px 8px 16px;
padding: 16px 16px 20px;
flex-wrap: wrap;
min-height: 68px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
@ -27,8 +26,18 @@
height: 36px;
}
.workbasket-list-toolbar__filter-button {
.workbasket-list-toolbar__filter {
display: flex;
justify-content: space-between;
padding: 16px 8px 0 4px;
}
.filter__filter-button {
padding: 0 5px;
color: #555;
height: 36px;
}
.filter__filter-component-wrapper {
width: 380px;
}

View File

@ -41,6 +41,7 @@ class SortStub {
@Component({ selector: 'taskana-shared-filter', template: '' })
class FilterStub {
@Input() isExpanded = false;
@Output() performFilter = new EventEmitter<Filter>();
}
@ -149,15 +150,20 @@ describe('WorkbasketListToolbarComponent', () => {
expect(debugElement.nativeElement.querySelector('taskana-shared-sort')).toBeTruthy();
});
it('should show filter component only when filter button is clicked', () => {
const button = debugElement.nativeElement.querySelector('.workbasket-list-toolbar__filter-button');
expect(button).toBeTruthy();
expect(button.textContent).toBe('filter_list');
expect(debugElement.nativeElement.querySelector('filter')).toBeFalsy();
button.click();
fixture.detectChanges();
expect(component.showFilter).toBe(true);
expect(button.textContent).toBe('keyboard_arrow_up');
it('should display filter component', () => {
expect(debugElement.nativeElement.querySelector('taskana-shared-filter')).toBeTruthy();
});
it('should show expanded filter component only when filter button is clicked', () => {
const button = debugElement.nativeElement.querySelector('.filter__filter-button');
expect(button).toBeTruthy();
button.click();
fixture.detectChanges();
expect(component.isExpanded).toBe(true);
expect(button.textContent).toBe('keyboard_arrow_up');
button.click();
fixture.detectChanges();
expect(component.isExpanded).toBe(false);
expect(button.textContent).toBe('keyboard_arrow_down');
});
});

View File

@ -42,6 +42,7 @@ export class WorkbasketListToolbarComponent implements OnInit {
filterParams = { name: '', key: '', type: '', description: '', owner: '' };
filterType = TaskanaType.WORKBASKETS;
isExpanded = false;
showFilter = false;
component = '';
@ -80,8 +81,8 @@ export class WorkbasketListToolbarComponent implements OnInit {
}
onClickFilter() {
this.showFilter = !this.showFilter;
this.workbasketService.expandWorkbasketActionToolbar(this.showFilter);
this.isExpanded = !this.isExpanded;
this.workbasketService.expandWorkbasketActionToolbar(this.isExpanded);
}
ngOnDestroy() {

View File

@ -6,7 +6,7 @@
align-items: stretch;
}
taskana-administration-workbasket-list {
width: 500px;
min-width: 500px;
}
taskana-administration-workbasket-details {
flex-grow: 1;

View File

@ -1,7 +1,28 @@
<div>
<!-- WORKBASKET FILTER -->
<div *ngIf="filterTypeIsWorkbasket(); else taskType">
<!-- WORKBASKET FILTER -->
<div class="filter" *ngIf="filterTypeIsWorkbasket(); else tasktype">
<!-- COLLAPSED WORKBASKET FILTER -->
<div class="filter__collapsed-filter" *ngIf="!isExpanded">
<!-- TEXT INPUT -->
<mat-form-field appearance="legacy" floatLabel="auto" class="collapsed-filter_input-field">
<mat-label>Filter by name</mat-label>
<input matInput [(ngModel)]="filter.filterParams.name" matTooltip="Type to filter by name" (keyup.enter)="search()">
</mat-form-field>
<!-- CLEAR BUTTON -->
<button mat-stroked-button (click)="clear(); search()" matTooltip="Clear workbasket filter" class="filter__undo-button">
<mat-icon style="color: #555">undo</mat-icon>
</button>
<!-- SEARCH BUTTON -->
<button mat-stroked-button (click)="search()" matTooltip="Search by given filter" class="filter__search-button">
<mat-icon>search</mat-icon>
</button>
</div>
<!-- EXPANDED WORKBASKET FILTER -->
<div class="filter" *ngIf="isExpanded">
<!-- TEXT INPUT -->
<div class="filter__text-input">
@ -17,15 +38,17 @@
</mat-form-field>
</div>
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
<mat-label>Filter by description</mat-label>
<input matInput [(ngModel)]="filter.filterParams.description" matTooltip="Type to filter by description" (keyup.enter)="search()">
</mat-form-field>
<div class="filter__name-and-key-input">
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
<mat-label>Filter by description</mat-label>
<input matInput [(ngModel)]="filter.filterParams.description" matTooltip="Type to filter by description" (keyup.enter)="search()">
</mat-form-field>
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
<mat-label>Filter by owner</mat-label>
<input matInput [(ngModel)]="filter.filterParams.owner" matTooltip="Type to filter by owner" (keyup.enter)="search()">
</mat-form-field>
<mat-form-field appearance="legacy" floatLabel="auto" class="filter__input-field">
<mat-label>Filter by owner</mat-label>
<input matInput [(ngModel)]="filter.filterParams.owner" matTooltip="Type to filter by owner" (keyup.enter)="search()">
</mat-form-field>
</div>
</div>
@ -47,59 +70,58 @@
<!-- CLEAR BUTTON -->
<button mat-stroked-button (click)="clear(); search()" matTooltip="Clear workbasket filter">
Reset filter
Reset
<mat-icon style="color: #555">undo</mat-icon>
</button>
<!-- SEARCH BUTTON -->
<button mat-stroked-button (click)="search()" matTooltip="Search by given filter" class="filter__search-button">
Apply filter
Apply
<mat-icon>search</mat-icon>
</button>
</div>
</div>
<!-- TASK FILTER -->
<ng-template #tasktype>
<div class="row">
<div class="col-xs-2">
<taskana-shared-number-picker [(ngModel)]="filter.filterParams.priority" (keyup.enter)="search()" title="priority" id="display-priority-filter"></taskana-shared-number-picker>
</div>
<div class="col-xs-4">
<input type="text" [(ngModel)]="filter.filterParams.name" (keyup.enter)="search()" class="form-control" id="display-name-filter"
placeholder="Filter name">
</div>
<div class="col-xs-4">
<input type="text" [(ngModel)]="filter.filterParams.owner" (keyup.enter)="search()" class="form-control" id="display-owner-filter"
placeholder="Filter owner">
</div>
<button (click)="clear(); search()" class="btn btn-default pull-right margin-right" type="button" data-toggle="tooltip"
title="Clear">
<span class="material-icons md-20 blue">clear</span>
</button>
</div>
<div class="row">
<div class="dropdown col-xs-2 col-xs-offset-2">
<button class="btn btn-default" data-toggle="dropdown" type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="true" title="State: {{filter.filterParams.state ? filter.filterParams?.state : 'All'}}">
<span>{{filter.filterParams.state ? filter.filterParams?.state : 'All'}}</span>
</button>
<ul class="dropdown-menu dropdown-menu-users" role="menu">
<li>
<a *ngFor="let state of allStates | mapValues" type="button" (click)="selectState(state.key); search()"
data-toggle="tooltip" [title]="state.value">
<label class="blue">{{state.value}}</label>
</a>
</li>
</ul>
</div>
<button (click)="search()" type="button" class="btn btn-default pull-right margin-right" data-toggle="tooltip"
title="Search">
<span class="material-icons md-20 blue">search</span>
</button>
</div>
</ng-template>
</div>
<!-- TASK FILTER -->
<ng-template #taskType>
<div class="row">
<div class="col-xs-2">
<taskana-shared-number-picker [(ngModel)]="filter.filterParams.priority" (keyup.enter)="search()" title="priority" id="display-priority-filter"></taskana-shared-number-picker>
</div>
<div class="col-xs-4">
<input type="text" [(ngModel)]="filter.filterParams.name" (keyup.enter)="search()" class="form-control" id="display-name-filter"
placeholder="Filter name">
</div>
<div class="col-xs-4">
<input type="text" [(ngModel)]="filter.filterParams.owner" (keyup.enter)="search()" class="form-control" id="display-owner-filter"
placeholder="Filter owner">
</div>
<button (click)="clear(); search()" class="btn btn-default pull-right margin-right" type="button" data-toggle="tooltip"
title="Clear">
<span class="material-icons md-20 blue">clear</span>
</button>
</div>
<div class="row">
<div class="dropdown col-xs-2 col-xs-offset-2">
<button class="btn btn-default" data-toggle="dropdown" type="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="true" title="State: {{filter.filterParams.state ? filter.filterParams?.state : 'All'}}">
<span>{{filter.filterParams.state ? filter.filterParams?.state : 'All'}}</span>
</button>
<ul class="dropdown-menu dropdown-menu-users" role="menu">
<li>
<a *ngFor="let state of allStates | mapValues" type="button" (click)="selectState(state.key); search()"
data-toggle="tooltip" [title]="state.value">
<label class="blue">{{state.value}}</label>
</a>
</li>
</ul>
</div>
<button (click)="search()" type="button" class="btn btn-default pull-right margin-right" data-toggle="tooltip"
title="Search">
<span class="material-icons md-20 blue">search</span>
</button>
</div>
</ng-template>

View File

@ -1,15 +1,21 @@
@import 'src/theme/_colors.scss';
.filter {
margin: -10px 0 0 8px;
display: flex;
flex-direction: column;
justify-content: space-between;
margin: 10px;
}
.filter__text-input {
.filter__collapsed-filter {
display: flex;
flex-direction: column;
height: 36px;
}
.collapsed-filter_input-field {
top: -10px;
flex-grow: 1;
margin: 0 8px 0 8px;
}
.filter__action-buttons {
@ -18,7 +24,9 @@
display: flex;
justify-content: space-between;
}
.filter__undo-buttons {
margin-left: 4px;
}
.filter__search-button {
background: $aquamarine;
color: white;
@ -38,14 +46,15 @@
}
.list-group-search {
padding: 0px 15px;
padding: 0 15px;
border-top: 1px solid #ddd;
}
.list-group-search {
border-top: none;
}
row.padding {
padding: 1px 0px;
padding: 1px 0;
}
.filter-list {

View File

@ -26,9 +26,8 @@ export class FilterComponent implements OnInit {
]);
@Input() filterParams = { name: '', key: '', type: '', description: '', owner: '' };
@Input() filterType = TaskanaType.WORKBASKETS;
@Input() isExpanded = true;
@Output() performFilter = new EventEmitter<Filter>();
@Output() inputComponent = new EventEmitter<string>();