1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 13:10:17 +00:00

open drawer as dialog

This commit is contained in:
William Martin
2025-04-03 21:05:09 -04:00
parent e23a353543
commit 5f2bdf0eae
10 changed files with 301 additions and 213 deletions

View File

@@ -1,31 +1,25 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import {
DEFAULT_DIALOG_CONFIG,
Dialog,
Dialog as CdkDialog,
DialogConfig,
DialogRef,
DIALOG_SCROLL_STRATEGY,
DialogRef as CdkDialogRef,
DIALOG_DATA,
DialogCloseOptions,
} from "@angular/cdk/dialog";
import { ComponentType, Overlay, OverlayContainer, ScrollStrategy } from "@angular/cdk/overlay";
import {
Inject,
Injectable,
Injector,
OnDestroy,
Optional,
SkipSelf,
TemplateRef,
} from "@angular/core";
import { ComponentType, ScrollStrategy } from "@angular/cdk/overlay";
import { ComponentPortal, Portal } from "@angular/cdk/portal";
import { Injectable, InjectionToken, Injector, TemplateRef, inject } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { NavigationEnd, Router } from "@angular/router";
import { filter, firstValueFrom, Subject, switchMap, takeUntil } from "rxjs";
import { filter, firstValueFrom, map, Observable, Subject, switchMap } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DrawerService } from "../drawer/drawer.service";
import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component";
import { SimpleDialogOptions, Translation } from "./simple-dialog/types";
import { SimpleDialogOptions } from "./simple-dialog/types";
/**
* The default `BlockScrollStrategy` does not work well with virtual scrolling.
@@ -48,53 +42,74 @@ class CustomBlockScrollStrategy implements ScrollStrategy {
detach() {}
}
export const IS_DRAWER_TOKEN = new InjectionToken<boolean>("IS_DRAWER");
export abstract class DialogRef<R = unknown, C = unknown>
implements Pick<CdkDialogRef<R, C>, "close" | "closed" | "disableClose" | "componentInstance">
{
abstract close(result?: R, options?: DialogCloseOptions): void;
abstract closed: Observable<R | undefined>;
abstract disableClose: boolean | undefined;
/**
* @deprecated
* Does not work with drawer dialogs.
**/
abstract componentInstance: C | null;
}
class DrawerDialogRef<R = unknown, C = unknown> implements DialogRef<R, C> {
private _closed = new Subject<R | undefined>();
closed = this._closed.asObservable();
disableClose = false;
/** The portal containing the drawer */
portal?: Portal<unknown>;
constructor(private drawerService: DrawerService) {}
close(result?: R, _options?: DialogCloseOptions): void {
if (this.disableClose) {
return;
}
this.drawerService.close(this.portal!);
this._closed.next(result);
this._closed.complete();
}
componentInstance: C | null = null;
}
@Injectable()
export class DialogService extends Dialog implements OnDestroy {
private _destroy$ = new Subject<void>();
export class DialogService {
private dialog = inject(CdkDialog);
private drawerService = inject(DrawerService);
private injector = inject(Injector);
private router = inject(Router, { optional: true });
private authService = inject(AuthService, { optional: true });
private i18nService = inject(I18nService);
private backDropClasses = ["tw-fixed", "tw-bg-black", "tw-bg-opacity-30", "tw-inset-0"];
private defaultScrollStrategy = new CustomBlockScrollStrategy();
private activeDrawer: DrawerDialogRef<any, any> | null = null;
constructor(
/** Parent class constructor */
_overlay: Overlay,
_injector: Injector,
@Optional() @Inject(DEFAULT_DIALOG_CONFIG) _defaultOptions: DialogConfig,
@Optional() @SkipSelf() _parentDialog: Dialog,
_overlayContainer: OverlayContainer,
@Inject(DIALOG_SCROLL_STRATEGY) scrollStrategy: any,
/** Not in parent class */
@Optional() router: Router,
@Optional() authService: AuthService,
protected i18nService: I18nService,
) {
super(_overlay, _injector, _defaultOptions, _parentDialog, _overlayContainer, scrollStrategy);
constructor() {
/** TODO: This logic should exist outside of `libs/components`. */
/** Close all open dialogs if the vault locks */
if (router && authService) {
router.events
if (this.router && this.authService) {
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
switchMap(() => authService.getAuthStatus()),
switchMap(() => this.authService!.getAuthStatus()),
filter((v) => v !== AuthenticationStatus.Unlocked),
takeUntil(this._destroy$),
takeUntilDestroyed(),
)
.subscribe(() => this.closeAll());
}
}
override ngOnDestroy(): void {
this._destroy$.next();
this._destroy$.complete();
super.ngOnDestroy();
}
override open<R = unknown, D = unknown, C = unknown>(
open<R = unknown, D = unknown, C = any>(
componentOrTemplateRef: ComponentType<C> | TemplateRef<C>,
config?: DialogConfig<D, DialogRef<R, C>>,
config?: DialogConfig<D, CdkDialogRef<R, C>>,
): DialogRef<R, C> {
config = {
backdropClass: this.backDropClasses,
@@ -102,7 +117,44 @@ export class DialogService extends Dialog implements OnDestroy {
...config,
};
return super.open(componentOrTemplateRef, config);
return this.dialog.open(componentOrTemplateRef, config);
}
/** Opens a dialog in the side drawer */
openDrawer<R = unknown, D = unknown, C = unknown>(
component: ComponentType<C>,
config?: DialogConfig<D, DialogRef<R, C>>,
): DialogRef<R, C> {
this.activeDrawer?.close();
this.activeDrawer = new DrawerDialogRef(this.drawerService);
const portal = new ComponentPortal(
component,
null,
Injector.create({
providers: [
{
provide: DIALOG_DATA,
useValue: config?.data,
},
{
provide: CdkDialogRef,
useValue: this.activeDrawer,
},
{
provide: DialogRef,
useValue: this.activeDrawer,
},
{
provide: IS_DRAWER_TOKEN,
useValue: true,
},
],
parent: this.injector,
}),
);
this.activeDrawer.portal = portal;
this.drawerService.open(portal);
return this.activeDrawer;
}
/**
@@ -113,8 +165,7 @@ export class DialogService extends Dialog implements OnDestroy {
*/
async openSimpleDialog(simpleDialogOptions: SimpleDialogOptions): Promise<boolean> {
const dialogRef = this.openSimpleDialogRef(simpleDialogOptions);
return firstValueFrom(dialogRef.closed);
return firstValueFrom(dialogRef.closed.pipe(map((v: boolean | undefined) => !!v)));
}
/**
@@ -134,20 +185,8 @@ export class DialogService extends Dialog implements OnDestroy {
});
}
protected translate(translation: string | Translation, defaultKey?: string): string {
if (translation == null && defaultKey == null) {
return null;
}
if (translation == null) {
return this.i18nService.t(defaultKey);
}
// Translation interface use implies we must localize.
if (typeof translation === "object") {
return this.i18nService.t(translation.key, ...(translation.placeholders ?? []));
}
return translation;
/** Close all open dialogs */
closeAll(): void {
return this.dialog.closeAll();
}
}

View File

@@ -1,12 +1,15 @@
<section
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-rounded-xl tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-text-main"
[ngClass]="width"
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-text-main"
[ngClass]="[width, isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-xl']"
@fadeIn
cdkTrapFocus
cdkTrapFocusAutoCapture
>
<header
class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300 tw-p-4"
class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
[ngClass]="[isDrawer ? 'tw-py-2 tw-px-4' : 'tw-p-4'].flat()"
>
<h1
<h2
bitDialogTitleContainer
bitTypography="h3"
noMargin
@@ -19,7 +22,7 @@
</span>
}
<ng-content select="[bitDialogTitle]"></ng-content>
</h1>
</h2>
<button
type="button"
bitIconButton="bwi-close"

View File

@@ -1,14 +1,16 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CdkTrapFocus } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { CommonModule } from "@angular/common";
import { Component, HostBinding, Input } from "@angular/core";
import { Component, HostBinding, Input, inject } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BitIconButtonComponent } from "../../icon-button/icon-button.component";
import { TypographyDirective } from "../../typography/typography.directive";
import { fadeIn } from "../animations";
import { DialogRef, IS_DRAWER_TOKEN } from "../dialog.service";
import { DialogCloseDirective } from "../directives/dialog-close.directive";
import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive";
@@ -17,6 +19,9 @@ import { DialogTitleContainerDirective } from "../directives/dialog-title-contai
templateUrl: "./dialog.component.html",
animations: [fadeIn],
standalone: true,
host: {
"(keydown.esc)": "handleEsc()",
},
imports: [
CommonModule,
DialogTitleContainerDirective,
@@ -24,9 +29,13 @@ import { DialogTitleContainerDirective } from "../directives/dialog-title-contai
BitIconButtonComponent,
DialogCloseDirective,
I18nPipe,
CdkTrapFocus,
],
})
export class DialogComponent {
private dialogRef = inject(DialogRef, { optional: true });
protected isDrawer = inject(IS_DRAWER_TOKEN, { optional: true }) ?? false;
/** Background color */
@Input()
background: "default" | "alt" = "default";
@@ -64,21 +73,28 @@ export class DialogComponent {
@HostBinding("class") get classes() {
// `tw-max-h-[90vh]` is needed to prevent dialogs from overlapping the desktop header
return ["tw-flex", "tw-flex-col", "tw-w-screen", "tw-p-4", "tw-max-h-[90vh]"].concat(
this.width,
);
return ["tw-flex", "tw-flex-col"]
.concat(
this.width,
!this.isDrawer ? ["tw-p-4", "tw-w-screen", "tw-max-h-[90vh]"] : ["tw-min-h-screen"],
)
.flat();
}
handleEsc() {
this.dialogRef.close();
}
get width() {
switch (this.dialogSize) {
case "small": {
return "tw-max-w-sm";
return "md:tw-max-w-sm";
}
case "large": {
return "tw-max-w-3xl";
return "md:tw-max-w-3xl";
}
default: {
return "tw-max-w-xl";
return "md:tw-max-w-xl";
}
}
}

View File

@@ -1,4 +1,4 @@
export * from "./dialog.module";
export * from "./simple-dialog/types";
export * from "./dialog.service";
export { DialogConfig, DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
export { DialogConfig, DIALOG_DATA } from "@angular/cdk/dialog";

View File

@@ -10,7 +10,7 @@ import {
viewChild,
} from "@angular/core";
import { DrawerHostDirective } from "./drawer-host.directive";
import { DrawerService } from "./drawer.service";
/**
* A drawer is a panel of supplementary content that is adjacent to the page's main content.
@@ -25,7 +25,7 @@ import { DrawerHostDirective } from "./drawer-host.directive";
templateUrl: "drawer.component.html",
})
export class DrawerComponent {
private drawerHost = inject(DrawerHostDirective);
private drawerHost = inject(DrawerService);
private portal = viewChild.required(CdkPortal);
/**

View File

@@ -0,0 +1,20 @@
import { Portal } from "@angular/cdk/portal";
import { Injectable, signal } from "@angular/core";
@Injectable({ providedIn: "root" })
export class DrawerService {
private _portal = signal<Portal<unknown> | undefined>(undefined);
/** The portal to display */
portal = this._portal.asReadonly();
open(portal: Portal<unknown>) {
this._portal.set(portal);
}
close(portal: Portal<unknown>) {
if (portal === this.portal()) {
this._portal.set(undefined);
}
}
}

View File

@@ -1,43 +1,53 @@
<div
class="tw-fixed tw-z-50 tw-w-full tw-flex tw-justify-center tw-opacity-0 focus-within:tw-opacity-100 tw-pointer-events-none focus-within:tw-pointer-events-auto"
>
<nav class="tw-bg-background-alt3 tw-rounded-md tw-rounded-t-none tw-py-2 tw-text-alt2">
<a
bitLink
class="tw-mx-6 focus-visible:before:!tw-ring-0"
[fragment]="mainContentId"
[routerLink]="[]"
(click)="focusMainContent()"
linkType="light"
>{{ "skipToContent" | i18n }}</a
>
</nav>
</div>
<div class="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 md:tw-ml-0 tw-ml-16"
>
<ng-content></ng-content>
<div class="tw-flex tw-w-full" cdkTrapFocus>
<div
class="tw-fixed tw-z-50 tw-w-full tw-flex tw-justify-center tw-opacity-0 focus-within:tw-opacity-100 tw-pointer-events-none focus-within:tw-pointer-events-auto"
>
<nav class="tw-bg-background-alt3 tw-rounded-md tw-rounded-t-none tw-py-2 tw-text-alt2">
<a
bitLink
class="tw-mx-6 focus-visible:before:!tw-ring-0"
[fragment]="mainContentId"
[routerLink]="[]"
(click)="focusMainContent()"
linkType="light"
>{{ "skipToContent" | i18n }}</a
>
</nav>
</div>
<ng-content select="bit-side-nav, [slot=side-nav]"></ng-content>
<main
[id]="mainContentId"
tabindex="-1"
class="tw-overflow-auto tw-max-h-screen 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 -->
@if (
{
open: sideNavService.open$ | async,
};
as data
) {
<div
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']"
>
@if (data.open) {
<div (click)="sideNavService.toggle()" class="tw-pointer-events-auto tw-size-full"></div>
}
</div>
}
</main>
<ng-template [cdkPortalOutlet]="drawerPortal()"></ng-template>
<!-- overlay backdrop for side-nav -->
@if (
{
open: sideNavService.open$ | async,
};
as data
) {
<div
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']"
>
@if (data.open) {
<div
(click)="sideNavService.toggle()"
class="tw-pointer-events-auto tw-size-full"
></div>
}
</div>
}
</main>
</div>
<!-- eslint-disable-next-line tailwindcss/enforces-shorthand -->
<div
class="tw-absolute tw-z-50 tw-left-0 md:tw-sticky tw-top-0 tw-h-screen tw-w-screen md:tw-w-auto"
>
<ng-template [cdkPortalOutlet]="drawerPortal()"></ng-template>
</div>
</div>

View File

@@ -1,9 +1,10 @@
import { A11yModule, CdkTrapFocus } from "@angular/cdk/a11y";
import { PortalModule } from "@angular/cdk/portal";
import { CommonModule } from "@angular/common";
import { Component, inject } from "@angular/core";
import { RouterModule } from "@angular/router";
import { DrawerHostDirective } from "../drawer/drawer-host.directive";
import { DrawerService } from "../drawer/drawer.service";
import { LinkModule } from "../link";
import { SideNavService } from "../navigation/side-nav.service";
import { SharedModule } from "../shared";
@@ -12,14 +13,21 @@ import { SharedModule } from "../shared";
selector: "bit-layout",
templateUrl: "layout.component.html",
standalone: true,
imports: [CommonModule, SharedModule, LinkModule, RouterModule, PortalModule],
hostDirectives: [DrawerHostDirective],
imports: [
CommonModule,
SharedModule,
LinkModule,
RouterModule,
PortalModule,
A11yModule,
CdkTrapFocus,
],
})
export class LayoutComponent {
protected mainContentId = "main-content";
protected sideNavService = inject(SideNavService);
protected drawerPortal = inject(DrawerHostDirective).portal;
protected drawerPortal = inject(DrawerService).portal;
focusMainContent() {
document.getElementById(this.mainContentId)?.focus();

View File

@@ -12,8 +12,69 @@ import { KitchenSinkToggleList } from "./kitchen-sink-toggle-list.component";
standalone: true,
imports: [KitchenSinkSharedModule],
template: `
<bit-dialog title="Dialog Title" dialogSize="large">
<span bitDialogContent> Dialog body text goes here. </span>
<bit-dialog title="Dialog Title" dialogSize="small">
<ng-container bitDialogContent>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<bit-form-field>
<bit-label>What did foo say to bar?</bit-label>
<input bitInput value="Baz" />
</bit-form-field>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
</ng-container>
<ng-container bitDialogFooter>
<button type="button" bitButton buttonType="primary" (click)="dialogRef.close()">OK</button>
<button type="button" bitButton buttonType="secondary" bitDialogClose>Cancel</button>
@@ -90,72 +151,6 @@ class KitchenSinkDialog {
</bit-section>
</bit-tab>
</bit-tab-group>
<bit-drawer [(open)]="drawerOpen">
<bit-drawer-header title="Foo ipsum"></bit-drawer-header>
<bit-drawer-body>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<bit-form-field>
<bit-label>What did foo say to bar?</bit-label>
<input bitInput value="Baz" />
</bit-form-field>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
<p bitTypography="body1">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.
</p>
</bit-drawer-body>
</bit-drawer>
`,
})
export class KitchenSinkMainComponent {
@@ -168,7 +163,7 @@ export class KitchenSinkMainComponent {
}
openDrawer() {
this.drawerOpen.set(true);
this.dialogService.openDrawer(KitchenSinkDialog);
}
navItems = [

View File

@@ -14,7 +14,7 @@ import {
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService } from "../../dialog";
// import { DialogService } from "../../dialog";
import { LayoutComponent } from "../../layout";
import { I18nMockService } from "../../utils/i18n-mock.service";
import { positionFixedWrapperDecorator } from "../storybook-decorators";
@@ -39,8 +39,20 @@ export default {
KitchenSinkTable,
KitchenSinkToggleList,
],
}),
applicationConfig({
providers: [
DialogService,
provideNoopAnimations(),
importProvidersFrom(
RouterModule.forRoot(
[
{ path: "", redirectTo: "bitwarden", pathMatch: "full" },
{ path: "bitwarden", component: KitchenSinkMainComponent },
{ path: "virtual-scroll", component: DialogVirtualScrollBlockComponent },
],
{ useHash: true },
),
),
{
provide: I18nService,
useFactory: () => {
@@ -58,21 +70,6 @@ export default {
},
],
}),
applicationConfig({
providers: [
provideNoopAnimations(),
importProvidersFrom(
RouterModule.forRoot(
[
{ path: "", redirectTo: "bitwarden", pathMatch: "full" },
{ path: "bitwarden", component: KitchenSinkMainComponent },
{ path: "virtual-scroll", component: DialogVirtualScrollBlockComponent },
],
{ useHash: true },
),
),
],
}),
],
} as Meta;