diff --git a/libs/angular/src/services/dialog/simple-dialog-options.ts b/libs/angular/src/services/dialog/simple-dialog-options.ts index c56e5b4703..aec6f00e32 100644 --- a/libs/angular/src/services/dialog/simple-dialog-options.ts +++ b/libs/angular/src/services/dialog/simple-dialog-options.ts @@ -48,4 +48,10 @@ export type SimpleDialogOptions = { /** Whether or not the user can use escape or clicking the backdrop to close the dialog */ disableClose?: boolean; + + /** + * Custom accept action. Runs when the user clicks the accept button and shows a loading spinner until the promise + * is resolved. + */ + acceptAction?: () => Promise; }; diff --git a/libs/components/src/async-actions/bit-action.directive.ts b/libs/components/src/async-actions/bit-action.directive.ts index 1012cdf631..2ff3a8d3eb 100644 --- a/libs/components/src/async-actions/bit-action.directive.ts +++ b/libs/components/src/async-actions/bit-action.directive.ts @@ -20,7 +20,7 @@ export class BitActionDirective implements OnDestroy { disabled = false; - @Input("bitAction") protected handler: FunctionReturningAwaitable; + @Input("bitAction") handler: FunctionReturningAwaitable; readonly loading$ = this._loading$.asObservable(); diff --git a/libs/components/src/async-actions/bit-submit.directive.ts b/libs/components/src/async-actions/bit-submit.directive.ts index 90b9a1f4cd..d691fdb1c2 100644 --- a/libs/components/src/async-actions/bit-submit.directive.ts +++ b/libs/components/src/async-actions/bit-submit.directive.ts @@ -18,7 +18,7 @@ export class BitSubmitDirective implements OnInit, OnDestroy { private _loading$ = new BehaviorSubject(false); private _disabled$ = new BehaviorSubject(false); - @Input("bitSubmit") protected handler: FunctionReturningAwaitable; + @Input("bitSubmit") handler: FunctionReturningAwaitable; @Input() allowDisabledFormSubmit?: boolean = false; diff --git a/libs/components/src/dialog/dialog.module.ts b/libs/components/src/dialog/dialog.module.ts index d02b4c0649..f7e0c1e957 100644 --- a/libs/components/src/dialog/dialog.module.ts +++ b/libs/components/src/dialog/dialog.module.ts @@ -1,8 +1,10 @@ import { DialogModule as CdkDialogModule } from "@angular/cdk/dialog"; import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; +import { AsyncActionsModule } from "../async-actions"; import { ButtonModule } from "../button"; import { IconButtonModule } from "../icon-button"; import { SharedModule } from "../shared"; @@ -15,7 +17,14 @@ import { SimpleConfigurableDialogComponent } from "./simple-configurable-dialog/ import { IconDirective, SimpleDialogComponent } from "./simple-dialog/simple-dialog.component"; @NgModule({ - imports: [SharedModule, IconButtonModule, CdkDialogModule, ButtonModule], + imports: [ + SharedModule, + AsyncActionsModule, + ButtonModule, + CdkDialogModule, + IconButtonModule, + ReactiveFormsModule, + ], declarations: [ DialogCloseDirective, DialogTitleContainerDirective, diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.html b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.html index a494d65b17..fd8b2e9017 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.html +++ b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.html @@ -1,28 +1,26 @@ - - +
+ + - {{ title }} + {{ title }} -
{{ content }}
+
{{ content }}
- - + + - - -
+ + + +
diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index 6309a5a8c6..d8824cd7dd 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -1,5 +1,6 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { FormGroup } from "@angular/forms"; import { SimpleDialogType, @@ -29,8 +30,8 @@ const DEFAULT_COLOR: Record = { templateUrl: "./simple-configurable-dialog.component.html", }) export class SimpleConfigurableDialogComponent { - SimpleDialogType = SimpleDialogType; - SimpleDialogCloseType = SimpleDialogCloseType; + protected SimpleDialogType = SimpleDialogType; + protected SimpleDialogCloseType = SimpleDialogCloseType; get iconClasses() { return [ @@ -39,12 +40,13 @@ export class SimpleConfigurableDialogComponent { ]; } - title: string; - content: string; - acceptButtonText: string; - cancelButtonText: string; + protected title: string; + protected content: string; + protected acceptButtonText: string; + protected cancelButtonText: string; + protected formGroup = new FormGroup({}); - showCancelButton = this.simpleDialogOpts.cancelButtonText !== null; + protected showCancelButton = this.simpleDialogOpts.cancelButtonText !== null; constructor( public dialogRef: DialogRef, @@ -54,6 +56,14 @@ export class SimpleConfigurableDialogComponent { this.localizeText(); } + protected accept = async () => { + if (this.simpleDialogOpts.acceptAction) { + await this.simpleDialogOpts.acceptAction(); + } + + this.dialogRef.close(SimpleDialogCloseType.ACCEPT); + }; + private localizeText() { this.title = this.translate(this.simpleDialogOpts.title); this.content = this.translate(this.simpleDialogOpts.content); diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts index 998749da84..0982db1ca7 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts @@ -15,96 +15,17 @@ import { DialogModule } from "../dialog.module"; @Component({ template: ` -

Dialog Type Examples:

-
- - - - - - - - - -
- -

Custom Button Examples:

-
- - - - - -
- -

Custom Icon Example:

-
- -
- -

Additional Examples:

-
- +
+

{{ group.title }}

+
+ +
@@ -113,72 +34,93 @@ import { DialogModule } from "../dialog.module"; `, }) class StoryDialogComponent { - primaryLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - }; - - successLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("successTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.SUCCESS, - }; - - infoLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("infoTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.INFO, - }; - - warningLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("warningTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.WARNING, - }; - - dangerLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("dangerTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.DANGER, - }; - - primarySingleBtnSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: "Ok", - cancelButtonText: null, - }; - - primaryCustomBtnsSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: this.i18nService.t("accept"), - cancelButtonText: this.i18nService.t("decline"), - }; - - primaryAcceptBtnOverrideSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: "Ok", - }; - - primaryCustomIconSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - icon: "bwi-family", - }; - - primaryDisableCloseSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - disableClose: true, - }; + protected dialogs: { title: string; dialogs: SimpleDialogOptions[] }[] = [ + { + title: "Regular", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + }, + { + title: this.i18nService.t("successTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.SUCCESS, + }, + { + title: this.i18nService.t("infoTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.INFO, + }, + { + title: this.i18nService.t("warningTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.WARNING, + }, + { + title: this.i18nService.t("dangerTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.DANGER, + }, + ], + }, + { + title: "Custom", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: "Ok", + cancelButtonText: null, + }, + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: this.i18nService.t("accept"), + cancelButtonText: this.i18nService.t("decline"), + }, + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: "Ok", + }, + ], + }, + { + title: "Icon", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + icon: "bwi-family", + }, + ], + }, + { + title: "Additional", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + disableClose: true, + }, + { + title: this.i18nService.t("asyncTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + acceptAction: () => { + return new Promise((resolve) => setTimeout(resolve, 10000)); + }, + type: SimpleDialogType.PRIMARY, + }, + ], + }, + ]; showCallout = false; calloutType = "info"; @@ -216,6 +158,7 @@ export default { infoTypeSimpleDialog: "Info Type Simple Dialog", warningTypeSimpleDialog: "Warning Type Simple Dialog", dangerTypeSimpleDialog: "Danger Type Simple Dialog", + asyncTypeSimpleDialog: "Async", dialogContent: "Dialog content goes here", yes: "Yes", no: "No",