Skip to content

Commit 0411f5c

Browse files
authored
611: Fix - Expand side menu workflows/project when workflow is open (#616)
1 parent 6ec57af commit 0411f5c

File tree

3 files changed

+135
-5
lines changed

3 files changed

+135
-5
lines changed

ui/src/app/components/workflows/workflows.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
All workflows
2929
</a>
3030
<section class="nav-group collapsible" *ngFor="let project of projects; let index = index;">
31-
<input [id]="index" type="checkbox" checked>
31+
<input [id]="index" type="checkbox" [checked]="isProjectClosed(project.name, project.workflows)" (change)="toggleProject(project.name)">
3232
<label [for]="index">{{project.name}}</label>
3333
<ul class="nav-list" *ngFor="let workflow of project.workflows">
3434
<li>
35-
<a routerLinkActive="active"
35+
<a [class]="isWorkflowHighlighted(workflow.id) ? 'active' : null"
3636
[routerLink]="[absoluteRoutes.SHOW_WORKFLOW, workflow.id]"
3737
class="nav-link">{{workflow.name}}</a>
3838
</li>

ui/src/app/components/workflows/workflows.component.spec.ts

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
1717
import { WorkflowsComponent } from './workflows.component';
1818
import { provideMockStore } from '@ngrx/store/testing';
19-
import { ProjectModelFactory } from '../../models/project.model';
19+
import { ProjectModelFactory, WorkflowIdentityModelFactory } from '../../models/project.model';
2020
import { WorkflowModelFactory } from '../../models/workflow.model';
21+
import { RouterTestingModule } from '@angular/router/testing';
22+
import { Router, Routes } from '@angular/router';
2123

2224
describe('WorkflowsComponent', () => {
2325
let fixture: ComponentFixture<WorkflowsComponent>;
2426
let underTest: WorkflowsComponent;
27+
let router: Router;
28+
29+
const routes = [{ path: 'workflows/show/:id', component: {} }] as Routes;
2530

2631
const initialAppState = {
2732
workflows: {
@@ -42,7 +47,9 @@ describe('WorkflowsComponent', () => {
4247
TestBed.configureTestingModule({
4348
providers: [provideMockStore({ initialState: initialAppState })],
4449
declarations: [WorkflowsComponent],
50+
imports: [RouterTestingModule.withRoutes(routes)],
4551
}).compileComponents();
52+
router = TestBed.inject(Router);
4653
}),
4754
);
4855

@@ -65,4 +72,106 @@ describe('WorkflowsComponent', () => {
6572
});
6673
}),
6774
);
75+
76+
describe('isWorkflowHighlighted', () => {
77+
it(
78+
'should return true when url contains input id',
79+
waitForAsync(() => {
80+
const idMatching = 555;
81+
router.navigate(['workflows/show', idMatching]).then(() => {
82+
expect(underTest.isWorkflowHighlighted(idMatching)).toBeTruthy();
83+
});
84+
}),
85+
);
86+
87+
it(
88+
'should return false when url does not contain input id',
89+
waitForAsync(() => {
90+
const idNonMatching = 5;
91+
router.navigate(['workflows/show', 555]).then(() => {
92+
expect(underTest.isWorkflowHighlighted(idNonMatching)).toBeFalse();
93+
});
94+
}),
95+
);
96+
});
97+
98+
it(
99+
'toggleProject() should toggle a project',
100+
waitForAsync(() => {
101+
const project = 'project';
102+
const projectOther = 'projectOther';
103+
104+
expect(underTest.openedProjects.size).toEqual(0);
105+
106+
underTest.toggleProject(project);
107+
expect(underTest.openedProjects.size).toEqual(1);
108+
expect(underTest.openedProjects).toContain(project);
109+
110+
underTest.toggleProject(projectOther);
111+
expect(underTest.openedProjects.size).toEqual(2);
112+
expect(underTest.openedProjects).toContain(project);
113+
expect(underTest.openedProjects).toContain(projectOther);
114+
115+
underTest.toggleProject(project);
116+
expect(underTest.openedProjects.size).toEqual(1);
117+
expect(underTest.openedProjects).toContain(projectOther);
118+
119+
underTest.toggleProject(projectOther);
120+
expect(underTest.openedProjects.size).toEqual(0);
121+
}),
122+
);
123+
124+
describe('isProjectClosed', () => {
125+
it(
126+
'should return true if project is not in opened projects no workflow is highlighted',
127+
waitForAsync(() => {
128+
const id = 555;
129+
const otherId = 5;
130+
router.navigate(['workflows/show', id]).then(() => {
131+
expect(underTest.openedProjects.size).toEqual(0);
132+
expect(underTest.isWorkflowHighlighted(otherId)).toBeFalse();
133+
134+
const result = underTest.isProjectClosed('project', [WorkflowIdentityModelFactory.create(otherId, 'name')]);
135+
136+
expect(result).toBeTruthy();
137+
});
138+
}),
139+
);
140+
141+
it(
142+
'should return false if project is in opened projects',
143+
waitForAsync(() => {
144+
const id = 555;
145+
const otherId = 5;
146+
const projectName = 'project';
147+
underTest.openedProjects.add(projectName);
148+
149+
router.navigate(['workflows/show', id]).then(() => {
150+
expect(underTest.openedProjects.size).toEqual(1);
151+
expect(underTest.openedProjects).toContain(projectName);
152+
expect(underTest.isWorkflowHighlighted(otherId)).toBeFalse();
153+
154+
const result = underTest.isProjectClosed(projectName, [WorkflowIdentityModelFactory.create(otherId, 'name')]);
155+
156+
expect(result).toBeFalse();
157+
});
158+
}),
159+
);
160+
161+
it(
162+
'should return false if project workflow is highlighted',
163+
waitForAsync(() => {
164+
const id = 555;
165+
router.navigate(['workflows/show', id]).then(() => {
166+
expect(underTest.openedProjects.size).toEqual(0);
167+
expect(underTest.isWorkflowHighlighted(id)).toBeTruthy();
168+
169+
const result = underTest.isProjectClosed('project', [WorkflowIdentityModelFactory.create(id, 'name')]);
170+
171+
expect(underTest.openedProjects.size).toEqual(1);
172+
expect(result).toBeFalse();
173+
});
174+
}),
175+
);
176+
});
68177
});

ui/src/app/components/workflows/workflows.component.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
*/
1515

1616
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
17-
import { ProjectModel } from '../../models/project.model';
17+
import { ProjectModel, WorkflowIdentityModel } from '../../models/project.model';
1818
import { Store } from '@ngrx/store';
1919
import { AppState, selectWorkflowState } from '../../stores/app.reducers';
2020
import { Subscription } from 'rxjs';
2121
import { InitializeWorkflows } from '../../stores/workflows/workflows.actions';
2222
import { absoluteRoutes } from '../../constants/routes.constants';
23+
import { Router } from '@angular/router';
2324

2425
@Component({
2526
selector: 'app-workflows',
@@ -31,10 +32,11 @@ export class WorkflowsComponent implements AfterViewInit, OnDestroy {
3132

3233
loading = true;
3334
projects: ProjectModel[] = [];
35+
openedProjects: Set<string> = new Set<string>();
3436

3537
absoluteRoutes = absoluteRoutes;
3638

37-
constructor(private store: Store<AppState>) {
39+
constructor(private store: Store<AppState>, private router: Router) {
3840
this.store.dispatch(new InitializeWorkflows());
3941
}
4042

@@ -45,6 +47,25 @@ export class WorkflowsComponent implements AfterViewInit, OnDestroy {
4547
});
4648
}
4749

50+
isWorkflowHighlighted(id: number): boolean {
51+
return this.router.url.split('/').some((part) => part === id.toString());
52+
}
53+
54+
isProjectClosed(project: string, workflows: WorkflowIdentityModel[]): boolean {
55+
if (!this.openedProjects.has(project) && workflows.some((workflow: WorkflowIdentityModel) => this.isWorkflowHighlighted(workflow.id))) {
56+
this.openedProjects.add(project);
57+
}
58+
return !this.openedProjects.has(project);
59+
}
60+
61+
toggleProject(project: string) {
62+
if (this.openedProjects.has(project)) {
63+
this.openedProjects.delete(project);
64+
} else {
65+
this.openedProjects.add(project);
66+
}
67+
}
68+
4869
ngOnDestroy(): void {
4970
!!this.workflowsSubscription && this.workflowsSubscription.unsubscribe();
5071
}

0 commit comments

Comments
 (0)