Skip to content

Commit 4731e77

Browse files
spike-rabbitkfenner
authored andcommitted
feat(list-details): support usage with the Angular router
This makes the `list-details` detects a `router-outlet` inside the `si-details-pane`. In addition, the "empty state" route must be flagged by setting `data: { SI_EMPTY_DETAILS: true }` in the route configuration. That way the `list-details` can detect whether the current route is the empty state or not.
1 parent 0d70b10 commit 4731e77

File tree

9 files changed

+828
-283
lines changed

9 files changed

+828
-283
lines changed

playwright/e2e/element-examples/si-list-details.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,13 @@ test.describe('si-list-details', () => {
103103
axeRulesSet: [{ id: 'scrollable-region-focusable', enabled: false }]
104104
});
105105
});
106+
107+
test('with router in mobile mode', async ({ page, si }) => {
108+
await page.setViewportSize({ width: 600, height: 800 }); // mdMinimum is 768px
109+
await si.visitExample('si-list-details/si-list-details-router');
110+
await page.getByRole('cell', { name: 'Max Meier 2', exact: true }).click();
111+
await expect(page.getByText(`"name": "Max Meier 2"`)).toBeInViewport();
112+
await page.goBack();
113+
await expect(page.getByRole('cell', { name: 'Max Meier 2', exact: true })).toBeInViewport();
114+
});
106115
});

projects/element-ng/list-details/si-details-pane-header/si-details-pane-header.component.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
viewChild
1414
} from '@angular/core';
1515
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
16+
import { ActivatedRoute, Router } from '@angular/router';
1617
import { SiTranslatePipe, t, TranslatableString } from '@siemens/element-translate-ng/translate';
1718

19+
import { SiDetailsPaneComponent } from '../si-details-pane/si-details-pane.component';
1820
import { SiListDetailsComponent } from '../si-list-details.component';
1921

2022
/** @experimental */
@@ -55,6 +57,18 @@ export class SiDetailsPaneHeaderComponent {
5557
*/
5658
readonly backButtonText = input(t(() => $localize`:@@SI_LIST_DETAILS.BACK:Back`));
5759

60+
/**
61+
* The URL to navigate to when the back buttons is clicked.
62+
* This is only used when the `si-details-pane` is used with a router-outlet.
63+
*
64+
* @defaultValue '../'
65+
*/
66+
readonly backButtonUrl = input('../');
67+
68+
private isRouterBased = inject(SiDetailsPaneComponent).isRouterBased;
69+
private router = inject(Router, { optional: true });
70+
private activatedRoute = inject(ActivatedRoute, { optional: true });
71+
5872
private readonly backButton = viewChild<ElementRef<HTMLElement>>('backButton');
5973

6074
constructor() {
@@ -74,6 +88,12 @@ export class SiDetailsPaneHeaderComponent {
7488
}
7589

7690
protected backClicked(): void {
77-
this.parent.detailsBackClicked();
91+
this.parent.detailsBackClicked({
92+
animationDone: this.isRouterBased()
93+
? // We navigate back after the animation is done.
94+
// This ensures, that the details pane visible while animating.
95+
() => this.router!.navigate([this.backButtonUrl()], { relativeTo: this.activatedRoute })
96+
: undefined
97+
});
7898
}
7999
}

projects/element-ng/list-details/si-details-pane/si-details-pane.component.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
* Copyright (c) Siemens 2016 - 2025
33
* SPDX-License-Identifier: MIT
44
*/
5-
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
5+
import {
6+
ChangeDetectionStrategy,
7+
Component,
8+
computed,
9+
contentChild,
10+
effect,
11+
inject
12+
} from '@angular/core';
13+
import { RouterOutlet } from '@angular/router';
614

715
import { SiListDetailsComponent } from '../si-list-details.component';
816

@@ -22,4 +30,19 @@ import { SiListDetailsComponent } from '../si-list-details.component';
2230
})
2331
export class SiDetailsPaneComponent {
2432
protected parent = inject(SiListDetailsComponent);
33+
34+
private readonly routerOutlet = contentChild(RouterOutlet);
35+
/** @internal */
36+
readonly isRouterBased = computed(() => !!this.routerOutlet());
37+
38+
constructor() {
39+
effect(() => {
40+
const outlet = this.routerOutlet();
41+
if (outlet) {
42+
outlet.activateEvents.subscribe(() =>
43+
this.parent.detailsActive.set(!outlet.activatedRouteData.SI_EMPTY_DETAILS)
44+
);
45+
}
46+
});
47+
}
2548
}

projects/element-ng/list-details/si-list-details.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
[class.gap-6]="hasLargeSize()"
3232
[class.details-active]="detailsActive() && !hasLargeSize()"
3333
[@detailsExpanded]="detailsExpandedAnimation()"
34+
(@detailsExpanded.done)="detailsExpandedAnimationDone()"
3435
>
3536
<ng-container *ngTemplateOutlet="listTemplate" />
3637
<ng-container *ngTemplateOutlet="detailsTemplate" />

0 commit comments

Comments
 (0)