import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { userEvent, getByText } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ButtonModule } from "../button"; import { CheckboxModule } from "../checkbox"; import { FormControlModule } from "../form-control"; import { FormFieldModule } from "../form-field"; import { trimValidator, forbiddenCharacters } from "../form-field/bit-validators"; import { InputModule } from "../input/input.module"; import { MultiSelectModule } from "../multi-select"; import { RadioButtonModule } from "../radio-button"; import { SelectModule } from "../select"; import { I18nMockService } from "../utils/i18n-mock.service"; import { countries } from "./countries"; export default { title: "Component Library/Form", decorators: [ moduleMetadata({ imports: [ FormsModule, ReactiveFormsModule, FormFieldModule, InputModule, ButtonModule, FormControlModule, CheckboxModule, RadioButtonModule, SelectModule, MultiSelectModule, ], providers: [ { provide: I18nService, useFactory: () => { return new I18nMockService({ selectPlaceholder: "-- Select --", required: "required", checkboxRequired: "Option is required", inputRequired: "Input is required.", inputEmail: "Input is not an email address.", inputForbiddenCharacters: (char) => `The following characters are not allowed: "${char}"`, inputMinValue: (min) => `Input value must be at least ${min}.`, inputMaxValue: (max) => `Input value must not exceed ${max}.`, inputMinLength: (min) => `Input value must be at least ${min} characters long.`, inputMaxLength: (max) => `Input value must not exceed ${max} characters in length.`, inputTrimValidator: `Input must not contain only whitespace.`, multiSelectPlaceholder: "-- Type to Filter --", multiSelectLoading: "Retrieving options...", multiSelectNotFound: "No items found", multiSelectClearAll: "Clear all", fieldsNeedAttention: "__$1__ field(s) above need your attention.", }); }, }, ], }), ], parameters: { design: { type: "figma", url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=13213-55392&t=b5tDKylm5sWm2yKo-4", }, }, } as Meta; const fb = new FormBuilder(); const exampleFormObj = fb.group({ name: ["", [Validators.required]], email: ["", [Validators.required, Validators.email, forbiddenCharacters(["#"])]], country: [undefined as string | undefined, [Validators.required]], groups: [], terms: [false, [Validators.requiredTrue]], updates: ["yes"], age: [null, [Validators.min(0), Validators.max(150)]], }); type Story = StoryObj; export const FullExample: Story = { render: (args) => ({ props: { formObj: exampleFormObj, submit: () => exampleFormObj.markAllAsTouched(), ...args, }, template: /*html*/ `
Name Email Country Groups Age Agree to terms Required for the service to work properly Subscribe to updates? Yes No Decide later
`, }), args: { countries, baseItems: [ { id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" }, { id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" }, { id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" }, { id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" }, { id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" }, { id: "6", listName: "Group 6", labelName: "Group 6", icon: "bwi-family" }, { id: "7", listName: "Group 7", labelName: "Group 7", icon: "bwi-family" }, ], }, }; const showValidationsFormObj = fb.group({ required: ["", [Validators.required]], whitespace: [" ", trimValidator], email: ["example?bad-email", [Validators.email]], minLength: ["Hello", [Validators.minLength(8)]], maxLength: ["Hello there", [Validators.maxLength(8)]], minValue: [9, [Validators.min(10)]], maxValue: [15, [Validators.max(10)]], forbiddenChars: ["Th!$ value cont#in$ forbidden char$", forbiddenCharacters(["#", "!", "$"])], }); export const Validations: Story = { render: (args) => ({ props: { formObj: showValidationsFormObj, submit: () => showValidationsFormObj.markAllAsTouched(), ...args, }, template: /*html*/ `
Required validation This field is required. Submit form or blur input to see error Email validation This field contains a malformed email address. Submit form or blur input to see error Min length validation Value must be at least 8 characters. Submit form or blur input to see error Max length validation Value must be less then 8 characters. Submit form or blur input to see error Min number value validation Value must be greater than 10. Submit form or blur input to see error Max number value validation Value must be less than than 10. Submit form or blur input to see error Forbidden characters validation Value must not contain '#', '!' or '$'. Submit form or blur input to see error White space validation This input contains only white space. Submit form or blur input to see error
`, }), play: async (context) => { const canvas = context.canvasElement; const submitButton = getByText(canvas, "Submit"); await userEvent.click(submitButton); }, };