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 gender identity input component tckt-365 #395

Merged
merged 6 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
10 changes: 10 additions & 0 deletions packages/common/src/locales/en/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,15 @@ export const en = {
hint: 'For example, 555-11-0000',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
},
genderId: {
...defaults,
displayName: 'Gender Identity label',
fieldLabel: 'Gender Identity label',
hintLabel: 'Gender Identity hint label',
hint: 'For example, man, woman, non-binary',
errorTextMustContainChar: 'String must contain at least 1 character(s)',
preferNotToAnswerTextLabel:
'Prefer not to share my gender identity checkbox label',
},
},
};
64 changes: 64 additions & 0 deletions packages/design/src/Form/components/GenderId/GenderId.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { type Meta, type StoryObj } from '@storybook/react';
import GenderIdPattern from './index.js';

const meta: Meta<typeof GenderIdPattern> = {
title: 'patterns/GenderIdPattern',
component: GenderIdPattern,
decorators: [
(Story, args) => {
const FormDecorator = () => {
const formMethods = useForm();
return (
<FormProvider {...formMethods}>
<Story {...args} />
</FormProvider>
);
};
return <FormDecorator />;
},
],
tags: ['autodocs'],
};

export default meta;

const defaultArgs = {
genderId: 'gender-identity',
label: 'Gender identity',
hint: 'For example, man, woman, non-binary',
required: true,
preferNotToAnswerText: 'Prefer not to share my gender identity',
};

export const Default: StoryObj<typeof GenderIdPattern> = {
args: { ...defaultArgs },
};

export const Optional: StoryObj<typeof GenderIdPattern> = {
args: { ...defaultArgs, required: false },
};

export const WithError: StoryObj<typeof GenderIdPattern> = {
args: {
...defaultArgs,
label: 'Gender identity with error',
error: {
type: 'custom',
message: 'This field has an error',
},
},
};

export const WithHint: StoryObj<typeof GenderIdPattern> = {
args: { ...defaultArgs },
};

export const WithCheckboxChecked: StoryObj<typeof GenderIdPattern> = {
args: { ...defaultArgs, preferNotToAnswerChecked: true },
};

export const WithoutCheckbox: StoryObj<typeof GenderIdPattern> = {
args: { ...defaultArgs, preferNotToAnswerText: undefined },
};
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 './GenderId.stories.js';

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

import { type PatternComponent } from '../../index.js';

const GenderIdPattern: PatternComponent<GenderIdProps> = ({
genderId,
hint,
label,
required,
error,
value,
preferNotToAnswerText,
preferNotToAnswerChecked: initialPreferNotToAnswerChecked = false,
}) => {
const { register, setValue } = useFormContext();
const [preferNotToAnswerChecked, setPreferNotToAnswerChecked] = useState(
initialPreferNotToAnswerChecked
);

const errorId = `input-error-message-${genderId}`;
const hintId = `hint-${genderId}`;
const preferNotToAnswerId = `prefer-not-to-answer-${genderId}`;

useEffect(() => {
danielnaab marked this conversation as resolved.
Show resolved Hide resolved
if (preferNotToAnswerChecked) {
setValue(genderId, preferNotToAnswerText, { shouldValidate: true });
} else {
setValue(genderId, value, { shouldValidate: true });
}
}, [
preferNotToAnswerChecked,
setValue,
genderId,
preferNotToAnswerText,
value,
]);

const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPreferNotToAnswerChecked(event.target.checked);
setValue(preferNotToAnswerId, event.target.checked);
};

return (
<fieldset className="usa-fieldset">
<div className={classNames('usa-form-group margin-top-2')}>
<label
className={classNames('usa-label', {
'usa-label--error': error,
})}
htmlFor={genderId}
>
{label || 'Gender identity'}
{required && <span className="required-indicator">*</span>}
</label>
{hint && (
<div className="usa-hint" id={hintId}>
{hint}
</div>
)}
{error && (
<div className="usa-error-message" id={errorId} role="alert">
{error.message}
</div>
)}
<input
className={classNames('usa-input', {
'usa-input--error': error,
})}
style={
preferNotToAnswerChecked
? {
backgroundColor: '#e9ecef',
pointerEvents: 'none',
opacity: 0.65,
}
: {}
}
id={genderId}
type="text"
defaultValue={value}
{...register(genderId, { required })}
aria-describedby={
`${hint ? `${hintId}` : ''}${error ? ` ${errorId}` : ''}`.trim() ||
undefined
}
/>
{preferNotToAnswerText && (
<div className="usa-checkbox">
<input
className="usa-checkbox__input"
id={preferNotToAnswerId}
type="checkbox"
value="prefer-not-to-answer"
checked={preferNotToAnswerChecked}
danielnaab marked this conversation as resolved.
Show resolved Hide resolved
{...register(preferNotToAnswerId)}
onChange={handleCheckboxChange}
/>
<label
className="usa-checkbox__label"
htmlFor={preferNotToAnswerId}
>
{preferNotToAnswerText}
</label>
</div>
)}
</div>
</fieldset>
);
};

export default GenderIdPattern;
kalasgarov marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -7,6 +7,7 @@ import DateOfBirth from './DateOfBirth/index.js';
import EmailInput from './EmailInput/index.js';
import Fieldset from './Fieldset/index.js';
import FormSummary from './FormSummary/index.js';
import GenderId from './GenderId/index.js';
import PackageDownload from './PackageDownload/index.js';
import Page from './Page/index.js';
import PageSet from './PageSet/index.js';
Expand All @@ -28,6 +29,7 @@ export const defaultPatternComponents: ComponentForPattern = {
'email-input': EmailInput as PatternComponent,
fieldset: Fieldset as PatternComponent,
'form-summary': FormSummary as PatternComponent,
'gender-id': GenderId as PatternComponent,
input: TextInput as PatternComponent,
'package-download': PackageDownload as PatternComponent,
page: Page as PatternComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import dateIcon from './images/date-icon.svg';
import dropDownIcon from './images/dropdown-icon.svg';
import dropDownOptionIcon from './images/dropdownoption-icon.svg';
import emailInputIcon from './images/email-icon.svg';
import genderId from './images/gender-id-icon.svg';
import longanswerIcon from './images/longanswer-icon.svg';
import pageIcon from './images/page-icon.svg';
import phoneIcon from './images/phone-icon.svg';
Expand All @@ -32,6 +33,7 @@ const icons: Record<string, string | any> = {
'dropdown-icon.svg': dropDownIcon,
'dropdownoption-icon.svg': dropDownOptionIcon,
'email-icon.svg': emailInputIcon,
'gender-id-icon.svg': genderId,
'longanswer-icon.svg': longanswerIcon,
'page-icon.svg': pageIcon,
'phone-icon.svg': phoneIcon,
Expand Down Expand Up @@ -101,6 +103,7 @@ const sidebarPatterns: DropdownPattern[] = [
['email-input', defaultFormConfig.patterns['email-input']],
['fieldset', defaultFormConfig.patterns['fieldset']],
['form-summary', defaultFormConfig.patterns['form-summary']],
['gender-id', defaultFormConfig.patterns['gender-id']],
['input', defaultFormConfig.patterns['input']],
['package-download', defaultFormConfig.patterns['package-download']],
['paragraph', defaultFormConfig.patterns['paragraph']],
Expand All @@ -120,6 +123,7 @@ export const fieldsetPatterns: DropdownPattern[] = [
['date-of-birth', defaultFormConfig.patterns['date-of-birth']],
['email-input', defaultFormConfig.patterns['email-input']],
['form-summary', defaultFormConfig.patterns['form-summary']],
['gender-id', defaultFormConfig.patterns['gender-id']],
['input', defaultFormConfig.patterns['input']],
['package-download', defaultFormConfig.patterns['package-download']],
['paragraph', defaultFormConfig.patterns['paragraph']],
Expand Down
Loading
Loading