TSK-1448: Expand workbasket path with selected tab (#1333)

This commit is contained in:
Sofie Hofmann 2020-11-16 12:59:58 +01:00 committed by GitHub
parent ea38c7ddba
commit b8156d75ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 76 additions and 52 deletions

View File

@ -51,9 +51,6 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
@Input() @Input()
action: ACTION; action: ACTION;
@Input()
active: string;
@ViewChildren('htmlInputElement') @ViewChildren('htmlInputElement')
inputs: QueryList<ElementRef>; inputs: QueryList<ElementRef>;
@ -142,14 +139,6 @@ export class WorkbasketAccessItemsComponent implements OnInit, OnChanges, OnDest
} }
ngOnChanges(changes?: SimpleChanges) { ngOnChanges(changes?: SimpleChanges) {
if (!this.initialized && changes.active && changes.active.currentValue === 'accessItems') {
this.init();
}
if (this.initialized && typeof changes.workbasket !== 'undefined') {
if (changes.workbasket.currentValue.workbasketId !== changes.workbasket.previousValue.workbasketId) {
this.init();
}
}
if (changes.action) { if (changes.action) {
this.setBadge(); this.setBadge();
} }

View File

@ -37,15 +37,15 @@
</button> </button>
</mat-menu> </mat-menu>
</mat-toolbar> </mat-toolbar>
<mat-tab-group animationDuration="0ms" (selectedIndexChange)="selectComponent($event)"> <mat-tab-group animationDuration="0ms" (selectedIndexChange)="selectComponent($event)" [selectedIndex]="selectedTab$ | async">
<mat-tab label="Information"> <mat-tab label="Information">
<taskana-administration-workbasket-information [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-information> <taskana-administration-workbasket-information [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-information>
</mat-tab> </mat-tab>
<mat-tab label="Access"> <mat-tab label="Access">
<taskana-administration-workbasket-access-items [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-access-items> <taskana-administration-workbasket-access-items [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-access-items>
</mat-tab> </mat-tab>
<mat-tab label="Distribution Targets"> <mat-tab label="Distribution Targets">
<taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action" [active]="tabSelected"></taskana-administration-workbasket-distribution-targets> <taskana-administration-workbasket-distribution-targets [workbasket]="workbasket" [action]="action"></taskana-administration-workbasket-distribution-targets>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
<mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar> <mat-progress-bar mode="query" *ngIf="requestInProgress"></mat-progress-bar>

View File

@ -15,11 +15,7 @@ import { RequestInProgressService } from '../../../shared/services/request-in-pr
import { SelectedRouteService } from '../../../shared/services/selected-route/selected-route'; import { SelectedRouteService } from '../../../shared/services/selected-route/selected-route';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { import { selectedWorkbasketMock, workbasketReadStateMock } from '../../../shared/store/mock-data/mock-store';
engineConfigurationMock,
selectedWorkbasketMock,
workbasketReadStateMock
} from '../../../shared/store/mock-data/mock-store';
import { StartupService } from '../../../shared/services/startup/startup.service'; import { StartupService } from '../../../shared/services/startup/startup.service';
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service'; import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
import { WindowRefService } from '../../../shared/services/window/window.service'; import { WindowRefService } from '../../../shared/services/window/window.service';
@ -29,6 +25,8 @@ import { MatTabsModule } from '@angular/material/tabs';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CreateWorkbasket } from '../../../shared/store/workbasket-store/workbasket.actions';
import { take } from 'rxjs/operators';
@Component({ selector: 'taskana-shared-spinner', template: '' }) @Component({ selector: 'taskana-shared-spinner', template: '' })
class SpinnerStub { class SpinnerStub {
@ -140,7 +138,6 @@ describe('WorkbasketDetailsComponent', () => {
workbasket: workbasketCreateState workbasket: workbasketCreateState
}); });
fixture.detectChanges(); fixture.detectChanges();
expect(component.tabSelected).toMatch('information');
expect(component.selectedId).toBeUndefined(); expect(component.selectedId).toBeUndefined();
}); });
@ -165,9 +162,13 @@ describe('WorkbasketDetailsComponent', () => {
expect(component.workbasket).toEqual(selectedWorkbasketMock); expect(component.workbasket).toEqual(selectedWorkbasketMock);
}); });
it('should select information tab when action is CREATE', () => { it('should select information tab when action is CREATE', (done) => {
component.action = ACTION.CREATE; component.selectComponent(1);
component.selectTab('workbasket'); store.dispatch(new CreateWorkbasket());
expect(component.tabSelected).toEqual('information'); fixture.detectChanges();
component.selectedTab$.pipe(take(1)).subscribe((tab) => {
expect(tab).toEqual(0);
done();
});
}); });
}); });

View File

@ -9,6 +9,7 @@ import { Select, Store } from '@ngxs/store';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { WorkbasketAndAction, WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors'; import { WorkbasketAndAction, WorkbasketSelectors } from '../../../shared/store/workbasket-store/workbasket.selectors';
import { TaskanaDate } from '../../../shared/util/taskana.date'; import { TaskanaDate } from '../../../shared/util/taskana.date';
import { Location } from '@angular/common';
import { ICONTYPES } from '../../../shared/models/icon-types'; import { ICONTYPES } from '../../../shared/models/icon-types';
import { import {
DeselectWorkbasket, DeselectWorkbasket,
@ -16,6 +17,7 @@ import {
SelectComponent SelectComponent
} from '../../../shared/store/workbasket-store/workbasket.actions'; } from '../../../shared/store/workbasket-store/workbasket.actions';
import { ButtonAction } from '../../models/button-action'; import { ButtonAction } from '../../models/button-action';
import { WorkbasketComponent } from '../../models/workbasket-component';
@Component({ @Component({
selector: 'taskana-administration-workbasket-details', selector: 'taskana-administration-workbasket-details',
@ -28,12 +30,14 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
selectedId: string; selectedId: string;
requestInProgress = false; requestInProgress = false;
action: ACTION; action: ACTION;
tabSelected = 'information';
badgeMessage = ''; badgeMessage = '';
@Select(WorkbasketSelectors.selectedWorkbasket) @Select(WorkbasketSelectors.selectedWorkbasket)
selectedWorkbasket$: Observable<Workbasket>; selectedWorkbasket$: Observable<Workbasket>;
@Select(WorkbasketSelectors.selectedComponent)
selectedTab$: Observable<number>;
@Select(WorkbasketSelectors.workbasketActiveAction) @Select(WorkbasketSelectors.workbasketActiveAction)
activeAction$: Observable<ACTION>; activeAction$: Observable<ACTION>;
@ -43,6 +47,7 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
destroy$ = new Subject<void>(); destroy$ = new Subject<void>();
constructor( constructor(
private location: Location,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private domainService: DomainService, private domainService: DomainService,
@ -54,7 +59,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
this.selectedWorkbasketAndAction$.pipe(takeUntil(this.destroy$)).subscribe((selectedWorkbasketAndAction) => { this.selectedWorkbasketAndAction$.pipe(takeUntil(this.destroy$)).subscribe((selectedWorkbasketAndAction) => {
this.action = selectedWorkbasketAndAction.action; this.action = selectedWorkbasketAndAction.action;
if (this.action === ACTION.CREATE) { if (this.action === ACTION.CREATE) {
this.tabSelected = 'information';
this.selectedId = undefined; this.selectedId = undefined;
this.badgeMessage = 'Creating new workbasket'; this.badgeMessage = 'Creating new workbasket';
this.initWorkbasket(); this.initWorkbasket();
@ -99,10 +103,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
this.router.navigate(['./'], { relativeTo: this.route.parent }); this.router.navigate(['./'], { relativeTo: this.route.parent });
} }
selectTab(tab) {
this.tabSelected = this.action === ACTION.CREATE ? 'information' : tab;
}
getWorkbasketInformation(selectedWorkbasket?: Workbasket) { getWorkbasketInformation(selectedWorkbasket?: Workbasket) {
let workbasketIdSelected: string; let workbasketIdSelected: string;
if (selectedWorkbasket) { if (selectedWorkbasket) {
@ -145,19 +145,6 @@ export class WorkbasketDetailsComponent implements OnInit, OnDestroy, OnChanges
selectComponent(index) { selectComponent(index) {
this.store.dispatch(new SelectComponent(index)); this.store.dispatch(new SelectComponent(index));
switch (index) {
case 0:
this.selectTab('information');
break;
case 1:
this.selectTab('access-items');
break;
case 2:
this.selectTab('distribution-targets');
break;
default:
break;
}
} }
onSubmit() { onSubmit() {

View File

@ -42,9 +42,6 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
@Input() @Input()
action: ACTION; action: ACTION;
@Input()
active: string;
badgeMessage = ''; badgeMessage = '';
distributionTargetsSelectedResource: WorkbasketDistributionTargets; distributionTargetsSelectedResource: WorkbasketDistributionTargets;
@ -95,9 +92,6 @@ export class WorkbasketDistributionTargetsComponent implements OnInit, OnChanges
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (!this.initialized && changes.active && changes.active.currentValue === 'distributionTargets') {
this.init();
}
if (changes.action) { if (changes.action) {
this.setBadge(); this.setBadge();
} }

View File

@ -74,7 +74,6 @@ export class WorkbasketInformationComponent implements OnInit, OnChanges, OnDest
) {} ) {}
ngOnInit() { ngOnInit() {
this.store.dispatch(new SelectComponent(WorkbasketComponent.INFORMATION));
this.allTypes = new Map([ this.allTypes = new Map([
['PERSONAL', 'Personal'], ['PERSONAL', 'Personal'],
['GROUP', 'Group'], ['GROUP', 'Group'],

View File

@ -16,6 +16,7 @@ import { TaskanaType } from '../../../shared/models/taskana-type';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { RouterTestingModule } from '@angular/router/testing';
const getDomainFn = jest.fn().mockReturnValue(true); const getDomainFn = jest.fn().mockReturnValue(true);
const domainServiceMock = jest.fn().mockImplementation( const domainServiceMock = jest.fn().mockImplementation(
@ -53,6 +54,7 @@ describe('WorkbasketListToolbarComponent', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
RouterTestingModule,
NgxsModule.forRoot([WorkbasketState]), NgxsModule.forRoot([WorkbasketState]),
BrowserAnimationsModule, BrowserAnimationsModule,
MatIconModule, MatIconModule,

View File

@ -22,6 +22,7 @@ import { MatSelectModule } from '@angular/material/select';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MatListModule, MatSelectionList } from '@angular/material/list'; import { MatListModule, MatSelectionList } from '@angular/material/list';
import { DomainService } from '../../../shared/services/domain/domain.service'; import { DomainService } from '../../../shared/services/domain/domain.service';
import { RouterTestingModule } from '@angular/router/testing';
const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1)); const workbasketSavedTriggeredFn = jest.fn().mockReturnValue(of(1));
const workbasketSummaryFn = jest.fn().mockReturnValue(of({})); const workbasketSummaryFn = jest.fn().mockReturnValue(of({}));
@ -99,6 +100,7 @@ describe('WorkbasketListComponent', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
NgxsModule.forRoot([WorkbasketState]), NgxsModule.forRoot([WorkbasketState]),
RouterTestingModule,
MatSnackBarModule, MatSnackBarModule,
MatDialogModule, MatDialogModule,
FormsModule, FormsModule,

View File

@ -31,6 +31,7 @@ import { WorkbasketDistributionTargets } from '../../models/workbasket-distribut
import { WorkbasketSummary } from '../../models/workbasket-summary'; import { WorkbasketSummary } from '../../models/workbasket-summary';
import { WorkbasketComponent } from '../../../administration/models/workbasket-component'; import { WorkbasketComponent } from '../../../administration/models/workbasket-component';
import { ButtonAction } from '../../../administration/models/button-action'; import { ButtonAction } from '../../../administration/models/button-action';
import { ActivatedRoute } from '@angular/router';
class InitializeStore { class InitializeStore {
static readonly type = '[Workbasket] Initializing state'; static readonly type = '[Workbasket] Initializing state';
@ -41,9 +42,36 @@ export class WorkbasketState implements NgxsAfterBootstrap {
constructor( constructor(
private workbasketService: WorkbasketService, private workbasketService: WorkbasketService,
private location: Location, private location: Location,
private notificationService: NotificationService private notificationService: NotificationService,
private route: ActivatedRoute
) {} ) {}
@Action(InitializeStore)
initializeStore(ctx: StateContext<WorkbasketStateModel>): Observable<any> {
// read the selected tab from the route
this.route.queryParams.pipe(take(2)).subscribe((params) => {
let tabName: string = params.tab;
let tab: number;
switch (tabName) {
case 'information':
tab = WorkbasketComponent.INFORMATION;
break;
case 'access-items':
tab = WorkbasketComponent.ACCESS_ITEMS;
break;
case 'distribution-targets':
tab = WorkbasketComponent.DISTRIBUTION_TARGETS;
break;
default:
tab = WorkbasketComponent.INFORMATION;
}
ctx.dispatch(new SelectComponent(tab));
});
return of();
}
@Action(GetWorkbasketsSummary) @Action(GetWorkbasketsSummary)
getWorkbasketsSummary(ctx: StateContext<WorkbasketStateModel>, action: GetWorkbasketsSummary): Observable<any> { getWorkbasketsSummary(ctx: StateContext<WorkbasketStateModel>, action: GetWorkbasketsSummary): Observable<any> {
ctx.patchState({ ctx.patchState({
@ -75,7 +103,25 @@ export class WorkbasketState implements NgxsAfterBootstrap {
@Action(SelectWorkbasket) @Action(SelectWorkbasket)
selectWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: SelectWorkbasket): Observable<any> { selectWorkbasket(ctx: StateContext<WorkbasketStateModel>, action: SelectWorkbasket): Observable<any> {
this.location.go(this.location.path().replace(/(workbaskets).*/g, `workbaskets/(detail:${action.workbasketId})`)); let selectedComponent;
switch (ctx.getState().selectedComponent) {
case WorkbasketComponent.INFORMATION:
selectedComponent = 'information';
break;
case WorkbasketComponent.ACCESS_ITEMS:
selectedComponent = 'access-items';
break;
case WorkbasketComponent.DISTRIBUTION_TARGETS:
selectedComponent = 'distribution-targets';
break;
}
this.location.go(
this.location
.path()
.replace(/(workbaskets).*/g, `workbaskets/(detail:${action.workbasketId})?tab=${selectedComponent}`)
);
const id = action.workbasketId; const id = action.workbasketId;
if (typeof id !== 'undefined') { if (typeof id !== 'undefined') {
return this.workbasketService.getWorkBasket(id).pipe( return this.workbasketService.getWorkBasket(id).pipe(
@ -106,6 +152,7 @@ export class WorkbasketState implements NgxsAfterBootstrap {
this.location.go(this.location.path().replace(/(workbaskets).*/g, 'workbaskets/(detail:new-workbasket)')); this.location.go(this.location.path().replace(/(workbaskets).*/g, 'workbaskets/(detail:new-workbasket)'));
ctx.patchState({ ctx.patchState({
selectedWorkbasket: undefined, selectedWorkbasket: undefined,
selectedComponent: WorkbasketComponent.INFORMATION,
action: ACTION.CREATE action: ACTION.CREATE
}); });
return of(null); return of(null);
@ -122,12 +169,15 @@ export class WorkbasketState implements NgxsAfterBootstrap {
switch (action.component) { switch (action.component) {
case WorkbasketComponent.INFORMATION: case WorkbasketComponent.INFORMATION:
ctx.patchState({ selectedComponent: WorkbasketComponent.INFORMATION }); ctx.patchState({ selectedComponent: WorkbasketComponent.INFORMATION });
this.location.go(this.location.path().replace(/(tab).*/g, 'tab=information'));
break; break;
case WorkbasketComponent.ACCESS_ITEMS: case WorkbasketComponent.ACCESS_ITEMS:
ctx.patchState({ selectedComponent: WorkbasketComponent.ACCESS_ITEMS }); ctx.patchState({ selectedComponent: WorkbasketComponent.ACCESS_ITEMS });
this.location.go(this.location.path().replace(/(tab).*/g, 'tab=access-items'));
break; break;
case WorkbasketComponent.DISTRIBUTION_TARGETS: case WorkbasketComponent.DISTRIBUTION_TARGETS:
ctx.patchState({ selectedComponent: WorkbasketComponent.DISTRIBUTION_TARGETS }); ctx.patchState({ selectedComponent: WorkbasketComponent.DISTRIBUTION_TARGETS });
this.location.go(this.location.path().replace(/(tab).*/g, 'tab=distribution-targets'));
break; break;
} }
return of(null); return of(null);