Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api-goldens/element-ng/navbar-vertical-next/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import * as _angular_core from '@angular/core';
import { OnChanges } from '@angular/core';
import { OnDestroy } from '@angular/core';
import { OnInit } from '@angular/core';
import * as _siemens_element_ng_navbar_vertical_next from '@siemens/element-ng/navbar-vertical-next';
import * as _siemens_element_translate_ng_translate from '@siemens/element-translate-ng/translate';
Expand All @@ -15,6 +16,7 @@ import { TemplateRef } from '@angular/core';
// @public (undocumented)
export class SiNavbarVerticalNextComponent implements OnChanges, OnInit {
constructor();
readonly alwaysOpenGroupsInFlyout: _angular_core.InputSignalWithTransform<boolean, unknown>;
collapse(): void;
readonly collapsed: _angular_core.ModelSignal<boolean>;
expand(): void;
Expand Down Expand Up @@ -44,7 +46,7 @@ export class SiNavbarVerticalNextGroupComponent {
}

// @public (undocumented)
export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit {
export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit, OnDestroy {
constructor();
// (undocumented)
readonly expanded: _angular_core.WritableSignal<boolean>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Injector,
input,
linkedSignal,
OnDestroy,
OnInit,
signal,
TemplateRef,
Expand Down Expand Up @@ -53,7 +54,7 @@ class SiNavbarFlyoutAnchorComponent {
'(click)': 'triggered()'
}
})
export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit {
export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit, OnDestroy {
private static idCounter = 0;

/** @internal */
Expand Down Expand Up @@ -121,6 +122,11 @@ export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit {
this.attachInline();
}

ngOnDestroy(): void {
this.flyoutOutsideClickSubscription?.unsubscribe();
this.overlayRef.dispose();
}

/** @internal */
hideFlyout(): void {
if (this.flyout()) {
Expand All @@ -134,7 +140,7 @@ export class SiNavbarVerticalNextGroupTriggerDirective implements OnInit {
}

protected triggered(): void {
if (this.navbar.collapsed()) {
if (this.navbar.collapsed() || this.navbar.alwaysOpenGroupsInFlyout()) {
this.toggleFlyout();
} else {
this.expanded.set(!this.expanded());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { SI_NAVBAR_VERTICAL_NEXT } from './si-navbar-vertical-next.provider';
[class.inline-group]="!flyout"
[class.dropdown-menu]="flyout"
[cdkTrapFocus]="flyout"
[cdkTrapFocusAutoCapture]="flyout"
[cdkTrapFocusAutoCapture]="autoCaptureFocus()"
>
<div [class.overflow-hidden]="!flyout">
<ng-content />
Expand All @@ -44,6 +44,10 @@ export class SiNavbarVerticalNextGroupComponent {
// Store initial value, as the mode for an instance never changes.
protected flyout = this.groupTrigger.flyout();

protected readonly autoCaptureFocus = computed(
() => this.flyout && !this.navbar.alwaysOpenGroupsInFlyout()
);

protected readonly visible = computed(() => {
return this.flyout || (!this.navbar.collapsed() && this.groupTrigger.expanded());
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
>
}
@if (group) {
<si-icon aria-hidden="true" class="dropdown-caret me-0 text-body" [icon]="icons.elementDown2" />
<si-icon
class="dropdown-caret me-0 text-body"
[icon]="openGroupsInFlyout() ? icons.elementRight2 : icons.elementDown2"
/>
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
}
}

:host-context(.dropdown-menu) :host(.navbar-vertical-item) {
padding-inline-start: 0;
}

:host(.dropdown-item) {
.item-title {
font-weight: normal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import { Component, signal } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SiNavbarVerticalNextGroupTriggerDirective } from './si-navbar-vertical-next-group-trigger.directive';
import { SiNavbarVerticalNextGroupComponent } from './si-navbar-vertical-next-group.component';
import { SiNavbarVerticalNextItemComponent } from './si-navbar-vertical-next-item.component';
import { SI_NAVBAR_VERTICAL_NEXT } from './si-navbar-vertical-next.provider';

Expand Down Expand Up @@ -51,12 +53,14 @@ describe('SiNavbarVerticalNextItemComponent', () => {
const mockNavbar = {
collapsed: signal(false),
textOnly: signal(false),
alwaysOpenGroupsInFlyout: signal(false),
itemTriggered: vi.fn()
};

beforeEach(async () => {
mockNavbar.collapsed.set(false);
mockNavbar.textOnly.set(false);
mockNavbar.alwaysOpenGroupsInFlyout.set(false);

await TestBed.configureTestingModule({
providers: [{ provide: SI_NAVBAR_VERTICAL_NEXT, useValue: mockNavbar }]
Expand Down Expand Up @@ -285,4 +289,53 @@ describe('SiNavbarVerticalNextItemComponent', () => {
expect(linkElement).not.toHaveClass('hide-badge-collapsed');
});
});

describe('dropdown-caret icon based on openGroupsInFlyout', () => {
@Component({
imports: [
SiNavbarVerticalNextItemComponent,
SiNavbarVerticalNextGroupTriggerDirective,
SiNavbarVerticalNextGroupComponent
],
template: `
<button
type="button"
si-navbar-vertical-next-item
[siNavbarVerticalNextGroupTriggerFor]="groupTemplate"
>
Group Item
</button>
<ng-template #groupTemplate>
<si-navbar-vertical-next-group />
</ng-template>
`
})
class TestGroupItemHostComponent {}

let groupFixture: ComponentFixture<TestGroupItemHostComponent>;

beforeEach(async () => {
TestBed.resetTestingModule();
await TestBed.configureTestingModule({
providers: [{ provide: SI_NAVBAR_VERTICAL_NEXT, useValue: mockNavbar }]
}).compileComponents();

mockNavbar.alwaysOpenGroupsInFlyout.set(false);
groupFixture = TestBed.createComponent(TestGroupItemHostComponent);
groupFixture.detectChanges();
});

it('should show down caret when alwaysOpenGroupsInFlyout is false', () => {
const caretIcon = groupFixture.nativeElement.querySelector('si-icon.dropdown-caret');
expect(caretIcon.getAttribute('data-icon')).toBe('elementDown2');
});

it('should show right caret when alwaysOpenGroupsInFlyout is true', async () => {
mockNavbar.alwaysOpenGroupsInFlyout.set(true);
await groupFixture.whenStable();

const caretIcon = groupFixture.nativeElement.querySelector('si-icon.dropdown-caret');
expect(caretIcon.getAttribute('data-icon')).toBe('elementRight2');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
OnInit
} from '@angular/core';
import { RouterLinkActive } from '@angular/router';
import { elementDown2 } from '@siemens/element-icons';
import { elementDown2, elementRight2 } from '@siemens/element-icons';
import { addIcons, SiIconComponent } from '@siemens/element-ng/icon';
import { SiLinkDirective } from '@siemens/element-ng/link';

Expand All @@ -29,15 +29,15 @@ import { SI_NAVBAR_VERTICAL_NEXT } from './si-navbar-vertical-next.provider';
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'class': 'focus-inside',
'[class.dropdown-item]': 'this.parent?.group?.flyout()',
'[class.navbar-vertical-item]': '!this.parent?.group?.flyout()',
'[class.dropdown-item]': '!openGroupsInFlyout() && this.parent?.group?.flyout()',
'[class.navbar-vertical-item]': '!this.parent?.group?.flyout() || openGroupsInFlyout()',
'[class.active]': 'active',
'[class.hide-badge-collapsed]': 'hideBadgeWhenCollapsed()',
'(click)': 'triggered()'
}
})
export class SiNavbarVerticalNextItemComponent implements OnInit {
protected readonly icons = addIcons({ elementDown2 });
protected readonly icons = addIcons({ elementDown2, elementRight2 });

/** Optional icon to render before the label. */
readonly icon = input<string>();
Expand Down Expand Up @@ -92,6 +92,8 @@ export class SiNavbarVerticalNextItemComponent implements OnInit {
return badge.toString();
});

protected readonly openGroupsInFlyout = computed(() => this.navbar.alwaysOpenGroupsInFlyout());

ngOnInit(): void {
if (this.group && this.active) {
this.group.expanded.set(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export class SiNavbarVerticalNextComponent implements OnChanges, OnInit {
*/
readonly textOnly = input(false, { transform: booleanAttribute });

/**
* When `true`, item-groups always open as a transient flyout panel adjacent to the
* trigger, regardless of whether the navbar is collapsed or expanded.
* Flyouts open and close on click.
*
* @defaultValue false
*/
readonly alwaysOpenGroupsInFlyout = input(false, { transform: booleanAttribute });

/**
* List of vertical navigation items
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class EmptyComponent {}
[textOnly]="textOnly()"
[stateId]="stateId"
[collapsed]="collapsed()"
[alwaysOpenGroupsInFlyout]="alwaysOpenGroupsInFlyout()"
>
<si-navbar-vertical-next-search [debounceTime]="0" (searchChange)="searchEvent($event)" />
@if (showDeclarativeFlyoutGroup()) {
Expand Down Expand Up @@ -158,6 +159,7 @@ class TestHostComponent {
readonly textOnly = signal(true);
stateId?: string;
readonly collapsed = signal(false);
readonly alwaysOpenGroupsInFlyout = signal(false);
readonly showDeclarativeFlyoutGroup = signal(false);
readonly showDeclarativeNavigationGroup = signal(false);
readonly showDeclarativeStateGroups = signal(false);
Expand Down Expand Up @@ -263,6 +265,17 @@ describe('SiNavbarVerticalNext', () => {
expect(await item.isFlyout()).toBe(false);
});

it('should open flyout on click when alwaysOpenGroupsInFlyout is set', async () => {
component.alwaysOpenGroupsInFlyout.set(true);
component.showDeclarativeFlyoutGroup.set(true);
fixture.detectChanges();
await fixture.whenStable();

const item = await harness.findItemByLabel('item-1');
await item.click();
expect(await item.isFlyout()).toBe(true);
});

it('should emit search event', async () => {
component.collapsed.set(true);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<div class="has-navbar-fixed-top">
<si-application-header>
<si-header-brand>
<a routerLink="/" siHeaderLogo></a>
<h1 class="application-name">Navbar Vertical Next — Always Open Groups In Flyout</h1>
</si-header-brand>
</si-application-header>

<si-navbar-vertical-next
alwaysOpenGroupsInFlyout
stateId="navbar-vertical-next-always-flyout"
navbarCollapseButtonText="collapse"
navbarExpandButtonText="expand"
>
<si-navbar-vertical-next-search placeholder="Search..." />
<si-navbar-vertical-next-items>
<a si-navbar-vertical-next-item routerLink="home" routerLinkActive icon="element-home">
Home
</a>
<si-navbar-vertical-next-header>Modules</si-navbar-vertical-next-header>
<a si-navbar-vertical-next-item routerLink="energy" routerLinkActive icon="element-trend">
Energy &amp; sustainability
</a>
<button
type="button"
si-navbar-vertical-next-item
icon="element-user-group"
[siNavbarVerticalNextGroupTriggerFor]="userManagement"
>
User management
</button>
<a
si-navbar-vertical-next-item
routerLink="coverage"
routerLinkActive
icon="element-diagnostic"
>
Test coverage
</a>
<si-navbar-vertical-next-divider />
<button
type="button"
si-navbar-vertical-next-item
icon="element-document"
[siNavbarVerticalNextGroupTriggerFor]="documentation"
>
Documentation
</button>
</si-navbar-vertical-next-items>
<si-navbar-vertical-next-footer-items>
<a
si-navbar-vertical-next-item
routerLink="settings"
routerLinkActive
icon="element-settings"
>
Settings
</a>
</si-navbar-vertical-next-footer-items>

<div class="si-layout-main-padding">
<div class="si-layout-header">
<h2 class="si-layout-title si-layout-top-element">Here is a title</h2>
</div>
<router-outlet />
</div>
</si-navbar-vertical-next>
</div>

<ng-template #userManagement>
<si-navbar-vertical-next-group routerLinkActive>
<a si-navbar-vertical-next-item routerLink="subItem" routerLinkActive icon="element-rocket">
Sub item
</a>
<a si-navbar-vertical-next-item routerLink="subItem2" routerLinkActive icon="element-rocket">
Sub item 2
</a>
<a si-navbar-vertical-next-item routerLink="subItem3" routerLinkActive icon="element-rocket">
Sub item 3
</a>
</si-navbar-vertical-next-group>
</ng-template>

<ng-template #documentation>
<si-navbar-vertical-next-group routerLinkActive>
<a si-navbar-vertical-next-item routerLink="subItem4" routerLinkActive icon="element-rocket">
Sub item 4
</a>
<a si-navbar-vertical-next-item routerLink="subItem5" routerLinkActive icon="element-rocket">
Sub item 5
</a>
<a si-navbar-vertical-next-item routerLink="subItem6" routerLinkActive icon="element-rocket">
Sub item 6
</a>
</si-navbar-vertical-next-group>
</ng-template>
Loading
Loading