diff --git a/app/javascript/packages/document-capture/components/full-address-search-input.tsx b/app/javascript/packages/address-search/components/full-address-search-input.tsx similarity index 96% rename from app/javascript/packages/document-capture/components/full-address-search-input.tsx rename to app/javascript/packages/address-search/components/full-address-search-input.tsx index e81c5f9f771..7d9561335c6 100644 --- a/app/javascript/packages/document-capture/components/full-address-search-input.tsx +++ b/app/javascript/packages/address-search/components/full-address-search-input.tsx @@ -5,8 +5,7 @@ import { useDidUpdateEffect } from '@18f/identity-react-hooks'; import { SpinnerButtonRefHandle, SpinnerButton } from '@18f/identity-spinner-button'; import { ValidatedField } from '@18f/identity-validated-field'; import { t } from '@18f/identity-i18n'; -import { useCallback, useContext, useEffect, useRef, useState } from 'react'; -import { InPersonContext } from '../context'; +import { useCallback, useEffect, useRef, useState } from 'react'; import useValidatedUspsLocations from '../hooks/use-validated-usps-locations'; interface FullAddressSearchProps { @@ -19,9 +18,11 @@ interface FullAddressSearchProps { onError?: (error: Error | null) => void; disabled?: boolean; locationsURL: string; + usStatesTerritories: [string, string][]; } export default function FullAddressSearchInput({ + usStatesTerritories, registerField = () => undefined, onFoundLocations = () => undefined, onLoadingLocations = () => undefined, @@ -81,8 +82,6 @@ export default function FullAddressSearchInput({ [addressValue, cityValue, stateValue, zipCodeValue], ); - const { usStatesTerritories } = useContext(InPersonContext); - return ( <> { const sandbox = useSandbox(); + const locationsURL = 'https://localhost:3000/locations/endpoint'; + const usStatesTerritories = [['Delware', 'DE']]; context('validates form', () => { it('displays an error for all required fields when input is empty', async () => { @@ -17,8 +18,9 @@ describe('FullAddressSearch', () => { const { findByText, findAllByText } = render( new Map() }}> { const { findByText, findByLabelText, findAllByText } = render( new Map() }}> { const { findByText, findByLabelText, queryByText } = render( new Map() }}> { let server: SetupServer; before(() => { server = setupServer( - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), ); server.listen(); }); @@ -128,8 +132,9 @@ describe('FullAddressSearch', () => { const { findByText, findByLabelText } = render( new Map() }}> {t('in_person_proofing.headings.po_search.location')}

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

{ + const locationsURL = 'https://localhost:3000/locations/endpoint'; let server: SetupServer; before(() => { @@ -44,13 +44,11 @@ describe('useValidatedUspsLocations', () => { beforeEach(() => { server.resetHandlers(); - server.use(rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json(USPS_RESPONSE)))); + server.use(rest.post(locationsURL, (_req, res, ctx) => res(ctx.json(USPS_RESPONSE)))); }); it('returns location results', async () => { - const { result, waitForNextUpdate } = renderHook(() => - useValidatedUspsLocations(LOCATIONS_URL), - ); + const { result, waitForNextUpdate } = renderHook(() => useValidatedUspsLocations(locationsURL)); const { handleLocationSearch } = result.current; handleLocationSearch(new Event('submit'), '200 main', 'Endeavor', 'DE', '12345'); diff --git a/app/javascript/packages/document-capture/hooks/use-validated-usps-locations.ts b/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts similarity index 100% rename from app/javascript/packages/document-capture/hooks/use-validated-usps-locations.ts rename to app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts diff --git a/app/javascript/packages/address-search/index.tsx b/app/javascript/packages/address-search/index.tsx index 4be29bd8694..e680b918900 100644 --- a/app/javascript/packages/address-search/index.tsx +++ b/app/javascript/packages/address-search/index.tsx @@ -1,6 +1,7 @@ import { snakeCase, formatLocations, transformKeys } from './utils'; import AddressInput from './components/address-input'; import AddressSearch from './components/address-search'; +import FullAddressSearch from './components/full-address-search'; import InPersonLocations from './components/in-person-locations'; import NoInPersonLocationsDisplay from './components/no-in-person-locations-display'; import { requestUspsLocations } from './hooks/use-usps-locations'; @@ -8,6 +9,7 @@ import { requestUspsLocations } from './hooks/use-usps-locations'; export { AddressInput, InPersonLocations, + FullAddressSearch, NoInPersonLocationsDisplay, formatLocations, snakeCase, diff --git a/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.spec.tsx index eb56ce29695..267bd2668dd 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.spec.tsx @@ -9,7 +9,6 @@ import { usePropertyValue } from '@18f/identity-test-helpers'; import { ComponentType } from 'react'; import { InPersonContext } from '../context'; import InPersonLocationFullAddressEntryPostOfficeSearchStep from './in-person-location-full-address-entry-post-office-search-step'; -import { LOCATIONS_URL } from './in-person-location-post-office-search-step'; const USPS_RESPONSE = [ { @@ -39,13 +38,25 @@ const USPS_RESPONSE = [ const DEFAULT_PROPS = { toPreviousStep() {}, onChange() {}, - value: {}, registerField() {}, }; describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { + const usStatesTerritories: [string, string][] = [['Delware', 'DE']]; + const locationsURL = 'https://localhost:3000/locations/endpoint'; const wrapper: ComponentType = ({ children }) => ( - new Map() }}>{children} + + new Map() }}>{children} + ); let server: SetupServer; @@ -62,23 +73,12 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { beforeEach(() => { server.resetHandlers(); // todo: should we return USPS_RESPONSE here? - server.use( - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), - ); + server.use(rest.post(locationsURL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }])))); }); it('renders the step', () => { const { getByRole } = render( - - , - , + , { wrapper }, ); @@ -87,21 +87,12 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { context('USPS request returns an error', () => { beforeEach(() => { - server.use(rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.status(500)))); + server.use(rest.post(locationsURL, (_req, res, ctx) => res(ctx.status(500)))); }); it('displays a try again error message', async () => { const { findByText, findByLabelText } = render( - - , - , + , { wrapper }, ); @@ -133,16 +124,7 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('displays validation error messages to the user if fields are empty', async () => { const { findAllByText, findByText } = render( - - , - , + , { wrapper }, ); @@ -156,16 +138,7 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('displays no post office results if a successful search is followed by an unsuccessful search', async () => { const { findByText, findByLabelText, queryByRole } = render( - - , - , + , { wrapper }, ); @@ -205,16 +178,7 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('clicking search again after first results do not clear results', async () => { const { findAllByText, findByText, findByLabelText } = render( - - , - , + , { wrapper }, ); @@ -253,16 +217,7 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('displays correct pluralization for a single location result', async () => { const { findByLabelText, findByText } = render( - - , - , + , { wrapper }, ); await userEvent.type( @@ -297,18 +252,9 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('displays correct pluralization for multiple location results', async () => { server.resetHandlers(); - server.use(rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json(USPS_RESPONSE)))); + server.use(rest.post(locationsURL, (_req, res, ctx) => res(ctx.json(USPS_RESPONSE)))); const { findByLabelText, findByText } = render( - - , - , + , { wrapper }, ); @@ -345,16 +291,7 @@ describe('InPersonLocationFullAddressEntryPostOfficeSearchStep', () => { it('allows user to select a location', async () => { const { findAllByText, findByLabelText, findByText, queryByText } = render( - - , - , + , { wrapper }, ); await userEvent.type( diff --git a/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.tsx b/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.tsx index 1ce97987a37..fca72bf0a9c 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-full-address-entry-post-office-search-step.tsx @@ -1,21 +1,19 @@ import { useState, useEffect, useCallback, useRef, useContext } from 'react'; import { request } from '@18f/identity-request'; import { forceRedirect } from '@18f/identity-url'; -import { transformKeys, snakeCase } from '@18f/identity-address-search'; +import { FullAddressSearch, transformKeys, snakeCase } from '@18f/identity-address-search'; import type { FormattedLocation } from '@18f/identity-address-search/types'; -import FullAddressSearch from './in-person-full-address-search'; import BackButton from './back-button'; import AnalyticsContext from '../context/analytics'; import { InPersonContext } from '../context'; import UploadContext from '../context/upload'; -import { LOCATIONS_URL } from './in-person-location-post-office-search-step'; function InPersonLocationFullAddressEntryPostOfficeSearchStep({ onChange, toPreviousStep, registerField, }) { - const { inPersonURL } = useContext(InPersonContext); + const { inPersonURL, locationsURL, usStatesTerritories } = useContext(InPersonContext); const [inProgress, setInProgress] = useState(false); const [autoSubmit, setAutoSubmit] = useState(false); const { trackEvent } = useContext(AnalyticsContext); @@ -60,7 +58,7 @@ function InPersonLocationFullAddressEntryPostOfficeSearchStep({ const selected = transformKeys(selectedLocation, snakeCase); setInProgress(true); try { - await request(LOCATIONS_URL, { + await request(locationsURL, { json: selected, method: 'PUT', }); @@ -96,8 +94,9 @@ function InPersonLocationFullAddressEntryPostOfficeSearchStep({ registerField={registerField} onFoundLocations={setLocationResults} disabled={disabledAddressSearch} - locationsURL={LOCATIONS_URL} + locationsURL={locationsURL} handleLocationSelect={handleLocationSelect} + usStatesTerritories={usStatesTerritories} /> diff --git a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx index e85f0d1575d..7be32c03b11 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx @@ -7,10 +7,8 @@ import { rest } from 'msw'; import type { SetupServer } from 'msw/node'; import { SWRConfig } from 'swr'; import { ComponentType } from 'react'; -import InPersonLocationPostOfficeSearchStep, { - ADDRESSES_URL, - LOCATIONS_URL, -} from './in-person-location-post-office-search-step'; +import { InPersonContext } from '../context'; +import InPersonLocationPostOfficeSearchStep from './in-person-location-post-office-search-step'; const DEFAULT_RESPONSE = [ { @@ -59,8 +57,22 @@ const DEFAULT_PROPS = { }; describe('InPersonLocationPostOfficeSearchStep', () => { + const usStatesTerritories: [string, string][] = [['Delware', 'DE']]; + const locationsURL = 'https://localhost:3000/locations/endpoint'; + const addressSearchURL = 'https://localhost:3000/addresses/endpoint'; const wrapper: ComponentType = ({ children }) => ( - new Map() }}>{children} + + new Map() }}>{children} + ); let server: SetupServer; @@ -80,7 +92,9 @@ describe('InPersonLocationPostOfficeSearchStep', () => { context('initial ArcGIS API request throws an error', () => { beforeEach(() => { - server.use(rest.post(ADDRESSES_URL, (_req, res, ctx) => res(ctx.json([]), ctx.status(422)))); + server.use( + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json([]), ctx.status(422))), + ); }); it('displays a try again error message', async () => { @@ -106,10 +120,10 @@ describe('InPersonLocationPostOfficeSearchStep', () => { context('initial USPS API request throws an error', () => { beforeEach(() => { server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE), ctx.status(200)), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.status(500))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.status(500))), ); }); @@ -136,10 +150,10 @@ describe('InPersonLocationPostOfficeSearchStep', () => { context('initial API request is successful', () => { beforeEach(() => { server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE), ctx.status(200)), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), ); }); @@ -255,10 +269,10 @@ describe('InPersonLocationPostOfficeSearchStep', () => { it('displays correct pluralization for multiple location results', async () => { server.resetHandlers(); server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE), ctx.status(200)), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json(MULTI_LOCATION_RESPONSE))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.json(MULTI_LOCATION_RESPONSE))), ); const { findByLabelText, findByText } = render( , @@ -285,10 +299,10 @@ describe('InPersonLocationPostOfficeSearchStep', () => { context('subsequent network failures clear results', () => { beforeEach(() => { server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE), ctx.status(200)), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), ); }); @@ -310,7 +324,7 @@ describe('InPersonLocationPostOfficeSearchStep', () => { expect(result).to.exist(); server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res( ctx.json([ { @@ -328,7 +342,7 @@ describe('InPersonLocationPostOfficeSearchStep', () => { ctx.status(200), ), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.status(500))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.status(500))), ); await userEvent.type( @@ -347,10 +361,10 @@ describe('InPersonLocationPostOfficeSearchStep', () => { context('user deletes text from searchbox after location results load', () => { beforeEach(() => { server.use( - rest.post(ADDRESSES_URL, (_req, res, ctx) => + rest.post(addressSearchURL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE), ctx.status(200)), ), - rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), + rest.post(locationsURL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))), ); }); diff --git a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx index 237003d2dac..44e45fea73d 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.tsx @@ -8,14 +8,8 @@ import AnalyticsContext from '../context/analytics'; import { InPersonContext } from '../context'; import UploadContext from '../context/upload'; -export const LOCATIONS_URL = new URL( - '/verify/in_person/usps_locations', - window.location.href, -).toString(); -export const ADDRESSES_URL = new URL('/api/addresses', window.location.href).toString(); - function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, registerField }) { - const { inPersonURL } = useContext(InPersonContext); + const { inPersonURL, locationsURL, addressSearchURL } = useContext(InPersonContext); const [inProgress, setInProgress] = useState(false); const [autoSubmit, setAutoSubmit] = useState(false); const { trackEvent } = useContext(AnalyticsContext); @@ -61,7 +55,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist const selected = transformKeys(selectedLocation, snakeCase); setInProgress(true); try { - await request(LOCATIONS_URL, { + await request(locationsURL, { json: selected, method: 'PUT', }); @@ -94,10 +88,10 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist return ( <> diff --git a/app/javascript/packages/document-capture/components/in-person-outage-alert.spec.tsx b/app/javascript/packages/document-capture/components/in-person-outage-alert.spec.tsx index f8440070c7a..65d4e7b955b 100644 --- a/app/javascript/packages/document-capture/components/in-person-outage-alert.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-outage-alert.spec.tsx @@ -8,6 +8,8 @@ describe('InPersonOutageAlert', () => { getByText = render( { const { queryByText } = render( { const { queryByText } = render( ({ + locationsURL: '', + addressSearchURL: '', inPersonOutageMessageEnabled: false, inPersonFullAddressEntryEnabled: false, usStatesTerritories: [], diff --git a/app/javascript/packs/document-capture.tsx b/app/javascript/packs/document-capture.tsx index 40d5a17fa5a..3448e037f5b 100644 --- a/app/javascript/packs/document-capture.tsx +++ b/app/javascript/packs/document-capture.tsx @@ -97,6 +97,8 @@ const App = composeComponents( { value: { inPersonURL, + locationsURL: new URL('/verify/in_person/usps_locations', window.location.href).toString(), + addressSearchURL: new URL('/api/addresses', window.location.href).toString(), inPersonOutageMessageEnabled: inPersonOutageMessageEnabled === 'true', inPersonOutageExpectedUpdateDate, inPersonFullAddressEntryEnabled: inPersonFullAddressEntryEnabled === 'true',