Skip to content

Commit 74168d0

Browse files
committed
feat(workbench): support navigation of views in the initial layout (or perspective)
Previously, views in the initial layout (or perspective) were limited to only supporting empty-path routes and could not be navigated or opened multiple times. BREAKING CHANGE: Views in the initial layout (or perspective) must now be navigated. Previously, no explicit navigation was required because view and route were coupled via route outlet and view id. closes #445 BREAKING CHANGE: Views in the initial layout (or perspective) must be navigated. Previously, no explicit navigation was required as the view and route were coupled via the route's outlet and the view's id. **Migrate the layout as follows:** - Navigate views added to the layout; - Pass an empty commands array to match routes with an empty path; - Pass a navigation hint to differentiate between routes with empty paths; you can use the same value as before for the view id; ```ts // Before Migration provideWorkbench({ layout: (factory: WorkbenchLayoutFactory) => factory .addPart(MAIN_AREA) .addPart('left', {relativeTo: MAIN_AREA, align: 'left'}) .addView('navigator', {partId: 'left', activateView: true}), }); // After Migration provideWorkbench({ layout: (factory: WorkbenchLayoutFactory) => factory .addPart(MAIN_AREA) .addPart('left', {relativeTo: MAIN_AREA, align: 'left'}) .addView('navigator', {partId: 'left', activateView: true}) // Navigate view, passing hint to match route. .navigateView('navigator', [], {hint: 'navigator'}), }); ``` **Migrate the routes as follows:** - Remove the outlet property; - Add `canMatchWorkbenchView` guard and initialize it with the hint passed to the navigation; you can use the same value as before for the outlet name; ```ts // Before Migration provideRouter([ { path: '', outlet: 'navigator', loadComponent: () => ..., }, ]); // After Migration provideRouter([ { path: '', // Match route only if navigated with specified hint. canMatch: [canMatchWorkbenchView('navigator')], loadComponent: () => ..., }, ]); ```
1 parent 9093a7e commit 74168d0

File tree

312 files changed

+9662
-5487
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

312 files changed

+9662
-5487
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ SCION Workbench enables the creation of Angular web applications that require a
1616
- [**Getting Started**][link-getting-started]\
1717
Follow these steps to install the SCION Workbench in your project and start with a basic introduction to the SCION Workbench.
1818

19-
#### Workbench Demo Applications
19+
#### Workbench Sample Applications
2020

21-
- [**SCION Workbench Testing App**][link-testing-app]\
22-
Visit our technical testing application to explore the workbench and experiment with its features.
21+
- [**Playground Application**][link-playground-app]\
22+
Visit our playground application to explore the workbench and experiment with its features.
2323

24-
- [**SCION Workbench Getting Started App**][link-getting-started-app]\
24+
- [**Getting Started Application**][link-getting-started-app]\
2525
Open the application developed in the [Getting Started][link-getting-started] guide.
2626

2727
#### Documentation
@@ -72,7 +72,7 @@ SCION Workbench enables the creation of Angular web applications that require a
7272
[link-getting-started]: /docs/site/getting-started.md
7373
[link-howto]: /docs/site/howto/how-to.md
7474
[link-demo-app]: https://schweizerischebundesbahnen.github.io/scion-workbench-demo/#/(view.24:person/64//view.22:person/32//view.5:person/79//view.3:person/15//view.2:person/38//view.1:person/66//activity:person-list)?viewgrid=eyJpZCI6MSwic2FzaDEiOlsidmlld3BhcnQuMSIsInZpZXcuMSIsInZpZXcuMiIsInZpZXcuMSJdLCJzYXNoMiI6eyJpZCI6Miwic2FzaDEiOlsidmlld3BhcnQuMiIsInZpZXcuMyIsInZpZXcuMyJdLCJzYXNoMiI6eyJpZCI6Mywic2FzaDEiOlsidmlld3BhcnQuNCIsInZpZXcuMjQiLCJ2aWV3LjI0Il0sInNhc2gyIjpbInZpZXdwYXJ0LjMiLCJ2aWV3LjIyIiwidmlldy41Iiwidmlldy4yMiJdLCJzcGxpdHRlciI6MC41MTk0Mzg0NDQ5MjQ0MDY2LCJoc3BsaXQiOmZhbHNlfSwic3BsaXR0ZXIiOjAuNTU5NDI0MzI2ODMzNzk3NSwiaHNwbGl0Ijp0cnVlfSwic3BsaXR0ZXIiOjAuMzIyNjI3NzM3MjI2Mjc3MywiaHNwbGl0IjpmYWxzZX0%3D
75-
[link-testing-app]: https://scion-workbench-testing-app.vercel.app
75+
[link-playground-app]: https://scion-workbench-testing-app.vercel.app
7676
[link-getting-started-app]: https://scion-workbench-getting-started.vercel.app
7777
[link-features]: /docs/site/features.md
7878
[link-announcements]: /docs/site/announcements.md

apps/workbench-client-testing-app/src/app/app.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<ng-container *ngIf="workbenchContextActive | async; else workbench_context_note">
22
<sci-viewport cdkTrapFocus>
3-
<router-outlet></router-outlet>
3+
<router-outlet/>
44
</sci-viewport>
55
<section class="metadata">
66
<span class="chip has-focus e2e-has-focus" *ngIf="focusMonitor!.focus$ | async">has-focus</span>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<input [formControl]="formControl" class="e2e-class" placeholder="class-1 class-2">
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@use '@scion/components.internal/design' as sci-design;
2+
3+
:host {
4+
display: inline-grid;
5+
6+
> input {
7+
@include sci-design.style-input-field();
8+
min-width: 0;
9+
}
10+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2018-2024 Swiss Federal Railways
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*/
10+
11+
import {Component, forwardRef} from '@angular/core';
12+
import {ControlValueAccessor, NG_VALUE_ACCESSOR, NonNullableFormBuilder, ReactiveFormsModule} from '@angular/forms';
13+
import {noop} from 'rxjs';
14+
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
15+
import {Arrays} from '@scion/toolkit/util';
16+
17+
@Component({
18+
selector: 'app-css-class',
19+
templateUrl: './css-class.component.html',
20+
styleUrls: ['./css-class.component.scss'],
21+
standalone: true,
22+
imports: [
23+
ReactiveFormsModule,
24+
],
25+
providers: [
26+
{provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => CssClassComponent)},
27+
],
28+
})
29+
export class CssClassComponent implements ControlValueAccessor {
30+
31+
private _cvaChangeFn: (cssClasses: string | string[] | undefined) => void = noop;
32+
private _cvaTouchedFn: () => void = noop;
33+
34+
protected formControl = this._formBuilder.control<string>('');
35+
36+
constructor(private _formBuilder: NonNullableFormBuilder) {
37+
this.formControl.valueChanges
38+
.pipe(takeUntilDestroyed())
39+
.subscribe(() => {
40+
this._cvaChangeFn(this.parse(this.formControl.value));
41+
this._cvaTouchedFn();
42+
});
43+
}
44+
45+
private parse(stringified: string): string[] | string | undefined {
46+
const cssClasses = stringified.split(/\s+/).filter(Boolean);
47+
switch (cssClasses.length) {
48+
case 0:
49+
return undefined;
50+
case 1:
51+
return cssClasses[0];
52+
default:
53+
return cssClasses;
54+
}
55+
}
56+
57+
private stringify(cssClasses: string | string[] | undefined | null): string {
58+
return Arrays.coerce(cssClasses).join(' ');
59+
}
60+
61+
/**
62+
* Method implemented as part of `ControlValueAccessor` to work with Angular forms API
63+
* @docs-private
64+
*/
65+
public writeValue(cssClasses: string | string[] | undefined | null): void {
66+
this.formControl.setValue(this.stringify(cssClasses), {emitEvent: false});
67+
}
68+
69+
/**
70+
* Method implemented as part of `ControlValueAccessor` to work with Angular forms API
71+
* @docs-private
72+
*/
73+
public registerOnChange(fn: any): void {
74+
this._cvaChangeFn = fn;
75+
}
76+
77+
/**
78+
* Method implemented as part of `ControlValueAccessor` to work with Angular forms API
79+
* @docs-private
80+
*/
81+
public registerOnTouched(fn: any): void {
82+
this._cvaTouchedFn = fn;
83+
}
84+
}

apps/workbench-client-testing-app/src/app/dialog-opener-page/dialog-opener-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</sci-form-field>
2828

2929
<sci-form-field label="CSS Class(es)">
30-
<input [formControl]="form.controls.options.controls.cssClass" class="e2e-class" placeholder="Separate multiple CSS classes by space">
30+
<app-css-class [formControl]="form.controls.options.controls.cssClass"/>
3131
</sci-form-field>
3232
</section>
3333
</form>

apps/workbench-client-testing-app/src/app/dialog-opener-page/dialog-opener-page.component.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010

1111
import {Component} from '@angular/core';
1212
import {FormGroup, NonNullableFormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
13-
import {WorkbenchDialogService, WorkbenchView} from '@scion/workbench-client';
13+
import {WorkbenchDialogService, WorkbenchView, ViewId} from '@scion/workbench-client';
1414
import {stringifyError} from '../common/stringify-error.util';
1515
import {SciFormFieldComponent} from '@scion/components.internal/form-field';
1616
import {KeyValueEntry, SciKeyValueFieldComponent} from '@scion/components.internal/key-value-field';
1717
import {SciCheckboxComponent} from '@scion/components.internal/checkbox';
1818
import {startWith} from 'rxjs/operators';
1919
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
20+
import {CssClassComponent} from '../css-class/css-class.component';
2021

2122
@Component({
2223
selector: 'app-dialog-opener-page',
@@ -28,6 +29,7 @@ import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
2829
SciFormFieldComponent,
2930
SciKeyValueFieldComponent,
3031
SciCheckboxComponent,
32+
CssClassComponent,
3133
],
3234
})
3335
export default class DialogOpenerPageComponent {
@@ -46,9 +48,9 @@ export default class DialogOpenerPageComponent {
4648
options: this._formBuilder.group({
4749
params: this._formBuilder.array<FormGroup<KeyValueEntry>>([]),
4850
modality: this._formBuilder.control<'application' | 'view' | ''>(''),
49-
contextualViewId: this._formBuilder.control(''),
51+
contextualViewId: this._formBuilder.control<ViewId | ''>(''),
5052
animate: this._formBuilder.control(undefined),
51-
cssClass: this._formBuilder.control(''),
53+
cssClass: this._formBuilder.control<string | string[] | undefined>(undefined),
5254
}),
5355
});
5456

@@ -76,7 +78,7 @@ export default class DialogOpenerPageComponent {
7678
context: {
7779
viewId: this.form.controls.options.controls.contextualViewId.value || undefined,
7880
},
79-
cssClass: this.form.controls.options.controls.cssClass.value.split(/\s+/).filter(Boolean),
81+
cssClass: this.form.controls.options.controls.cssClass.value,
8082
})
8183
.then(result => this.returnValue = result)
8284
.catch(error => this.dialogError = stringifyError(error) || 'Dialog was closed with an error');

apps/workbench-client-testing-app/src/app/message-box-opener-page/message-box-opener-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</sci-form-field>
4545

4646
<sci-form-field label="CSS Class(es)">
47-
<input [formControl]="form.controls.cssClass" class="e2e-class" placeholder="Separate multiple CSS classes by space">
47+
<app-css-class [formControl]="form.controls.cssClass"/>
4848
</sci-form-field>
4949
</section>
5050

apps/workbench-client-testing-app/src/app/message-box-opener-page/message-box-opener-page.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {SciFormFieldComponent} from '@scion/components.internal/form-field';
1717
import {NgIf} from '@angular/common';
1818
import {stringifyError} from '../common/stringify-error.util';
1919
import {SciCheckboxComponent} from '@scion/components.internal/checkbox';
20+
import {CssClassComponent} from '../css-class/css-class.component';
2021

2122
@Component({
2223
selector: 'app-message-box-opener-page',
@@ -29,6 +30,7 @@ import {SciCheckboxComponent} from '@scion/components.internal/checkbox';
2930
SciFormFieldComponent,
3031
SciKeyValueFieldComponent,
3132
SciCheckboxComponent,
33+
CssClassComponent,
3234
],
3335
})
3436
export default class MessageBoxOpenerPageComponent {
@@ -42,7 +44,7 @@ export default class MessageBoxOpenerPageComponent {
4244
severity: this._formBuilder.control<'info' | 'warn' | 'error' | ''>(''),
4345
modality: this._formBuilder.control<'application' | 'view' | ''>(''),
4446
contentSelectable: this._formBuilder.control(true),
45-
cssClass: this._formBuilder.control(''),
47+
cssClass: this._formBuilder.control<string | string[] | undefined>(undefined),
4648
viewContext: this._formBuilder.control(true),
4749
});
4850

@@ -76,7 +78,7 @@ export default class MessageBoxOpenerPageComponent {
7678
severity: this.form.controls.severity.value || undefined,
7779
modality: this.form.controls.modality.value || undefined,
7880
contentSelectable: this.form.controls.contentSelectable.value || undefined,
79-
cssClass: this.form.controls.cssClass.value.split(/\s+/).filter(Boolean),
81+
cssClass: this.form.controls.cssClass.value,
8082
}, qualifier ?? undefined)
8183
.then(closeAction => this.closeAction = closeAction)
8284
.catch(error => this.openError = stringifyError(error));

apps/workbench-client-testing-app/src/app/notification-opener-page/notification-opener-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</sci-form-field>
4444

4545
<sci-form-field label="CSS Class(es)">
46-
<input [formControl]="form.controls.cssClass" class="e2e-class" placeholder="Separate multiple CSS classes by space">
46+
<app-css-class [formControl]="form.controls.cssClass"/>
4747
</sci-form-field>
4848
</section>
4949
</form>

0 commit comments

Comments
 (0)