mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 06:43:35 +00:00
[EC-558] Reflecting async progress on buttons and forms (#3548)
* [EC-556] feat: convert button into component * [EC-556] feat: implement loading state * [EC-556] feat: remove loading from submit button * [EC-556] fix: add missing import * [EC-556] fix: disabling button using regular attribute * [EC-556] feat: implement bitFormButton * [EC-556] feat: use bitFormButton in submit button * [EC-556] fix: missing import * [EC-558] chore: rename file to match class name * [EC-558] feat: allow skipping bitButton on form buttons * [EC-558]: only show spinner on submit button * [EC-558] feat: add new bit async directive * [EC-558] feat: add functionToObservable util * [EC-558] feat: implement bitAction directive * [EC-558] refactor: simplify bitSubmit using functionToObservable * [EC-558] feat: connect bit action with form button * [EC-558] feat: execute function immediately to allow for form validation * [EC-558] feat: disable form on loading * [EC-558] chore: remove duplicate types * [EC-558] feat: move validation service to common * [EC-558] feat: add error handling using validation service * [EC-558] feat: add support for icon button * [EC-558] fix: icon button hover border styles * [EC-558] chore: refactor icon button story to show all styles * [EC-558] fix: better align loading spinner to middle * [EC-558] fix: simplify try catch * [EC-558] chore: reorganize async actions * [EC-558] chore: rename stories * [EC-558] docs: add documentation * [EC-558] feat: decouple buttons and form buttons * [EC-558] chore: rename button like abstraction * [EC-558] chore: remove null check * [EC-558] docs: add jsdocs to directives * [EC-558] fix: switch abs imports to relative * [EC-558] chore: add async actions module to web shared module * [EC-558] chore: remove unecessary null check * [EC-558] chore: apply suggestions from code review Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com> * [EC-558] fix: whitespaces * [EC-558] feat: dont disable form by default * [EC-558] fix: bug where form could be submit during a previous submit * [EC-558] feat: remove ability to disable form Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>
This commit is contained in:
156
libs/components/src/async-actions/in-forms.stories.ts
Normal file
156
libs/components/src/async-actions/in-forms.stories.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule, Validators, FormBuilder } from "@angular/forms";
|
||||
import { action } from "@storybook/addon-actions";
|
||||
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||
import { delay, of } from "rxjs";
|
||||
|
||||
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
||||
import { I18nService } from "@bitwarden/common/src/abstractions/i18n.service";
|
||||
|
||||
import { ButtonModule } from "../button";
|
||||
import { FormFieldModule } from "../form-field";
|
||||
import { IconButtonModule } from "../icon-button";
|
||||
import { InputModule } from "../input/input.module";
|
||||
import { I18nMockService } from "../utils/i18n-mock.service";
|
||||
|
||||
import { BitActionDirective } from "./bit-action.directive";
|
||||
import { BitSubmitDirective } from "./bit-submit.directive";
|
||||
import { BitFormButtonDirective } from "./form-button.directive";
|
||||
|
||||
const template = `
|
||||
<form [formGroup]="formObj" [bitSubmit]="submit" [disableFormOnLoading]="disableFormOnLoading">
|
||||
<bit-form-field>
|
||||
<bit-label>Name</bit-label>
|
||||
<input bitInput formControlName="name" />
|
||||
</bit-form-field>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>Email</bit-label>
|
||||
<input bitInput formControlName="email" />
|
||||
</bit-form-field>
|
||||
|
||||
<button class="tw-mr-2" type="submit" buttonType="primary" bitButton bitFormButton>Submit</button>
|
||||
<button class="tw-mr-2" type="button" buttonType="secondary" bitButton bitFormButton>Cancel</button>
|
||||
<button class="tw-mr-2" type="button" buttonType="danger" bitButton bitFormButton [bitAction]="delete">Delete</button>
|
||||
<button class="tw-mr-2" type="button" buttonType="secondary" bitIconButton="bwi-star" bitFormButton [bitAction]="delete">Delete</button>
|
||||
</form>`;
|
||||
|
||||
@Component({
|
||||
selector: "app-promise-example",
|
||||
template,
|
||||
})
|
||||
class PromiseExampleComponent {
|
||||
formObj = this.formBuilder.group({
|
||||
name: ["", [Validators.required]],
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
@Input() disableFormOnLoading: boolean;
|
||||
|
||||
constructor(private formBuilder: FormBuilder) {}
|
||||
|
||||
submit = async () => {
|
||||
this.formObj.markAllAsTouched();
|
||||
|
||||
if (!this.formObj.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-observable-example",
|
||||
template,
|
||||
})
|
||||
class ObservableExampleComponent {
|
||||
formObj = this.formBuilder.group({
|
||||
name: ["", [Validators.required]],
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
@Input() disableFormOnLoading: boolean;
|
||||
|
||||
constructor(private formBuilder: FormBuilder) {}
|
||||
|
||||
submit = () => {
|
||||
this.formObj.markAllAsTouched();
|
||||
|
||||
if (!this.formObj.valid) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return of("fake observable").pipe(delay(2000));
|
||||
};
|
||||
|
||||
delete = () => {
|
||||
return of("fake observable").pipe(delay(2000));
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "Component Library/Async Actions/In Forms",
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
declarations: [
|
||||
BitSubmitDirective,
|
||||
BitFormButtonDirective,
|
||||
PromiseExampleComponent,
|
||||
ObservableExampleComponent,
|
||||
BitActionDirective,
|
||||
],
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
ButtonModule,
|
||||
IconButtonModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: I18nService,
|
||||
useFactory: () => {
|
||||
return new I18nMockService({
|
||||
required: "required",
|
||||
inputRequired: "Input is required.",
|
||||
inputEmail: "Input is not an email-address.",
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: ValidationService,
|
||||
useValue: {
|
||||
showError: action("ValidationService.showError"),
|
||||
} as Partial<ValidationService>,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
disableFormOnLoading: false,
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const PromiseTemplate: Story<PromiseExampleComponent> = (args: PromiseExampleComponent) => ({
|
||||
props: args,
|
||||
template: `<app-promise-example [disableFormOnLoading]="disableFormOnLoading"></app-promise-example>`,
|
||||
});
|
||||
|
||||
export const UsingPromise = PromiseTemplate.bind({});
|
||||
|
||||
const ObservableTemplate: Story<PromiseExampleComponent> = (args: PromiseExampleComponent) => ({
|
||||
props: args,
|
||||
template: `<app-observable-example [disableFormOnLoading]="disableFormOnLoading"></app-observable-example>`,
|
||||
});
|
||||
|
||||
export const UsingObservable = ObservableTemplate.bind({});
|
||||
Reference in New Issue
Block a user