Skip to content

Commit

Permalink
feat(workbench): propagate color scheme to embedded content
Browse files Browse the repository at this point in the history
  • Loading branch information
danielwiehl authored and Marcarrian committed Oct 26, 2023
1 parent 41be222 commit 276fcf3
Showing 20 changed files with 245 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="theme-switcher">
<button (click)="onActivateDarkTheme()" sciMaterialIcon>dark_mode</button>
<sci-toggle-button [formControl]="lightThemeActiveFormControl"></sci-toggle-button>
<sci-toggle-button [formControl]="lightThemeActiveFormControl" class="e2e-color-scheme"></sci-toggle-button>
<button (click)="onActivateLightTheme()" sciMaterialIcon>light_mode</button>
</div>

Original file line number Diff line number Diff line change
@@ -168,7 +168,7 @@ export class HeaderComponent {
this.workbenchService.theme$
.pipe(takeUntilDestroyed())
.subscribe(theme => {
this.lightThemeActiveFormControl.setValue(theme === 'scion-light', {emitEvent: false});
this.lightThemeActiveFormControl.setValue(theme?.colorScheme === 'light', {emitEvent: false});
});

this.lightThemeActiveFormControl.valueChanges
5 changes: 5 additions & 0 deletions apps/workbench-testing-app/src/app/test-pages/routes.ts
Original file line number Diff line number Diff line change
@@ -39,6 +39,11 @@ export default [
loadComponent: () => import('./blank-test-page/blank-test-page.component'),
data: {[WorkbenchRouteData.title]: 'Blank Test Page', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage'},
},
{
path: 'workbench-theme-test-page',
loadComponent: (): any => import('./workbench-theme-test-page/workbench-theme-test-page.component'),
data: {[WorkbenchRouteData.title]: 'Workbench Theme Test Page', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-workbench-theme'},
},
] satisfies Routes;

/**
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<section *ngIf="workbenchService.theme$ | async as theme">
<sci-form-field label="Theme">
<span class="e2e-theme">{{theme.name}}</span>
</sci-form-field>

<sci-form-field label="Color Scheme">
<span class="e2e-color-scheme">{{theme.colorScheme}}</span>
</sci-form-field>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:host {
display: block;
padding: 1em;

> section {
display: flex;
flex-direction: column;
gap: .5em;
border: 1px solid var(--sci-color-border);
border-radius: var(--sci-corner);
padding: 1em;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2023 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 {SciFormFieldComponent} from '@scion/components.internal/form-field';
import {AsyncPipe, NgIf} from '@angular/common';
import {WorkbenchService} from '@scion/workbench';

@Component({
selector: 'app-workbench-theme-test-page',
templateUrl: './workbench-theme-test-page.component.html',
styleUrls: ['./workbench-theme-test-page.component.scss'],
standalone: true,
imports: [
NgIf,
AsyncPipe,
SciFormFieldComponent,
],
})
export default class WorkbenchThemeTestPageComponent {

constructor(public workbenchService: WorkbenchService) {
}
}
3 changes: 1 addition & 2 deletions apps/workbench-testing-app/src/styles.scss
Original file line number Diff line number Diff line change
@@ -5,19 +5,18 @@
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded');

html {
all: unset;
font-size: 14px; // defines 1rem as 14px
width: 100vw;
height: 100vh;
}

body {
all: unset;
font-family: Roboto, Arial, sans-serif;
color: var(--sci-color-text);
background-color: var(--sci-color-background-primary);
width: 100vw;
height: 100vh;
margin: 0;
}

* {
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2018-2023 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms from the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

export * from './toggle-button.po';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2023 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms from 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 {Locator} from '@playwright/test';

/**
* Page object for {@link SciToggleButtonComponent}.
*/
export class SciToggleButtonPO {

private _inputLocator: Locator;

constructor(private _locator: Locator) {
this._inputLocator = this._locator.locator('input[type="checkbox"]');
}

public async toggle(on: boolean): Promise<void> {
if (await this._inputLocator.isChecked() !== on) {
await this._locator.click();
}
}
}
9 changes: 9 additions & 0 deletions projects/scion/e2e-testing/src/app-header.po.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@

import {Locator} from '@playwright/test';
import {PerspectiveTogglePO} from './perspective-toggle-button.po';
import {SciToggleButtonPO} from './@scion/components.internal/toggle-button.po';

/**
* Handle for interacting with the header of the testing application.
@@ -29,6 +30,14 @@ export class AppHeaderPO {
return new PerspectiveTogglePO(this._locator.locator('div.e2e-perspective-toggles').locator(`button.e2e-perspective[data-perspectiveid="${locateBy.perspectiveId}"]`));
}

/**
* Changes the color scheme of the workbench.
*/
public async changeColorScheme(colorScheme: 'light' | 'dark'): Promise<void> {
const toggleButton = new SciToggleButtonPO(this._locator.locator('sci-toggle-button.e2e-color-scheme'));
await toggleButton.toggle(colorScheme === 'light');
}

/**
* Opens the application menu and clicks the specified menu item.
*/
7 changes: 7 additions & 0 deletions projects/scion/e2e-testing/src/app.po.ts
Original file line number Diff line number Diff line change
@@ -247,6 +247,13 @@ export class AppPO {
await this.header.perspectiveToggleButton({perspectiveId}).click();
}

/**
* Changes the color scheme of the workbench.
*/
public async changeColorScheme(colorScheme: 'light' | 'dark'): Promise<void> {
await this.header.changeColorScheme(colorScheme);
}

/**
* Waits until the workbench finished startup.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2023 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 {AppPO} from '../../../app.po';
import {Locator} from '@playwright/test';
import {WorkbenchNavigator} from '../../workbench-navigator';
import {RouterPagePO} from '../router-page.po';

export class WorkbenchThemeTestPagePO {

public readonly locator: Locator;

public readonly theme: Locator;
public readonly colorScheme: Locator;

constructor(private _appPO: AppPO, viewId: string) {
this.locator = this._appPO.view({viewId}).locate('app-workbench-theme-test-page');

this.theme = this.locator.locator('span.e2e-theme');
this.colorScheme = this.locator.locator('span.e2e-color-scheme');
}

public static async openInNewTab(appPO: AppPO, workbenchNavigator: WorkbenchNavigator): Promise<WorkbenchThemeTestPagePO> {
const routerPage = await workbenchNavigator.openInNewTab(RouterPagePO);
await routerPage.enterPath('test-pages/workbench-theme-test-page');
await routerPage.enterTarget(routerPage.viewId);
await routerPage.clickNavigate();

const view = appPO.view({cssClass: 'e2e-test-workbench-theme', viewId: routerPage.viewId});
await view.waitUntilAttached();
return new WorkbenchThemeTestPagePO(appPO, await view.getViewId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2023 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 {expect} from '@playwright/test';
import {test} from '../fixtures';
import {WorkbenchThemeTestPagePO} from './page-object/test-pages/workbench-theme-test-page.po';

test.describe('Workbench', () => {

test('should provide light and dark theme', async ({appPO, workbenchNavigator}) => {
await appPO.navigateTo({microfrontendSupport: false});

const testPage = await WorkbenchThemeTestPagePO.openInNewTab(appPO, workbenchNavigator);

await test.step('light theme', async () => {
await appPO.changeColorScheme('light');

await expect(testPage.theme).toHaveText('scion-light');
await expect(testPage.colorScheme).toHaveText('light');

await expect(appPO.workbenchLocator).toHaveCSS('background-color', 'rgb(255, 255, 255)');
await expect(appPO.workbenchLocator).toHaveCSS('color-scheme', 'light');
});

await test.step('dark theme', async () => {
await appPO.changeColorScheme('dark');

await expect(testPage.theme).toHaveText('scion-dark');
await expect(testPage.colorScheme).toHaveText('dark');

await expect(appPO.workbenchLocator).toHaveCSS('background-color', 'rgb(29, 29, 29)');
await expect(appPO.workbenchLocator).toHaveCSS('color-scheme', 'dark');
});
});
});
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import {NgClass} from '@angular/common';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {WorkbenchLayoutService} from '../../layout/workbench-layout.service';
import {WorkbenchService} from '../../workbench.service';
import {WorkbenchTheme} from '../../workbench.model';

/**
* Displays the microfrontend of a popup capability inside a workbench popup.
@@ -134,17 +135,19 @@ export class MicrofrontendPopupComponent implements OnInit, OnDestroy {
}

/**
* Propagates the current workbench theme to the popup microfrontend via router outlet context.
* Propagates the current theme and color scheme to embedded content.
*/
private installThemePropagator(): void {
this._workbenchService.theme$
.pipe(takeUntilDestroyed(this._destroyRef))
.subscribe(theme => {
if (theme) {
this.routerOutletElement.nativeElement.setContextValue(ɵTHEME_CONTEXT_KEY, theme);
this.routerOutletElement.nativeElement.setContextValue<WorkbenchTheme>(ɵTHEME_CONTEXT_KEY, theme);
this.routerOutletElement.nativeElement.setContextValue('color-scheme', theme.colorScheme);
}
else {
this.routerOutletElement.nativeElement.removeContextValue(ɵTHEME_CONTEXT_KEY);
this.routerOutletElement.nativeElement.removeContextValue('color-scheme');
}
});
}
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ import {Application, ManifestService, mapToBody, MessageClient, MessageHeaders,
import {WorkbenchViewCapability, ɵMicrofrontendRouteParams, ɵTHEME_CONTEXT_KEY, ɵVIEW_ID_CONTEXT_KEY, ɵViewParamsUpdateCommand, ɵWorkbenchCommands} from '@scion/workbench-client';
import {Arrays, Dictionaries, Maps} from '@scion/toolkit/util';
import {Logger, LoggerNames} from '../../logging';
import {WorkbenchViewPreDestroy} from '../../workbench.model';
import {WorkbenchTheme, WorkbenchViewPreDestroy} from '../../workbench.model';
import {IFRAME_HOST, ViewContainerReference} from '../../content-projection/view-container.reference';
import {serializeExecution} from '../../common/operators';
import {ɵWorkbenchView} from '../../view/ɵworkbench-view.model';
@@ -320,17 +320,19 @@ export class MicrofrontendViewComponent implements OnInit, OnDestroy, WorkbenchV
}

/**
* Propagates the current workbench theme to the view microfrontend via router outlet context.
* Propagates the current theme and color scheme to embedded content.
*/
private installThemePropagator(): void {
this._workbenchService.theme$
.pipe(takeUntilDestroyed(this._destroyRef))
.subscribe(theme => {
if (theme) {
this.routerOutletElement.nativeElement.setContextValue(ɵTHEME_CONTEXT_KEY, theme);
this.routerOutletElement.nativeElement.setContextValue<WorkbenchTheme>(ɵTHEME_CONTEXT_KEY, theme);
this.routerOutletElement.nativeElement.setContextValue('color-scheme', theme.colorScheme);
}
else {
this.routerOutletElement.nativeElement.removeContextValue(ɵTHEME_CONTEXT_KEY);
this.routerOutletElement.nativeElement.removeContextValue('color-scheme');
}
});
}
2 changes: 1 addition & 1 deletion projects/scion/workbench/src/lib/public_api.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
export {WorkbenchModuleConfig, MenuItemConfig, ViewMenuItemsConfig} from './workbench-module-config';
export {WorkbenchModule} from './workbench.module';
export {WorkbenchService} from './workbench.service';
export {WorkbenchViewPreDestroy, WorkbenchPartAction} from './workbench.model';
export {WorkbenchViewPreDestroy, WorkbenchPartAction, WorkbenchTheme, CanMatchPartFn, WorkbenchMenuItem, WorkbenchMenuItemFactoryFn} from './workbench.model';
export {WorkbenchComponent} from './workbench.component';
export {VIEW_TAB_RENDERING_CONTEXT, ViewTabRenderingContext} from './workbench.constants';

Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import {DOCUMENT} from '@angular/common';
import {fromMutation$} from '@scion/toolkit/observable';
import {distinctUntilChanged, map, startWith} from 'rxjs/operators';
import {WorkbenchStorage} from '../storage/workbench-storage';
import {WorkbenchTheme} from '../workbench.model';

/**
* Represents the key to associate the activated theme in the storage.
@@ -29,11 +30,11 @@ export class WorkbenchThemeSwitcher {
private readonly _documentRoot = inject<Document>(DOCUMENT).documentElement;

/**
* Emits the name of the current workbench theme.
* Emits the current workbench theme.
*
* Upon subscription, emits the name of the current theme, and then continuously emits when switching the theme. It never completes.
* Upon subscription, emits the current theme, and then continuously emits when switching the theme. It never completes.
*/
public readonly theme$: Observable<string | null>;
public readonly theme$: Observable<WorkbenchTheme | null>;

constructor(private _workbenchStorage: WorkbenchStorage) {
this.theme$ = this.detectTheme$();
@@ -53,12 +54,21 @@ export class WorkbenchThemeSwitcher {
/**
* Detects the current workbench theme from the HTML root element.
*/
private detectTheme$(): Observable<string | null> {
return new Observable<string | null>(observer => {
private detectTheme$(): Observable<WorkbenchTheme | null> {
return new Observable<WorkbenchTheme | null>(observer => {
const subscription = fromMutation$(this._documentRoot, {attributeFilter: ['sci-theme']})
.pipe(
startWith(undefined as void),
map(() => getComputedStyle(this._documentRoot).getPropertyValue('--sci-theme') || null),
map((): WorkbenchTheme | null => {
const activeTheme = getComputedStyle(this._documentRoot).getPropertyValue('--sci-theme') || null;
if (!activeTheme) {
return null;
}
return {
name: activeTheme,
colorScheme: getComputedStyle(this._documentRoot).colorScheme as 'light' | 'dark',
};
}),
distinctUntilChanged(),
share({connector: () => new ReplaySubject(1), resetOnRefCountZero: false}),
)
Loading

0 comments on commit 276fcf3

Please sign in to comment.