1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[CL-118][CL-164][PM-8019] collapsible side navigation (#6383)

This commit is contained in:
Will Martin
2024-06-17 14:10:50 -04:00
committed by GitHub
parent 3bfdc50d5d
commit 06410a0633
30 changed files with 624 additions and 184 deletions

View File

@@ -13,24 +13,28 @@
>
</nav>
</div>
<div class="tw-flex tw-w-full">
<aside
[ngStyle]="
variant === 'secondary' && {
'--color-text-alt2': 'var(--color-text-main)',
'--color-background-alt3': 'var(--color-secondary-100)',
'--color-background-alt4': 'var(--color-secondary-300)'
}
"
class="tw-sticky tw-inset-y-0 tw-h-screen tw-w-60 tw-overflow-auto tw-bg-background-alt3"
>
<ng-content select="[slot=sidebar]"></ng-content>
</aside>
<div class="tw-group tw-flex tw-w-full">
<ng-content select="bit-side-nav, [slot=side-nav]"></ng-content>
<main
[id]="mainContentId"
tabindex="-1"
class="tw-overflow-auto tw-min-w-0 tw-flex-1 tw-bg-background tw-p-6"
class="tw-overflow-auto tw-min-w-0 tw-flex-1 tw-bg-background tw-p-6 md:tw-ml-0 tw-ml-16"
>
<ng-content></ng-content>
<!-- overlay backdrop for side-nav -->
<div
*ngIf="{
open: sideNavService.open$ | async
} as data"
class="tw-pointer-events-none tw-fixed tw-inset-0 tw-z-10 tw-bg-black tw-bg-opacity-0 motion-safe:tw-transition-colors md:tw-hidden"
[ngClass]="[data.open ? 'tw-bg-opacity-30 md:tw-bg-opacity-0' : 'tw-bg-opacity-0']"
>
<div
*ngIf="data.open"
(click)="sideNavService.toggle()"
class="tw-pointer-events-auto tw-h-full tw-w-full"
></div>
</div>
</main>
</div>

View File

@@ -1,21 +1,21 @@
import { Component, Input } from "@angular/core";
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { RouterModule } from "@angular/router";
import { LinkModule } from "../link";
import { SideNavService } from "../navigation/side-nav.service";
import { SharedModule } from "../shared";
export type LayoutVariant = "primary" | "secondary";
@Component({
selector: "bit-layout",
templateUrl: "layout.component.html",
standalone: true,
imports: [SharedModule, LinkModule, RouterModule],
imports: [CommonModule, SharedModule, LinkModule, RouterModule],
})
export class LayoutComponent {
protected mainContentId = "main-content";
@Input() variant: LayoutVariant = "primary";
constructor(protected sideNavService: SideNavService) {}
focusMainContent() {
document.getElementById(this.mainContentId)?.focus();

View File

@@ -1,5 +1,5 @@
import { RouterTestingModule } from "@angular/router/testing";
import { Meta, StoryObj, componentWrapperDecorator, moduleMetadata } from "@storybook/angular";
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
import { userEvent } from "@storybook/testing-library";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -7,6 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { CalloutModule } from "../callout";
import { NavigationModule } from "../navigation";
import { I18nMockService } from "../utils/i18n-mock.service";
import { positionFixedWrapperDecorator } from "../utils/position-fixed-wrapper-decorator";
import { LayoutComponent } from "./layout.component";
@@ -14,16 +15,7 @@ export default {
title: "Component Library/Layout",
component: LayoutComponent,
decorators: [
componentWrapperDecorator(
/**
* Applying a CSS transform makes a `position: fixed` element act like it is `position: relative`
* https://github.com/storybookjs/storybook/issues/8011#issue-490251969
*/
(story) =>
/* HTML */ `<div class="tw-scale-100 tw-border-2 tw-border-solid tw-border-[red]">
${story}
</div>`,
),
positionFixedWrapperDecorator(),
moduleMetadata({
imports: [NavigationModule, RouterTestingModule, CalloutModule],
providers: [
@@ -31,6 +23,7 @@ export default {
provide: I18nService,
useFactory: () => {
return new I18nMockService({
toggleSideNavigation: "Toggle side navigation",
skipToContent: "Skip to content",
submenu: "submenu",
toggleCollapse: "toggle collapse",
@@ -40,6 +33,9 @@ export default {
],
}),
],
parameters: {
chromatic: { viewports: [640, 1280] },
},
} as Meta;
type Story = StoryObj<LayoutComponent>;
@@ -47,7 +43,9 @@ type Story = StoryObj<LayoutComponent>;
export const Empty: Story = {
render: (args) => ({
props: args,
template: /* HTML */ `<bit-layout></bit-layout>`,
template: /* HTML */ `<bit-layout>
<bit-side-nav></bit-side-nav>
</bit-layout>`,
}),
};
@@ -56,13 +54,9 @@ export const WithContent: Story = {
props: args,
template: /* HTML */ `
<bit-layout>
<nav slot="sidebar">
<bit-nav-item text="Item A" icon="bwi-collection"></bit-nav-item>
<bit-nav-item text="Item B" icon="bwi-collection"></bit-nav-item>
<bit-nav-divider></bit-nav-divider>
<bit-nav-item text="Item C" icon="bwi-collection"></bit-nav-item>
<bit-nav-item text="Item D" icon="bwi-collection"></bit-nav-item>
<bit-nav-group text="Tree example" icon="bwi-collection" [open]="true">
<bit-side-nav>
<bit-nav-item text="Item A" route="#" icon="bwi-lock"></bit-nav-item>
<bit-nav-group text="Tree A" icon="bwi-family" [open]="true">
<bit-nav-group
text="Level 1 - with children (empty)"
route="#"
@@ -129,7 +123,141 @@ export const WithContent: Story = {
variant="tree"
></bit-nav-item>
</bit-nav-group>
</nav>
<bit-nav-group text="Tree B" icon="bwi-collection" [open]="true">
<bit-nav-group
text="Level 1 - with children (empty)"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-group>
<bit-nav-item
text="Level 1 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
<bit-nav-group
text="Level 1 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-group
text="Level 2 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-item
text="Level 3 - no children, no icon"
route="#"
variant="tree"
></bit-nav-item>
<bit-nav-group
text="Level 3 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-item
text="Level 4 - no children, no icon"
route="#"
variant="tree"
></bit-nav-item>
</bit-nav-group>
</bit-nav-group>
<bit-nav-group
text="Level 2 - with children (empty)"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
></bit-nav-group>
<bit-nav-item
text="Level 2 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
</bit-nav-group>
<bit-nav-item
text="Level 1 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
</bit-nav-group>
<bit-nav-group text="Tree C" icon="bwi-key" [open]="true">
<bit-nav-group
text="Level 1 - with children (empty)"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-group>
<bit-nav-item
text="Level 1 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
<bit-nav-group
text="Level 1 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-group
text="Level 2 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-item
text="Level 3 - no children, no icon"
route="#"
variant="tree"
></bit-nav-item>
<bit-nav-group
text="Level 3 - with children"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
>
<bit-nav-item
text="Level 4 - no children, no icon"
route="#"
variant="tree"
></bit-nav-item>
</bit-nav-group>
</bit-nav-group>
<bit-nav-group
text="Level 2 - with children (empty)"
route="#"
icon="bwi-collection"
variant="tree"
[open]="true"
></bit-nav-group>
<bit-nav-item
text="Level 2 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
</bit-nav-group>
<bit-nav-item
text="Level 1 - no children"
route="#"
icon="bwi-collection"
variant="tree"
></bit-nav-item>
</bit-nav-group>
</bit-side-nav>
<bit-callout title="Foobar"> Hello world! </bit-callout>
</bit-layout>
`,
@@ -147,8 +275,8 @@ export const Secondary: Story = {
render: (args) => ({
props: args,
template: /* HTML */ `
<bit-layout variant="secondary">
<nav slot="sidebar">
<bit-layout>
<bit-side-nav variant="secondary">
<bit-nav-item text="Item A" icon="bwi-collection"></bit-nav-item>
<bit-nav-item text="Item B" icon="bwi-collection"></bit-nav-item>
<bit-nav-divider></bit-nav-divider>
@@ -221,7 +349,7 @@ export const Secondary: Story = {
variant="tree"
></bit-nav-item>
</bit-nav-group>
</nav>
</bit-side-nav>
<bit-callout title="Foobar"> Hello world! </bit-callout>
</bit-layout>
`,