diff --git a/.eslintrc.json b/.eslintrc.json
index 962713016..c98a70d6a 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -110,7 +110,13 @@
"implicit-arrow-linebreak": "error",
"object-curly-spacing": "error",
"nonblock-statement-body-position": "error",
- "rxjs/no-implicit-any-catch": "off"
+ "rxjs/no-implicit-any-catch": "off",
+ "rxjs/no-unsafe-takeuntil": [
+ "error",
+ {
+ "alias": ["takeUntil", "takeUntilDestroyed"]
+ }
+ ]
}
},
{
@@ -118,7 +124,8 @@
"*.html"
],
"extends": [
- "plugin:@angular-eslint/template/recommended"
+ "plugin:@angular-eslint/template/recommended",
+ "plugin:@angular-eslint/template/accessibility"
],
"rules": {}
}
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 79110fb71..35101375f 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -1,7 +1,7 @@
name: Continuous Integration and Delivery
on: [ push, pull_request ]
env:
- NODE_VERSION: 16.16.0
+ NODE_VERSION: 18.16.0
jobs:
install:
name: 'Installing NPM modules'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f1cbdde8f..2a994a22b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,7 +36,7 @@ This section explains how to submit a pull request.
Development
-Make sure to use Node.js version 16.16.0 for contributing to SCION. We suggest using [Node Version Manager](https://github.com/nvm-sh/nvm) if you need different Node.js versions for other projects.
+Make sure to use Node.js version 18.16.0 for contributing to SCION. We suggest using [Node Version Manager](https://github.com/nvm-sh/nvm) if you need different Node.js versions for other projects.
For development, you can uncomment the section `PATH-OVERRIDE-FOR-DEVELOPMENT` in `tsconfig.json`. This allows running tests or serving applications without having to build dependent modules first.
diff --git a/README.md b/README.md
index 2ee71d05f..c2e4a0f69 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,7 @@ SCION Workbench enables the creation of Angular web applications that require a
***
### Versions
+- `v16.0.0-beta.1` and newer are compatible with Angular version 16.x.
- `v15.0.0-beta.1` and newer are compatible with Angular version 15.x.
- `v14.0.0-beta.1` and newer are compatible with Angular version 14.x.
- `v13.0.0-beta.1` and newer are compatible with Angular version 13.x.
diff --git a/apps/workbench-client-testing-app/src/app/activator/activator.module.ts b/apps/workbench-client-testing-app/src/app/activator/activator.module.ts
index 47483a619..cb351320c 100644
--- a/apps/workbench-client-testing-app/src/app/activator/activator.module.ts
+++ b/apps/workbench-client-testing-app/src/app/activator/activator.module.ts
@@ -73,25 +73,6 @@ export default class ActivatorModule {
},
});
- // Register view to navigate using the workbench router legacy API.
- // @deprecated since version 14; API will be removed in version 16; used internally to test not to break old workbench clients
- await this._manifestService.registerCapability({
- type: WorkbenchCapabilities.View,
- qualifier: {
- component: 'router-legacy',
- app,
- },
- description: '[e2e] Allows opening a microfrontend in a workbench view using the "old" router API',
- private: false,
- properties: {
- path: 'test-router-legacy',
- pinToStartPage: true,
- title: 'Workbench Router (legacy)',
- heading,
- cssClass: 'e2e-test-router-legacy',
- },
- });
-
// Register view to register workbench capabilities dynamically at runtime.
await this._manifestService.registerCapability({
type: WorkbenchCapabilities.View,
diff --git a/apps/workbench-client-testing-app/src/app/app.routes.ts b/apps/workbench-client-testing-app/src/app/app.routes.ts
index bf7457554..206d36eca 100644
--- a/apps/workbench-client-testing-app/src/app/app.routes.ts
+++ b/apps/workbench-client-testing-app/src/app/app.routes.ts
@@ -19,10 +19,6 @@ export const routes: Routes = [
path: 'test-router',
loadComponent: () => import('./router-page/router-page.component'),
},
- {
- path: 'test-router-legacy',
- loadComponent: () => import('./router-page-legacy/router-page-legacy.component'),
- },
{
path: 'test-view',
loadComponent: () => import('./view-page/view-page.component'),
diff --git a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.html b/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.html
deleted file mode 100644
index 185ab599f..000000000
--- a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
diff --git a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.scss b/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.scss
deleted file mode 100644
index 4bfcecbf1..000000000
--- a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-:host {
- display: grid;
- grid-auto-rows: max-content;
- row-gap: 1em;
- padding: 1em;
-
- > section, form {
- display: grid;
- grid-row-gap: .5em;
- border: 1px solid var(--sci-color-P400);
- border-radius: 5px;
- padding: 1em;
-
- > header {
- margin-top: 0;
- margin-bottom: 2em;
- font-weight: bold;
- }
- }
-
- > output.navigate-error {
- border: 1px solid var(--sci-color-warn);
- background-color: var(--sci-color-W100);
- border-radius: 3px;
- padding: 1em;
- }
-}
diff --git a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.ts b/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.ts
deleted file mode 100644
index 2787a3aec..000000000
--- a/apps/workbench-client-testing-app/src/app/router-page-legacy/router-page-legacy.component.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2018-2022 Swiss Federal Railways
- *
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-
-import {Component} from '@angular/core';
-import {ReactiveFormsModule, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators} from '@angular/forms';
-import {ɵWorkbenchLegacyNavigationExtras, ɵWorkbenchLegacyRouter} from '@scion/workbench-client';
-import {SciParamsEnterComponent, SciParamsEnterModule} from '@scion/components.internal/params-enter';
-import {coerceNumberProperty} from '@angular/cdk/coercion';
-import {convertValueFromUI} from '../common/convert-value-from-ui.util';
-import {NgIf} from '@angular/common';
-import {SciFormFieldModule} from '@scion/components.internal/form-field';
-import {SciCheckboxModule} from '@scion/components.internal/checkbox';
-
-const QUALIFIER = 'qualifier';
-const PARAMS = 'params';
-const ACTIVATE_IF_PRESENT = 'activateIfPresent';
-const CLOSE_IF_PRESENT = 'closeIfPresent';
-const TARGET = 'target';
-const SELF_VIEW_ID = 'selfViewId';
-const INSERTION_INDEX = 'insertionIndex';
-const CSS_CLASS = 'cssClass';
-
-/**
- * @deprecated since version 14; API will be removed in version 16; used internally to test not to break old workbench clients
- */
-@Component({
- selector: 'app-router-page-legacy',
- templateUrl: './router-page-legacy.component.html',
- styleUrls: ['./router-page-legacy.component.scss'],
- standalone: true,
- imports: [
- NgIf,
- ReactiveFormsModule,
- SciFormFieldModule,
- SciParamsEnterModule,
- SciCheckboxModule,
- ],
-})
-export default class RouterPageLegacyComponent {
-
- public readonly QUALIFIER = QUALIFIER;
- public readonly PARAMS = PARAMS;
- public readonly TARGET = TARGET;
- public readonly SELF_VIEW_ID = SELF_VIEW_ID;
- public readonly INSERTION_INDEX = INSERTION_INDEX;
- public readonly ACTIVATE_IF_PRESENT = ACTIVATE_IF_PRESENT;
- public readonly CLOSE_IF_PRESENT = CLOSE_IF_PRESENT;
- public readonly CSS_CLASS = CSS_CLASS;
-
- public form: UntypedFormGroup;
- public navigateError: string;
-
- private _router: ɵWorkbenchLegacyRouter;
-
- constructor(formBuilder: UntypedFormBuilder) {
- this._router = new ɵWorkbenchLegacyRouter();
- this.form = formBuilder.group({
- [QUALIFIER]: formBuilder.array([], Validators.required),
- [PARAMS]: formBuilder.array([]),
- [TARGET]: formBuilder.control(''),
- [SELF_VIEW_ID]: formBuilder.control(''),
- [INSERTION_INDEX]: formBuilder.control(''),
- [ACTIVATE_IF_PRESENT]: formBuilder.control(undefined),
- [CLOSE_IF_PRESENT]: formBuilder.control(undefined),
- [CSS_CLASS]: formBuilder.control(undefined),
- });
- }
-
- public async onNavigate(): Promise {
- this.navigateError = undefined;
-
- const qualifier = SciParamsEnterComponent.toParamsDictionary(this.form.get(QUALIFIER) as UntypedFormArray);
- const params = SciParamsEnterComponent.toParamsDictionary(this.form.get(PARAMS) as UntypedFormArray);
-
- // Convert entered params to their actual values.
- params && Object.entries(params).forEach(([paramName, paramValue]) => params[paramName] = convertValueFromUI(paramValue));
-
- const extras: ɵWorkbenchLegacyNavigationExtras = {
- activateIfPresent: this.form.get(ACTIVATE_IF_PRESENT).value,
- closeIfPresent: this.form.get(CLOSE_IF_PRESENT).value,
- target: this.form.get(TARGET).value || undefined,
- selfViewId: this.form.get(SELF_VIEW_ID).value || undefined,
- blankInsertionIndex: coerceInsertionIndex(this.form.get(INSERTION_INDEX).value),
- params: params || undefined,
- cssClass: this.form.get(CSS_CLASS).value?.split(/\s+/).filter(Boolean),
- };
- await this._router.navigate(qualifier, extras).catch(error => this.navigateError = error);
- }
-}
-
-function coerceInsertionIndex(value: any): number | 'start' | 'end' | undefined {
- if (value === '') {
- return undefined;
- }
- if (value === 'start' || value === 'end' || value === undefined) {
- return value;
- }
- return coerceNumberProperty(value);
-}
diff --git a/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.html b/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.html
index 290654591..9face4de4 100644
--- a/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.html
+++ b/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.html
@@ -29,10 +29,10 @@
-
diff --git a/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.scss b/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.scss
index 19b4c9fe4..e3f6700ea 100644
--- a/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.scss
+++ b/apps/workbench-client-testing-app/src/app/test-pages/angular-zone-test-page/angular-zone-test-page.component.scss
@@ -12,14 +12,14 @@
flex-direction: column;
gap: .5em;
- label {
+ > div.run-in-angular-checkbox {
display: flex;
gap: .75em;
align-items: center;
user-select: none;
}
- output.zone {
+ > output.zone {
padding: 1em;
border-width: 1px;
border-style: solid;
diff --git a/apps/workbench-client-testing-app/src/app/view-page/view-page.component.ts b/apps/workbench-client-testing-app/src/app/view-page/view-page.component.ts
index fff148247..7a75c8110 100644
--- a/apps/workbench-client-testing-app/src/app/view-page/view-page.component.ts
+++ b/apps/workbench-client-testing-app/src/app/view-page/view-page.component.ts
@@ -13,8 +13,8 @@ import {ReactiveFormsModule, UntypedFormArray, UntypedFormBuilder, UntypedFormGr
import {ViewClosingEvent, ViewClosingListener, WorkbenchMessageBoxService, WorkbenchRouter, WorkbenchView} from '@scion/workbench-client';
import {ActivatedRoute} from '@angular/router';
import {UUID} from '@scion/toolkit/uuid';
-import {MonoTypeOperatorFunction, NEVER, Subject} from 'rxjs';
-import {finalize, startWith, take, takeUntil} from 'rxjs/operators';
+import {MonoTypeOperatorFunction, NEVER} from 'rxjs';
+import {finalize, startWith, take} from 'rxjs/operators';
import {APP_INSTANCE_ID} from '../app-instance-id';
import {SciParamsEnterComponent, SciParamsEnterModule} from '@scion/components.internal/params-enter';
import {AsyncPipe, JsonPipe, Location, NgIf} from '@angular/common';
@@ -26,6 +26,7 @@ import {SciPropertyModule} from '@scion/components.internal/property';
import {AppendParamDataTypePipe} from '../common/append-param-data-type.pipe';
import {SciCheckboxModule} from '@scion/components.internal/checkbox';
import {SciViewportModule} from '@scion/components/viewport';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
const TITLE = 'title';
const HEADING = 'heading';
@@ -70,8 +71,6 @@ export default class ViewPageComponent implements ViewClosingListener, OnDestroy
public form: UntypedFormGroup;
public uuid = UUID.randomUUID();
- private _destroy$ = new Subject();
-
constructor(formBuilder: UntypedFormBuilder,
public view: WorkbenchView,
public route: ActivatedRoute,
@@ -104,7 +103,7 @@ export default class ViewPageComponent implements ViewClosingListener, OnDestroy
this.view.capability$
.pipe(
take(1),
- takeUntil(this._destroy$),
+ takeUntilDestroyed(),
)
.subscribe(capability => {
console.debug(`[ViewCapability$::first] [component=ViewPageComponent@${this.uuid}, capabilityId=${capability.metadata.id}]`);
@@ -163,7 +162,7 @@ export default class ViewPageComponent implements ViewClosingListener, OnDestroy
this.form.get(CONFIRM_CLOSING).valueChanges
.pipe(
startWith(this.form.get(CONFIRM_CLOSING).value as boolean),
- takeUntil(this._destroy$),
+ takeUntilDestroyed(),
)
.subscribe(confirmClosing => {
if (confirmClosing) {
@@ -189,7 +188,7 @@ export default class ViewPageComponent implements ViewClosingListener, OnDestroy
private installViewActiveStateLogger(): void {
this.view.active$
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(active => {
if (active) {
console.debug(`[ViewActivate] [component=ViewPageComponent@${this.uuid}]`);
@@ -221,6 +220,5 @@ export default class ViewPageComponent implements ViewClosingListener, OnDestroy
public ngOnDestroy(): void {
this.view.removeClosingListener(this);
- this._destroy$.next();
}
}
diff --git a/apps/workbench-getting-started-app/src/app/todo/todo.component.html b/apps/workbench-getting-started-app/src/app/todo/todo.component.html
index 0f5ddb574..d9574d0e4 100644
--- a/apps/workbench-getting-started-app/src/app/todo/todo.component.html
+++ b/apps/workbench-getting-started-app/src/app/todo/todo.component.html
@@ -1,5 +1,5 @@
- {{todo.task}}
- {{todo.dueDate | date:'short'}}
- {{todo.notes}}
+ Task:{{todo.task}}
+ Due Date:{{todo.dueDate | date:'short'}}
+ Notes:{{todo.notes}}
diff --git a/apps/workbench-testing-app/src/app/app.component.ts b/apps/workbench-testing-app/src/app/app.component.ts
index 3a00c494e..5d1c5cb2f 100644
--- a/apps/workbench-testing-app/src/app/app.component.ts
+++ b/apps/workbench-testing-app/src/app/app.component.ts
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: EPL-2.0
*/
-import {Component, HostBinding, OnDestroy} from '@angular/core';
-import {filter, takeUntil} from 'rxjs/operators';
+import {Component, HostBinding} from '@angular/core';
+import {filter} from 'rxjs/operators';
import {NavigationCancel, NavigationEnd, NavigationError, Router, RouterOutlet} from '@angular/router';
-import {Subject} from 'rxjs';
import {UUID} from '@scion/toolkit/uuid';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
@Component({
selector: 'app-root',
@@ -21,9 +21,7 @@ import {UUID} from '@scion/toolkit/uuid';
standalone: true,
imports: [RouterOutlet],
})
-export class AppComponent implements OnDestroy {
-
- private _destroy$ = new Subject();
+export class AppComponent {
/**
* Unique id that is set after a navigation has been performed.
@@ -42,14 +40,10 @@ export class AppComponent implements OnDestroy {
this._router.events
.pipe(
filter(event => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError),
- takeUntil(this._destroy$),
+ takeUntilDestroyed(),
)
.subscribe(() => {
this.navigationId = UUID.randomUUID();
});
}
-
- public ngOnDestroy(): void {
- this._destroy$.next();
- }
}
diff --git a/apps/workbench-testing-app/src/app/inspect-message-box-provider/inspect-message-box.component.ts b/apps/workbench-testing-app/src/app/inspect-message-box-provider/inspect-message-box.component.ts
index f2da64017..01d31df39 100644
--- a/apps/workbench-testing-app/src/app/inspect-message-box-provider/inspect-message-box.component.ts
+++ b/apps/workbench-testing-app/src/app/inspect-message-box-provider/inspect-message-box.component.ts
@@ -8,10 +8,8 @@
* SPDX-License-Identifier: EPL-2.0
*/
-import {Component, OnDestroy} from '@angular/core';
+import {Component} from '@angular/core';
import {MessageBox} from '@scion/workbench';
-import {Subject} from 'rxjs';
-import {takeUntil} from 'rxjs/operators';
import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms';
import {UUID} from '@scion/toolkit/uuid';
import {NgIf} from '@angular/common';
@@ -19,6 +17,7 @@ import {SciFormFieldModule} from '@scion/components.internal/form-field';
import {SciViewportModule} from '@scion/components/viewport';
import {SciParamsEnterModule} from '@scion/components.internal/params-enter';
import {StringifyPipe} from '../common/stringify.pipe';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
const TITLE = 'title';
const SEVERITY = 'severity';
@@ -40,7 +39,7 @@ const RETURN_VALUE = 'returnValue';
SciParamsEnterModule,
],
})
-export class InspectMessageBoxComponent implements OnDestroy {
+export class InspectMessageBoxComponent {
public readonly TITLE = TITLE;
public readonly SEVERITY = SEVERITY;
@@ -48,8 +47,6 @@ export class InspectMessageBoxComponent implements OnDestroy {
public readonly ACTIONS = ACTIONS;
public readonly RETURN_VALUE = RETURN_VALUE;
- private _destroy$ = new Subject();
-
public uuid = UUID.randomUUID();
public form: UntypedFormGroup;
@@ -63,25 +60,25 @@ export class InspectMessageBoxComponent implements OnDestroy {
});
this.form.get(TITLE).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(title => {
this.messageBox.setTitle(title || undefined);
});
this.form.get(SEVERITY).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(severity => {
this.messageBox.setSeverity(severity || undefined);
});
this.form.get(CSS_CLASS).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(cssClass => {
this.messageBox.setCssClass(cssClass.split(/\s+/).filter(Boolean));
});
this.form.get(ACTIONS).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe((actions: Array<{paramName: string; paramValue: string}>) => {
this.messageBox.setActions(actions.map(action => ({
key: action.paramName,
@@ -91,8 +88,4 @@ export class InspectMessageBoxComponent implements OnDestroy {
);
});
}
-
- public ngOnDestroy(): void {
- this._destroy$.next();
- }
}
diff --git a/apps/workbench-testing-app/src/app/inspect-notification-provider/inspect-notification.component.ts b/apps/workbench-testing-app/src/app/inspect-notification-provider/inspect-notification.component.ts
index 854bd3062..71707d484 100644
--- a/apps/workbench-testing-app/src/app/inspect-notification-provider/inspect-notification.component.ts
+++ b/apps/workbench-testing-app/src/app/inspect-notification-provider/inspect-notification.component.ts
@@ -8,16 +8,15 @@
* SPDX-License-Identifier: EPL-2.0
*/
-import {Component, OnDestroy} from '@angular/core';
+import {Component} from '@angular/core';
import {Notification} from '@scion/workbench';
-import {Subject} from 'rxjs';
import {UUID} from '@scion/toolkit/uuid';
import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms';
-import {takeUntil} from 'rxjs/operators';
import {NgIf} from '@angular/common';
import {SciFormFieldModule} from '@scion/components.internal/form-field';
import {SciViewportModule} from '@scion/components/viewport';
import {StringifyPipe} from '../common/stringify.pipe';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
const TITLE = 'title';
const SEVERITY = 'severity';
@@ -37,15 +36,13 @@ const CSS_CLASS = 'cssClass';
SciViewportModule,
],
})
-export class InspectNotificationComponent implements OnDestroy {
+export class InspectNotificationComponent {
public readonly TITLE = TITLE;
public readonly SEVERITY = SEVERITY;
public readonly DURATION = DURATION;
public readonly CSS_CLASS = CSS_CLASS;
- private _destroy$ = new Subject();
-
public uuid = UUID.randomUUID();
public form: UntypedFormGroup;
@@ -58,25 +55,25 @@ export class InspectNotificationComponent implements OnDestroy {
});
this.form.get(TITLE).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(title => {
this.notification.setTitle(title || undefined);
});
this.form.get(SEVERITY).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(severity => {
this.notification.setSeverity(severity || undefined);
});
this.form.get(DURATION).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(duration => {
this.notification.setDuration(this.parseDurationFromUI(duration));
});
this.form.get(CSS_CLASS).valueChanges
- .pipe(takeUntil(this._destroy$))
+ .pipe(takeUntilDestroyed())
.subscribe(cssClass => {
this.notification.setCssClass(cssClass.split(/\s+/).filter(Boolean));
});
@@ -91,8 +88,4 @@ export class InspectNotificationComponent implements OnDestroy {
}
return Number(duration);
}
-
- public ngOnDestroy(): void {
- this._destroy$.next();
- }
}
diff --git a/apps/workbench-testing-app/src/app/menu/menu.component.html b/apps/workbench-testing-app/src/app/menu/menu.component.html
index 32767e6e0..8869fbe26 100644
--- a/apps/workbench-testing-app/src/app/menu/menu.component.html
+++ b/apps/workbench-testing-app/src/app/menu/menu.component.html
@@ -1,11 +1,11 @@
-
+
diff --git a/apps/workbench-testing-app/src/app/menu/menu.component.scss b/apps/workbench-testing-app/src/app/menu/menu.component.scss
index 92f4772ac..0c6d2615b 100644
--- a/apps/workbench-testing-app/src/app/menu/menu.component.scss
+++ b/apps/workbench-testing-app/src/app/menu/menu.component.scss
@@ -5,9 +5,9 @@
background-color: var(--sci-color-P50);
box-shadow: 8px 8px 9px -9px rgba(var(--sci-color-primary-rgb), .2);
- > div.menu-item {
+ > button.menu-item {
+ all: unset;
padding: .6em 1.5em;
- cursor: default;
font-size: smaller;
user-select: none;
diff --git a/apps/workbench-testing-app/src/app/menu/menu.component.ts b/apps/workbench-testing-app/src/app/menu/menu.component.ts
index 47779257b..6e7c81253 100644
--- a/apps/workbench-testing-app/src/app/menu/menu.component.ts
+++ b/apps/workbench-testing-app/src/app/menu/menu.component.ts
@@ -8,13 +8,13 @@
* SPDX-License-Identifier: EPL-2.0
*/
-import {Component, HostListener, Inject, InjectionToken, OnDestroy, OnInit} from '@angular/core';
+import {Component, DestroyRef, HostListener, Inject, InjectionToken, OnInit} from '@angular/core';
import {OverlayRef} from '@angular/cdk/overlay';
-import {fromEvent, Subject} from 'rxjs';
-import {takeUntil} from 'rxjs/operators';
+import {fromEvent} from 'rxjs';
import {MenuItem, MenuItemSeparator} from './menu-item';
import {KeyValuePipe, NgClass, NgFor, NgIf} from '@angular/common';
import {InstanceofPipe} from '../common/instanceof.pipe';
+import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
/**
* DI token to provide menu items to the menu.
@@ -33,18 +33,18 @@ export const MENU_ITEMS = new InjectionToken
InstanceofPipe,
],
})
-export class MenuComponent implements OnInit, OnDestroy {
-
- private _destroy$ = new Subject();
+export class MenuComponent implements OnInit {
public MenuItem = MenuItem;
- constructor(private _overlayRef: OverlayRef, @Inject(MENU_ITEMS) public menuItems: Array