TSK-1411: Update Update Workbasket Access Items Component MD (#1310)
* TSK-1411: updated workbasket access items functionality to work with new action bar * TSK-1411: updated new design, accessibility, tests for workbasket access items * TSK-1411: remove container ripple effect which causes discrepancy between firefox and chromium * TSK-1411: minor CSS update
This commit is contained in:
parent
356e41ea27
commit
a139940bd2
|
@ -46,6 +46,8 @@ import { MatDividerModule } from '@angular/material/divider';
|
|||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
|
||||
const MODULES = [
|
||||
CommonModule,
|
||||
|
@ -93,7 +95,9 @@ const DECLARATIONS = [
|
|||
MatDividerModule,
|
||||
MatListModule,
|
||||
MatProgressBarModule,
|
||||
MatToolbarModule
|
||||
MatToolbarModule,
|
||||
MatCheckboxModule,
|
||||
MatRippleModule
|
||||
],
|
||||
providers: [
|
||||
ClassificationDefinitionService,
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
color: $invalid;
|
||||
}
|
||||
|
||||
td {
|
||||
&.has-changes {
|
||||
.has-changes {
|
||||
border-bottom: 1px solid $brown;
|
||||
}
|
||||
}
|
||||
|
||||
.table > thead > tr > th {
|
||||
max-width: 150px;
|
||||
border-bottom: none;
|
||||
|
|
|
@ -1,32 +1,16 @@
|
|||
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>
|
||||
<div *ngIf="workbasket" id="wb-information">
|
||||
|
||||
<!-- ACTION TOOLBAR -->
|
||||
<!--
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" (click)="onSubmit()" [disabled]="action === 'COPY'" data-toggle="tooltip" title="Save" class="btn btn-default btn-primary">
|
||||
<span class="material-icons md-20">save</span>
|
||||
</button>
|
||||
<button type="button" (click)="clear()" data-toggle="tooltip" title="Undo Changes" class="btn btn-default undo-button">
|
||||
<span class="material-icons md-20 blue">undo</span>
|
||||
</button>
|
||||
</div>
|
||||
<h4 class="panel-header">{{workbasket.name}}
|
||||
<span *ngIf="!workbasket.workbasketId" class="badge warning"> {{badgeMessage}}</span>
|
||||
</h4>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- ACCESS ITEMS -->
|
||||
<div class="workbasket-access-items">
|
||||
<form [formGroup]="AccessItemsForm">
|
||||
<table formArrayName="accessItemsGroups" id="table-access-items" class="table table-striped table-center">
|
||||
<table formArrayName="accessItemsGroups" id="table-access-items" class="workbasket-access-items__table table-striped">
|
||||
|
||||
<!-- TITLE ROW -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-align required-header">AccessID</th>
|
||||
<th class="required-header">AccessID</th>
|
||||
<th>Select all</th>
|
||||
<th>Read</th>
|
||||
<th>Open</th>
|
||||
|
@ -44,71 +28,70 @@
|
|||
<tr *ngFor="let accessItem of accessItemsGroups.controls; let index = index;" [formGroupName]="index">
|
||||
<!-- REMOVE BUTTON -->
|
||||
<td>
|
||||
<button type="button" style="padding: 3px;" (click)="remove(index)" data-toggle="tooltip" title="Remove" class="btn btn-default">
|
||||
<button mat-button type="button" style="padding: 3px;" (click)="remove(index)" title="Remove">
|
||||
<span class="material-icons md-24 red">clear</span>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
<!-- ACCESS ID -->
|
||||
<td *ngIf="(accessItemsCustomization$ | async)?.accessId.lookupField else accessIdInput" class="input-group text-align text-width taskana-type-ahead"
|
||||
[ngClass]="{
|
||||
'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
|
||||
'has-error': !accessItem.value.accessId }">
|
||||
<taskana-shared-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required" [validationValue]="toggleValidationAccessIdMap.get(index)"
|
||||
[displayError]="!isFieldValid('accessItem.value.accessId', index)" (selectedItem)="accessItemSelected($event, index)" (inputField)="focusNewInput($event)"></taskana-shared-type-ahead>
|
||||
<td *ngIf="(accessItemsCustomization$ | async)?.accessId.lookupField else accessIdInput"
|
||||
class="workbasket-access-items__typeahead"
|
||||
[ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
|
||||
'has-error': !accessItem.value.accessId }">
|
||||
|
||||
<taskana-shared-type-ahead formControlName="accessId" placeHolderMessage="* Access id is required"
|
||||
[validationValue]="toggleValidationAccessIdMap.get(index)"
|
||||
[displayError]="!isFieldValid('accessItem.value.accessId', index)"
|
||||
(selectedItem)="accessItemSelected($event, index)"
|
||||
(inputField)="focusNewInput($event)">
|
||||
</taskana-shared-type-ahead>
|
||||
</td>
|
||||
|
||||
<ng-template #accessIdInput>
|
||||
<td class="input-group text-align text-width">
|
||||
<div [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !==accessItem.value.accessId), 'has-error':
|
||||
!accessItem.value.accessId && formsValidatorService.formSubmitAttempt}">
|
||||
<input type="text" class="form-control" formControlName="accessId" placeholder="{{accessItem.invalid?
|
||||
'* Access id is required': ''}}"
|
||||
[@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
|
||||
<td>
|
||||
<div [ngClass]="{ 'has-warning': (accessItemsClone[index].accessId !== accessItem.value.accessId),
|
||||
'has-error': !accessItem.value.accessId && formsValidatorService.formSubmitAttempt}">
|
||||
<input matInput type="text" formControlName="accessId"
|
||||
placeholder="{{accessItem.invalid ? '* Access id is required': ''}}"
|
||||
[@validation]="toggleValidationAccessIdMap.get(index)" #htmlInputElement>
|
||||
</div>
|
||||
</td>
|
||||
</ng-template>
|
||||
|
||||
<!-- SELECT ALL -->
|
||||
<td>
|
||||
<input id="checkbox-{{index}}-00" type="checkbox" (change)="checkAll(index, $event)">
|
||||
<label for="checkbox-{{index}}-00"></label>
|
||||
</td>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-00" (change)="checkAll(index, $event)" aria-label="checkAll" aria-labelledby="checkAll">
|
||||
</td>
|
||||
|
||||
<!-- READ -->
|
||||
<!-- READ -->
|
||||
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permRead !== accessItem.value.permRead)}">
|
||||
<input id="checkbox-{{index}}-0" type="checkbox" formControlName="permRead" class="regular-checkbox">
|
||||
<label for="checkbox-{{index}}-0"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-0" formControlName="permRead" aria-label="permRead" aria-labelledby="permRead">
|
||||
</td>
|
||||
|
||||
<!-- OPEN -->
|
||||
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permOpen !== accessItem.value.permOpen)}">
|
||||
<input id="checkbox-{{index}}-1" type="checkbox" formControlName="permOpen">
|
||||
<label for="checkbox-{{index}}-1"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-1" formControlName="permOpen" aria-label="permOpen" aria-labelledby="permOpen">
|
||||
</td>
|
||||
|
||||
<!-- APPEND -->
|
||||
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permAppend !== accessItem.value.permAppend)}">
|
||||
<input id="checkbox-{{index}}-2" type="checkbox" formControlName="permAppend">
|
||||
<label for="checkbox-{{index}}-2"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-2" formControlName="permAppend" aria-label="permAppend" aria-labelledby="permAppend">
|
||||
</td>
|
||||
|
||||
<!-- TRANSFER -->
|
||||
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permTransfer !== accessItem.value.permTransfer)}">
|
||||
<input id="checkbox-{{index}}-3" type="checkbox" formControlName="permTransfer">
|
||||
<label for="checkbox-{{index}}-3"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-3" formControlName="permTransfer" aria-label="permTransfer" aria-labelledby="permTransfer">
|
||||
</td>
|
||||
|
||||
<!-- DISTRIBUTE -->
|
||||
<td [ngClass]="{ 'has-changes': (accessItemsClone[index].permDistribute !== accessItem.value.permDistribute)}">
|
||||
<input id="checkbox-{{index}}-4" type="checkbox" formControlName="permDistribute">
|
||||
<label for="checkbox-{{index}}-4"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-4" formControlName="permDistribute" aria-label="permDistribute" aria-labelledby="permDistribute">
|
||||
</td>
|
||||
|
||||
<!-- CUSTOM FIELDS -->
|
||||
<ng-container *ngFor="let customField of customFields$ | async; let customIndex = index">
|
||||
<td *ngIf="customField.visible" [ngClass]="{ 'has-changes': accessItemsClone[index][getAccessItemCustomProperty(customIndex + 1)] !== accessItem.value[getAccessItemCustomProperty(customIndex+1)] }">
|
||||
<input id="checkbox-{{index}}-{{customIndex + 5}}" type="checkbox" formControlName="permCustom{{customIndex+1}}">
|
||||
<label for="checkbox-{{index}}-{{customIndex + 5}}"></label>
|
||||
<input class="workbasket-access-items__permission-checkbox" type="checkbox" id="checkbox-{{index}}-{{customIndex + 5}}" formControlName="permCustom{{customIndex+1}}" aria-label="customField" aria-labelledby="customField">
|
||||
</td>
|
||||
</ng-container>
|
||||
</tr>
|
||||
|
@ -117,10 +100,9 @@
|
|||
</form>
|
||||
|
||||
<!-- ADD ACCESS ITEM -->
|
||||
<button type="button" (click)="addAccessItem()" data-toggle="tooltip" title="Add new access" class="btn btn-default add-access-item">
|
||||
<button mat-stroked-button type="button" class="workbasket-access-items__add-access" (click)="addAccessItem()" data-toggle="tooltip" title="Add new access">
|
||||
<span class="material-icons md-20 green-blue">add</span>
|
||||
<span>Add new access</span>
|
||||
</button>
|
||||
<taskana-shared-spinner [isRunning]="requestInProgress" [positionClass]=""></taskana-shared-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
.workbasket-access-items {
|
||||
max-width: calc(100vw - 500px);
|
||||
}
|
||||
|
||||
.workbasket-access-items__typeahead {
|
||||
text-align: left;
|
||||
min-width: 180px;
|
||||
width: calc(100% + 20px);
|
||||
}
|
||||
td > input[type='checkbox'] {
|
||||
margin-top: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
overflow-x: auto;
|
||||
padding-top: 0;
|
||||
}
|
||||
.text-width {
|
||||
width: 100%;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.required-header {
|
||||
width: 200px;
|
||||
}
|
||||
|
@ -23,22 +28,54 @@ td > input[type='checkbox'] {
|
|||
}
|
||||
|
||||
th {
|
||||
padding: 0.25rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 3;
|
||||
background: white;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: bottom !important;
|
||||
&.has-changes {
|
||||
border-bottom: 1px solid #f0ad4e;
|
||||
}
|
||||
.workbasket-access-items__table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.table > thead > tr > th {
|
||||
.workbasket-access-items__table thead th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
}
|
||||
|
||||
.workbasket-access-items__table td,
|
||||
.table th {
|
||||
padding: 0.5rem;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.workbasket-access-items__table > thead > tr > th {
|
||||
max-width: 150px;
|
||||
border-bottom: none;
|
||||
}
|
||||
taskana-shared-type-ahead {
|
||||
top: 0;
|
||||
|
||||
.workbasket-access-items__permission-checkbox {
|
||||
display: inline-block;
|
||||
height: 1rem;
|
||||
line-height: 0;
|
||||
margin: auto;
|
||||
order: 0;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: 1rem;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
|
||||
@supports (-moz-appearance: none) {
|
||||
//bigger checkboxes for firefox because firefox renders differently
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
input[type='checkbox']:checked {
|
||||
filter: hue-rotate(320deg) brightness(1);
|
||||
}
|
||||
|
||||
.workbasket-access-items__add-access {
|
||||
margin: 16px 0 16px 16px;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ACTION } from '../../../shared/models/action';
|
||||
import { WorkbasketAccessItems } from '../../../shared/models/workbasket-access-items';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
|
||||
@Component({ selector: 'taskana-shared-spinner', template: '' })
|
||||
class SpinnerStub {
|
||||
|
@ -88,7 +89,8 @@ describe('WorkbasketAccessItemsComponent', () => {
|
|||
NgxsModule.forRoot([WorkbasketState, EngineConfigurationState]),
|
||||
HttpClientTestingModule,
|
||||
RouterTestingModule.withRoutes([]),
|
||||
BrowserAnimationsModule
|
||||
BrowserAnimationsModule,
|
||||
MatProgressBarModule
|
||||
],
|
||||
declarations: [WorkbasketAccessItemsComponent, TypeAheadComponent, SpinnerStub],
|
||||
providers: [
|
||||
|
@ -120,7 +122,6 @@ describe('WorkbasketAccessItemsComponent', () => {
|
|||
workbasketAccessItems: workbasketAccessItemsMock
|
||||
}
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
afterEach(async(() => {
|
||||
|
@ -149,7 +150,8 @@ describe('WorkbasketAccessItemsComponent', () => {
|
|||
});
|
||||
|
||||
it('should add accessItems when add access item button is clicked', () => {
|
||||
const addAccessItemButton = debugElement.nativeElement.querySelector('button.add-access-item');
|
||||
fixture.detectChanges();
|
||||
const addAccessItemButton = debugElement.nativeElement.querySelector('button.workbasket-access-items__add-access');
|
||||
const clearSpy = jest.spyOn(component, 'addAccessItem');
|
||||
expect(addAccessItemButton.title).toMatch('Add new access');
|
||||
|
||||
|
@ -158,12 +160,14 @@ describe('WorkbasketAccessItemsComponent', () => {
|
|||
});
|
||||
|
||||
it('should undo changes when undo button is clicked', () => {
|
||||
fixture.detectChanges();
|
||||
const clearSpy = jest.spyOn(component, 'clear');
|
||||
component.clear();
|
||||
expect(clearSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check all permissions when check all box is checked', () => {
|
||||
fixture.detectChanges();
|
||||
const checkAllSpy = jest.spyOn(component, 'checkAll');
|
||||
const checkAllButton = debugElement.nativeElement.querySelector('#checkbox-0-00');
|
||||
expect(checkAllButton).toBeTruthy();
|
||||
|
|
|
@ -25,15 +25,18 @@ import { highlight } from 'app/shared/animations/validation.animation';
|
|||
import { FormsValidatorService } from 'app/shared/services/forms-validator/forms-validator.service';
|
||||
import { AccessIdDefinition } from 'app/shared/models/access-id';
|
||||
import { EngineConfigurationSelectors } from 'app/shared/store/engine-configuration-store/engine-configuration.selectors';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { filter, take, takeUntil } from 'rxjs/operators';
|
||||
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
|
||||
import { NotificationService } from '../../../shared/services/notifications/notification.service';
|
||||
import { AccessItemsCustomisation, CustomField, getCustomFields } from '../../../shared/models/customisation';
|
||||
import {
|
||||
GetWorkbasketAccessItems,
|
||||
OnButtonPressed,
|
||||
UpdateWorkbasketAccessItems
|
||||
} from '../../../shared/store/workbasket-store/workbasket.actions';
|
||||
import { WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
|
||||
import { WorkbasketComponent } from '../../models/workbasket-component';
|
||||
import { ButtonAction } from '../../models/button-action';
|
||||
|
||||
@Component({
|
||||
selector: 'taskana-administration-workbasket-access-items',
|
||||
|
@ -77,11 +80,17 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
|
|||
@Select(WorkbasketSelectors.workbasketAccessItems)
|
||||
accessItemsRepresentation$: Observable<WorkbasketAccessItemsRepresentation>;
|
||||
|
||||
@Select(WorkbasketSelectors.buttonAction)
|
||||
buttonAction$: Observable<ButtonAction>;
|
||||
|
||||
@Select(WorkbasketSelectors.selectedComponent)
|
||||
selectedComponent$: Observable<WorkbasketComponent>;
|
||||
|
||||
constructor(
|
||||
private savingWorkbaskets: SavingWorkbasketService,
|
||||
private requestInProgressService: RequestInProgressService,
|
||||
private formBuilder: FormBuilder,
|
||||
private formsValidatorService: FormsValidatorService,
|
||||
public formsValidatorService: FormsValidatorService,
|
||||
private notificationsService: NotificationService,
|
||||
private store: Store
|
||||
) {}
|
||||
|
@ -91,6 +100,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
this.customFields$ = this.accessItemsCustomization$.pipe(getCustomFields(customFieldCount));
|
||||
this.accessItemsRepresentation$.subscribe((accessItemsRepresentation) => {
|
||||
if (typeof accessItemsRepresentation !== 'undefined') {
|
||||
|
@ -100,6 +110,27 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
|
|||
this.accessItemsResetClone = this.cloneAccessItems(accessItemsRepresentation.accessItems);
|
||||
}
|
||||
});
|
||||
|
||||
this.buttonAction$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.pipe(filter((buttonAction) => typeof buttonAction !== 'undefined'))
|
||||
.subscribe((button) => {
|
||||
this.selectedComponent$
|
||||
.pipe(take(1))
|
||||
.pipe(filter((component) => component === WorkbasketComponent.ACCESS_ITEMS))
|
||||
.subscribe((component) => {
|
||||
switch (button) {
|
||||
case ButtonAction.SAVE:
|
||||
this.onSubmit();
|
||||
break;
|
||||
case ButtonAction.UNDO:
|
||||
this.clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
@ -195,6 +226,7 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
|
|||
}
|
||||
|
||||
clear() {
|
||||
this.store.dispatch(new OnButtonPressed(undefined));
|
||||
this.formsValidatorService.formSubmitAttempt = false;
|
||||
this.AccessItemsForm.reset();
|
||||
this.setAccessItemsGroups(this.accessItemsResetClone);
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
</div>
|
||||
<div class="navbar__logo">
|
||||
<svg-icon class="navbar__logo-icon" src="./assets/icons/logo-copy.svg"></svg-icon>
|
||||
<div class="navbar__title">/ {{title}}</div>
|
||||
<div class="navbar__title">{{ title }}</div>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
|
|
@ -262,6 +262,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
|
|||
ctx: StateContext<WorkbasketStateModel>,
|
||||
action: UpdateWorkbasketAccessItems
|
||||
): Observable<any> {
|
||||
ctx.dispatch(new OnButtonPressed(undefined));
|
||||
return this.workbasketService.updateWorkBasketAccessItem(action.url, action.workbasketAccessItems).pipe(
|
||||
take(1),
|
||||
tap(
|
||||
|
|
Loading…
Reference in New Issue