Skip to content

Commit

Permalink
feat: create phone number component tckt-363 (#382)
Browse files Browse the repository at this point in the history
* feat: create phone number pattern tckt-363

* feat: create phone number pattern edit form tckt-363

* test: add tests phone number config tckt-363

* refactor: Clear old error message on session object when updating valid submission data and pass previous value into React component tckt-363

Co-authored-by: Daniel Naab [email protected]

* test: update tests tckt-363

* feat: update error state and message for phone pattern tckt-363

---------

Co-authored-by: kalasgarov <[email protected]>
  • Loading branch information
kalasgarov and kalasgarov authored Nov 15, 2024
1 parent 5ee1c8c commit 5ee8954
Show file tree
Hide file tree
Showing 22 changed files with 693 additions and 31 deletions.
18 changes: 13 additions & 5 deletions packages/common/src/locales/en/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
},
Expand All @@ -48,14 +48,14 @@ 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)',
Expand All @@ -66,5 +66,13 @@ export const en = {
fieldLabel: 'Email Input label',
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: '10-digit, U.S. only, for example 999-999-9999',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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<typeof PhoneNumberPattern> = {
title: 'patterns/PhoneNumberPattern',
component: PhoneNumberPattern,
decorators: [
(Story, args) => {
const FormDecorator = () => {
const formMethods = useForm();
return (
<FormProvider {...formMethods}>
<Story {...args} />
</FormProvider>
);
};
return <FormDecorator />;
},
],
tags: ['autodocs'],
};

export default meta;

export const Default: StoryObj<typeof PhoneNumberPattern> = {
args: {
phoneId: 'phone',
label: 'Phone number',
required: false,
},
};

export const WithRequired: StoryObj<typeof PhoneNumberPattern> = {
args: {
phoneId: 'phone',
label: 'Phone number',
required: true,
},
};

export const WithError: StoryObj<typeof PhoneNumberPattern> = {
args: {
phoneId: 'phone',
label: 'Phone number with error',
required: true,
error: {
type: 'custom',
message: 'This field has an error',
},
},
};

export const WithHint: StoryObj<typeof PhoneNumberPattern> = {
args: {
phoneId: 'phone',
label: 'Phone number',
hint: '10-digit, U.S. only, for example 999-999-9999',
required: true,
},
};

export const WithHintAndError: StoryObj<typeof PhoneNumberPattern> = {
args: {
phoneId: 'phone',
label: 'Phone number',
hint: '10-digit, U.S. only, for example 999-999-9999',
required: true,
error: {
type: 'custom',
message: 'This field has an error',
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper.js';
import meta, * as stories from './PhoneNumber.stories.js';

describeStories(meta, stories);
57 changes: 57 additions & 0 deletions packages/design/src/Form/components/PhoneNumber/PhoneNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import { type PhoneNumberProps } from '@atj/forms';
import { type PatternComponent } from '../../index.js';

export const PhoneNumberPattern: PatternComponent<PhoneNumberProps> = ({
phoneId,
hint,
label,
required,
error,
value,
}) => {
const { register } = useFormContext();

return (
<fieldset className="usa-fieldset">
<div className={classNames('usa-form-group margin-top-2')}>
<label
className={classNames('usa-label', {
'usa-label--error': error,
})}
id={`input-message-${phoneId}`}
htmlFor={phoneId}
>
{label}
{required && <span className="required-indicator">*</span>}
</label>
{hint && (
<div className="usa-hint" id="primaryPnHint">
{hint}
</div>
)}
{error && (
<div
className="usa-error-message"
id={`input-error-message-${phoneId}`}
role="alert"
>
{error.message}
</div>
)}
<input
className={classNames('usa-input', {
'usa-input--error': error,
})}
id={phoneId}
type="tel"
defaultValue={value}
{...register(phoneId, { required })}
aria-describedby="primaryPnHint"
/>
</div>
</fieldset>
);
};
3 changes: 3 additions & 0 deletions packages/design/src/Form/components/PhoneNumber/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PhoneNumberPattern } from './PhoneNumber.js';

export default PhoneNumberPattern;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { type Meta, type StoryObj } from '@storybook/react';
import { SelectDropdownPattern } from './SelectDropdown.js';

const meta: Meta<typeof SelectDropdownPattern> = {
title: 'patterns/SelectPattern',
title: 'patterns/SelectDropdownPattern',
component: SelectDropdownPattern,
decorators: [
(Story, args) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/design/src/Form/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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';
Expand All @@ -29,6 +30,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import dropDownOptionIcon from './images/dropdownoption-icon.svg';
import emailInputIcon from './images/email-icon.svg';
import longanswerIcon from './images/longanswer-icon.svg';
import pageIcon from './images/page-icon.svg';
import phoneIcon from './images/phone-icon.svg';
import richTextIcon from './images/richtext-icon.svg';
import shortanswerIcon from './images/shortanswer-icon.svg';
import singleselectIcon from './images/singleselect-icon.svg';
Expand All @@ -30,6 +31,7 @@ const icons: Record<string, string | any> = {
'email-icon.svg': emailInputIcon,
'longanswer-icon.svg': longanswerIcon,
'page-icon.svg': pageIcon,
'phone-icon.svg': phoneIcon,
'richtext-icon.svg': richTextIcon,
'shortanswer-icon.svg': shortanswerIcon,
'singleselect-icon.svg': singleselectIcon,
Expand Down Expand Up @@ -98,6 +100,7 @@ const sidebarPatterns: DropdownPattern[] = [
['input', defaultFormConfig.patterns['input']],
['package-download', defaultFormConfig.patterns['package-download']],
['paragraph', defaultFormConfig.patterns['paragraph']],
['phone-number', defaultFormConfig.patterns['phone-number']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['select-dropdown', defaultFormConfig.patterns['select-dropdown']],
Expand All @@ -110,6 +113,7 @@ export const fieldsetPatterns: DropdownPattern[] = [
['input', defaultFormConfig.patterns['input']],
['package-download', defaultFormConfig.patterns['package-download']],
['paragraph', defaultFormConfig.patterns['paragraph']],
['phone-number', defaultFormConfig.patterns['phone-number']],
['radio-group', defaultFormConfig.patterns['radio-group']],
['rich-text', defaultFormConfig.patterns['rich-text']],
['select-dropdown', defaultFormConfig.patterns['select-dropdown']],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const EmailInputEdit: PatternEditComponent<EmailInputProps> = ({
></PatternEditForm>
) : (
<div
className={`${styles.emailInput} padding-left-3 padding-bottom-3 padding-right-3`}
className={`${styles.emailInputPattern} padding-left-3 padding-bottom-3 padding-right-3`}
>
<EmailInput {...previewProps} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<typeof FormEdit>;

export default storyConfig;

export const Basic: StoryObj<typeof FormEdit> = {
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<typeof FormEdit> = {
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<typeof FormEdit> = {
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();
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @vitest-environment jsdom
*/
import { describeStories } from '../../../test-helper.js';
import meta, * as stories from './PhoneNumberPatternEdit.stories.js';

describeStories(meta, stories);
Loading

0 comments on commit 5ee8954

Please sign in to comment.