1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-03-27 16:10:08 +01:00

[CL-29] Modal Components for the component library ()

The purpose of this PR is to add Modals into the component library.

To note, this PR is focused on the visual of the modal that will "pop-up" and not the actual pop-up implementation.
This commit is contained in:
Thomas Avery 2022-07-22 10:55:45 -05:00 committed by GitHub
parent 9fef1d21a4
commit 8cb92d425c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 264 additions and 0 deletions

View File

@ -5,6 +5,7 @@ export * from "./toggle-group";
export * from "./callout";
export * from "./form-field";
export * from "./menu";
export * from "./modal";
export * from "./utils/i18n-mock.service";
export * from "./tabs";
export * from "./submit-button";

View File

@ -0,0 +1,3 @@
export * from "./modal.component";
export * from "./modal-simple.component";
export * from "./modal.module";

View File

@ -0,0 +1,19 @@
<div
class="tw-max-w-sm tw-max-h-screen tw-my-4 tw-flex tw-flex-col tw-bg-text-contrast tw-border tw-border-solid tw-border-secondary-300 tw-rounded tw-text-main tw-overflow-hidden"
>
<div class="tw-text-center tw-flex tw-flex-col tw-items-center tw-px-4 tw-pt-4 tw-gap-2">
<ng-content *ngIf="hasIcon; else elseBlock" select="[bit-modal-icon]"></ng-content>
<ng-template #elseBlock>
<i class="bwi bwi-exclamation-triangle tw-text-3xl tw-text-warning" aria-hidden="true"></i>
</ng-template>
<h2 class="tw-font-semibold tw-text-base tw-mb-0">
<ng-content select="[bit-modal-title]"></ng-content>
</h2>
</div>
<div class="tw-text-center tw-text-base tw-pt-2 tw-pb-4 tw-px-4 tw-overflow-y-auto">
<ng-content select="[bit-modal-content]"></ng-content>
</div>
<div class="tw-p-4 tw-border-solid tw-border-secondary-300 tw-border-0 tw-border-t">
<ng-content select="[bit-modal-footer]"></ng-content>
</div>
</div>

View File

@ -0,0 +1,16 @@
import { Component, ContentChild, Directive } from "@angular/core";
@Directive({ selector: "[bit-modal-icon]" })
export class IconDirective {}
@Component({
selector: "bit-simple-modal",
templateUrl: "./modal-simple.component.html",
})
export class ModalSimpleComponent {
@ContentChild(IconDirective) icon!: IconDirective;
get hasIcon() {
return this.icon != null;
}
}

View File

@ -0,0 +1,85 @@
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { ButtonModule } from "../button";
import { IconDirective, ModalSimpleComponent } from "./modal-simple.component";
export default {
title: "Component Library/Modals/Simple Modal",
component: ModalSimpleComponent,
decorators: [
moduleMetadata({
imports: [ButtonModule],
declarations: [IconDirective],
}),
],
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library",
},
},
} as Meta;
const Template: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
props: args,
template: `
<bit-simple-modal>
<span bit-modal-title> Alert Modal
</span>
<span bit-modal-content> Message Content
</span>
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary"> Yes </button>
<button bitButton buttonType="secondary"> No </button>
</div>
</bit-simple-modal>
`,
});
export const Default = Template.bind({});
const TemplateWithIcon: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
props: args,
template: `
<bit-simple-modal>
<i bit-modal-icon class="bwi bwi-star tw-text-3xl tw-text-success" aria-hidden="true"></i>
<span bit-modal-title> Premium Subscription Available
</span>
<span bit-modal-content> Message Content
</span>
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary"> Yes </button>
<button bitButton buttonType="secondary"> No </button>
</div>
</bit-simple-modal>
`,
});
export const CustomIcon = TemplateWithIcon.bind({});
const TemplateScroll: Story<ModalSimpleComponent> = (args: ModalSimpleComponent) => ({
props: args,
template: `
<bit-simple-modal>
<span bit-modal-title> Alert Modal
</span>
<span bit-modal-content> Message Content
Message text goes here.<br>
<ng-container *ngFor="let _ of [].constructor(100)">
repeating lines of characters <br>
</ng-container>
end of sequence!
</span>
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary"> Yes </button>
<button bitButton buttonType="secondary"> No </button>
</div>
</bit-simple-modal>
`,
});
export const ScrollingContent = TemplateScroll.bind({});
ScrollingContent.args = {
useDefaultIcon: true,
};

View File

@ -0,0 +1,25 @@
<div
[ngClass]="width"
class="tw-max-h-screen tw-my-4 tw-flex tw-flex-col tw-bg-text-contrast tw-border tw-border-solid tw-border-secondary-300 tw-rounded tw-text-main tw-overflow-hidden"
>
<div
class="tw-flex tw-gap-4 tw-p-4 tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
>
<h2 class="tw-text-lg tw-uppercase tw-grow tw-mb-0">
<ng-content select="[bit-modal-title]"></ng-content>
</h2>
<button class="tw-border-0 tw-bg-transparent tw-p-0">
<i class="bwi bwi-close tw-text-xs tw-font-bold tw-text-main" aria-hidden="true"></i>
</button>
</div>
<div class="tw-pb-8 tw-p-4 tw-overflow-y-auto">
<ng-content select="[bit-modal-content]"></ng-content>
</div>
<div
class="tw-p-4 tw-bg-background-alt tw-border-solid tw-border-secondary-300 tw-border-0 tw-border-t"
>
<ng-content select="[bit-modal-footer]"></ng-content>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { Component, Input } from "@angular/core";
@Component({
selector: "bit-modal",
templateUrl: "./modal.component.html",
})
export class ModalComponent {
@Input() modalSize: "small" | "default" | "large";
get width() {
switch (this.modalSize) {
case "small": {
return "tw-max-w-xs";
}
case "large": {
return "tw-max-w-4xl";
}
default: {
return "tw-max-w-xl";
}
}
}
}

View File

@ -0,0 +1,12 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { ModalSimpleComponent } from "./modal-simple.component";
import { ModalComponent } from "./modal.component";
@NgModule({
imports: [CommonModule],
exports: [ModalComponent, ModalSimpleComponent],
declarations: [ModalComponent, ModalSimpleComponent],
})
export class ModalModule {}

View File

@ -0,0 +1,80 @@
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { ButtonModule } from "../button";
import { ModalComponent } from "./modal.component";
export default {
title: "Component Library/Modals/Modal",
component: ModalComponent,
decorators: [
moduleMetadata({
imports: [ButtonModule],
}),
],
args: {
modalSize: "small",
},
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library",
},
},
} as Meta;
const Template: Story<ModalComponent> = (args: ModalComponent) => ({
props: args,
template: `
<bit-modal [modalSize]="modalSize">
<span bit-modal-title> Modal Title </span>
<span bit-modal-content>
Modal body text goes here.
</span>
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary"> Save </button>
<button bitButton buttonType="secondary"> Cancel </button>
</div>
</bit-modal>
`,
});
export const Default = Template.bind({});
Default.args = {
modalSize: "default",
};
export const Small = Template.bind({});
Small.args = {
modalSize: "small",
};
export const Large = Template.bind({});
Large.args = {
modalSize: "large",
};
const TemplateScrolling: Story<ModalComponent> = (args: ModalComponent) => ({
props: args,
template: `
<bit-modal [modalSize]="modalSize">
<span bit-modal-title> Modal Title </span>
<span bit-modal-content>
Modal body text goes here.<br>
<ng-container *ngFor="let _ of [].constructor(100)">
repeating lines of characters <br>
</ng-container>
end of sequence!
</span>
<div bit-modal-footer class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary"> Save </button>
<button bitButton buttonType="secondary"> Cancel </button>
</div>
</bit-modal>
`,
});
export const ScrollingContent = TemplateScrolling.bind({});
ScrollingContent.args = {
modalSize: "small",
};