diff --git a/apps/modernization-ui/src/apps/patient/add/extended/AddPatientExtendedForm.tsx b/apps/modernization-ui/src/apps/patient/add/extended/AddPatientExtendedForm.tsx index 6dc74e7784..1d3e0a2dd4 100644 --- a/apps/modernization-ui/src/apps/patient/add/extended/AddPatientExtendedForm.tsx +++ b/apps/modernization-ui/src/apps/patient/add/extended/AddPatientExtendedForm.tsx @@ -1,10 +1,11 @@ import { AddressEntry, AdministrativeEntry, - IdentificationEntry, - PhoneEmailEntry, RaceEntry, - NameEntry + PhoneEmailEntry, + NameEntry, + EthnicityEntry, + IdentificationEntry } from 'apps/patient/data/entry'; import { internalizeDate } from 'date'; import { useState } from 'react'; @@ -12,15 +13,17 @@ import { FormProvider, useForm } from 'react-hook-form'; import { AddressMultiEntry } from './inputs/address/AddressMultiEntry'; import { Administrative } from './inputs/administrative/Administrative'; import { IdentificationMultiEntry } from './inputs/identification/IdentificationMultiEntry'; -import { NameMultiEntry } from './inputs/name/NameMultiEntry'; import { PhoneAndEmailMultiEntry } from './inputs/phone/PhoneAndEmailMultiEntry'; import { RaceMultiEntry } from './inputs/race/RaceMultiEntry'; import { AddPatientExtendedNav } from './nav/AddPatientExtendedNav'; import styles from './add-patient-extended-form.module.scss'; +import { EthnicityEntryCard } from './inputs/ethnicity/EthnicityEntryCard'; +import { NameMultiEntry } from './inputs/name/NameMultiEntry'; // Once all sections have been updated with proper types this will be removed type ExtendedPatientCreationForm = { administrative: AdministrativeEntry; + ethnicity: EthnicityEntry; address: AddressEntry[]; phone: PhoneEmailEntry[]; race: RaceEntry[]; @@ -37,12 +40,16 @@ type DirtyState = { race: boolean; }; export const AddPatientExtendedForm = () => { + const today = internalizeDate(new Date()); const form = useForm({ defaultValues: { phone: [], administrative: { - asOf: internalizeDate(new Date()), + asOf: today, comment: '' + }, + ethnicity: { + asOf: today } }, mode: 'onBlur' @@ -91,6 +98,7 @@ export const AddPatientExtendedForm = () => { form.setValue('race', raceData); }} /> + diff --git a/apps/modernization-ui/src/apps/patient/add/extended/card/Card.module.scss b/apps/modernization-ui/src/apps/patient/add/extended/card/Card.module.scss index f593fa0de2..18452c0eeb 100644 --- a/apps/modernization-ui/src/apps/patient/add/extended/card/Card.module.scss +++ b/apps/modernization-ui/src/apps/patient/add/extended/card/Card.module.scss @@ -14,6 +14,4 @@ align-items: center; @extend %thin-bottom; } - - } diff --git a/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.spec.tsx b/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.spec.tsx new file mode 100644 index 0000000000..bdf8c49c93 --- /dev/null +++ b/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.spec.tsx @@ -0,0 +1,30 @@ +import { render } from '@testing-library/react'; +import { PatientEthnicityCodedValue } from 'apps/patient/profile/ethnicity'; +import { FormProvider, useForm } from 'react-hook-form'; +import { EthnicityEntryCard } from './EthnicityEntryCard'; + +const mockEthnicityValues: PatientEthnicityCodedValue = { + ethnicGroups: [{ name: 'Hispanic or Latino', value: '2135-2' }], + ethnicityUnknownReasons: [{ name: 'Not asked', value: '6' }], + detailedEthnicities: [{ name: 'Central American', value: '2155-0' }] +}; + +jest.mock('apps/patient/profile/ethnicity/usePatientEthnicityCodedValues', () => ({ + usePatientEthnicityCodedValues: () => mockEthnicityValues +})); + +const Fixture = () => { + const form = useForm(); + return ( + + + + ); +}; + +describe('EthnicityEntryCard', () => { + it('should display Ethnicity header on card', () => { + const { getAllByText } = render(); + expect(getAllByText('Ethnicity')).toHaveLength(2); + }); +}); diff --git a/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.tsx b/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.tsx new file mode 100644 index 0000000000..be59b4ad31 --- /dev/null +++ b/apps/modernization-ui/src/apps/patient/add/extended/inputs/ethnicity/EthnicityEntryCard.tsx @@ -0,0 +1,10 @@ +import { EthnicityEntryFields } from 'apps/patient/data/ethnicity/EthnicityEntryFields'; +import { Card } from '../../card/Card'; + +export const EthnicityEntryCard = () => { + return ( + + + + ); +}; diff --git a/apps/modernization-ui/src/apps/patient/add/extended/nav/AddPatientExtendedNav.tsx b/apps/modernization-ui/src/apps/patient/add/extended/nav/AddPatientExtendedNav.tsx index 0942144012..d3227212a3 100644 --- a/apps/modernization-ui/src/apps/patient/add/extended/nav/AddPatientExtendedNav.tsx +++ b/apps/modernization-ui/src/apps/patient/add/extended/nav/AddPatientExtendedNav.tsx @@ -12,7 +12,7 @@ export const AddPatientExtendedNav = () => { Phone & email Identification Race - Ethnicity + Ethnicity Sex & birth Mortality General patient information diff --git a/apps/modernization-ui/src/apps/patient/data/api.ts b/apps/modernization-ui/src/apps/patient/data/api.ts index fcd46a9469..dfd84b573f 100644 --- a/apps/modernization-ui/src/apps/patient/data/api.ts +++ b/apps/modernization-ui/src/apps/patient/data/api.ts @@ -60,6 +60,7 @@ type Race = EffectiveDated & { type Ethnicity = EffectiveDated & { ethnicity: string; detailed: string[]; + reasonUnknown?: string; }; type Sex = { diff --git a/apps/modernization-ui/src/apps/patient/data/entry.ts b/apps/modernization-ui/src/apps/patient/data/entry.ts index 3b2a595d33..c285e0c750 100644 --- a/apps/modernization-ui/src/apps/patient/data/entry.ts +++ b/apps/modernization-ui/src/apps/patient/data/entry.ts @@ -61,6 +61,7 @@ type RaceEntry = EffectiveDated & { type EthnicityEntry = EffectiveDated & { ethnicity: Selectable; detailed: Selectable[]; + reasonUnknown?: Selectable; }; type SexEntry = EffectiveDated & { diff --git a/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.spec.tsx b/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.spec.tsx new file mode 100644 index 0000000000..303fa72e32 --- /dev/null +++ b/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.spec.tsx @@ -0,0 +1,80 @@ +import { FormProvider, useForm } from 'react-hook-form'; +import { EthnicityEntryFields } from './EthnicityEntryFields'; +import { PatientEthnicityCodedValue } from 'apps/patient/profile/ethnicity'; +import { EthnicityEntry } from '../entry'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { act } from 'react-dom/test-utils'; + +const mockEthnicityValues: PatientEthnicityCodedValue = { + ethnicGroups: [ + { name: 'Hispanic or Latino', value: '2135-2' }, + { name: 'Unknown', value: 'UNK' } + ], + ethnicityUnknownReasons: [{ name: 'Not asked', value: '6' }], + detailedEthnicities: [{ name: 'Central American', value: '2155-0' }] +}; + +jest.mock('apps/patient/profile/ethnicity/usePatientEthnicityCodedValues', () => ({ + usePatientEthnicityCodedValues: () => mockEthnicityValues +})); +const Fixture = () => { + const form = useForm({ mode: 'onBlur' }); + return ( + + + + ); +}; + +describe('EthnicityEntryFields', () => { + it('should render the proper labels', () => { + const { getByLabelText, queryByLabelText, getByText } = render(); + + expect(getByLabelText('Ethnicity information as of')).toBeInTheDocument(); + expect(getByText('Ethnicity information as of')).toHaveClass('required'); + expect(getByLabelText('Ethnicity')).toBeInTheDocument(); + expect(getByText('Ethnicity')).not.toHaveClass('required'); + expect(queryByLabelText('Spanish origin')).not.toBeInTheDocument(); + expect(queryByLabelText('Reason unknown')).not.toBeInTheDocument(); + }); + + it('should require as of date', async () => { + const { getByLabelText, getByText } = render(); + const asOf = getByLabelText('Ethnicity information as of'); + act(() => { + userEvent.click(asOf); + userEvent.tab(); + }); + + await waitFor(() => { + expect(getByText('As of date is required.')).toBeInTheDocument(); + }); + }); + + it('should display spanish origin when hispanic or latino selected', async () => { + const { getByLabelText, queryByLabelText } = render(); + + act(() => { + userEvent.selectOptions(getByLabelText('Ethnicity'), '2135-2'); + }); + + await waitFor(() => { + expect(getByLabelText('Spanish origin')).toBeInTheDocument(); + }); + expect(queryByLabelText('Reason unknown')).not.toBeInTheDocument(); + }); + + it('should display unknown reason when unknown selected', async () => { + const { getByLabelText, queryByLabelText } = render(); + + act(() => { + userEvent.selectOptions(getByLabelText('Ethnicity'), 'UNK'); + }); + + await waitFor(() => { + expect(getByLabelText('Reason unknown')).toBeInTheDocument(); + }); + expect(queryByLabelText('Spanish origin')).not.toBeInTheDocument(); + }); +}); diff --git a/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.tsx b/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.tsx new file mode 100644 index 0000000000..ed24ba3914 --- /dev/null +++ b/apps/modernization-ui/src/apps/patient/data/ethnicity/EthnicityEntryFields.tsx @@ -0,0 +1,98 @@ +import { usePatientEthnicityCodedValues } from 'apps/patient/profile/ethnicity'; +import { DatePickerInput } from 'components/FormInputs/DatePickerInput'; +import { MultiSelect, SingleSelect } from 'design-system/select'; +import { useEffect } from 'react'; +import { Controller, useFormContext, useWatch } from 'react-hook-form'; +import { EthnicityEntry } from '../entry'; + +const UNKNOWN = 'UNK'; +const HISPANIC = '2135-2'; + +export const EthnicityEntryFields = () => { + const { control, setValue } = useFormContext<{ ethnicity: EthnicityEntry }>(); + const coded = usePatientEthnicityCodedValues(); + const selectedEthinicity = useWatch({ control, name: 'ethnicity.ethnicity' }); + + useEffect(() => { + setValue('ethnicity.reasonUnknown', undefined); + setValue('ethnicity.detailed', []); + }, [selectedEthinicity]); + + return ( +
+ ( + + )} + /> + ( + + )} + /> + + {selectedEthinicity?.value === HISPANIC && ( + ( + + )} + /> + )} + {selectedEthinicity?.value === UNKNOWN && ( + ( + + )} + /> + )} +
+ ); +}; diff --git a/apps/modernization-ui/src/apps/patient/data/asEthnicity.spec.ts b/apps/modernization-ui/src/apps/patient/data/ethnicity/asEthnicity.spec.ts similarity index 100% rename from apps/modernization-ui/src/apps/patient/data/asEthnicity.spec.ts rename to apps/modernization-ui/src/apps/patient/data/ethnicity/asEthnicity.spec.ts diff --git a/apps/modernization-ui/src/apps/patient/data/asEthnicity.ts b/apps/modernization-ui/src/apps/patient/data/ethnicity/asEthnicity.ts similarity index 56% rename from apps/modernization-ui/src/apps/patient/data/asEthnicity.ts rename to apps/modernization-ui/src/apps/patient/data/ethnicity/asEthnicity.ts index abda48758a..5f5b0fcdf3 100644 --- a/apps/modernization-ui/src/apps/patient/data/asEthnicity.ts +++ b/apps/modernization-ui/src/apps/patient/data/ethnicity/asEthnicity.ts @@ -1,13 +1,14 @@ import { asValue, asValues } from 'options'; -import { EthnicityEntry } from './entry'; -import { Ethnicity } from './api'; +import { EthnicityEntry } from '../entry'; +import { Ethnicity } from '../api'; const asEthnicity = (entry: EthnicityEntry): Ethnicity => { - const { ethnicity, detailed, ...remaining } = entry; + const { ethnicity, detailed, reasonUnknown, ...remaining } = entry; return { ethnicity: asValue(ethnicity), detailed: asValues(detailed), + reasonUnknown: asValue(reasonUnknown), ...remaining }; }; diff --git a/apps/modernization-ui/src/apps/patient/data/index.ts b/apps/modernization-ui/src/apps/patient/data/index.ts index 3a6fdf0f6e..a53ac632ee 100644 --- a/apps/modernization-ui/src/apps/patient/data/index.ts +++ b/apps/modernization-ui/src/apps/patient/data/index.ts @@ -4,7 +4,7 @@ export { asAddress } from './address/asAddress'; export { asPhoneEmail } from './phoneEmail/asPhoneEmail'; export { asIdentification } from './identification/asIdentification'; export { asRace } from './race/asRace'; -export { asEthnicity } from './asEthnicity'; +export { asEthnicity } from './ethnicity/asEthnicity'; export { asSex } from './asSex'; export { asBirth } from './asBirth'; export { asMortality } from './asMortality'; diff --git a/apps/modernization-ui/src/apps/patient/profile/ethnicity/usePatientEthnicityCodedValues.ts b/apps/modernization-ui/src/apps/patient/profile/ethnicity/usePatientEthnicityCodedValues.ts index 04f569f252..42eeac3e96 100644 --- a/apps/modernization-ui/src/apps/patient/profile/ethnicity/usePatientEthnicityCodedValues.ts +++ b/apps/modernization-ui/src/apps/patient/profile/ethnicity/usePatientEthnicityCodedValues.ts @@ -40,14 +40,14 @@ const initialCoded = { detailedEthnicities: [] }; -type PatietnEthnicityCodedValue = { +type PatientEthnicityCodedValue = { ethnicGroups: CodedValue[]; ethnicityUnknownReasons: CodedValue[]; detailedEthnicities: CodedValue[]; }; const usePatientEthnicityCodedValues = () => { - const [coded, setCoded] = useState(initialCoded); + const [coded, setCoded] = useState(initialCoded); const handleComplete = (data: Result) => { setCoded({ @@ -65,4 +65,4 @@ const usePatientEthnicityCodedValues = () => { }; export { usePatientEthnicityCodedValues }; -export type { PatietnEthnicityCodedValue }; +export type { PatientEthnicityCodedValue }; diff --git a/apps/modernization-ui/src/design-system/select/multi/MultiSelect.tsx b/apps/modernization-ui/src/design-system/select/multi/MultiSelect.tsx index c0c048732f..7ac1d42f38 100644 --- a/apps/modernization-ui/src/design-system/select/multi/MultiSelect.tsx +++ b/apps/modernization-ui/src/design-system/select/multi/MultiSelect.tsx @@ -68,7 +68,7 @@ export const MultiSelect = ({ theme={theme} styles={styles} isMulti - id={id} + inputId={id} name={name} options={options} value={value}