Skip to content

Commit

Permalink
fix(workbench): activate part when microfrontend gains focus
Browse files Browse the repository at this point in the history
  • Loading branch information
danielwiehl authored and Marcarrian committed Oct 10, 2023
1 parent 2e2368a commit 6e05d8c
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class AngularZoneTestPagePO {
await startPagePO.clickTestCapability('e2e-test-angular-zone', 'app1');

// Create the page object.
const view = await appPO.view({cssClass: 'e2e-test-angular-zone'});
const view = await appPO.view({cssClass: 'e2e-test-angular-zone', viewId: startPagePO.viewId});
await view.waitUntilPresent();
return new AngularZoneTestPagePO(appPO, await view.getViewId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class BulkNavigationTestPagePO {
await startPagePO.clickTestCapability('e2e-test-bulk-navigation', 'app1');

// Create the page object.
const view = await appPO.view({cssClass: 'e2e-test-bulk-navigation'});
const view = await appPO.view({cssClass: 'e2e-test-bulk-navigation', viewId: startPagePO.viewId});
await view.waitUntilPresent();
return new BulkNavigationTestPagePO(appPO, await view.getViewId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class InputFieldTestPagePO {
await startPagePO.clickTestCapability('e2e-test-input-field', 'app1');

// Create the page object.
const view = await appPO.view({cssClass: 'e2e-test-input-field'});
const view = await appPO.view({cssClass: 'e2e-test-input-field', viewId: startPagePO.viewId});
await view.waitUntilPresent();
return new InputFieldTestPagePO(appPO, await view.getViewId());
}
Expand Down
81 changes: 81 additions & 0 deletions projects/scion/e2e-testing/src/workbench-client/part.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 {expect} from '@playwright/test';
import {test} from '../fixtures';
import {InputFieldTestPagePO as MicrofrontendInputFieldTestPagePO} from './page-object/test-pages/input-field-test-page.po';
import {MPart, MTreeNode} from '../matcher/to-equal-workbench-layout.matcher';
import {waitUntilStable} from '../helper/testing.util';

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

test('should activate part when view microfrontend gains focus', async ({appPO, microfrontendNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

// Open test view "left".
const leftTestPagePO = await MicrofrontendInputFieldTestPagePO.openInNewTab(appPO, microfrontendNavigator);
// Open test view "right".
const rightTestPagePO = await MicrofrontendInputFieldTestPagePO.openInNewTab(appPO, microfrontendNavigator);
// Move test view to the right
await rightTestPagePO.view.viewTab.dragTo({partId: await appPO.activePart({inMainArea: true}).getPartId(), region: 'east'});

// Capture part and view identities.
const leftPartId = await leftTestPagePO.view.part.getPartId();
const rightPartId = await rightTestPagePO.view.part.getPartId();
const leftViewId = await leftTestPagePO.view.getViewId();
const rightViewId = await rightTestPagePO.view.getViewId();

// Expect right part to be activated.
await expect(appPO.workbenchLocator).toEqualWorkbenchLayout({
mainAreaGrid: {
root: new MTreeNode({
direction: 'row',
ratio: .5,
child1: new MPart({
id: leftPartId,
views: [{id: leftViewId}],
activeViewId: leftViewId,
}),
child2: new MPart({
id: rightPartId,
views: [{id: rightViewId}],
activeViewId: rightViewId,
}),
}),
activePartId: rightPartId,
},
});

// When clicking left test view.
await leftTestPagePO.clickInputField();
await waitUntilStable(() => appPO.activePart({inMainArea: true}).getPartId());

// Expect left part to be activated.
await expect(appPO.workbenchLocator).toEqualWorkbenchLayout({
mainAreaGrid: {
root: new MTreeNode({
direction: 'row',
ratio: .5,
child1: new MPart({
id: leftPartId,
views: [{id: leftViewId}],
activeViewId: leftViewId,
}),
child2: new MPart({
id: rightPartId,
views: [{id: rightViewId}],
activeViewId: rightViewId,
}),
}),
activePartId: leftPartId,
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class BulkNavigationTestPagePO {
await routerPagePO.enterTarget(routerPagePO.viewId);
await routerPagePO.clickNavigate();

const view = await appPO.view({cssClass: 'e2e-test-bulk-navigation'});
const view = await appPO.view({cssClass: 'e2e-test-bulk-navigation', viewId: routerPagePO.viewId});
await view.waitUntilPresent();
return new BulkNavigationTestPagePO(appPO, await view.getViewId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class InputFieldTestPagePO {
await routerPagePO.enterCssClass(cssClass);
await routerPagePO.clickNavigate();

const view = await appPO.view({cssClass});
const view = await appPO.view({cssClass, viewId: routerPagePO.viewId});
await view.waitUntilPresent();
return new InputFieldTestPagePO(appPO, await view.getViewId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[attr.data-capabilityid]="viewCapability && viewCapability.metadata!.id"
[attr.data-app]="viewCapability && viewCapability.metadata!.appSymbolicName"
[ngClass]="viewCssClasses" class="e2e-view"
(focuswithin)="onFocusWithin($event)"
[keystrokes]="keystrokesToBubble$ | async">
</sci-router-outlet>
</wb-content-as-overlay>
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export class MicrofrontendViewComponent implements OnInit, OnDestroy, WorkbenchV
*/
public keystrokesToBubble$: Observable<string[]>;

constructor(private _route: ActivatedRoute,
constructor(private _host: ElementRef<HTMLElement>,
private _route: ActivatedRoute,
private _view: ɵWorkbenchView,
private _outletRouter: OutletRouter,
private _manifestService: ManifestService,
Expand Down Expand Up @@ -262,6 +263,13 @@ export class MicrofrontendViewComponent implements OnInit, OnDestroy, WorkbenchV
return firstValueFrom(doit, {defaultValue: true});
}

public onFocusWithin(event: Event): void {
const {detail: focusWithin} = event as CustomEvent<boolean>;
if (focusWithin) {
this._host.nativeElement.dispatchEvent(new CustomEvent('sci-microfrontend-focusin', {bubbles: true}));
}
}

/**
* Upon subscription, emits the keystrokes registered with menu items of this view's context menu,
* and then continuously when they change. The observable never completes.
Expand Down
6 changes: 3 additions & 3 deletions projects/scion/workbench/src/lib/part/part.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import {ChangeDetectorRef, Component, ElementRef, HostBinding, inject, Injector, NgZone, OnDestroy, OnInit} from '@angular/core';
import {combineLatestWith, EMPTY, from, fromEvent, mergeMap, switchMap} from 'rxjs';
import {combineLatestWith, EMPTY, from, fromEvent, merge, mergeMap, switchMap} from 'rxjs';
import {ViewDropZoneDirective, WbViewDropEvent} from '../view-dnd/view-drop-zone.directive';
import {take} from 'rxjs/operators';
import {ViewDragService} from '../view-dnd/view-drag.service';
Expand Down Expand Up @@ -126,8 +126,8 @@ export class PartComponent implements OnInit, OnDestroy {
// Wait until the zone has stabilized to not activate the part on creation, but only on user interaction.
// For example, if the view sets the initial focus, the related `focusin` event should not activate the part.
combineLatestWith(inject(NgZone).onStable.pipe(take(1))),
// Suspend listening for `focusin` events while this part is active.
switchMap(([active]) => active ? EMPTY : fromEvent<FocusEvent>(host, 'focusin', {once: true})),
// Suspend listening for `focusin` or `sci-microfrontend-focusin` events while this part is active.
switchMap(([active]) => active ? EMPTY : merge(fromEvent<FocusEvent>(host, 'focusin', {once: true}), fromEvent(host, 'sci-microfrontend-focusin', {once: true}))),
takeUntilDestroyed(),
)
.subscribe(() => {
Expand Down

0 comments on commit 6e05d8c

Please sign in to comment.