Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit c4ed954

Browse files
oodamienilayaperumalg
authored andcommitted
Back action in child pages
Resolves #699
1 parent f4154ad commit c4ed954

16 files changed

+223
-76
lines changed

ui/src/app/app.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ToastyConfig } from 'ng2-toasty';
33
import { Renderer2 } from '@angular/core';
44
import { OnInit } from '@angular/core';
55
import { BusyService } from './shared/services/busy.service';
6+
import { RoutingStateService } from './shared/services/routing-state.service';
67

78
@Component({
89
selector: 'app-root',
@@ -14,6 +15,7 @@ export class AppComponent implements OnInit {
1415

1516
constructor(private toastyConfig: ToastyConfig,
1617
private renderer: Renderer2,
18+
private routingStateService: RoutingStateService,
1719
private busyService: BusyService) {
1820
this.toastyConfig.theme = 'bootstrap';
1921
this.toastyConfig.limit = 5;
@@ -23,6 +25,7 @@ export class AppComponent implements OnInit {
2325
}
2426

2527
ngOnInit() {
28+
this.routingStateService.watchRouting();
2629
this.renderer.listen('document', 'scroll', (evt) => {
2730
this.updateToasty();
2831
});

ui/src/app/apps/app-details/app-details.component.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,7 @@ <h1>
9696
<hr/>
9797

9898
<div class="footer-actions">
99-
<button id="back-button" type="button" class="btn btn-default" (click)="cancel()">
100-
Cancel
101-
</button>
99+
<button id="back-button" type="button" class="btn btn-default" (click)="cancel()">Back</button>
102100
</div>
103101

104102
</div>

ui/src/app/apps/app-details/app-details.component.spec.ts

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
1-
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
2-
import {RouterTestingModule} from '@angular/router/testing';
3-
import {BsDropdownModule, BsModalRef, BsModalService, ModalModule, PopoverModule, TooltipModule} from 'ngx-bootstrap';
4-
import {AppsService} from '../apps.service';
5-
import {ToastyService} from 'ng2-toasty';
6-
import {MockToastyService} from '../../tests/mocks/toasty';
7-
import {MockAppsService} from '../../tests/mocks/apps';
8-
import {ReactiveFormsModule, FormsModule} from '@angular/forms';
9-
import {AppDetailsComponent} from './app-details.component';
10-
import {SharedAboutService} from '../../shared/services/shared-about.service';
11-
import {AppTypeComponent} from '../components/app-type/app-type.component';
12-
import {RolesDirective} from '../../auth/directives/roles.directive';
13-
import {MockActivatedRoute} from '../../tests/mocks/activated-route';
14-
import {MocksSharedAboutService} from '../../tests/mocks/shared-about';
15-
import {ActivatedRoute} from '@angular/router';
16-
import {MockAuthService} from '../../tests/mocks/auth';
17-
import {AuthService} from '../../auth/auth.service';
18-
import {By} from '@angular/platform-browser';
19-
import {DebugElement} from '@angular/core';
20-
import {APPS} from '../../tests/mocks/mock-data';
21-
import {MockConfirmService} from '../../tests/mocks/confirm';
22-
import {ConfirmService} from '../../shared/components/confirm/confirm.service';
23-
import {AppVersionLabelComponent} from '../components/app-versions-label/app-versions-label.component';
24-
import {SortComponent} from '../../shared/components/sort/sort.component';
25-
import {OrderByPipe} from '../../shared/pipes/orderby.pipe';
26-
import {MockModalService} from '../../tests/mocks/modal';
27-
import {AppVersionsComponent} from '../app-versions/app-versions.component';
28-
import {BusyService} from '../../shared/services/busy.service';
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { RouterTestingModule } from '@angular/router/testing';
3+
import { BsDropdownModule, BsModalRef, BsModalService, ModalModule, PopoverModule, TooltipModule } from 'ngx-bootstrap';
4+
import { AppsService } from '../apps.service';
5+
import { ToastyService } from 'ng2-toasty';
6+
import { MockToastyService } from '../../tests/mocks/toasty';
7+
import { MockAppsService } from '../../tests/mocks/apps';
8+
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
9+
import { AppDetailsComponent } from './app-details.component';
10+
import { SharedAboutService } from '../../shared/services/shared-about.service';
11+
import { AppTypeComponent } from '../components/app-type/app-type.component';
12+
import { RolesDirective } from '../../auth/directives/roles.directive';
13+
import { MockActivatedRoute } from '../../tests/mocks/activated-route';
14+
import { MocksSharedAboutService } from '../../tests/mocks/shared-about';
15+
import { ActivatedRoute } from '@angular/router';
16+
import { MockAuthService } from '../../tests/mocks/auth';
17+
import { AuthService } from '../../auth/auth.service';
18+
import { By } from '@angular/platform-browser';
19+
import { DebugElement } from '@angular/core';
20+
import { APPS } from '../../tests/mocks/mock-data';
21+
import { MockConfirmService } from '../../tests/mocks/confirm';
22+
import { ConfirmService } from '../../shared/components/confirm/confirm.service';
23+
import { AppVersionLabelComponent } from '../components/app-versions-label/app-versions-label.component';
24+
import { SortComponent } from '../../shared/components/sort/sort.component';
25+
import { OrderByPipe } from '../../shared/pipes/orderby.pipe';
26+
import { MockModalService } from '../../tests/mocks/modal';
27+
import { AppVersionsComponent } from '../app-versions/app-versions.component';
28+
import { BusyService } from '../../shared/services/busy.service';
29+
import { RoutingStateService } from '../../shared/services/routing-state.service';
30+
import { MockRoutingStateService } from '../../tests/mocks/routing-state';
2931

3032
/**
3133
* Test {@link AppDetailsComponent}.
@@ -41,9 +43,10 @@ describe('AppDetailsComponent', () => {
4143
const sharedAboutService = new MocksSharedAboutService();
4244
const modalService = new MockModalService();
4345
const confirmService = new MockConfirmService();
46+
const routingStateService = new MockRoutingStateService();
4447
let activeRoute: MockActivatedRoute;
4548

46-
const commonTestParams = {appName: 'foo', appType: 'source'};
49+
const commonTestParams = { appName: 'foo', appType: 'source' };
4750

4851
const sourceMock = JSON.parse(JSON.stringify(APPS));
4952
const appMock = sourceMock.items[0];
@@ -69,14 +72,15 @@ describe('AppDetailsComponent', () => {
6972
RouterTestingModule.withRoutes([])
7073
],
7174
providers: [
72-
{provide: AppsService, useValue: appsService},
73-
{provide: AuthService, useValue: authService},
74-
{provide: ActivatedRoute, useValue: activeRoute},
75-
{provide: BsModalService, useValue: modalService},
76-
{provide: ConfirmService, useValue: confirmService},
77-
{provide: BusyService, useValue: new BusyService()},
78-
{provide: SharedAboutService, useValue: sharedAboutService},
79-
{provide: ToastyService, useValue: toastyService}
75+
{ provide: AppsService, useValue: appsService },
76+
{ provide: AuthService, useValue: authService },
77+
{ provide: ActivatedRoute, useValue: activeRoute },
78+
{ provide: BsModalService, useValue: modalService },
79+
{ provide: ConfirmService, useValue: confirmService },
80+
{ provide: BusyService, useValue: new BusyService() },
81+
{ provide: RoutingStateService, useValue: routingStateService },
82+
{ provide: SharedAboutService, useValue: sharedAboutService },
83+
{ provide: ToastyService, useValue: toastyService }
8084
]
8185
})
8286
.compileComponents();
@@ -99,11 +103,11 @@ describe('AppDetailsComponent', () => {
99103

100104
it('Should cancel (footer close)', () => {
101105
fixture.detectChanges();
102-
const navigate = spyOn((<any>component).router, 'navigate');
106+
const navigate = spyOn(routingStateService, 'back');
103107
const bt: HTMLElement = fixture.debugElement.query(By.css('.footer-actions .btn-default')).nativeElement;
104108
bt.click();
105109
fixture.detectChanges();
106-
expect(navigate).toHaveBeenCalledWith(['apps']);
110+
expect(navigate).toHaveBeenCalled();
107111
});
108112

109113
});
@@ -140,9 +144,9 @@ describe('AppDetailsComponent', () => {
140144
it('should apply a sort on name and uri', () => {
141145
const sortProperty: HTMLElement = fixture.debugElement.query(By.css('#sort-name a')).nativeElement;
142146
[
143-
{click: sortProperty, nameAsc: true, sort: 'name', order: 'ASC'},
144-
{click: sortProperty, nameDesc: true, sort: 'name', order: 'DESC'},
145-
{click: sortProperty, sort: '', order: ''},
147+
{ click: sortProperty, nameAsc: true, sort: 'name', order: 'ASC' },
148+
{ click: sortProperty, nameDesc: true, sort: 'name', order: 'DESC' },
149+
{ click: sortProperty, sort: '', order: '' },
146150
].forEach((test) => {
147151
test.click.click();
148152
fixture.detectChanges();
@@ -270,7 +274,7 @@ describe('AppDetailsComponent', () => {
270274
const spy = spyOn(modalService, 'show');
271275
fixture.debugElement.query(By.css('#no-default-version a')).nativeElement.click();
272276
fixture.detectChanges();
273-
expect(spy).toHaveBeenCalledWith(AppVersionsComponent, {class: 'modal-xl'});
277+
expect(spy).toHaveBeenCalledWith(AppVersionsComponent, { class: 'modal-xl' });
274278
});
275279

276280
// Note: have to be review (related to the workaround #1871)

ui/src/app/apps/app-details/app-details.component.ts

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import {Component, OnInit, OnDestroy} from '@angular/core';
2-
import {Router, ActivatedRoute, Params} from '@angular/router';
3-
import {AppsService} from '../apps.service';
4-
import {ToastyService} from 'ng2-toasty';
5-
import {ApplicationType, DetailedAppRegistration} from '../../shared/model';
6-
import {Subscription} from 'rxjs/Subscription';
7-
import {SharedAboutService} from '../../shared/services/shared-about.service';
8-
import {AppRegistration} from '../../shared/model/app-registration.model';
9-
import {FeatureInfo} from '../../shared/model/about/feature-info.model';
10-
import {AppVersionsComponent} from '../app-versions/app-versions.component';
11-
import {BsModalService} from 'ngx-bootstrap';
12-
import {SortParams} from '../../shared/components/shared.interface';
13-
import {combineLatest} from 'rxjs/operators/combineLatest';
14-
import {Subject} from 'rxjs/Subject';
15-
import {takeUntil} from 'rxjs/operators';
16-
import {BusyService} from '../../shared/services/busy.service';
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
2+
import { Router, ActivatedRoute, Params } from '@angular/router';
3+
import { AppsService } from '../apps.service';
4+
import { ToastyService } from 'ng2-toasty';
5+
import { ApplicationType, DetailedAppRegistration } from '../../shared/model';
6+
import { Subscription } from 'rxjs/Subscription';
7+
import { SharedAboutService } from '../../shared/services/shared-about.service';
8+
import { AppRegistration } from '../../shared/model/app-registration.model';
9+
import { FeatureInfo } from '../../shared/model/about/feature-info.model';
10+
import { AppVersionsComponent } from '../app-versions/app-versions.component';
11+
import { BsModalService } from 'ngx-bootstrap';
12+
import { SortParams } from '../../shared/components/shared.interface';
13+
import { combineLatest } from 'rxjs/operators/combineLatest';
14+
import { Subject } from 'rxjs/Subject';
15+
import { takeUntil } from 'rxjs/operators';
16+
import { BusyService } from '../../shared/services/busy.service';
17+
import { Location } from '@angular/common';
18+
import { RoutingStateService } from '../../shared/services/routing-state.service';
1719

1820
/**
1921
* Provides details for an App Registration
@@ -74,15 +76,15 @@ export class AppDetailsComponent implements OnInit, OnDestroy {
7476
* @param {SharedAboutService} sharedAboutService
7577
* @param {ToastyService} toastyService
7678
* @param {ActivatedRoute} route
77-
* @param {Router} router
79+
* @param {RoutingStateService} routingStateService
7880
* @param {BusyService} busyService
7981
* @param {BsModalService} modalService
8082
*/
8183
constructor(private appsService: AppsService,
8284
private sharedAboutService: SharedAboutService,
8385
private toastyService: ToastyService,
84-
private router: Router,
8586
private route: ActivatedRoute,
87+
private routingStateService: RoutingStateService,
8688
private busyService: BusyService,
8789
private modalService: BsModalService) {
8890

@@ -188,7 +190,7 @@ export class AppDetailsComponent implements OnInit, OnDestroy {
188190
*/
189191
versions(appRegistration: AppRegistration) {
190192
console.log(`Manage versions ${appRegistration.name} app.`, appRegistration);
191-
const modal = this.modalService.show(AppVersionsComponent, {class: 'modal-xl'});
193+
const modal = this.modalService.show(AppVersionsComponent, { class: 'modal-xl' });
192194
modal.content.open(appRegistration).subscribe(() => {
193195
this.loadVersions();
194196
});
@@ -208,9 +210,10 @@ export class AppDetailsComponent implements OnInit, OnDestroy {
208210
}
209211

210212
/**
211-
* Back to the applications list
213+
* Back action
214+
* Navigate to the previous URL or /apps
212215
*/
213216
cancel() {
214-
this.router.navigate(['apps']);
217+
this.routingStateService.back('/apps', /^(\/apps\/)/);
215218
}
216219
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Injectable } from '@angular/core';
2+
import { NavigationEnd, Router } from '@angular/router';
3+
import { filter } from 'rxjs/operators';
4+
5+
/**
6+
* This service provides a way to navigate into the history
7+
*
8+
* @author Damien Vitrac
9+
*/
10+
@Injectable()
11+
export class RoutingStateService {
12+
13+
/**
14+
* History array
15+
* @type {Array}
16+
*/
17+
private history = [];
18+
19+
/**
20+
* Constructor
21+
*
22+
* @param {Router} router
23+
*/
24+
constructor(private router: Router) {
25+
}
26+
27+
/**
28+
* Start to watch and store the route changes
29+
*/
30+
public watchRouting(): void {
31+
this.router.events
32+
.pipe(filter(event => event instanceof NavigationEnd))
33+
.subscribe(({ urlAfterRedirects }: NavigationEnd) => {
34+
this.history = [...this.history, { time: new Date().getTime(), url: urlAfterRedirects }];
35+
});
36+
}
37+
38+
/**
39+
* Return the store history
40+
* @returns {string[]}
41+
*/
42+
public getHistory(): string[] {
43+
return this.history.map((history) => history.url);
44+
}
45+
46+
/**
47+
* Performs a back action
48+
* Update the history variable
49+
*
50+
* @param {string} defaultUrl Redirect to this URL if the history is not available
51+
* @param {RegExp} isNotRegex Negative regex
52+
*/
53+
public back(defaultUrl: string, isNotRegex?: RegExp) {
54+
let url = defaultUrl;
55+
if (this.history.length > 1) {
56+
const item = this.history.slice(0, this.history.length - 1)
57+
.reverse()
58+
.find((history) => {
59+
return !isNotRegex || !(isNotRegex.test(history.url));
60+
});
61+
this.history = this.history.slice(0, this.history.indexOf(item));
62+
url = item.url;
63+
}
64+
this.router.navigate([url]);
65+
}
66+
67+
}

ui/src/app/shared/shared.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { LoaderComponent } from './components/loader/loader.component';
4242
import { TruncatorComponent } from './components/truncator/truncator.component';
4343
import { TruncatorWidthProviderDirective } from './components/truncator/truncator-width-provider.directive';
4444
import { PagerComponent } from './components/pager/pager.component';
45+
import { RoutingStateService } from './services/routing-state.service';
4546

4647
const busyConfig: BusyConfig = {
4748
message: 'Processing..',
@@ -111,7 +112,8 @@ const busyConfig: BusyConfig = {
111112
ParserService,
112113
ErrorHandler,
113114
ConfirmService,
114-
BusyService
115+
BusyService,
116+
RoutingStateService
115117
],
116118
exports: [
117119
StreamDslComponent,

ui/src/app/streams/stream/stream.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ <h1>Detailed view of stream <strong>'{{ id }}'</strong></h1>
1818
</div>
1919

2020
<div class="footer-actions" style="text-align: center;margin: 1rem 0">
21-
<a id="btn-cancel" class="btn btn-default" routerLink="/streams">Cancel</a>
21+
<button type="button" id="btn-cancel" class="btn btn-default" (click)="cancel()">Back</button>
2222
</div>

ui/src/app/streams/stream/stream.component.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { RenderService } from '../components/flo/render.service';
1313
import { MockSharedAppService } from '../../tests/mocks/shared-app';
1414
import { BusyService } from '../../shared/services/busy.service';
1515
import { StreamComponent } from './stream.component';
16+
import { RoutingStateService } from '../../shared/services/routing-state.service';
17+
import { MockRoutingStateService } from '../../tests/mocks/routing-state';
1618

1719

1820
/**
@@ -30,6 +32,7 @@ describe('StreamComponent', () => {
3032
const metamodelService = new MetamodelService(new MockSharedAppService());
3133
const renderService = new RenderService(metamodelService);
3234
const busyService = new BusyService();
35+
const routingStateService = new MockRoutingStateService();
3336

3437
beforeEach(async(() => {
3538
activeRoute = new MockActivatedRoute();
@@ -48,6 +51,7 @@ describe('StreamComponent', () => {
4851
{ provide: ActivatedRoute, useValue: activeRoute },
4952
{ provide: BusyService, useValue: busyService },
5053
{ provide: ToastyService, useValue: toastyService },
54+
{ provide: RoutingStateService, useValue: routingStateService },
5155
{ provide: MetamodelService, useValue: metamodelService },
5256
{ provide: RenderService, useValue: renderService }
5357
]

ui/src/app/streams/stream/stream.component.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
22
import { ActivatedRoute } from '@angular/router';
3+
import { RoutingStateService } from '../../shared/services/routing-state.service';
34

45
/**
56
* Component that shows the details of a Stream Definition
@@ -26,8 +27,10 @@ export class StreamComponent implements OnInit {
2627
/**
2728
* Constructor
2829
* @param {ActivatedRoute} route
30+
* @param {RoutingStateService} routingStateService
2931
*/
30-
constructor(private route: ActivatedRoute) {
32+
constructor(private route: ActivatedRoute,
33+
private routingStateService: RoutingStateService) {
3134
}
3235

3336
/**
@@ -40,4 +43,12 @@ export class StreamComponent implements OnInit {
4043
});
4144
}
4245

46+
/**
47+
* Back action
48+
* Navigate to the previous URL or /streams/definitions
49+
*/
50+
cancel() {
51+
this.routingStateService.back('/streams/definitions', /^(\/streams\/definitions\/)/);
52+
}
53+
4354
}

0 commit comments

Comments
 (0)