mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 06:54:07 +00:00
[CL-239][CL-251][CL-342] dialog style refresh (#10096)
* update styles and add card story * update docs * add important to typography styles * [CL-239] simple dialog style refresh * [CL-342] fix text overflow in dialog; add story * use main color for headers * update loading styles: spinner height and alt text; body min height * update simple dialog border radius * add badge to story
This commit is contained in:
@@ -78,6 +78,7 @@ export default {
|
||||
useFactory: () => {
|
||||
return new I18nMockService({
|
||||
close: "Close",
|
||||
loading: "Loading",
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
<section
|
||||
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-text-main"
|
||||
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"
|
||||
@fadeIn
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<h1 bitDialogTitleContainer bitTypography="h3" noMargin class="tw-mb-0 tw-truncate">
|
||||
<h1
|
||||
bitDialogTitleContainer
|
||||
bitTypography="h3"
|
||||
noMargin
|
||||
class="tw-text-main tw-mb-0 tw-truncate"
|
||||
>
|
||||
{{ title }}
|
||||
<span *ngIf="subtitle" class="tw-text-muted tw-font-normal tw-text-sm">
|
||||
{{ subtitle }}
|
||||
@@ -24,19 +29,25 @@
|
||||
></button>
|
||||
</header>
|
||||
|
||||
<div class="tw-relative tw-flex tw-flex-col tw-overflow-hidden">
|
||||
<div
|
||||
class="tw-relative tw-flex tw-flex-col tw-overflow-hidden"
|
||||
[ngClass]="{
|
||||
'tw-min-h-60': loading
|
||||
}"
|
||||
>
|
||||
<div
|
||||
*ngIf="loading"
|
||||
class="tw-absolute tw-flex tw-h-full tw-w-full tw-items-center tw-justify-center"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x"></i>
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-lg" [attr.aria-label]="'loading' | i18n"></i>
|
||||
</div>
|
||||
<div
|
||||
class="tw-pb-8"
|
||||
[ngClass]="{
|
||||
'tw-p-4': !disablePadding,
|
||||
'tw-overflow-y-auto': !loading,
|
||||
'tw-invisible tw-overflow-y-hidden': loading
|
||||
'tw-invisible tw-overflow-y-hidden': loading,
|
||||
'tw-bg-background': background === 'default',
|
||||
'tw-bg-background-alt': background === 'alt'
|
||||
}"
|
||||
>
|
||||
<ng-content select="[bitDialogContent]"></ng-content>
|
||||
@@ -44,7 +55,7 @@
|
||||
</div>
|
||||
|
||||
<footer
|
||||
class="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-border-0 tw-border-t tw-border-solid tw-border-secondary-300 tw-bg-background-alt tw-p-4"
|
||||
class="tw-flex tw-flex-row tw-items-center tw-gap-2 tw-border-0 tw-border-t tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-4"
|
||||
>
|
||||
<ng-content select="[bitDialogFooter]"></ng-content>
|
||||
</footer>
|
||||
|
||||
@@ -9,6 +9,10 @@ import { fadeIn } from "../animations";
|
||||
animations: [fadeIn],
|
||||
})
|
||||
export class DialogComponent {
|
||||
/** Background color */
|
||||
@Input()
|
||||
background: "default" | "alt" = "default";
|
||||
|
||||
/**
|
||||
* Dialog size, more complex dialogs should use large, otherwise default is fine.
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,10 @@ import * as stories from "./dialog.stories";
|
||||
|
||||
<Meta of={stories} />
|
||||
|
||||
```ts
|
||||
import { DialogModule } from "@bitwarden/components";
|
||||
```
|
||||
|
||||
# Dialog
|
||||
|
||||
Dialogs are used throughout the app to help the user focus on a specific action. Use this dialog
|
||||
@@ -71,3 +75,10 @@ loading state.
|
||||
Use tabs to separate related content within a dialog.
|
||||
|
||||
<Story of={stories.TabContent} />
|
||||
|
||||
## Background Color
|
||||
|
||||
The `background` input can be set to `alt` to change the background color. This is useful for
|
||||
dialogs that contain multiple card sections.
|
||||
|
||||
<Story of={stories.WithCards} />
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
import { BadgeModule } from "../../badge";
|
||||
import { ButtonModule } from "../../button";
|
||||
import { CardComponent } from "../../card";
|
||||
import { FormFieldModule } from "../../form-field";
|
||||
import { IconButtonModule } from "../../icon-button";
|
||||
import { InputModule } from "../../input";
|
||||
import { SectionComponent, SectionHeaderComponent } from "../../section";
|
||||
import { SharedModule } from "../../shared";
|
||||
import { TabsModule } from "../../tabs";
|
||||
import { TypographyModule } from "../../typography";
|
||||
import { I18nMockService } from "../../utils/i18n-mock.service";
|
||||
import { DialogModule } from "../dialog.module";
|
||||
|
||||
@@ -19,11 +26,20 @@ export default {
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
DialogModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
SharedModule,
|
||||
IconButtonModule,
|
||||
TabsModule,
|
||||
NoopAnimationsModule,
|
||||
SectionComponent,
|
||||
SectionHeaderComponent,
|
||||
CardComponent,
|
||||
TypographyModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@@ -31,6 +47,7 @@ export default {
|
||||
useFactory: () => {
|
||||
return new I18nMockService({
|
||||
close: "Close",
|
||||
loading: "Loading",
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -63,6 +80,9 @@ export const Default: Story = {
|
||||
props: args,
|
||||
template: `
|
||||
<bit-dialog [dialogSize]="dialogSize" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding">
|
||||
<ng-container bitDialogTitle>
|
||||
<span bitBadge variant="success">Foobar</span>
|
||||
</ng-container>
|
||||
<ng-container bitDialogContent>Dialog body text goes here.</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton buttonType="primary" [disabled]="loading">Save</button>
|
||||
@@ -173,3 +193,79 @@ export const TabContent: Story = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCards: Story = {
|
||||
render: (args: DialogComponent) => ({
|
||||
props: {
|
||||
formObj: new FormGroup({
|
||||
name: new FormControl(""),
|
||||
}),
|
||||
...args,
|
||||
},
|
||||
template: /*html*/ `
|
||||
<form [formGroup]="formObj">
|
||||
<bit-dialog background="alt" [dialogSize]="dialogSize" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding">
|
||||
<ng-container bitDialogContent>
|
||||
<bit-section>
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6">
|
||||
Foo
|
||||
</h2>
|
||||
<button bitIconButton="bwi-star" size="small" slot="end"></button>
|
||||
</bit-section-header>
|
||||
<bit-card>
|
||||
<bit-form-field>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
<bit-hint>Optional Hint</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-field disableMargin>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
<bit-hint>Optional Hint</bit-hint>
|
||||
</bit-form-field>
|
||||
</bit-card>
|
||||
</bit-section>
|
||||
<bit-section>
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6">
|
||||
Bar
|
||||
</h2>
|
||||
<button bitIconButton="bwi-star" size="small" slot="end"></button>
|
||||
</bit-section-header>
|
||||
<bit-card>
|
||||
<bit-form-field>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
<bit-hint>Optional Hint</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-field disableMargin>
|
||||
<bit-label>Label</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
<bit-hint>Optional Hint</bit-hint>
|
||||
</bit-form-field>
|
||||
</bit-card>
|
||||
</bit-section>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton buttonType="primary" [disabled]="loading">Save</button>
|
||||
<button bitButton buttonType="secondary" [disabled]="loading">Cancel</button>
|
||||
<button
|
||||
[disabled]="loading"
|
||||
class="tw-ml-auto"
|
||||
bitIconButton="bwi-trash"
|
||||
buttonType="danger"
|
||||
size="default"
|
||||
title="Delete"
|
||||
aria-label="Delete"></button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
||||
`,
|
||||
}),
|
||||
args: {
|
||||
dialogSize: "default",
|
||||
title: "Default",
|
||||
subtitle: "Subtitle",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,6 +2,10 @@ import { Meta, Story, Source } from "@storybook/addon-docs";
|
||||
|
||||
<Meta title="Component Library/Dialogs" />
|
||||
|
||||
```ts
|
||||
import { DialogModule } from "@bitwarden/components";
|
||||
```
|
||||
|
||||
# Dialog
|
||||
|
||||
Dialogs are used throughout the app to help the user focus on a specific action.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -144,7 +145,7 @@ export default {
|
||||
component: StoryDialogComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [ButtonModule, DialogModule, CalloutModule],
|
||||
imports: [ButtonModule, BrowserAnimationsModule, DialogModule, CalloutModule],
|
||||
}),
|
||||
applicationConfig({
|
||||
providers: [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div
|
||||
class="tw-my-4 tw-flex tw-max-h-screen tw-w-96 tw-max-w-90vw tw-flex-col tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-text-contrast tw-text-main"
|
||||
class="tw-my-4 tw-flex tw-max-h-screen tw-w-96 tw-max-w-90vw tw-flex-col tw-overflow-hidden tw-rounded-3xl tw-border tw-border-solid tw-border-secondary-300 tw-bg-text-contrast tw-text-main"
|
||||
@fadeIn
|
||||
>
|
||||
<div class="tw-flex tw-flex-col tw-items-center tw-gap-2 tw-px-4 tw-pt-4 tw-text-center">
|
||||
@@ -9,11 +9,18 @@
|
||||
<ng-template #elseBlock>
|
||||
<i class="bwi bwi-exclamation-triangle tw-text-3xl tw-text-warning" aria-hidden="true"></i>
|
||||
</ng-template>
|
||||
<h1 bitDialogTitleContainer class="tw-mb-0 tw-text-base tw-font-semibold">
|
||||
<h1
|
||||
bitDialogTitleContainer
|
||||
bitTypography="h3"
|
||||
noMargin
|
||||
class="tw-w-full tw-text-main tw-break-words tw-hyphens-auto"
|
||||
>
|
||||
<ng-content select="[bitDialogTitle]"></ng-content>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="tw-overflow-y-auto tw-px-4 tw-pb-4 tw-pt-2 tw-text-center tw-text-base">
|
||||
<div
|
||||
class="tw-overflow-y-auto tw-px-4 tw-pb-4 tw-text-center tw-text-base tw-break-words tw-hyphens-auto"
|
||||
>
|
||||
<ng-content select="[bitDialogContent]"></ng-content>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -4,6 +4,10 @@ import * as stories from "./simple-dialog.stories";
|
||||
|
||||
<Meta of={stories} />
|
||||
|
||||
```ts
|
||||
import { DialogModule } from "@bitwarden/components";
|
||||
```
|
||||
|
||||
# Simple Dialogs
|
||||
|
||||
Simple Dialogs are used throughout the app for simple alert or confirmation actions such as
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DialogModule, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -8,11 +9,8 @@ import { ButtonModule } from "../../button";
|
||||
import { IconButtonModule } from "../../icon-button";
|
||||
import { SharedModule } from "../../shared/shared.module";
|
||||
import { I18nMockService } from "../../utils/i18n-mock.service";
|
||||
import { DialogModule } from "../dialog.module";
|
||||
import { DialogService } from "../dialog.service";
|
||||
import { DialogCloseDirective } from "../directives/dialog-close.directive";
|
||||
import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive";
|
||||
|
||||
import { SimpleDialogComponent } from "./simple-dialog.component";
|
||||
|
||||
interface Animal {
|
||||
animal: string;
|
||||
@@ -65,13 +63,14 @@ export default {
|
||||
component: StoryDialogComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
declarations: [
|
||||
StoryDialogContentComponent,
|
||||
DialogCloseDirective,
|
||||
DialogTitleContainerDirective,
|
||||
SimpleDialogComponent,
|
||||
declarations: [StoryDialogContentComponent],
|
||||
imports: [
|
||||
SharedModule,
|
||||
IconButtonModule,
|
||||
ButtonModule,
|
||||
BrowserAnimationsModule,
|
||||
DialogModule,
|
||||
],
|
||||
imports: [SharedModule, IconButtonModule, ButtonModule, DialogModule],
|
||||
providers: [
|
||||
DialogService,
|
||||
{
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { ButtonModule } from "../../button";
|
||||
import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive";
|
||||
import { DialogModule } from "../dialog.module";
|
||||
|
||||
import { IconDirective, SimpleDialogComponent } from "./simple-dialog.component";
|
||||
import { SimpleDialogComponent } from "./simple-dialog.component";
|
||||
|
||||
export default {
|
||||
title: "Component Library/Dialogs/Simple Dialog",
|
||||
component: SimpleDialogComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [ButtonModule],
|
||||
declarations: [IconDirective, DialogTitleContainerDirective],
|
||||
imports: [ButtonModule, NoopAnimationsModule, DialogModule],
|
||||
}),
|
||||
],
|
||||
parameters: {
|
||||
@@ -82,3 +82,19 @@ export const ScrollingContent: Story = {
|
||||
useDefaultIcon: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const TextOverlow: Story = {
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `
|
||||
<bit-simple-dialog>
|
||||
<span bitDialogTitle>Alert Dialogdialogdialogdialogdialogdialogdialogdialogdialogdialogdialogdialogdialog</span>
|
||||
<span bitDialogContent>Message Contentcontentcontentcontentcontentcontentcontentcontentcontentcontentcontent</span>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton buttonType="primary">Yes</button>
|
||||
<button bitButton buttonType="secondary">No</button>
|
||||
</ng-container>
|
||||
</bit-simple-dialog>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -4,15 +4,15 @@ import { Directive, HostBinding, Input } from "@angular/core";
|
||||
type TypographyType = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "body1" | "body2" | "helper";
|
||||
|
||||
const styles: Record<TypographyType, string[]> = {
|
||||
h1: ["tw-text-3xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h2: ["tw-text-2xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h3: ["tw-text-xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h4: ["tw-text-lg", "tw-font-semibold", "tw-text-headers"],
|
||||
h5: ["tw-text-base", "tw-font-bold", "tw-text-headers"],
|
||||
h6: ["tw-text-sm", "tw-font-bold", "tw-text-headers"],
|
||||
body1: ["tw-text-base"],
|
||||
body2: ["tw-text-sm"],
|
||||
helper: ["tw-text-xs"],
|
||||
h1: ["!tw-text-3xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h2: ["!tw-text-2xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h3: ["!tw-text-xl", "tw-font-semibold", "tw-text-headers"],
|
||||
h4: ["!tw-text-lg", "tw-font-semibold", "tw-text-headers"],
|
||||
h5: ["!tw-text-base", "tw-font-bold", "tw-text-headers"],
|
||||
h6: ["!tw-text-sm", "tw-font-bold", "tw-text-headers"],
|
||||
body1: ["!tw-text-base"],
|
||||
body2: ["!tw-text-sm"],
|
||||
helper: ["!tw-text-xs"],
|
||||
};
|
||||
|
||||
const margins: Record<TypographyType, string[]> = {
|
||||
|
||||
Reference in New Issue
Block a user