mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
[CL-428] create drawer component (#12812)
* remove private/protected/lifecycle fields from Storybook docs table * move theme override decorator into util method * implement base drawer component * update bit-layout to be drawer container * create drawer helper components * expose new APIs to DS barrel file * write docs * update docs; add role input * use host directive instead of service * clean up logic a tad * add start slot to story * update docs * Apply suggestions from code review Co-authored-by: Victoria League <vleague@bitwarden.com> * update docs * Update libs/components/src/drawer/drawer.mdx Co-authored-by: Victoria League <vleague@bitwarden.com> * update docs / stories * add non text element to drawer --------- Co-authored-by: Victoria League <vleague@bitwarden.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component } from "@angular/core";
|
||||
import { Component, signal } from "@angular/core";
|
||||
|
||||
import { DialogService } from "../../../dialog";
|
||||
import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module";
|
||||
@@ -28,13 +28,7 @@ class KitchenSinkDialog {
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: "bit-tab-main",
|
||||
imports: [
|
||||
KitchenSinkSharedModule,
|
||||
KitchenSinkTable,
|
||||
KitchenSinkToggleList,
|
||||
KitchenSinkForm,
|
||||
KitchenSinkDialog,
|
||||
],
|
||||
imports: [KitchenSinkSharedModule, KitchenSinkTable, KitchenSinkToggleList, KitchenSinkForm],
|
||||
template: `
|
||||
<bit-banner bannerType="info" class="-tw-m-6 tw-flex tw-flex-col tw-pb-6">
|
||||
Kitchen Sink test zone
|
||||
@@ -48,6 +42,11 @@ class KitchenSinkDialog {
|
||||
</bit-breadcrumbs>
|
||||
</p>
|
||||
|
||||
<div class="tw-mb-6 tw-mt-6">
|
||||
<h1 bitTypography="h1">Bitwarden Kitchen Sink<bit-avatar text="Bit Warden"></bit-avatar></h1>
|
||||
<a bitLink linkType="primary" href="#">Learn more</a>
|
||||
</div>
|
||||
|
||||
<bit-callout type="info" title="About the Kitchen Sink">
|
||||
<p bitTypography="body1">
|
||||
The purpose of this story is to compose together all of our components. When snapshot tests
|
||||
@@ -63,18 +62,14 @@ class KitchenSinkDialog {
|
||||
</p>
|
||||
</bit-callout>
|
||||
|
||||
<div class="tw-mb-6 tw-mt-6">
|
||||
<h1 bitTypography="h1">Bitwarden <bit-avatar text="Bit Warden"></bit-avatar></h1>
|
||||
<a bitLink linkType="primary" href="#">Learn more</a>
|
||||
</div>
|
||||
|
||||
<bit-tab-group label="Main content tabs" class="tw-text-main">
|
||||
<bit-tab label="Evaluation">
|
||||
<bit-section>
|
||||
<h2 bitTypography="h2" class="tw-mb-6">About</h2>
|
||||
<bit-kitchen-sink-table></bit-kitchen-sink-table>
|
||||
|
||||
<button bitButton (click)="openDefaultDialog()">Open Dialog</button>
|
||||
<button bitButton (click)="openDialog()">Open Dialog</button>
|
||||
<button bitButton (click)="openDrawer()">Open Drawer</button>
|
||||
</bit-section>
|
||||
<bit-section>
|
||||
<h2 bitTypography="h2" class="tw-mb-6">Companies using Bitwarden</h2>
|
||||
@@ -99,15 +94,87 @@ 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 {
|
||||
constructor(public dialogService: DialogService) {}
|
||||
|
||||
openDefaultDialog() {
|
||||
protected drawerOpen = signal(false);
|
||||
|
||||
openDialog() {
|
||||
this.dialogService.open(KitchenSinkDialog);
|
||||
}
|
||||
|
||||
openDrawer() {
|
||||
this.drawerOpen.set(true);
|
||||
}
|
||||
|
||||
navItems = [
|
||||
{ icon: "bwi-collection", name: "Password Managers", route: "/" },
|
||||
{ icon: "bwi-collection", name: "Favorites", route: "/" },
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CalloutModule } from "../../callout";
|
||||
import { CheckboxModule } from "../../checkbox";
|
||||
import { ColorPasswordModule } from "../../color-password";
|
||||
import { DialogModule } from "../../dialog";
|
||||
import { DrawerModule } from "../../drawer";
|
||||
import { FormControlModule } from "../../form-control";
|
||||
import { FormFieldModule } from "../../form-field";
|
||||
import { IconModule } from "../../icon";
|
||||
@@ -48,6 +49,7 @@ import { TypographyModule } from "../../typography";
|
||||
ColorPasswordModule,
|
||||
CommonModule,
|
||||
DialogModule,
|
||||
DrawerModule,
|
||||
FormControlModule,
|
||||
FormFieldModule,
|
||||
FormsModule,
|
||||
@@ -85,6 +87,7 @@ import { TypographyModule } from "../../typography";
|
||||
ColorPasswordModule,
|
||||
CommonModule,
|
||||
DialogModule,
|
||||
DrawerModule,
|
||||
FormControlModule,
|
||||
FormFieldModule,
|
||||
FormsModule,
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { importProvidersFrom } from "@angular/core";
|
||||
import { provideNoopAnimations } from "@angular/platform-browser/animations";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import {
|
||||
Meta,
|
||||
StoryObj,
|
||||
applicationConfig,
|
||||
componentWrapperDecorator,
|
||||
moduleMetadata,
|
||||
} from "@storybook/angular";
|
||||
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
|
||||
import {
|
||||
userEvent,
|
||||
getAllByRole,
|
||||
@@ -23,6 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { DialogService } from "../../dialog";
|
||||
import { LayoutComponent } from "../../layout";
|
||||
import { I18nMockService } from "../../utils/i18n-mock.service";
|
||||
import { disableBothThemeDecorator, positionFixedWrapperDecorator } from "../storybook-decorators";
|
||||
|
||||
import { DialogVirtualScrollBlockComponent } from "./components/dialog-virtual-scroll-block.component";
|
||||
import { KitchenSinkForm } from "./components/kitchen-sink-form.component";
|
||||
@@ -35,25 +30,8 @@ export default {
|
||||
title: "Documentation / Kitchen Sink",
|
||||
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) => {
|
||||
return /* HTML */ `<div class="tw-scale-100 tw-border-2 tw-border-solid tw-border-[red]">
|
||||
${story}
|
||||
</div>`;
|
||||
},
|
||||
({ globals }) => {
|
||||
/**
|
||||
* avoid a bug with the way that we render the same component twice in the same iframe and how
|
||||
* that interacts with the router-outlet
|
||||
*/
|
||||
const themeOverride = globals["theme"] === "both" ? "light" : globals["theme"];
|
||||
return { theme: themeOverride };
|
||||
},
|
||||
),
|
||||
positionFixedWrapperDecorator(),
|
||||
disableBothThemeDecorator,
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
KitchenSinkSharedModule,
|
||||
@@ -135,7 +113,7 @@ export const MenuOpen: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultDialogOpen: Story = {
|
||||
export const DialogOpen: Story = {
|
||||
...Default,
|
||||
play: async (context) => {
|
||||
const canvas = context.canvasElement;
|
||||
@@ -148,6 +126,19 @@ export const DefaultDialogOpen: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const DrawerOpen: Story = {
|
||||
...Default,
|
||||
play: async (context) => {
|
||||
const canvas = context.canvasElement;
|
||||
const drawerButton = getByRole(canvas, "button", {
|
||||
name: "Open Drawer",
|
||||
});
|
||||
|
||||
// workaround for userEvent not firing in FF https://github.com/testing-library/user-event/issues/1075
|
||||
await fireEvent.click(drawerButton);
|
||||
},
|
||||
};
|
||||
|
||||
export const PopoverOpen: Story = {
|
||||
...Default,
|
||||
play: async (context) => {
|
||||
|
||||
31
libs/components/src/stories/storybook-decorators.ts
Normal file
31
libs/components/src/stories/storybook-decorators.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { componentWrapperDecorator } from "@storybook/angular";
|
||||
|
||||
/**
|
||||
* Render a story that uses `position: fixed`
|
||||
* Used in layout and navigation components
|
||||
**/
|
||||
export const positionFixedWrapperDecorator = (wrapper?: (story: string) => string) =>
|
||||
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-h-screen tw-border-2 tw-border-solid tw-border-secondary-300 tw-overflow-auto"
|
||||
>
|
||||
${wrapper ? wrapper(story) : story}
|
||||
</div>`,
|
||||
);
|
||||
|
||||
export const disableBothThemeDecorator = componentWrapperDecorator(
|
||||
(story) => story,
|
||||
({ globals }) => {
|
||||
/**
|
||||
* avoid a bug with the way that we render the same component twice in the same iframe and how
|
||||
* that interacts with the router-outlet
|
||||
*/
|
||||
const themeOverride = globals["theme"] === "both" ? "light" : globals["theme"];
|
||||
return { theme: themeOverride };
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user