diff --git a/libs/components/src/dialog/dialog.module.ts b/libs/components/src/dialog/dialog.module.ts index 828ae368adf..535972417c1 100644 --- a/libs/components/src/dialog/dialog.module.ts +++ b/libs/components/src/dialog/dialog.module.ts @@ -1,15 +1,22 @@ import { DialogModule as CdkDialogModule } from "@angular/cdk/dialog"; -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { DialogCloseDirective } from "./dialog-close.directive"; +import { SharedModule } from "../shared"; + import { DialogService } from "./dialog.service"; import { DialogComponent } from "./dialog/dialog.component"; +import { DialogCloseDirective } from "./directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component"; @NgModule({ - imports: [CommonModule, CdkDialogModule], - declarations: [DialogCloseDirective, DialogComponent, SimpleDialogComponent], + imports: [SharedModule, CdkDialogModule], + declarations: [ + DialogCloseDirective, + DialogComponent, + DialogTitleContainerDirective, + SimpleDialogComponent, + ], exports: [CdkDialogModule, DialogComponent, SimpleDialogComponent], providers: [DialogService], }) diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts new file mode 100644 index 00000000000..942891ba061 --- /dev/null +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -0,0 +1,97 @@ +import { DialogModule, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; +import { Meta, moduleMetadata, Story } from "@storybook/angular"; + +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; + +import { ButtonModule } from "../button"; +import { I18nMockService } from "../utils/i18n-mock.service"; + +import { DialogService } from "./dialog.service"; +import { DialogComponent } from "./dialog/dialog.component"; +import { DialogCloseDirective } from "./directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; + +interface Animal { + animal: string; +} + +@Component({ + selector: "app-story-dialog", + template: ``, +}) +class StoryDialogComponent { + constructor(public dialogService: DialogService) {} + + openDialog() { + this.dialogService.open(StoryDialogContentComponent, { + data: { + animal: "panda", + }, + }); + } +} + +@Component({ + selector: "story-dialog-content", + template: ` + + Dialog Title + + Dialog body text goes here. +
+ Animal: {{ animal }} +
+
+ + +
+
+ `, +}) +class StoryDialogContentComponent { + constructor(public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal) {} + + get animal() { + return this.data?.animal; + } +} + +export default { + title: "Component Library/Dialogs/Service", + component: StoryDialogComponent, + decorators: [ + moduleMetadata({ + declarations: [ + DialogCloseDirective, + DialogComponent, + DialogTitleContainerDirective, + StoryDialogContentComponent, + ], + imports: [ButtonModule, DialogModule], + providers: [ + DialogService, + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + close: "Close", + }); + }, + }, + ], + }), + ], + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library", + }, + }, +} as Meta; + +const Template: Story = (args: StoryDialogComponent) => ({ + props: args, +}); + +export const Default = Template.bind({}); diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index c71e38120b9..1bc95d5208d 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -5,21 +5,26 @@
-

- -

-
- +
- +
diff --git a/libs/components/src/dialog/dialog/dialog.stories.ts b/libs/components/src/dialog/dialog/dialog.stories.ts index 3f2220d49fb..3bf8457d128 100644 --- a/libs/components/src/dialog/dialog/dialog.stories.ts +++ b/libs/components/src/dialog/dialog/dialog.stories.ts @@ -1,6 +1,12 @@ import { Meta, moduleMetadata, Story } from "@storybook/angular"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; + import { ButtonModule } from "../../button"; +import { SharedModule } from "../../shared"; +import { I18nMockService } from "../../utils/i18n-mock.service"; +import { DialogCloseDirective } from "../directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; import { DialogComponent } from "./dialog.component"; @@ -9,7 +15,18 @@ export default { component: DialogComponent, decorators: [ moduleMetadata({ - imports: [ButtonModule], + imports: [SharedModule, ButtonModule], + declarations: [DialogTitleContainerDirective, DialogCloseDirective], + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + close: "Close", + }); + }, + }, + ], }), ], args: { @@ -27,9 +44,9 @@ const Template: Story = (args: DialogComponent) => ({ props: args, template: ` - {{title}} - Dialog body text goes here. -
+ {{title}} + Dialog body text goes here. +
@@ -59,15 +76,15 @@ const TemplateScrolling: Story = (args: DialogComponent) => ({ props: args, template: ` - Scrolling Example - + Scrolling Example + Dialog body text goes here.
repeating lines of characters
end of sequence!
-
+
diff --git a/libs/components/src/dialog/dialog-close.directive.ts b/libs/components/src/dialog/directives/dialog-close.directive.ts similarity index 72% rename from libs/components/src/dialog/dialog-close.directive.ts rename to libs/components/src/dialog/directives/dialog-close.directive.ts index 11f894e04dc..a45991bb5e6 100644 --- a/libs/components/src/dialog/dialog-close.directive.ts +++ b/libs/components/src/dialog/directives/dialog-close.directive.ts @@ -1,18 +1,15 @@ import { DialogRef } from "@angular/cdk/dialog"; -import { Directive, Input, Optional } from "@angular/core"; +import { Directive, HostListener, Input, Optional } from "@angular/core"; @Directive({ selector: "[bitDialogClose]", - host: { - "(click)": "close()", - }, }) export class DialogCloseDirective { @Input("bit-dialog-close") dialogResult: any; constructor(@Optional() public dialogRef: DialogRef) {} - close() { + @HostListener("click") close(): void { this.dialogRef.close(this.dialogResult); } } diff --git a/libs/components/src/dialog/directives/dialog-title-container.directive.ts b/libs/components/src/dialog/directives/dialog-title-container.directive.ts new file mode 100644 index 00000000000..96664a5f94b --- /dev/null +++ b/libs/components/src/dialog/directives/dialog-title-container.directive.ts @@ -0,0 +1,30 @@ +import { CdkDialogContainer, DialogRef } from "@angular/cdk/dialog"; +import { Directive, HostBinding, Input, OnInit, Optional } from "@angular/core"; + +// Increments for each instance of this component +let nextId = 0; + +@Directive({ + selector: "[bitDialogTitleContainer]", +}) +export class DialogTitleContainerDirective implements OnInit { + @HostBinding("id") id = `bit-dialog-title-${nextId++}`; + + @Input() simple = false; + + constructor(@Optional() private dialogRef: DialogRef) {} + + ngOnInit(): void { + // Based on angular/components, licensed under MIT + // https://github.com/angular/components/blob/14.2.0/src/material/dialog/dialog-content-directives.ts#L121-L128 + if (this.dialogRef) { + Promise.resolve().then(() => { + const container = this.dialogRef.containerInstance as CdkDialogContainer; + + if (container && !container._ariaLabelledBy) { + container._ariaLabelledBy = this.id; + } + }); + } + } +} diff --git a/libs/components/src/dialog/dialog-service.stories.ts b/libs/components/src/dialog/simple-dialog.service.stories.ts similarity index 68% rename from libs/components/src/dialog/dialog-service.stories.ts rename to libs/components/src/dialog/simple-dialog.service.stories.ts index 50514d3c756..90963515aaa 100644 --- a/libs/components/src/dialog/dialog-service.stories.ts +++ b/libs/components/src/dialog/simple-dialog.service.stories.ts @@ -4,9 +4,10 @@ import { Meta, moduleMetadata, Story } from "@storybook/angular"; import { ButtonModule } from "../button"; -import { DialogCloseDirective } from "./dialog-close.directive"; import { DialogService } from "./dialog.service"; -import { DialogComponent } from "./dialog/dialog.component"; +import { DialogCloseDirective } from "./directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; +import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component"; interface Animal { animal: string; @@ -14,7 +15,7 @@ interface Animal { @Component({ selector: "app-story-dialog", - template: ``, + template: ``, }) class StoryDialogComponent { constructor(public dialogService: DialogService) {} @@ -31,18 +32,18 @@ class StoryDialogComponent { @Component({ selector: "story-dialog-content", template: ` - - Dialog Title - + + Dialog Title + Dialog body text goes here.
Animal: {{ animal }}
-
+
- + `, }) class StoryDialogContentComponent { @@ -54,11 +55,16 @@ class StoryDialogContentComponent { } export default { - title: "Component Library/Dialogs/Service", + title: "Component Library/Dialogs/Service/Simple", component: StoryDialogComponent, decorators: [ moduleMetadata({ - declarations: [DialogComponent, StoryDialogContentComponent, DialogCloseDirective], + declarations: [ + DialogCloseDirective, + SimpleDialogComponent, + DialogTitleContainerDirective, + StoryDialogContentComponent, + ], imports: [ButtonModule, DialogModule], providers: [DialogService], }), diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html index 90a4a51e064..78ef971753f 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.component.html +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.component.html @@ -6,14 +6,14 @@ -

- -

+

+ +

- +
- +
diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.stories.ts index a74220f3cb9..307b21b4c22 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.stories.ts @@ -1,6 +1,7 @@ import { Meta, moduleMetadata, Story } from "@storybook/angular"; import { ButtonModule } from "../../button"; +import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; import { IconDirective, SimpleDialogComponent } from "./simple-dialog.component"; @@ -10,7 +11,7 @@ export default { decorators: [ moduleMetadata({ imports: [ButtonModule], - declarations: [IconDirective], + declarations: [IconDirective, DialogTitleContainerDirective], }), ], parameters: { @@ -25,9 +26,9 @@ const Template: Story = (args: SimpleDialogComponent) => props: args, template: ` - Alert Dialog - Message Content -
+ Alert Dialog + Message Content +
@@ -42,9 +43,9 @@ const TemplateWithIcon: Story = (args: SimpleDialogCompon template: ` - Premium Subscription Available - Message Content -
+ Premium Subscription Available + Message Content +
@@ -58,8 +59,8 @@ const TemplateScroll: Story = (args: SimpleDialogComponen props: args, template: ` - Alert Dialog - + Alert Dialog + Message Content Message text goes here.
@@ -67,7 +68,7 @@ const TemplateScroll: Story = (args: SimpleDialogComponen end of sequence!
-
+
diff --git a/libs/components/src/form-field/form-field.module.ts b/libs/components/src/form-field/form-field.module.ts index 1796bae4d71..c07f21bab99 100644 --- a/libs/components/src/form-field/form-field.module.ts +++ b/libs/components/src/form-field/form-field.module.ts @@ -1,10 +1,8 @@ -import { CommonModule } from "@angular/common"; -import { NgModule, Pipe, PipeTransform } from "@angular/core"; - -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { NgModule } from "@angular/core"; import { BitInputDirective } from "../input/input.directive"; import { InputModule } from "../input/input.module"; +import { SharedModule } from "../shared"; import { BitErrorSummary } from "./error-summary.component"; import { BitErrorComponent } from "./error.component"; @@ -14,22 +12,8 @@ import { BitLabel } from "./label.directive"; import { BitPrefixDirective } from "./prefix.directive"; import { BitSuffixDirective } from "./suffix.directive"; -/** - * Temporarily duplicate this pipe - */ -@Pipe({ - name: "i18n", -}) -export class I18nPipe implements PipeTransform { - constructor(private i18nService: I18nService) {} - - transform(id: string, p1?: string, p2?: string, p3?: string): string { - return this.i18nService.t(id, p1, p2, p3); - } -} - @NgModule({ - imports: [CommonModule, InputModule], + imports: [SharedModule, InputModule], exports: [ BitErrorComponent, BitErrorSummary, @@ -48,7 +32,6 @@ export class I18nPipe implements PipeTransform { BitLabel, BitPrefixDirective, BitSuffixDirective, - I18nPipe, ], }) export class FormFieldModule {} diff --git a/libs/components/src/shared/i18n.pipe.ts b/libs/components/src/shared/i18n.pipe.ts new file mode 100644 index 00000000000..f1ec9a29cc4 --- /dev/null +++ b/libs/components/src/shared/i18n.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; + +/** + * Temporarily duplicate this pipe + */ +@Pipe({ + name: "i18n", +}) +export class I18nPipe implements PipeTransform { + constructor(private i18nService: I18nService) {} + + transform(id: string, p1?: string, p2?: string, p3?: string): string { + return this.i18nService.t(id, p1, p2, p3); + } +} diff --git a/libs/components/src/shared/index.ts b/libs/components/src/shared/index.ts new file mode 100644 index 00000000000..7a1160c4105 --- /dev/null +++ b/libs/components/src/shared/index.ts @@ -0,0 +1 @@ +export * from "./shared.module"; diff --git a/libs/components/src/shared/shared.module.ts b/libs/components/src/shared/shared.module.ts new file mode 100644 index 00000000000..dcf2e2bc05f --- /dev/null +++ b/libs/components/src/shared/shared.module.ts @@ -0,0 +1,11 @@ +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; + +import { I18nPipe } from "./i18n.pipe"; + +@NgModule({ + imports: [CommonModule], + declarations: [I18nPipe], + exports: [CommonModule, I18nPipe], +}) +export class SharedModule {}