Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create phone number component tckt-363 #382

Merged
merged 6 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading