From 9093a7ead13bd98376479be191aaa010b1ad601c Mon Sep 17 00:00:00 2001 From: danielwiehl Date: Wed, 17 Apr 2024 16:06:28 +0200 Subject: [PATCH] chore(workbench/router): remove option to close view via workbench router link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The router link behaves differently depending on whether it is used inside or outside a view and whether the user presses the CTRL key (Mac: ⌘, Windows: ⊞). Closing a view should be an explicit action, regardless of the environment. We have, therefore, removed the option to close a view using the router link. BREAKING CHANGE: Removed the option to close a view via the `wbRouterLink` directive. The router link can no longer be used to close a view. To close a view, use the `WorkbenchView`, the `WorkbenchRouter`, or the `WorkbenchService` instead. Examples: ```ts // Closing a view via `WorkbenchView` handle inject(WorkbenchView).close(); // Closing view(s) via `WorkbenchRouter` inject(WorkbenchRouter).navigate(['path/*/view'], {close: true}); // Closing view(s) via `WorkbenchService` inject(WorkbenchService).closeViews('view.1', 'view.2'); ``` --- docs/site/howto/how-to-close-view.md | 12 +- docs/site/howto/how-to-open-view.md | 12 +- .../src/workbench/router-link.e2e-spec.ts | 121 ------------------ .../workbench-router-link.directive.ts | 30 +++-- 4 files changed, 36 insertions(+), 139 deletions(-) diff --git a/docs/site/howto/how-to-close-view.md b/docs/site/howto/how-to-close-view.md index 8bfb40413..cd8e06c43 100644 --- a/docs/site/howto/how-to-close-view.md +++ b/docs/site/howto/how-to-close-view.md @@ -7,9 +7,9 @@ ### How to close a view -A view can be closed via navigation, the view's handle `WorkbenchView`, or the `WorkbenchService`. +A view can be closed via the view's handle `WorkbenchView`, the `WorkbenchService`, or the `WorkbenchRouter`. -#### Closing the view using its handle +#### Closing a view using its handle Inject `WorkbenchView` handle and invoke the `close` method. ```ts @@ -17,16 +17,18 @@ inject(WorkbenchView).close(); ``` #### Closing view(s) using the `WorkbenchService` -Inject `WorkbenchService` and invoke `close`, passing the identifies of the views to close. +Inject `WorkbenchService` and invoke `closeViews`, passing the ids of the views to close. ```ts inject(WorkbenchService).closeViews('view.1', 'view.2'); ``` -#### Closing view(s) via navigation +#### Closing view(s) via `WorkbenchRouter` -Views can be closed by performing a navigation with the `close` flag set in the navigation extras. Views matching the path will be closed. The path supports the asterisk wildcard segment (`*`) to match view(s) with any value in that segment. To close a specific view, set a view `target` instead of a path. +The router supports for closing views matching the routing commands by setting `close` in navigation extras. + +Matrix parameters do not affect view resolution. The path supports the asterisk wildcard segment (`*`) to match views with any value in a segment. To close a specific view, set a view target instead of a path. ```ts inject(WorkbenchRouter).navigate(['path/*/view'], {close: true}); diff --git a/docs/site/howto/how-to-open-view.md b/docs/site/howto/how-to-open-view.md index 21867b443..6bc54e0f3 100644 --- a/docs/site/howto/how-to-open-view.md +++ b/docs/site/howto/how-to-open-view.md @@ -50,18 +50,24 @@ The default behavior can be overridden by specifying a `target` via navigation e ### How to navigate in a template The workbench provides the `wbRouterLink` directive for navigation in a template. The `wbRouterLink` directive is the workbench equivalent of the Angular `routerLink`. +Use this directive to navigate the current view. If the user presses the CTRL key (Mac: ⌘, Windows: ⊞), this directive will open a new view. + ```html Link ``` - -If in the context of a view in the main area and CTRL (Mac: ⌘, Windows: ⊞) key is not pressed, by default, navigation replaces the content of the current view. Override this default behavior by setting a view target strategy in navigation extras. +You can override the default behavior by setting an explicit navigation target in navigation extras. ```html Link ``` -By default, navigation is relative to the currently activated route, if any. Prepend the path with a forward slash `/` to navigate absolutely, or set `relativeTo` property in navigational extras to `null`. +By default, navigation is relative to the currently activated route, if any. + +Prepend the path with a forward slash `/` to navigate absolutely, or set `relativeTo` property in navigational extras to `null`. +```html +Link +``` *** #### Related Links: - [Learn how to provide a view.][link-how-to-provide-view] diff --git a/projects/scion/e2e-testing/src/workbench/router-link.e2e-spec.ts b/projects/scion/e2e-testing/src/workbench/router-link.e2e-spec.ts index d5064380e..e2e32f4e8 100644 --- a/projects/scion/e2e-testing/src/workbench/router-link.e2e-spec.ts +++ b/projects/scion/e2e-testing/src/workbench/router-link.e2e-spec.ts @@ -17,7 +17,6 @@ import {MPart, MTreeNode} from '../matcher/to-equal-workbench-layout.matcher'; import {MAIN_AREA} from '../workbench.model'; import {expectView} from '../matcher/view-matcher'; import {ViewPagePO} from './page-object/view-page.po'; -import {NavigationTestPagePO} from './page-object/test-pages/navigation-test-page.po'; test.describe('Workbench RouterLink', () => { @@ -132,126 +131,6 @@ test.describe('Workbench RouterLink', () => { await expectView(testeeViewPage).toBeActive(); }); - test('should close view by path', async ({appPO, workbenchNavigator}) => { - await appPO.navigateTo({microfrontendSupport: false}); - - const routerPage = await workbenchNavigator.openInNewTab(RouterPagePO); - - // GIVEN - // Open test view 1 (but do not activate it) - await routerPage.enterPath('/test-view'); - await routerPage.enterCssClass('testee'); - await routerPage.checkActivate(false); - await routerPage.clickNavigate(); - - const testeeViewPage = new ViewPagePO(appPO, {cssClass: 'testee'}); - await expectView(routerPage).toBeActive(); - await expectView(testeeViewPage).toBeInactive(); - - // WHEN - await routerPage.enterPath('/test-view'); - await routerPage.checkClose(true); - await routerPage.clickNavigateViaRouterLink(); - - // THEN - await expectView(routerPage).toBeActive(); - await expectView(testeeViewPage).not.toBeAttached(); - await expect(appPO.views()).toHaveCount(1); - }); - - test('should close view by id', async ({appPO, workbenchNavigator}) => { - await appPO.navigateTo({microfrontendSupport: false}); - - const routerPage = await workbenchNavigator.openInNewTab(RouterPagePO); - - // GIVEN - // Open test view 1 (but do not activate it) - await routerPage.enterPath('/test-view'); - await routerPage.enterTarget('view.101'); - await routerPage.checkActivate(false); - await routerPage.clickNavigate(); - - const testeeViewPage = new ViewPagePO(appPO, {viewId: 'view.101'}); - await expectView(routerPage).toBeActive(); - await expectView(testeeViewPage).toBeInactive(); - - // WHEN - await routerPage.enterPath(''); - await routerPage.enterTarget('view.101'); - await routerPage.checkClose(true); - await routerPage.clickNavigateViaRouterLink(); - - // THEN - await expectView(routerPage).toBeActive(); - await expectView(testeeViewPage).not.toBeAttached(); - await expect(appPO.views()).toHaveCount(1); - }); - - test('should close the current view without explicit target', async ({appPO, workbenchNavigator}) => { - await appPO.navigateTo({microfrontendSupport: false}); - - // GIVEN - const routerPage1 = await workbenchNavigator.openInNewTab(RouterPagePO); - const routerPage2 = await workbenchNavigator.openInNewTab(RouterPagePO); - const routerPage3 = await workbenchNavigator.openInNewTab(RouterPagePO); - - // WHEN - await routerPage2.view.tab.click(); - await routerPage2.enterPath(''); - await routerPage2.checkClose(true); - await routerPage2.clickNavigateViaRouterLink(); - - // THEN - await expectView(routerPage1).toBeInactive(); - await expectView(routerPage2).not.toBeAttached(); - await expectView(routerPage3).toBeActive(); - await expect(appPO.views()).toHaveCount(2); - }); - - test('should close matching views', async ({appPO, workbenchNavigator}) => { - await appPO.navigateTo({microfrontendSupport: false}); - - // GIVEN - // Open test view 1 (but do not activate it) - const routerPage = await workbenchNavigator.openInNewTab(RouterPagePO); - await routerPage.enterPath('/test-pages/navigation-test-page/1'); - await routerPage.enterCssClass('testee-1'); - await routerPage.checkActivate(false); - await routerPage.clickNavigate(); - - // Open test view 2 (but do not activate it) - await routerPage.enterPath('/test-pages/navigation-test-page/2'); - await routerPage.enterCssClass('testee-2'); - await routerPage.checkActivate(false); - await routerPage.clickNavigate(); - - // Open test view 3 (but do not activate it) - await routerPage.enterPath('/test-pages/navigation-test-page/3'); - await routerPage.enterCssClass('testee-3'); - await routerPage.checkActivate(false); - await routerPage.clickNavigate(); - - const testViewPage1 = new NavigationTestPagePO(appPO, {cssClass: 'testee-1'}); - const testViewPage2 = new NavigationTestPagePO(appPO, {cssClass: 'testee-2'}); - const testViewPage3 = new NavigationTestPagePO(appPO, {cssClass: 'testee-3'}); - - await expectView(routerPage).toBeActive(); - await expectView(testViewPage1).toBeInactive(); - await expectView(testViewPage2).toBeInactive(); - await expectView(testViewPage3).toBeInactive(); - - // WHEN - await routerPage.enterPath('/test-pages/navigation-test-page/*'); - await routerPage.checkClose(true); - await routerPage.clickNavigateViaRouterLink(); - - // THEN - await expectView(routerPage).toBeActive(); - await expectView(testViewPage1).not.toBeAttached(); - await expectView(testViewPage2).not.toBeAttached(); - await expectView(testViewPage3).not.toBeAttached(); - }); - test('should navigate present view(s) if navigating outside a view and not setting a target', async ({appPO, workbenchNavigator}) => { await appPO.navigateTo({microfrontendSupport: false}); diff --git a/projects/scion/workbench/src/lib/routing/workbench-router-link.directive.ts b/projects/scion/workbench/src/lib/routing/workbench-router-link.directive.ts index 4d378911b..ca5ca5fba 100644 --- a/projects/scion/workbench/src/lib/routing/workbench-router-link.directive.ts +++ b/projects/scion/workbench/src/lib/routing/workbench-router-link.directive.ts @@ -19,19 +19,33 @@ import {Defined} from '@scion/toolkit/util'; import {RouterUtils} from './router.util'; /** - * Like 'RouterLink' but with functionality to target a view outlet. + * Like the Angular 'RouterLink' directive but with functionality to navigate a view. * - * If in the context of a view in the main area and CTRL (Mac: ⌘, Windows: ⊞) key is not pressed, by default, navigation - * replaces the content of the current view. Override this default behavior by setting a view target strategy in navigation extras. + * Use this directive to navigate the current view. If the user presses the CTRL key (Mac: ⌘, Windows: ⊞), this directive will open a new view. + * + * ```html + * Link + * ``` + * + * You can override the default behavior by setting an explicit navigation target in navigation extras. + * + * ```html + * Link + * ``` * * By default, navigation is relative to the currently activated route, if any. + * * Prepend the path with a forward slash '/' to navigate absolutely, or set `relativeTo` property in navigational extras to `null`. + * + * ```html + * Link + * ``` */ @Directive({selector: '[wbRouterLink]', standalone: true}) export class WorkbenchRouterLinkDirective implements OnChanges, OnDestroy { private _commands: any[] = []; - private _extras: WorkbenchNavigationExtras = {}; + private _extras: Omit = {}; private _ngOnChange$ = new Subject(); private _ngOnDestroy$ = new Subject(); @@ -44,7 +58,7 @@ export class WorkbenchRouterLinkDirective implements OnChanges, OnDestroy { } @Input('wbRouterLinkExtras') // eslint-disable-line @angular-eslint/no-input-rename - public set extras(extras: WorkbenchNavigationExtras | undefined) { + public set extras(extras: Omit | undefined) { this._extras = extras || {}; } @@ -74,7 +88,7 @@ export class WorkbenchRouterLinkDirective implements OnChanges, OnDestroy { /** * Computes navigation extras based on the given extras and this directive's injection context. */ - protected computeNavigationExtras(ctrlKey: boolean = false, metaKey: boolean = false): WorkbenchNavigationExtras { + protected computeNavigationExtras(ctrlKey: boolean = false, metaKey: boolean = false): Omit { const contextualView = this._view ?? undefined; const contextualPart = this._view?.part; const controlPressed = ctrlKey || metaKey; @@ -86,10 +100,6 @@ export class WorkbenchRouterLinkDirective implements OnChanges, OnDestroy { return isAbsolute ? null : this._route; }), target: Defined.orElse(this._extras.target, () => { - // When closing a view, derive the target only if no path is set. - if (this._extras.close && this._commands.length) { - return undefined; - } // Navigate in new tab if CTRL or META modifier key is pressed. if (controlPressed) { return 'blank';