Skip to content

Commit

Permalink
chore(workbench/router): remove option to close view via workbench ro…
Browse files Browse the repository at this point in the history
…uter link

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');
```
  • Loading branch information
danielwiehl committed May 6, 2024
1 parent 574de56 commit 9093a7e
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 139 deletions.
12 changes: 7 additions & 5 deletions docs/site/howto/how-to-close-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,28 @@

### 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
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});
Expand Down
12 changes: 9 additions & 3 deletions docs/site/howto/how-to-open-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<a [wbRouterLink]="['../path/to/view']">Link</a>
```

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
<a [wbRouterLink]="['../path/to/view']" [wbRouterLinkExtras]="{target: 'blank'}">Link</a>
```

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
<a [wbRouterLink]="['/path/to/view']">Link</a>
```
***
#### Related Links:
- [Learn how to provide a view.][link-how-to-provide-view]
Expand Down
121 changes: 0 additions & 121 deletions projects/scion/e2e-testing/src/workbench/router-link.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {

Expand Down Expand Up @@ -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});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
* <a [wbRouterLink]="['../path/to/view']">Link</a>
* ```
*
* You can override the default behavior by setting an explicit navigation target in navigation extras.
*
* ```html
* <a [wbRouterLink]="['../path/to/view']" [wbRouterLinkExtras]="{target: 'blank'}">Link</a>
* ```
*
* 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
* <a [wbRouterLink]="['/path/to/view']">Link</a>
* ```
*/
@Directive({selector: '[wbRouterLink]', standalone: true})
export class WorkbenchRouterLinkDirective implements OnChanges, OnDestroy {

private _commands: any[] = [];
private _extras: WorkbenchNavigationExtras = {};
private _extras: Omit<WorkbenchNavigationExtras, 'close'> = {};
private _ngOnChange$ = new Subject<void>();
private _ngOnDestroy$ = new Subject<void>();

Expand All @@ -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<WorkbenchNavigationExtras, 'close'> | undefined) {
this._extras = extras || {};
}

Expand Down Expand Up @@ -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<WorkbenchNavigationExtras, 'close'> {
const contextualView = this._view ?? undefined;
const contextualPart = this._view?.part;
const controlPressed = ctrlKey || metaKey;
Expand All @@ -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';
Expand Down

0 comments on commit 9093a7e

Please sign in to comment.