diff --git a/packages/common/src/locales/en/app.ts b/packages/common/src/locales/en/app.ts index aa7c383e..404069c3 100644 --- a/packages/common/src/locales/en/app.ts +++ b/packages/common/src/locales/en/app.ts @@ -31,7 +31,7 @@ export const en = { fieldLabel: 'Page title', }, paragraph: { - fieldLabel: 'Paragraph Text', + fieldLabel: 'Paragraph text', displayName: 'Paragraph', errorTextMustContainChar: 'String must contain at least 1 character(s)', }, @@ -48,17 +48,25 @@ export const en = { }, selectDropdown: { ...defaults, - displayName: 'Select Dropdown label', - fieldLabel: 'Select Dropdown label', + displayName: 'Select dropdown label', + fieldLabel: 'Select dropdown label', errorTextMustContainChar: 'String must contain at least 1 character(s)', }, dateOfBirth: { ...defaults, - displayName: 'Date of Birth label', - fieldLabel: 'Date Of Birth label', + displayName: 'Date of birth label', + fieldLabel: 'Date of birth label', hintLabel: 'Date of Birth Hint label', hint: 'For example: January 19 2000', errorTextMustContainChar: 'String must contain at least 1 character(s)', }, + phoneNumber: { + ...defaults, + displayName: 'Phone number label', + fieldLabel: 'Phone number label', + hintLabel: 'Phone number hint label', + hint: 'Should include country code and optional leading zero', + errorTextMustContainChar: 'String must contain at least 1 character(s)', + }, }, }; diff --git a/packages/design/src/Form/components/PhoneNumber/PhoneNumber.stories.tsx b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.stories.tsx new file mode 100644 index 00000000..cc3859c6 --- /dev/null +++ b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { type Meta, type StoryObj } from '@storybook/react'; + +import { PhoneNumberPattern } from './PhoneNumber.js'; + +const meta: Meta = { + title: 'patterns/PhoneNumberPattern', + component: PhoneNumberPattern, + decorators: [ + (Story, args) => { + const FormDecorator = () => { + const formMethods = useForm({ + defaultValues: { + phoneId: '', + }, + }); + return ( + + + + ); + }; + return ; + }, + ], + tags: ['autodocs'], +}; + +export default meta; + +export const Default: StoryObj = { + args: { + phoneId: 'phone', + label: 'Phone number', + required: true, + }, +}; + +export const WithoutRequired: StoryObj = { + args: { + phoneId: 'phone', + label: 'Phone number', + required: false, + }, +}; + +export const WithError: StoryObj = { + args: { + phoneId: 'phone', + label: 'Phone number with error', + required: true, + error: { + type: 'custom', + message: 'This field has an error', + }, + }, +}; + +export const WithHint: StoryObj = { + args: { + phoneId: 'phone', + label: 'Phone number', + hint: 'Should include country code and optional leading zero', + required: true, + }, +}; diff --git a/packages/design/src/Form/components/PhoneNumber/PhoneNumber.test.tsx b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.test.tsx new file mode 100644 index 00000000..fb57ad66 --- /dev/null +++ b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.test.tsx @@ -0,0 +1,7 @@ +/** + * @vitest-environment jsdom + */ +import { describeStories } from '../../../test-helper.js'; +import meta, * as stories from './PhoneNumber.stories.js'; + +describeStories(meta, stories); diff --git a/packages/design/src/Form/components/PhoneNumber/PhoneNumber.tsx b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.tsx new file mode 100644 index 00000000..8f243748 --- /dev/null +++ b/packages/design/src/Form/components/PhoneNumber/PhoneNumber.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; +import { type PhoneNumberProps } from '@atj/forms'; +import { type PatternComponent } from '../../index.js'; + +export const PhoneNumberPattern: PatternComponent = ({ + phoneId, + hint, + label, + required, + error, + value, +}) => { + const { register } = useFormContext(); + + return ( +
+
+ + {hint && ( +
+ {hint} +
+ )} + + {error && ( +
+ {error.message} +
+ )} +
+
+ ); +}; diff --git a/packages/design/src/Form/components/PhoneNumber/index.tsx b/packages/design/src/Form/components/PhoneNumber/index.tsx new file mode 100644 index 00000000..a9232364 --- /dev/null +++ b/packages/design/src/Form/components/PhoneNumber/index.tsx @@ -0,0 +1,3 @@ +import { PhoneNumberPattern } from './PhoneNumber.js'; + +export default PhoneNumberPattern; diff --git a/packages/design/src/Form/components/SelectDropdown/SelectDropdown.stories.tsx b/packages/design/src/Form/components/SelectDropdown/SelectDropdown.stories.tsx index 51fe214c..5d9eaa93 100644 --- a/packages/design/src/Form/components/SelectDropdown/SelectDropdown.stories.tsx +++ b/packages/design/src/Form/components/SelectDropdown/SelectDropdown.stories.tsx @@ -5,7 +5,7 @@ import { type Meta, type StoryObj } from '@storybook/react'; import { SelectDropdownPattern } from './SelectDropdown.js'; const meta: Meta = { - title: 'patterns/SelectPattern', + title: 'patterns/SelectDropdownPattern', component: SelectDropdownPattern, decorators: [ (Story, args) => { diff --git a/packages/design/src/Form/components/index.tsx b/packages/design/src/Form/components/index.tsx index 87d376fa..ed2fb103 100644 --- a/packages/design/src/Form/components/index.tsx +++ b/packages/design/src/Form/components/index.tsx @@ -8,6 +8,7 @@ import PackageDownload from './PackageDownload/index.js'; import Page from './Page/index.js'; import PageSet from './PageSet/index.js'; import Paragraph from './Paragraph/index.js'; +import PhoneNumber from './PhoneNumber/index.js'; import RadioGroup from './RadioGroup/index.js'; import RichText from './RichText/index.js'; import Sequence from './Sequence/index.js'; @@ -26,6 +27,7 @@ export const defaultPatternComponents: ComponentForPattern = { page: Page as PatternComponent, 'page-set': PageSet as PatternComponent, paragraph: Paragraph as PatternComponent, + 'phone-number': PhoneNumber as PatternComponent, 'radio-group': RadioGroup as PatternComponent, 'rich-text': RichText as PatternComponent, 'select-dropdown': SelectDropdown as PatternComponent, diff --git a/packages/design/src/FormManager/FormEdit/AddPatternDropdown.tsx b/packages/design/src/FormManager/FormEdit/AddPatternDropdown.tsx index f542356d..5053be4a 100644 --- a/packages/design/src/FormManager/FormEdit/AddPatternDropdown.tsx +++ b/packages/design/src/FormManager/FormEdit/AddPatternDropdown.tsx @@ -13,6 +13,7 @@ import dropDownOptionIcon from './images/dropdownoption-icon.svg'; import richTextIcon from './images/richtext-icon.svg'; import longanswerIcon from './images/longanswer-icon.svg'; import pageIcon from './images/page-icon.svg'; +import phoneIcon from './images/phone-icon.svg'; import shortanswerIcon from './images/shortanswer-icon.svg'; import singleselectIcon from './images/singleselect-icon.svg'; import templateIcon from './images/template-icon.svg'; @@ -28,6 +29,7 @@ const icons: Record = { 'richtext-icon.svg': richTextIcon, 'longanswer-icon.svg': longanswerIcon, 'page-icon.svg': pageIcon, + 'phone-icon.svg': phoneIcon, 'shortanswer-icon.svg': shortanswerIcon, 'singleselect-icon.svg': singleselectIcon, 'template-icon.svg': templateIcon, @@ -95,6 +97,7 @@ const sidebarPatterns: DropdownPattern[] = [ ['rich-text', defaultFormConfig.patterns['rich-text']], ['radio-group', defaultFormConfig.patterns['radio-group']], ['package-download', defaultFormConfig.patterns['package-download']], + ['phone-number', defaultFormConfig.patterns['phone-number']], ['select-dropdown', defaultFormConfig.patterns['select-dropdown']], ['date-of-birth', defaultFormConfig.patterns['date-of-birth']], ] as const; @@ -106,6 +109,7 @@ export const fieldsetPatterns: DropdownPattern[] = [ ['rich-text', defaultFormConfig.patterns['rich-text']], ['radio-group', defaultFormConfig.patterns['radio-group']], ['package-download', defaultFormConfig.patterns['package-download']], + ['phone-number', defaultFormConfig.patterns['phone-number']], ['select-dropdown', defaultFormConfig.patterns['select-dropdown']], ['date-of-birth', defaultFormConfig.patterns['date-of-birth']], ] as const; diff --git a/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.stories.tsx b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.stories.tsx new file mode 100644 index 00000000..a5755a28 --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.stories.tsx @@ -0,0 +1,104 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, userEvent } from '@storybook/test'; +import { within } from '@testing-library/react'; + +import { type PhoneNumberPattern } from '@atj/forms'; +import { createPatternEditStoryMeta } from './common/story-helper.js'; +import FormEdit from '../index.js'; +import { enLocale as message } from '@atj/common'; + +const pattern: PhoneNumberPattern = { + id: 'phone-number-1', + type: 'phone-number', + data: { + label: message.patterns.phoneNumber.displayName, + required: false, + hint: undefined, + }, +}; + +const storyConfig: Meta = { + title: 'Edit components/PhoneNumberPattern', + ...createPatternEditStoryMeta({ + pattern, + }), +} as Meta; + +export default storyConfig; + +export const Basic: StoryObj = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const updatedLabel = 'Phone Number update'; + const updatedHint = 'Updated hint for Phone Number'; + + await userEvent.click( + canvas.getByText(message.patterns.phoneNumber.displayName) + ); + + const labelInput = canvas.getByLabelText( + message.patterns.phoneNumber.fieldLabel + ); + await userEvent.clear(labelInput); + await userEvent.type(labelInput, updatedLabel); + + const hintInput = canvas.getByLabelText( + message.patterns.phoneNumber.hintLabel + ); + await userEvent.clear(hintInput); + await userEvent.type(hintInput, updatedHint); + + const form = labelInput?.closest('form'); + form?.requestSubmit(); + + await expect(await canvas.findByText(updatedLabel)).toBeInTheDocument(); + await expect(await canvas.findByText(updatedHint)).toBeInTheDocument(); + }, +}; + +export const WithoutHint: StoryObj = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const updatedLabel = 'Phone Number update'; + + await userEvent.click( + canvas.getByText(message.patterns.phoneNumber.displayName) + ); + + const labelInput = canvas.getByLabelText( + message.patterns.phoneNumber.fieldLabel + ); + await userEvent.clear(labelInput); + await userEvent.type(labelInput, updatedLabel); + + const form = labelInput?.closest('form'); + form?.requestSubmit(); + + await expect(await canvas.findByText(updatedLabel)).toBeInTheDocument(); + await expect( + await canvas.queryByLabelText(message.patterns.phoneNumber.hintLabel) + ).toBeNull(); + }, +}; + +export const Error: StoryObj = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await userEvent.click( + canvas.getByText(message.patterns.phoneNumber.displayName) + ); + + const labelInput = canvas.getByLabelText( + message.patterns.phoneNumber.fieldLabel + ); + await userEvent.clear(labelInput); + labelInput.blur(); + + await expect( + await canvas.findByText( + message.patterns.selectDropdown.errorTextMustContainChar + ) + ).toBeInTheDocument(); + }, +}; diff --git a/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.test.tsx b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.test.tsx new file mode 100644 index 00000000..29ccbe4a --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.test.tsx @@ -0,0 +1,7 @@ +/** + * @vitest-environment jsdom + */ +import { describeStories } from '../../../test-helper.js'; +import meta, * as stories from './PhoneNumberPatternEdit.stories.js'; + +describeStories(meta, stories); diff --git a/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.tsx b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.tsx new file mode 100644 index 00000000..51e08782 --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/components/PhoneNumberPatternEdit.tsx @@ -0,0 +1,115 @@ +import classnames from 'classnames'; +import React from 'react'; + +import { type PhoneNumberProps } from '@atj/forms'; +import { type PhoneNumberPattern } from '@atj/forms'; + +import PhoneNumber from '../../../Form/components/PhoneNumber/index.js'; +import { PatternEditComponent } from '../types.js'; + +import { PatternEditActions } from './common/PatternEditActions.js'; +import { PatternEditForm } from './common/PatternEditForm.js'; +import { usePatternEditFormContext } from './common/hooks.js'; +import { enLocale as message } from '@atj/common'; +import styles from '../formEditStyles.module.css'; + +const PhoneNumberPatternEdit: PatternEditComponent = ({ + focus, + previewProps, +}) => { + return ( + <> + {focus ? ( + } + > + ) : ( +
+ +
+ )} + + ); +}; + +const EditComponent = ({ pattern }: { pattern: PhoneNumberPattern }) => { + const { fieldId, getFieldState, register } = + usePatternEditFormContext(pattern.id); + const label = getFieldState('label'); + const hint = getFieldState('hint'); + + return ( +
+
+ +
+
+ +
+
+ + + + + + +
+
+ ); +}; + +export default PhoneNumberPatternEdit; diff --git a/packages/design/src/FormManager/FormEdit/components/index.ts b/packages/design/src/FormManager/FormEdit/components/index.ts index 4e4f355a..8f15b47c 100644 --- a/packages/design/src/FormManager/FormEdit/components/index.ts +++ b/packages/design/src/FormManager/FormEdit/components/index.ts @@ -13,6 +13,7 @@ import { PageEdit } from './PageEdit.js'; import PageSetEdit from './PageSetEdit.js'; import ParagraphPatternEdit from './ParagraphPatternEdit.js'; import { PatternPreviewSequence } from './PreviewSequencePattern/index.js'; +import PhoneNumberPatternEdit from './PhoneNumberPatternEdit.js'; import RadioGroupPatternEdit from './RadioGroupPatternEdit.js'; import RichTextPatternEdit from './RichTextPatternEdit/index.js'; import SelectDropdownPatternEdit from './SelectDropdownPatternEdit.js'; @@ -28,6 +29,7 @@ export const defaultPatternEditComponents: EditComponentForPattern = { 'package-download': PackageDownloadPatternEdit as PatternEditComponent, page: PageEdit as PatternEditComponent, 'page-set': PageSetEdit as PatternEditComponent, + 'phone-number': PhoneNumberPatternEdit as PatternEditComponent, 'radio-group': RadioGroupPatternEdit as PatternEditComponent, 'rich-text': RichTextPatternEdit as PatternEditComponent, 'select-dropdown': SelectDropdownPatternEdit as PatternEditComponent, diff --git a/packages/design/src/FormManager/FormEdit/formEditStyles.module.css b/packages/design/src/FormManager/FormEdit/formEditStyles.module.css index 36f46de2..3b39bfc7 100644 --- a/packages/design/src/FormManager/FormEdit/formEditStyles.module.css +++ b/packages/design/src/FormManager/FormEdit/formEditStyles.module.css @@ -55,7 +55,14 @@ padding-left: 0; } -.draggableListItemWrapper .dateOfBirthPattern legend { +.draggableListItemWrapper .dateOfBirthPattern legend, +.draggableListItemWrapper + .emailInput + legend + .draggableListItemWrapper + .radioFormPattern + legend, +.draggableListItemWrapper .phoneNumberPattern legend { padding-left: 0; } diff --git a/packages/design/src/FormManager/FormEdit/images/phone-icon.svg b/packages/design/src/FormManager/FormEdit/images/phone-icon.svg new file mode 100644 index 00000000..ef0386c7 --- /dev/null +++ b/packages/design/src/FormManager/FormEdit/images/phone-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/forms/src/components.ts b/packages/forms/src/components.ts index a4f4b5da..8f98b33f 100644 --- a/packages/forms/src/components.ts +++ b/packages/forms/src/components.ts @@ -112,6 +112,16 @@ export type DateOfBirthProps = PatternProps<{ error?: FormError; }>; +export type PhoneNumberProps = PatternProps<{ + type: 'phone-number'; + phoneId: string; + hint?: string; + label: string; + required: boolean; + error?: FormError; + value: string; +}>; + export type SequenceProps = PatternProps<{ type: 'sequence'; }>; diff --git a/packages/forms/src/pattern.ts b/packages/forms/src/pattern.ts index b0a39eec..4fcd2beb 100644 --- a/packages/forms/src/pattern.ts +++ b/packages/forms/src/pattern.ts @@ -121,6 +121,8 @@ export const validatePattern = ( pattern: Pattern, value: any ): r.Result => { + console.log('TEST Validating value:', value); + if (!patternConfig.parseUserInput) { return { success: true, @@ -154,7 +156,7 @@ export const validatePatternAndChildren = ( values: Record, result: { values: Record; - errors: Record; + errors: Record; } = { values: {}, errors: {} } ) => { const aggregatedValues = aggregateValuesByPrefix(values); @@ -165,6 +167,7 @@ export const validatePatternAndChildren = ( if (parseResult.success) { result.values[pattern.id] = parseResult.data; + result.errors[pattern.id] = undefined; } else { result.values[pattern.id] = values[pattern.id]; result.errors[pattern.id] = parseResult.error; diff --git a/packages/forms/src/patterns/index.ts b/packages/forms/src/patterns/index.ts index 4fe5dbcd..8fe6645c 100644 --- a/packages/forms/src/patterns/index.ts +++ b/packages/forms/src/patterns/index.ts @@ -10,6 +10,7 @@ import { packageDownloadConfig } from './package-download/index.js'; import { pageConfig } from './page/index.js'; import { pageSetConfig } from './page-set/index.js'; import { paragraphConfig } from './paragraph.js'; +import { phoneNumberConfig } from './phone-number/phone-number.js'; import { radioGroupConfig } from './radio-group.js'; import { richTextConfig } from './rich-text.js'; import { selectDropdownConfig } from './select-dropdown/select-dropdown.js'; @@ -30,6 +31,7 @@ export const defaultFormConfig: FormConfig = { page: pageConfig, 'page-set': pageSetConfig, paragraph: paragraphConfig, + 'phone-number': phoneNumberConfig, 'rich-text': richTextConfig, 'radio-group': radioGroupConfig, 'select-dropdown': selectDropdownConfig, @@ -51,6 +53,7 @@ export { type PagePattern } from './page/config.js'; export * from './page-set/index.js'; export { type PageSetPattern } from './page-set/config.js'; export * from './paragraph.js'; +export * from './phone-number/phone-number.js'; export * from './radio-group.js'; export * from './select-dropdown/select-dropdown.js'; export * from './sequence.js'; diff --git a/packages/forms/src/patterns/page-set/submit.ts b/packages/forms/src/patterns/page-set/submit.ts index 9cadb644..780d4180 100644 --- a/packages/forms/src/patterns/page-set/submit.ts +++ b/packages/forms/src/patterns/page-set/submit.ts @@ -43,6 +43,8 @@ export const submitPage: SubmitHandler = async ( opts.data ); + console.log('TEST Result:', result); + // Increment the page number if there are no errors and this isn't the last page. const lastPage = opts.pattern.data.pages.length - 1; const nextPage = diff --git a/packages/forms/src/patterns/phone-number/phone-number.test.ts b/packages/forms/src/patterns/phone-number/phone-number.test.ts new file mode 100644 index 00000000..074595ca --- /dev/null +++ b/packages/forms/src/patterns/phone-number/phone-number.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from 'vitest'; +import { + createPhoneSchema, + phoneNumberConfig, + type PhoneNumberPattern, +} from './phone-number'; + +describe('PhoneNumberPattern tests', () => { + describe('createPhoneSchema', () => { + it('should create schema for required phone input', () => { + const data: PhoneNumberPattern['data'] = { + label: 'Test Phone Input Label', + required: true, + }; + + const schema = createPhoneSchema(data); + const validInput = { phone: '+12223334444' }; + const invalidInput = { phone: '123456abc' }; + + expect(schema.safeParse(validInput).success).toBe(true); + expect(schema.safeParse(invalidInput).success).toBe(false); + }); + + it('should create schema for optional phone input', () => { + const data: PhoneNumberPattern['data'] = { + label: 'Test phone Input Label', + required: false, + }; + + const schema = createPhoneSchema(data); + const validInput = { phone: '+12223334444' }; + const emptyInput = {}; + + expect(schema.safeParse(validInput).success).toBe(true); + expect(schema.safeParse(emptyInput).success).toBe(true); + }); + }); + + describe('phoneNumberConfig', () => { + it('should parse user input correctly', () => { + const pattern: PhoneNumberPattern = { + type: 'phone-number', + id: 'test', + data: { + label: 'Test Phone Input Label', + required: true, + }, + }; + + const inputValue = { phone: '+12223334444' }; + if (!phoneNumberConfig.parseUserInput) { + expect.fail('phoneNumberConfig.parseUserInput is undefined'); + } + const result = phoneNumberConfig.parseUserInput(pattern, inputValue); + if (result.success) { + expect(result.data).toEqual(inputValue); + } else { + expect.fail('Unexpected validation failure'); + } + }); + + it('should handle validation error for user input', () => { + const pattern: PhoneNumberPattern = { + type: 'phone-number', + id: 'test', + data: { + label: 'Test Phone Input Label', + required: true, + }, + }; + + const invalidInput = { phone: '123456abc' }; + if (!phoneNumberConfig.parseUserInput) { + expect.fail('phoneNumberConfig.parseUserInput is undefined'); + } + const result = phoneNumberConfig.parseUserInput(pattern, invalidInput); + if (!result.success) { + expect(result.error).toBeDefined(); + } else { + expect.fail('Unexpected validation success'); + } + }); + + it('should parse config data correctly', () => { + const obj = { + label: 'Test Phone Input Label', + required: true, + }; + + if (!phoneNumberConfig.parseConfigData) { + expect.fail('phoneNumberConfig.parseConfigData is undefined'); + } + const result = phoneNumberConfig.parseConfigData(obj); + if (result.success) { + expect(result.data.label).toBe('Test Phone Input Label'); + expect(result.data.required).toBe(true); + } else { + expect.fail('Unexpected validation failure'); + } + }); + + it('should handle invalid config data', () => { + const obj = { + label: '', + required: true, + }; + + if (!phoneNumberConfig.parseConfigData) { + expect.fail('phoneNumberConfig.parseConfigData is undefined'); + } + const result = phoneNumberConfig.parseConfigData(obj); + if (!result.success) { + expect(result.error).toBeDefined(); + } else { + expect.fail('Unexpected validation success'); + } + }); + }); +}); diff --git a/packages/forms/src/patterns/phone-number/phone-number.ts b/packages/forms/src/patterns/phone-number/phone-number.ts new file mode 100644 index 00000000..1fc0464b --- /dev/null +++ b/packages/forms/src/patterns/phone-number/phone-number.ts @@ -0,0 +1,104 @@ +import * as z from 'zod'; + +import { type PhoneNumberProps } from '../../components.js'; +import { + type Pattern, + type PatternConfig, + validatePattern, +} from '../../pattern.js'; +import { getFormSessionValue } from '../../session.js'; +import { + safeZodParseFormErrors, + safeZodParseToFormError, +} from '../../util/zod.js'; + +const configSchema = z.object({ + label: z.string().min(1), + required: z.boolean(), + hint: z.string().optional(), +}); + +export type PhoneNumberPattern = Pattern>; + +export type PhoneNumberPatternOutput = z.infer< + ReturnType +>; + +export const createPhoneSchema = (data: PhoneNumberPattern['data']) => { + let phoneSchema = z + .string() + .regex( + /^\+?(\d{1,3})?[-. \()]?((\d{1,4}[-. \()]?){2,6}\d{1,4}?)$/, + 'Invalid phone format' + ); + + if (!data.required) { + return phoneSchema.optional(); + } + + return phoneSchema; +}; + +export const phoneNumberConfig: PatternConfig< + PhoneNumberPattern, + PhoneNumberPatternOutput +> = { + displayName: 'Phone Number', + iconPath: 'phone-icon.svg', + initial: { + label: 'Phone Number', + required: true, + hint: 'Should include country code and optional leading zero', + }, + + parseUserInput: (pattern, inputValue) => { + console.log('TEST Parsing user input:', inputValue); + const result = safeZodParseToFormError( + createPhoneSchema(pattern.data), + inputValue + ); + console.log('TEST Parsing user input result:', result); + return result; + }, + + parseConfigData: obj => { + return safeZodParseFormErrors(configSchema, obj); + }, + getChildren() { + return []; + }, + + createPrompt(_, session, pattern, options) { + const extraAttributes: Record = {}; + const sessionValue = getFormSessionValue(session, pattern.id); + const error = session.data.errors[pattern.id]; + + /* + if (options.validate) { + const isValidResult = validatePattern( + phoneNumberConfig, + pattern, + sessionValue + ); + if (!isValidResult.success) { + extraAttributes['error'] = isValidResult.error; + } + } + */ + + return { + props: { + _patternId: pattern.id, + type: 'phone-number', + label: pattern.data.label, + phoneId: pattern.id, + required: pattern.data.required, + hint: pattern.data.hint, + value: sessionValue, + error, + ...extraAttributes, + } as PhoneNumberProps, + children: [], + }; + }, +};