-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* implementation complete, need to get default value to work * fixes the names of BasicAddressFields to use the correct names that correspond to the form objects. * clean up * console log * imports --------- Co-authored-by: Adam Loup <[email protected]>
- Loading branch information
1 parent
1c5837d
commit 32aa873
Showing
7 changed files
with
357 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
apps/modernization-ui/src/apps/patient/add/basic/address/BasicAddressFields.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { AddressEntry } from 'apps/patient/data'; | ||
import { FormProvider, useForm } from 'react-hook-form'; | ||
import { BasicAddressFields } from './BasicAddressFields'; | ||
import { render, waitFor } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
const mockLocationCodedValues = { | ||
states: { | ||
all: [{ name: 'StateName', value: '1' }] | ||
}, | ||
counties: { | ||
byState: (state: string) => [{ name: 'CountyName', value: '2' }] | ||
}, | ||
countries: [{ name: 'CountryName', value: '3' }] | ||
}; | ||
|
||
jest.mock('location/useLocationCodedValues', () => ({ | ||
useLocationCodedValues: () => mockLocationCodedValues | ||
})); | ||
|
||
const Fixture = () => { | ||
const form = useForm<AddressEntry>({ | ||
mode: 'onBlur', | ||
defaultValues: { | ||
address1: undefined, | ||
address2: undefined, | ||
city: undefined, | ||
state: undefined, | ||
zipcode: undefined, | ||
county: undefined, | ||
country: undefined, | ||
censusTract: undefined | ||
} | ||
}); | ||
return ( | ||
<FormProvider {...form}> | ||
<BasicAddressFields /> | ||
</FormProvider> | ||
); | ||
}; | ||
|
||
describe('when entering address section', () => { | ||
it('should render with proper labels', () => { | ||
const { getByLabelText } = render(<Fixture />); | ||
|
||
expect(getByLabelText('Street address 1')).toBeInTheDocument(); | ||
expect(getByLabelText('Street address 2')).toBeInTheDocument(); | ||
expect(getByLabelText('City')).toBeInTheDocument(); | ||
expect(getByLabelText('State')).toBeInTheDocument(); | ||
expect(getByLabelText('Zip')).toBeInTheDocument(); | ||
expect(getByLabelText('County')).toBeInTheDocument(); | ||
expect(getByLabelText('Census tract')).toBeInTheDocument(); | ||
expect(getByLabelText('Country')).toBeInTheDocument(); | ||
}); | ||
|
||
test.each([ | ||
{ value: '0000.00', valid: false }, | ||
{ value: '0001.00', valid: false }, | ||
{ value: '0001.01', valid: true }, | ||
{ value: '1000.00', valid: false }, | ||
{ value: '9999.99', valid: false }, | ||
{ value: '9999.98', valid: true }, | ||
{ value: '0001', valid: true }, | ||
{ value: '9999', valid: true }, | ||
{ value: '0000', valid: false }, | ||
{ value: '9999.00', valid: false }, | ||
{ value: '0001.99', valid: false }, | ||
{ value: '1234.56', valid: true } | ||
])('should validate Census Tract format for value: $value', async ({ value, valid }) => { | ||
const { getByLabelText, queryByText } = render(<Fixture />); | ||
const censusTractInput = getByLabelText('Census tract'); | ||
|
||
userEvent.clear(censusTractInput); | ||
userEvent.paste(censusTractInput, value); | ||
userEvent.tab(); | ||
|
||
const validationMessage = | ||
'The Census tract should be in numeric XXXX or XXXX.xx format where XXXX is the basic tract and xx is the suffix. XXXX ranges from 0001 to 9999. The suffix is limited to a range between .01 and .98.'; | ||
|
||
await waitFor(() => { | ||
const validationError = queryByText(validationMessage); | ||
if (valid) { | ||
expect(validationError).not.toBeInTheDocument(); | ||
} else { | ||
expect(validationError).toBeInTheDocument(); | ||
} | ||
}); | ||
}); | ||
it('should validate address 1', async () => { | ||
const { getByLabelText, queryByText } = render(<Fixture />); | ||
|
||
const address = getByLabelText('Street address 1'); | ||
userEvent.clear(address); | ||
userEvent.paste( | ||
address, | ||
'hsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhjhsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhj dkacindijsnjpasdfilksbdvsdovbadkhv zcasjkfasnj hasb fkasj asjks s jdasjaksdb fbashf asfasfaskbf as faskj bfkdsbfkasb f' | ||
); | ||
userEvent.tab(); | ||
|
||
const validationMessage = 'The Street address 1 only allows 100 characters'; | ||
|
||
await waitFor(() => { | ||
const validationError = queryByText(validationMessage); | ||
expect(validationError).toBeInTheDocument(); | ||
}); | ||
}); | ||
it('should validate address 2', async () => { | ||
const { getByLabelText, queryByText } = render(<Fixture />); | ||
|
||
const address = getByLabelText('Street address 2'); | ||
userEvent.clear(address); | ||
userEvent.paste( | ||
address, | ||
'hsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhjhsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhj dkacindijsnjpasdfilksbdvsdovbadkhv zcasjkfasnj hasb fkasj asjks s jdasjaksdb fbashf asfasfaskbf as faskj bfkdsbfkasb f' | ||
); | ||
userEvent.tab(); | ||
|
||
const validationMessage = 'The Street address 2 only allows 100 characters'; | ||
|
||
await waitFor(() => { | ||
const validationError = queryByText(validationMessage); | ||
expect(validationError).toBeInTheDocument(); | ||
}); | ||
}); | ||
it('should validate city', async () => { | ||
const { getByLabelText, queryByText } = render(<Fixture />); | ||
|
||
const city = getByLabelText('City'); | ||
userEvent.clear(city); | ||
userEvent.paste( | ||
city, | ||
'hsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhjhsdfjhsjfjshkfhskhfkhskjfhkjshfhsdskjhfjsdhfjshjfhjsdhfsdhdfjsdhfhjshjfsdhfsdfhjsfjhsjdfsfasdjhvmbsauhcjdbkashjiodjbkdsnachudihbjsnjacibhj dkacindijsnjpasdfilksbdvsdovbadkhv zcasjkfasnj hasb fkasj asjks s jdasjaksdb fbashf asfasfaskbf as faskj bfkdsbfkasb f' | ||
); | ||
userEvent.tab(); | ||
|
||
const validationMessage = 'The City only allows 100 characters'; | ||
|
||
await waitFor(() => { | ||
const validationError = queryByText(validationMessage); | ||
expect(validationError).toBeInTheDocument(); | ||
}); | ||
}); | ||
}); |
193 changes: 193 additions & 0 deletions
193
apps/modernization-ui/src/apps/patient/add/basic/address/BasicAddressFields.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { AddressSuggestion, AddressSuggestionInput } from 'address/suggestion'; | ||
import { validCensusTractRule, CensusTractInputField } from 'apps/patient/data/address'; | ||
import { Input } from 'components/FormInputs/Input'; | ||
import { EntryFieldsProps } from 'design-system/entry'; | ||
import { SingleSelect } from 'design-system/select'; | ||
import { validZipCodeRule, ZipCodeInputField } from 'libs/demographics/location'; | ||
import { useLocationCodedValues } from 'location'; | ||
import { Controller, useFormContext, useWatch } from 'react-hook-form'; | ||
import { maxLengthRule } from 'validation/entry'; | ||
import { BasicNewPatientEntry } from '../entry'; | ||
|
||
const STREET_ADDRESS_LABEL = 'Street address 1'; | ||
const STREET_ADDRESS_2_LABEL = 'Street address 2'; | ||
const CITY_LABEL = 'City'; | ||
const ZIP_LABEL = 'Zip'; | ||
const CENSUS_TRACT_LABEL = 'Census tract'; | ||
|
||
type AddressEntryFieldsProps = EntryFieldsProps; | ||
|
||
export const BasicAddressFields = ({ orientation = 'horizontal' }: AddressEntryFieldsProps) => { | ||
const { control, reset } = useFormContext<BasicNewPatientEntry>(); | ||
const location = useLocationCodedValues(); | ||
const selectedState = useWatch({ control, name: 'address.state' }); | ||
const enteredCity = useWatch({ control, name: 'address.city' }); | ||
const enteredZip = useWatch({ control, name: 'address.zipcode' }); | ||
const counties = location.counties.byState(selectedState?.value ?? ''); | ||
|
||
const handleSuggestionSelection = (selected: AddressSuggestion) => { | ||
reset( | ||
{ | ||
address: { | ||
address1: selected.address1, | ||
city: selected.city, | ||
state: selected.state ?? undefined, | ||
zipcode: selected.zip | ||
} | ||
}, | ||
{ keepDefaultValues: true } | ||
); | ||
}; | ||
|
||
return ( | ||
<section> | ||
<Controller | ||
control={control} | ||
name="address.address1" | ||
rules={maxLengthRule(100, STREET_ADDRESS_LABEL)} | ||
render={({ field: { onChange, onBlur, value, name }, fieldState: { error } }) => ( | ||
<AddressSuggestionInput | ||
label={STREET_ADDRESS_LABEL} | ||
orientation={orientation} | ||
sizing="compact" | ||
id={name} | ||
locations={location} | ||
criteria={{ | ||
city: enteredCity ?? undefined, | ||
state: selectedState?.value ?? undefined, | ||
zip: enteredZip ?? undefined | ||
}} | ||
defaultValue={value ?? ''} | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
onSelection={handleSuggestionSelection} | ||
error={error?.message} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.address2" | ||
rules={maxLengthRule(100, STREET_ADDRESS_2_LABEL)} | ||
render={({ field: { onChange, onBlur, value, name }, fieldState: { error } }) => ( | ||
<Input | ||
label={STREET_ADDRESS_2_LABEL} | ||
orientation={orientation} | ||
sizing="compact" | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
defaultValue={value} | ||
type="text" | ||
name={name} | ||
htmlFor={name} | ||
id={name} | ||
error={error?.message} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.city" | ||
rules={maxLengthRule(100, CITY_LABEL)} | ||
render={({ field: { onChange, onBlur, value, name }, fieldState: { error } }) => ( | ||
<Input | ||
label={CITY_LABEL} | ||
orientation={orientation} | ||
sizing="compact" | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
defaultValue={value} | ||
type="text" | ||
name={name} | ||
htmlFor={name} | ||
id={name} | ||
error={error?.message} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.state" | ||
render={({ field: { onChange, value, name } }) => ( | ||
<SingleSelect | ||
label="State" | ||
orientation={orientation} | ||
sizing="compact" | ||
value={value} | ||
onChange={onChange} | ||
id={name} | ||
name={name} | ||
options={location.states.all} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.zipcode" | ||
rules={validZipCodeRule(ZIP_LABEL)} | ||
render={({ field: { onChange, value, name, onBlur }, fieldState: { error } }) => ( | ||
<ZipCodeInputField | ||
id={name} | ||
label={ZIP_LABEL} | ||
value={value} | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
orientation={orientation} | ||
sizing="compact" | ||
error={error?.message} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.county" | ||
render={({ field: { onChange, value, name } }) => ( | ||
<SingleSelect | ||
label="County" | ||
orientation={orientation} | ||
sizing="compact" | ||
value={value} | ||
onChange={onChange} | ||
id={name} | ||
name={name} | ||
options={counties} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.censusTract" | ||
rules={validCensusTractRule(CENSUS_TRACT_LABEL)} | ||
render={({ field: { onChange, onBlur, value, name }, fieldState: { error } }) => ( | ||
<CensusTractInputField | ||
id={name} | ||
label={CENSUS_TRACT_LABEL} | ||
value={value} | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
orientation={orientation} | ||
sizing="compact" | ||
error={error?.message} | ||
/> | ||
)} | ||
/> | ||
<Controller | ||
control={control} | ||
name="address.country" | ||
render={({ field: { onChange, value, name } }) => ( | ||
<SingleSelect | ||
label="Country" | ||
orientation={orientation} | ||
sizing="compact" | ||
value={value} | ||
onChange={onChange} | ||
id={name} | ||
name={name} | ||
options={location.countries} | ||
autoComplete="off" | ||
/> | ||
)} | ||
/> | ||
</section> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.