From d32eb9f286a07f931cb38f0a0ef58ca6331e87c5 Mon Sep 17 00:00:00 2001 From: gina-yamada <125507397+gina-yamada@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:19:05 -0600 Subject: [PATCH] LG-11082 - Add optional info alert to FullAddressSearch (#9276) * Add FullAddressSearchProps to types.d.ts * Rename interfact, alpha order args * Add resultsHeaderComponent arg, alpha order * fix linter errors * increase version of identity-address-search * clean up duplicate interface * update data type and tests * delete unused var * changelog: Upcoming Features, In-person proofing, Add new optional resultsHeaderComponent prop onto FullAddressSearch so it can be passed in from help center * change data type * Move FullAddressSearchInputProps to types.d * Added logic to show/hide header and info text * moved props back inside component * Add show/hide logic to AddressSearch * fix linter errors * change prop type * change data type * update test * lint fix * add tests for address-search * fix linter errors --- .../components/address-search.spec.tsx | 64 +++++++++++++++++ .../components/address-search.tsx | 8 ++- .../components/full-address-search-input.tsx | 24 +++---- .../components/full-address-search.spec.tsx | 70 ++++++++++++++++--- .../components/full-address-search.tsx | 23 +++--- .../components/in-person-locations.tsx | 10 +-- .../packages/address-search/package.json | 2 +- .../packages/address-search/types.d.ts | 19 ++++- 8 files changed, 177 insertions(+), 43 deletions(-) create mode 100644 app/javascript/packages/address-search/components/address-search.spec.tsx diff --git a/app/javascript/packages/address-search/components/address-search.spec.tsx b/app/javascript/packages/address-search/components/address-search.spec.tsx new file mode 100644 index 00000000000..2ea8c677245 --- /dev/null +++ b/app/javascript/packages/address-search/components/address-search.spec.tsx @@ -0,0 +1,64 @@ +import { render } from '@testing-library/react'; +import sinon from 'sinon'; +import { useSandbox } from '@18f/identity-test-helpers'; +import { SWRConfig } from 'swr'; +import AddressSearch from './address-search'; + +describe('AddressSearch', () => { + const sandbox = useSandbox(); + const locationsURL = 'https://localhost:3000/locations/endpoint'; + + context('Page Heading and PO Search About Message', () => { + it('both render when handleLocationSelect is not null', async () => { + const handleLocationsFound = sandbox.stub(); + const onSelect = sinon.stub(); + const { queryByText, queryByRole } = render( + new Map() }}> + undefined} + /> + , + ); + + const heading = await queryByText('in_person_proofing.headings.po_search.location'); + const aboutMessage = await queryByText( + 'in_person_proofing.body.location.po_search.po_search_about', + ); + + expect(heading).to.exist(); + expect(aboutMessage).to.exist(); + expect( + queryByRole('heading', { name: 'in_person_proofing.headings.po_search.location' }), + ).to.exist(); + }); + + it('both do not render when handleLocationSelect is null', async () => { + const handleLocationsFound = sandbox.stub(); + const onSelect = sinon.stub(); + const { queryByText } = render( + new Map() }}> + undefined} + /> + , + ); + + const heading = await queryByText('in_person_proofing.headings.po_search.location'); + const aboutMessage = await queryByText( + 'in_person_proofing.body.location.po_search.po_search_about', + ); + expect(heading).to.be.empty; + expect(aboutMessage).to.be.empty; + }); + }); +}); diff --git a/app/javascript/packages/address-search/components/address-search.tsx b/app/javascript/packages/address-search/components/address-search.tsx index e69cce8a817..3cd5f863ef3 100644 --- a/app/javascript/packages/address-search/components/address-search.tsx +++ b/app/javascript/packages/address-search/components/address-search.tsx @@ -30,8 +30,12 @@ function AddressSearch({ {t('idv.failure.exceptions.post_office_search_error')} )} - {t('in_person_proofing.headings.po_search.location')} -

{t('in_person_proofing.body.location.po_search.po_search_about')}

+ {handleLocationSelect && ( + <> + {t('in_person_proofing.headings.po_search.location')} +

{t('in_person_proofing.body.location.po_search.po_search_about')}

+ + )} void; onFoundLocations?: ( address: LocationQuery | null, locations: FormattedLocation[] | null | undefined, ) => void; onLoadingLocations?: (isLoading: boolean) => void; - onError?: (error: Error | null) => void; - disabled?: boolean; - locationsURL: string; - usStatesTerritories: [string, string][]; + registerField?: RegisterFieldCallback; + usStatesTerritories: string[][]; } export default function FullAddressSearchInput({ - usStatesTerritories, - registerField = () => undefined, - onFoundLocations = () => undefined, - onLoadingLocations = () => undefined, - onError = () => undefined, disabled = false, locationsURL, -}: FullAddressSearchProps) { + onError = () => undefined, + onFoundLocations = () => undefined, + onLoadingLocations = () => undefined, + registerField = () => undefined, + usStatesTerritories, +}: FullAddressSearchInputProps) { const spinnerButtonRef = useRef(null); const [addressValue, setAddressValue] = useState(''); const [cityValue, setCityValue] = useState(''); diff --git a/app/javascript/packages/address-search/components/full-address-search.spec.tsx b/app/javascript/packages/address-search/components/full-address-search.spec.tsx index 16dec24a275..f8a663bcb71 100644 --- a/app/javascript/packages/address-search/components/full-address-search.spec.tsx +++ b/app/javascript/packages/address-search/components/full-address-search.spec.tsx @@ -1,4 +1,5 @@ import { render } from '@testing-library/react'; +import sinon from 'sinon'; import { useSandbox } from '@18f/identity-test-helpers'; import userEvent from '@testing-library/user-event'; import { setupServer } from 'msw/node'; @@ -12,6 +13,59 @@ describe('FullAddressSearch', () => { const locationsURL = 'https://localhost:3000/locations/endpoint'; const usStatesTerritories = [['Delware', 'DE']]; + context('Page Heading and PO Search About Message', () => { + it('both render when handleLocationSelect is not null', async () => { + const handleLocationsFound = sandbox.stub(); + const onSelect = sinon.stub(); + const { queryByText, queryByRole } = render( + new Map() }}> + undefined} + handleLocationSelect={onSelect} + disabled={false} + /> + , + ); + + const heading = await queryByText('in_person_proofing.headings.po_search.location'); + const aboutMessage = await queryByText( + 'in_person_proofing.body.location.po_search.po_search_about', + ); + + expect(heading).to.exist(); + expect(aboutMessage).to.exist(); + expect( + queryByRole('heading', { name: 'in_person_proofing.headings.po_search.location' }), + ).to.exist(); + }); + + it('both do not render when handleLocationSelect is null', async () => { + const handleLocationsFound = sandbox.stub(); + const { queryByText } = render( + new Map() }}> + undefined} + handleLocationSelect={null} + disabled={false} + /> + , + ); + + const heading = await queryByText('in_person_proofing.headings.po_search.location'); + const aboutMessage = await queryByText( + 'in_person_proofing.body.location.po_search.po_search_about', + ); + expect(heading).to.be.empty; + expect(aboutMessage).to.be.empty; + }); + }); + context('validates form', () => { it('displays an error for all required fields when input is empty', async () => { const handleLocationsFound = sandbox.stub(); @@ -21,9 +75,9 @@ describe('FullAddressSearch', () => { usStatesTerritories={usStatesTerritories} onFoundLocations={handleLocationsFound} locationsURL={locationsURL} - registerField={undefined} + registerField={() => undefined} handleLocationSelect={undefined} - disabled={undefined} + disabled={false} /> , ); @@ -44,9 +98,9 @@ describe('FullAddressSearch', () => { usStatesTerritories={usStatesTerritories} onFoundLocations={handleLocationsFound} locationsURL={locationsURL} - registerField={undefined} + registerField={() => undefined} handleLocationSelect={undefined} - disabled={undefined} + disabled={false} /> , ); @@ -83,9 +137,9 @@ describe('FullAddressSearch', () => { usStatesTerritories={usStatesTerritories} onFoundLocations={handleLocationsFound} locationsURL={locationsURL} - registerField={undefined} + registerField={() => undefined} handleLocationSelect={undefined} - disabled={undefined} + disabled={false} /> , ); @@ -135,9 +189,9 @@ describe('FullAddressSearch', () => { usStatesTerritories={usStatesTerritories} onFoundLocations={handleLocationsFound} locationsURL={locationsURL} - registerField={undefined} + registerField={() => undefined} handleLocationSelect={undefined} - disabled={undefined} + disabled={false} /> , ); diff --git a/app/javascript/packages/address-search/components/full-address-search.tsx b/app/javascript/packages/address-search/components/full-address-search.tsx index 45885824cca..f21f05fcd80 100644 --- a/app/javascript/packages/address-search/components/full-address-search.tsx +++ b/app/javascript/packages/address-search/components/full-address-search.tsx @@ -4,16 +4,18 @@ import { t } from '@18f/identity-i18n'; import { InPersonLocations, NoInPersonLocationsDisplay } from '@18f/identity-address-search'; import type { LocationQuery, FormattedLocation } from '@18f/identity-address-search/types'; import FullAddressSearchInput from './full-address-search-input'; +import type { FullAddressSearchProps } from '../types'; function FullAddressSearch({ - usStatesTerritories, - registerField, - locationsURL, - handleLocationSelect, disabled, - onFoundLocations, + handleLocationSelect, + locationsURL, noInPersonLocationsDisplay = NoInPersonLocationsDisplay, -}) { + onFoundLocations, + registerField, + resultsHeaderComponent, + usStatesTerritories, +}: FullAddressSearchProps) { const [apiError, setApiError] = useState(null); const [foundAddress, setFoundAddress] = useState(null); const [locationResults, setLocationResults] = useState( @@ -28,8 +30,12 @@ function FullAddressSearch({ {t('idv.failure.exceptions.post_office_search_error')} )} - {t('in_person_proofing.headings.po_search.location')} -

{t('in_person_proofing.body.location.po_search.po_search_about')}

+ {handleLocationSelect && ( + <> + {t('in_person_proofing.headings.po_search.location')} +

{t('in_person_proofing.body.location.po_search.po_search_about')}

+ + )} )} diff --git a/app/javascript/packages/address-search/components/in-person-locations.tsx b/app/javascript/packages/address-search/components/in-person-locations.tsx index 8873e76f134..7b446bb2def 100644 --- a/app/javascript/packages/address-search/components/in-person-locations.tsx +++ b/app/javascript/packages/address-search/components/in-person-locations.tsx @@ -1,7 +1,7 @@ -import { ComponentType } from 'react'; import { t } from '@18f/identity-i18n'; import LocationCollection from './location-collection'; import LocationCollectionItem from './location-collection-item'; +import type { InPersonLocationsProps } from '../types'; export interface FormattedLocation { formattedCityStateZip: string; @@ -15,14 +15,6 @@ export interface FormattedLocation { isPilot: boolean; } -interface InPersonLocationsProps { - locations: FormattedLocation[] | null | undefined; - onSelect; - address: string; - noInPersonLocationsDisplay: ComponentType<{ address: string }>; - resultsHeaderComponent?: ComponentType; -} - function InPersonLocations({ locations, onSelect, diff --git a/app/javascript/packages/address-search/package.json b/app/javascript/packages/address-search/package.json index 3057582c13c..79ed5c5289a 100644 --- a/app/javascript/packages/address-search/package.json +++ b/app/javascript/packages/address-search/package.json @@ -1,6 +1,6 @@ { "name": "@18f/identity-address-search", - "version": "3.0.0", + "version": "3.1.0", "type": "module", "private": false, "files": [ diff --git a/app/javascript/packages/address-search/types.d.ts b/app/javascript/packages/address-search/types.d.ts index 9656c055113..86455ca4f6f 100644 --- a/app/javascript/packages/address-search/types.d.ts +++ b/app/javascript/packages/address-search/types.d.ts @@ -58,17 +58,19 @@ interface AddressSearchProps { addressSearchURL: string; disabled: boolean; handleLocationSelect: ((e: any, id: number) => Promise) | null | undefined; - noInPersonLocationsDisplay?: ComponentType<{ address: string }>; - resultsHeaderComponent?: ComponentType; locationsURL: string; + noInPersonLocationsDisplay?: ComponentType<{ address: string }>; onFoundLocations: Dispatch>; registerField: RegisterFieldCallback; + resultsHeaderComponent?: ComponentType; } interface InPersonLocationsProps { + address: string; locations: FormattedLocation[] | null | undefined; + noInPersonLocationsDisplay: ComponentType<{ address: string }>; onSelect; - address: string; + resultsHeaderComponent?: ComponentType; } interface LocationCollectionItemProps { @@ -88,3 +90,14 @@ interface LocationCollectionProps { children?: ReactNode; } + +interface FullAddressSearchProps { + disabled: boolean; + handleLocationSelect: ((e: any, id: number) => Promise) | null | undefined; + locationsURL: string; + noInPersonLocationsDisplay?: ComponentType<{ address: string }>; + onFoundLocations: Dispatch>; + registerField: RegisterFieldCallback; + resultsHeaderComponent?: ComponentType; + usStatesTerritories: string[][]; +}