mirror of
https://github.com/bitwarden/browser
synced 2026-01-21 20:03:43 +00:00
[CL-947] drawer width variants (#18156)
* allow drawer to adapt to size input * add new drawer sizes * move logic back to dialog component * convert width to computed signal * fix template error and remove duplicate class * use normal const object * ensure dialogSize undefined is handled
This commit is contained in:
@@ -31,7 +31,11 @@ interface Animal {
|
||||
<button class="tw-mr-2" bitButton type="button" (click)="openDialogNonDismissable()">
|
||||
Open Non-Dismissable Dialog
|
||||
</button>
|
||||
<button bitButton type="button" (click)="openDrawer()">Open Drawer</button>
|
||||
<button class="tw-mr-2" bitButton type="button" (click)="openDrawer()">Open Drawer</button>
|
||||
<button class="tw-mr-2" bitButton size="small" type="button" (click)="openSmallDrawer()">
|
||||
Open Small Drawer
|
||||
</button>
|
||||
<button bitButton type="button" (click)="openLargeDrawer()">Open Large Drawer</button>
|
||||
</bit-layout>
|
||||
`,
|
||||
imports: [ButtonModule, LayoutComponent],
|
||||
@@ -63,13 +67,29 @@ class StoryDialogComponent {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
openSmallDrawer() {
|
||||
this.dialogService.openDrawer(SmallDrawerContentComponent, {
|
||||
data: {
|
||||
animal: "panda",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
openLargeDrawer() {
|
||||
this.dialogService.openDrawer(LargeDrawerContentComponent, {
|
||||
data: {
|
||||
animal: "panda",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
template: `
|
||||
<bit-dialog title="Dialog Title" dialogSize="large">
|
||||
<bit-dialog title="Dialog Title">
|
||||
<span bitDialogContent>
|
||||
Dialog body text goes here.
|
||||
<br />
|
||||
@@ -100,7 +120,6 @@ class StoryDialogContentComponent {
|
||||
template: `
|
||||
<bit-dialog
|
||||
title="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore"
|
||||
dialogSize="large"
|
||||
>
|
||||
<span bitDialogContent>
|
||||
Dialog body text goes here.
|
||||
@@ -125,6 +144,64 @@ class NonDismissableContentComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
template: `
|
||||
<bit-dialog title="Small Drawer" dialogSize="small">
|
||||
<span bitDialogContent>
|
||||
Dialog body text goes here.
|
||||
<br />
|
||||
Animal: {{ animal }}
|
||||
</span>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="button" bitButton buttonType="primary" (click)="dialogRef.close()">
|
||||
Save
|
||||
</button>
|
||||
<button type="button" bitButton buttonType="secondary" bitDialogClose>Cancel</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
`,
|
||||
imports: [DialogModule, ButtonModule],
|
||||
})
|
||||
class SmallDrawerContentComponent {
|
||||
dialogRef = inject(DialogRef);
|
||||
private data = inject<Animal>(DIALOG_DATA);
|
||||
|
||||
get animal() {
|
||||
return this.data?.animal;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
template: `
|
||||
<bit-dialog title="Large Drawer" dialogSize="large">
|
||||
<span bitDialogContent>
|
||||
Dialog body text goes here.
|
||||
<br />
|
||||
Animal: {{ animal }}
|
||||
</span>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="button" bitButton buttonType="primary" (click)="dialogRef.close()">
|
||||
Save
|
||||
</button>
|
||||
<button type="button" bitButton buttonType="secondary" bitDialogClose>Cancel</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
`,
|
||||
imports: [DialogModule, ButtonModule],
|
||||
})
|
||||
class LargeDrawerContentComponent {
|
||||
dialogRef = inject(DialogRef);
|
||||
private data = inject<Animal>(DIALOG_DATA);
|
||||
|
||||
get animal() {
|
||||
return this.data?.animal;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "Component Library/Dialogs/Service",
|
||||
component: StoryDialogComponent,
|
||||
@@ -206,3 +283,21 @@ export const Drawer: Story = {
|
||||
await userEvent.click(button);
|
||||
},
|
||||
};
|
||||
|
||||
export const DrawerSmall: Story = {
|
||||
play: async (context) => {
|
||||
const canvas = context.canvasElement;
|
||||
|
||||
const button = getAllByRole(canvas, "button")[3];
|
||||
await userEvent.click(button);
|
||||
},
|
||||
};
|
||||
|
||||
export const DrawerLarge: Story = {
|
||||
play: async (context) => {
|
||||
const canvas = context.canvasElement;
|
||||
|
||||
const button = getAllByRole(canvas, "button")[4];
|
||||
await userEvent.click(button);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<section
|
||||
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-100 tw-bg-background tw-text-main"
|
||||
[ngClass]="[
|
||||
width,
|
||||
width(),
|
||||
isDrawer ? 'tw-h-full tw-border-t-0' : 'tw-rounded-t-xl md:tw-rounded-xl tw-shadow-lg',
|
||||
]"
|
||||
cdkTrapFocus
|
||||
|
||||
@@ -26,6 +26,20 @@ import { DialogRef } from "../dialog.service";
|
||||
import { DialogCloseDirective } from "../directives/dialog-close.directive";
|
||||
import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive";
|
||||
|
||||
type DialogSize = "small" | "default" | "large";
|
||||
|
||||
const dialogSizeToWidth = {
|
||||
small: "md:tw-max-w-sm",
|
||||
default: "md:tw-max-w-xl",
|
||||
large: "md:tw-max-w-3xl",
|
||||
} as const;
|
||||
|
||||
const drawerSizeToWidth = {
|
||||
small: "md:tw-max-w-sm",
|
||||
default: "md:tw-max-w-lg",
|
||||
large: "md:tw-max-w-2xl",
|
||||
} as const;
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
@@ -71,7 +85,7 @@ export class DialogComponent {
|
||||
/**
|
||||
* Dialog size, more complex dialogs should use large, otherwise default is fine.
|
||||
*/
|
||||
readonly dialogSize = input<"small" | "default" | "large">("default");
|
||||
readonly dialogSize = input<DialogSize>("default");
|
||||
|
||||
/**
|
||||
* Title to show in the dialog's header
|
||||
@@ -100,21 +114,31 @@ export class DialogComponent {
|
||||
|
||||
private readonly animationCompleted = signal(false);
|
||||
|
||||
protected readonly width = computed(() => {
|
||||
const size = this.dialogSize() ?? "default";
|
||||
const isDrawer = this.dialogRef?.isDrawer;
|
||||
|
||||
if (isDrawer) {
|
||||
return drawerSizeToWidth[size];
|
||||
}
|
||||
|
||||
return dialogSizeToWidth[size];
|
||||
});
|
||||
|
||||
protected readonly classes = computed(() => {
|
||||
// `tw-max-h-[90vh]` is needed to prevent dialogs from overlapping the desktop header
|
||||
const baseClasses = ["tw-flex", "tw-flex-col", "tw-w-screen"];
|
||||
const sizeClasses = this.dialogRef?.isDrawer
|
||||
? ["tw-h-full", "md:tw-w-[23rem]"]
|
||||
: ["md:tw-p-4", "tw-w-screen", "tw-max-h-[90vh]"];
|
||||
const sizeClasses = this.dialogRef?.isDrawer ? ["tw-h-full"] : ["md:tw-p-4", "tw-max-h-[90vh]"];
|
||||
|
||||
const size = this.dialogSize() ?? "default";
|
||||
const animationClasses =
|
||||
this.disableAnimations() || this.animationCompleted() || this.dialogRef?.isDrawer
|
||||
? []
|
||||
: this.dialogSize() === "small"
|
||||
: size === "small"
|
||||
? ["tw-animate-slide-down"]
|
||||
: ["tw-animate-slide-up", "md:tw-animate-slide-down"];
|
||||
|
||||
return [...baseClasses, this.width, ...sizeClasses, ...animationClasses];
|
||||
return [...baseClasses, this.width(), ...sizeClasses, ...animationClasses];
|
||||
});
|
||||
|
||||
handleEsc(event: Event) {
|
||||
@@ -124,20 +148,6 @@ export class DialogComponent {
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
switch (this.dialogSize()) {
|
||||
case "small": {
|
||||
return "md:tw-max-w-sm";
|
||||
}
|
||||
case "large": {
|
||||
return "md:tw-max-w-3xl";
|
||||
}
|
||||
default: {
|
||||
return "md:tw-max-w-xl";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAnimationEnd() {
|
||||
this.animationCompleted.set(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user