From c2f51c43615db0b1439feb4a6c4ce39ce1045234 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Fri, 9 Dec 2022 16:43:20 -0500 Subject: [PATCH 01/15] Permit generic type for request library --- app/javascript/packages/request/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/javascript/packages/request/index.ts b/app/javascript/packages/request/index.ts index 411c08f5ad5..b7854df46cb 100644 --- a/app/javascript/packages/request/index.ts +++ b/app/javascript/packages/request/index.ts @@ -15,7 +15,10 @@ interface RequestOptions extends RequestInit { const getCSRFToken = () => document.querySelector('meta[name="csrf-token"]')?.content; -export const request = async (url: string, options: Partial = {}) => { +export async function request( + url: string, + options: Partial = {}, +): Promise { const { csrf = true, json = true, ...fetchOptions } = options; let { body, headers } = fetchOptions; headers = new Headers(headers); @@ -39,4 +42,4 @@ export const request = async (url: string, options: Partial = {} const response = await window.fetch(url, { ...fetchOptions, headers, body }); return json ? response.json() : response.text(); -}; +} From 956b04199a6657995811775aa73b6fc18dec18e0 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Fri, 9 Dec 2022 16:44:05 -0500 Subject: [PATCH 02/15] Install SWR --- .../packages/document-capture/package.json | 3 ++- yarn.lock | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/javascript/packages/document-capture/package.json b/app/javascript/packages/document-capture/package.json index 5beb2ac765b..76877772573 100644 --- a/app/javascript/packages/document-capture/package.json +++ b/app/javascript/packages/document-capture/package.json @@ -4,6 +4,7 @@ "version": "1.0.0", "dependencies": { "react": "^17.0.2", - "react-dom": "^17.0.2" + "react-dom": "^17.0.2", + "swr": "^2.0.0" } } diff --git a/yarn.lock b/yarn.lock index a63f6e0c83b..5f07fe15ba0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5995,6 +5995,13 @@ svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" +swr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.0.0.tgz#91d999359e2be92de1a41f6b6711d72be20ffdbd" + integrity sha512-IhUx5yPkX+Fut3h0SqZycnaNLXLXsb2ECFq0Y29cxnK7d8r7auY2JWNbCW3IX+EqXUg3rwNJFlhrw5Ye/b6k7w== + dependencies: + use-sync-external-store "^1.2.0" + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6227,6 +6234,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + uswds@^2.13.3: version "2.13.3" resolved "https://registry.yarnpkg.com/uswds/-/uswds-2.13.3.tgz#f2a0623b496941ff30ad3a0ea1610407d35a6b14" From dde0cb5e6780e305bfa79d94ff82795ac0550357 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Fri, 9 Dec 2022 16:51:59 -0500 Subject: [PATCH 03/15] Refactor: use SWR; peel LocationResults into new component; --- .../components/address-search.tsx | 6 +- ...erson-location-post-office-search-step.tsx | 103 +++++------------- .../components/in-person-locations.tsx | 47 ++++++++ 3 files changed, 82 insertions(+), 74 deletions(-) create mode 100644 app/javascript/packages/document-capture/components/in-person-locations.tsx diff --git a/app/javascript/packages/document-capture/components/address-search.tsx b/app/javascript/packages/document-capture/components/address-search.tsx index 8a83acc45f3..828469933bb 100644 --- a/app/javascript/packages/document-capture/components/address-search.tsx +++ b/app/javascript/packages/document-capture/components/address-search.tsx @@ -3,6 +3,7 @@ import { request } from '@18f/identity-request'; import { useState, useCallback, ChangeEvent, useRef, Ref } from 'react'; import { useI18n } from '@18f/identity-react-i18n'; import ValidatedField from '@18f/identity-validated-field/validated-field'; +import { SpinnerButton } from '@18f/identity-spinner-button'; interface Location { street_address: string; @@ -68,7 +69,10 @@ function AddressSearch({ onAddressFound = () => {}, registerField }: AddressSear - <>{addressQuery.address} + <> +
+ {addressQuery.address} + ); } 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 03a6d68a676..9ff95949082 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 @@ -1,13 +1,13 @@ import { useState, useEffect, useCallback, useRef, useContext } from 'react'; import { useI18n } from '@18f/identity-react-i18n'; -import { PageHeading, SpinnerDots } from '@18f/identity-components'; +import { PageHeading } from '@18f/identity-components'; import { request } from '@18f/identity-request'; +import useSWR from 'swr'; import BackButton from './back-button'; -import LocationCollection from './location-collection'; -import LocationCollectionItem from './location-collection-item'; import AnalyticsContext from '../context/analytics'; import AddressSearch from './address-search'; import InPersonContext from '../context/in-person'; +import InPersonLocations, { FormattedLocation } from './in-person-locations'; interface PostOffice { address: string; @@ -22,16 +22,6 @@ interface PostOffice { zip_code_5: string; } -interface FormattedLocation { - formattedCityStateZip: string; - id: number; - name: string; - phone: string; - saturdayHours: string; - streetAddress: string; - sundayHours: string; - weekdayHours: string; -} interface LocationQuery { streetAddress: string; city: string; @@ -41,11 +31,11 @@ interface LocationQuery { export const LOCATIONS_URL = '/verify/in_person/usps_locations'; -const getUspsLocations = (address) => - request(LOCATIONS_URL, { - method: 'post', - json: { address }, - }); +// const getUspsLocations = (address) => +// request(LOCATIONS_URL, { +// method: 'post', +// json: { address }, +// }); const formatLocation = (postOffices: PostOffice[]) => { const formattedLocations = [] as FormattedLocation[]; @@ -72,23 +62,36 @@ const snakeCase = (value: string) => .toLowerCase(); // snake case the keys of the location -const prepToSend = (location: object) => { +const transformKeys = (location: object, predicate = snakeCase) => { const sendObject = {}; Object.keys(location).forEach((key) => { - sendObject[snakeCase(key)] = location[key]; + sendObject[predicate(key)] = location[key]; }); return sendObject; }; +const requestUspsLocations = async (address: LocationQuery): Promise => { + const response = await request(LOCATIONS_URL, { + method: 'post', + json: { address: transformKeys(address) }, + }); + + return formatLocation(response); +}; + function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, registerField }) { const { t } = useI18n(); - const [locationData, setLocationData] = useState([] as FormattedLocation[]); - const [foundAddress, setFoundAddress] = useState({} as LocationQuery); + // const [locationData, setLocationData] = useState([] as FormattedLocation[]); + const [foundAddress, setFoundAddress] = useState(null); const [inProgress, setInProgress] = useState(false); const [autoSubmit, setAutoSubmit] = useState(false); - const [isLoadingComplete, setIsLoadingComplete] = useState(false); + // const [isLoadingComplete, setIsLoadingComplete] = useState(false); const { setSubmitEventMetadata } = useContext(AnalyticsContext); const { arcgisSearchEnabled } = useContext(InPersonContext); + const { data: locationResults, isLoading } = useSWR( + [LOCATIONS_URL, foundAddress], + ([, address]) => (address ? requestUspsLocations(address) : null), + ); // ref allows us to avoid a memory leak const mountedRef = useRef(false); @@ -103,7 +106,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist // useCallBack here prevents unnecessary rerenders due to changing function identity const handleLocationSelect = useCallback( async (e: any, id: number) => { - const selectedLocation = locationData[id]; + const selectedLocation = locationResults![id]!; const { name: selectedLocationName } = selectedLocation; setSubmitEventMetadata({ selected_location: selectedLocationName }); onChange({ selectedLocationName }); @@ -115,7 +118,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist if (inProgress) { return; } - const selected = prepToSend(selectedLocation); + const selected = transformKeys(selectedLocation); setInProgress(true); await request(LOCATIONS_URL, { json: selected, @@ -140,7 +143,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist setInProgress(false); }); }, - [locationData, inProgress], + [locationResults, inProgress], ); const handleFoundAddress = useCallback((address) => { @@ -152,52 +155,6 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist }); }, []); - useEffect(() => { - let didCancel = false; - (async () => { - try { - const fetchedLocations = await getUspsLocations(prepToSend(foundAddress)); - - if (!didCancel) { - const formattedLocations = formatLocation(fetchedLocations); - setLocationData(formattedLocations); - } - } finally { - if (!didCancel) { - setIsLoadingComplete(true); - } - } - })(); - return () => { - didCancel = true; - }; - }, [foundAddress]); - - let locationsContent: React.ReactNode; - if (!isLoadingComplete) { - locationsContent = ; - } else if (locationData.length < 1) { - locationsContent =

{t('in_person_proofing.body.location.none_found')}

; - } else { - locationsContent = ( - - {locationData.map((item, index) => ( - - ))} - - ); - } - return ( <> {t('in_person_proofing.headings.po_search.location')} @@ -206,7 +163,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist )}

{t('in_person_proofing.body.location.location_step_about')}

- {locationsContent} + ); diff --git a/app/javascript/packages/document-capture/components/in-person-locations.tsx b/app/javascript/packages/document-capture/components/in-person-locations.tsx new file mode 100644 index 00000000000..490e1c81c8b --- /dev/null +++ b/app/javascript/packages/document-capture/components/in-person-locations.tsx @@ -0,0 +1,47 @@ +import { useI18n } from '@18f/identity-react-i18n'; +import LocationCollection from './location-collection'; +import LocationCollectionItem from './location-collection-item'; + +export interface FormattedLocation { + formattedCityStateZip: string; + id: number; + name: string; + phone: string; + saturdayHours: string; + streetAddress: string; + sundayHours: string; + weekdayHours: string; +} + +interface InPersonLocationsProps { + locations: FormattedLocation[] | null | undefined; + didSelect; +} + +function InPersonLocations({ locations, didSelect }: InPersonLocationsProps) { + const { t } = useI18n(); + + if (locations?.length === 0) { + return

{t('in_person_proofing.body.location.none_found')}

; + } + + return ( + + {(locations || []).map((item, index) => ( + + ))} + + ); +} + +export default InPersonLocations; From 395d797bd964830e1fc5935b58582242dd02fe9d Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Tue, 13 Dec 2022 12:52:24 -0500 Subject: [PATCH 04/15] Exclude SWR from ie11 check --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index fafdea71132..5e67ce3186a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,7 +56,7 @@ module.exports = /** @type {import('webpack').Configuration} */ ({ { test: /\.[jt]sx?$/, exclude: - /node_modules\/(?!@18f\/identity-|identity-style-guide|uswds|receptor|elem-dataset)/, + /node_modules\/(?!@18f\/identity-|identity-style-guide|uswds|receptor|elem-dataset|swr)/, use: { loader: 'babel-loader', }, From e27c30ac5f3bb89b418f66cc03e39690a7bb6b39 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 13 Dec 2022 15:15:36 -0500 Subject: [PATCH 05/15] Consider ESM file extensions in Webpack file handling --- webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 5e67ce3186a..265090dfccc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -43,7 +43,7 @@ module.exports = /** @type {import('webpack').Configuration} */ ({ devServerPort && isLocalhost ? `http://localhost:${devServerPort}/packs/` : '/packs/', }, resolve: { - extensions: ['.js', '.jsx', '.ts', '.tsx'], + extensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'], }, module: { rules: [ @@ -54,7 +54,7 @@ module.exports = /** @type {import('webpack').Configuration} */ ({ use: ['source-map-loader'], }, { - test: /\.[jt]sx?$/, + test: /\.[cm]?[jt]sx?$/, exclude: /node_modules\/(?!@18f\/identity-|identity-style-guide|uswds|receptor|elem-dataset|swr)/, use: { From af80882f0ec61bd4bc9a4b323009336873a4c39c Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Tue, 13 Dec 2022 19:55:18 -0500 Subject: [PATCH 06/15] Clear comments and unused variables --- .../components/address-search.tsx | 1 - ...-person-location-post-office-search-step.tsx | 17 +++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/app/javascript/packages/document-capture/components/address-search.tsx b/app/javascript/packages/document-capture/components/address-search.tsx index 828469933bb..b4c6905cbb4 100644 --- a/app/javascript/packages/document-capture/components/address-search.tsx +++ b/app/javascript/packages/document-capture/components/address-search.tsx @@ -3,7 +3,6 @@ import { request } from '@18f/identity-request'; import { useState, useCallback, ChangeEvent, useRef, Ref } from 'react'; import { useI18n } from '@18f/identity-react-i18n'; import ValidatedField from '@18f/identity-validated-field/validated-field'; -import { SpinnerButton } from '@18f/identity-spinner-button'; interface Location { street_address: string; 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 9ff95949082..5ff5c261be8 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 @@ -31,12 +31,6 @@ interface LocationQuery { export const LOCATIONS_URL = '/verify/in_person/usps_locations'; -// const getUspsLocations = (address) => -// request(LOCATIONS_URL, { -// method: 'post', -// json: { address }, -// }); - const formatLocation = (postOffices: PostOffice[]) => { const formattedLocations = [] as FormattedLocation[]; postOffices.forEach((po: PostOffice, index) => { @@ -81,16 +75,13 @@ const requestUspsLocations = async (address: LocationQuery): Promise(null); const [inProgress, setInProgress] = useState(false); const [autoSubmit, setAutoSubmit] = useState(false); - // const [isLoadingComplete, setIsLoadingComplete] = useState(false); const { setSubmitEventMetadata } = useContext(AnalyticsContext); const { arcgisSearchEnabled } = useContext(InPersonContext); - const { data: locationResults, isLoading } = useSWR( - [LOCATIONS_URL, foundAddress], - ([, address]) => (address ? requestUspsLocations(address) : null), + const { data: locationResults } = useSWR([LOCATIONS_URL, foundAddress], ([, address]) => + address ? requestUspsLocations(address) : null, ); // ref allows us to avoid a memory leak @@ -159,9 +150,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist <> {t('in_person_proofing.headings.po_search.location')}

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

- {arcgisSearchEnabled && ( - - )} +

{t('in_person_proofing.body.location.location_step_about')}

From 0565404d47d4aeed1b34cb859e92643f0198c596 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Tue, 13 Dec 2022 20:00:45 -0500 Subject: [PATCH 07/15] Specify type for request now --- .../packages/document-capture/components/address-search.tsx | 2 +- .../document-capture/components/in-person-location-step.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/packages/document-capture/components/address-search.tsx b/app/javascript/packages/document-capture/components/address-search.tsx index b4c6905cbb4..fdaf878fc4b 100644 --- a/app/javascript/packages/document-capture/components/address-search.tsx +++ b/app/javascript/packages/document-capture/components/address-search.tsx @@ -32,7 +32,7 @@ function AddressSearch({ onAddressFound = () => {}, registerField }: AddressSear if (unvalidatedAddressInput === '') { return; } - const addressCandidates = await request(ADDRESS_SEARCH_URL, { + const addressCandidates = await request(ADDRESS_SEARCH_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, json: { address: unvalidatedAddressInput }, diff --git a/app/javascript/packages/document-capture/components/in-person-location-step.tsx b/app/javascript/packages/document-capture/components/in-person-location-step.tsx index 23cd4647ade..be7a744126b 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-step.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-step.tsx @@ -40,7 +40,7 @@ interface LocationQuery { export const LOCATIONS_URL = '/verify/in_person/usps_locations'; const getUspsLocations = (address) => - request(LOCATIONS_URL, { + request(LOCATIONS_URL, { method: 'post', json: { address }, }); From 745ee809be30f9d5ff64f2633ee4403f55b8ad54 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 10:28:21 -0500 Subject: [PATCH 08/15] Add remaining translations; get full address string from arcgis; --- .../components/address-search.tsx | 4 -- ...erson-location-post-office-search-step.tsx | 14 ++++-- .../components/in-person-locations.tsx | 49 ++++++++++++------- config/locales/in_person_proofing/en.yml | 4 ++ config/locales/in_person_proofing/es.yml | 4 ++ config/locales/in_person_proofing/fr.yml | 6 ++- 6 files changed, 55 insertions(+), 26 deletions(-) diff --git a/app/javascript/packages/document-capture/components/address-search.tsx b/app/javascript/packages/document-capture/components/address-search.tsx index fdaf878fc4b..25cc1d1c8e7 100644 --- a/app/javascript/packages/document-capture/components/address-search.tsx +++ b/app/javascript/packages/document-capture/components/address-search.tsx @@ -68,10 +68,6 @@ function AddressSearch({ onAddressFound = () => {}, registerField }: AddressSear - <> -
- {addressQuery.address} - ); } 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 5ff5c261be8..01d239bd37f 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 @@ -6,7 +6,6 @@ import useSWR from 'swr'; import BackButton from './back-button'; import AnalyticsContext from '../context/analytics'; import AddressSearch from './address-search'; -import InPersonContext from '../context/in-person'; import InPersonLocations, { FormattedLocation } from './in-person-locations'; interface PostOffice { @@ -27,6 +26,7 @@ interface LocationQuery { city: string; state: string; zipCode: string; + address: string; } export const LOCATIONS_URL = '/verify/in_person/usps_locations'; @@ -79,7 +79,6 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist const [inProgress, setInProgress] = useState(false); const [autoSubmit, setAutoSubmit] = useState(false); const { setSubmitEventMetadata } = useContext(AnalyticsContext); - const { arcgisSearchEnabled } = useContext(InPersonContext); const { data: locationResults } = useSWR([LOCATIONS_URL, foundAddress], ([, address]) => address ? requestUspsLocations(address) : null, ); @@ -143,6 +142,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist city: address.city, state: address.state, zipCode: address.zip_code, + address: address.address, }); }, []); @@ -151,8 +151,14 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist {t('in_person_proofing.headings.po_search.location')}

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

-

{t('in_person_proofing.body.location.location_step_about')}

- + {locationResults && ( + + )} +
); diff --git a/app/javascript/packages/document-capture/components/in-person-locations.tsx b/app/javascript/packages/document-capture/components/in-person-locations.tsx index 490e1c81c8b..3d7ff79c25b 100644 --- a/app/javascript/packages/document-capture/components/in-person-locations.tsx +++ b/app/javascript/packages/document-capture/components/in-person-locations.tsx @@ -16,31 +16,46 @@ export interface FormattedLocation { interface InPersonLocationsProps { locations: FormattedLocation[] | null | undefined; didSelect; + address: string; } -function InPersonLocations({ locations, didSelect }: InPersonLocationsProps) { +function InPersonLocations({ locations, didSelect, address }: InPersonLocationsProps) { const { t } = useI18n(); if (locations?.length === 0) { - return

{t('in_person_proofing.body.location.none_found')}

; + return ( + <> +

{t('in_person_proofing.body.location.po_search.none_found', { address })}

+

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

+ + ); } return ( - - {(locations || []).map((item, index) => ( - - ))} - + <> +

+ {t('in_person_proofing.body.location.po_search.results_description', { + address, + count: locations?.length, + })} +

+

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

+ + {(locations || []).map((item, index) => ( + + ))} + + ); } diff --git a/config/locales/in_person_proofing/en.yml b/config/locales/in_person_proofing/en.yml index 2d9a904db10..dca42cc4111 100644 --- a/config/locales/in_person_proofing/en.yml +++ b/config/locales/in_person_proofing/en.yml @@ -38,6 +38,10 @@ en: po_search_about: If you are having trouble adding your ID, you may be able to verify your identity in person at a local United States Post Office. search_button: Search + none_found: Sorry, there are no participating Post Offices within 50 miles of %{address}. + none_found_tip: You can search using a different address, or add photos of your ID to try and verify your identity online again. + results_description: There are %{count} participating Post Offices within 50 miles of %{address}. + results_instructions: Select a Post Office location below, or search again using a different address. For facility accessibility, use the contact information listed for the Post Office location. post_office: Post Office ™ retail_hours_heading: Retail Hours retail_hours_sat: 'Sat:' diff --git a/config/locales/in_person_proofing/es.yml b/config/locales/in_person_proofing/es.yml index c256ea2d705..bac25a834e5 100644 --- a/config/locales/in_person_proofing/es.yml +++ b/config/locales/in_person_proofing/es.yml @@ -44,6 +44,10 @@ es: posible que pueda verificar su identidad en persona en una oficina de correos local de los Estados Unidos. search_button: Búsqueda + none_found: Lo sentimos, no hay Oficinas de Correos participantes en un radio de 50 millas de la %{address}. + none_found_tip: Puede buscar utilizando una dirección diferente, o añadir fotos de su documento de identidad para intentar verificar su identidad en línea de nuevo. + results_description: Hay %{count} de oficinas de correos participantes en un radio de 50 millas de la %{address}. + results_instructions: Seleccione una ubicación de la Oficina de Correos a continuación, o busque de nuevo utilizando una dirección diferente. Para la accesibilidad de las instalaciones, utilice la información de contacto que aparece para la ubicación de la Oficina de Correos. post_office: Oficina de Correos retail_hours_heading: Horario de atención al público retail_hours_sat: 'Sáb:' diff --git a/config/locales/in_person_proofing/fr.yml b/config/locales/in_person_proofing/fr.yml index ee2c9fc103f..cb74741183a 100644 --- a/config/locales/in_person_proofing/fr.yml +++ b/config/locales/in_person_proofing/fr.yml @@ -43,7 +43,11 @@ fr: po_search_about: Si vous avez des difficultés à ajouter votre pièce d’identité, vous pouvez vérifier votre identité en personne dans un bureau de poste américain proche. - search_button: Chercher + search_button: Rechercher + none_found: + none_found_tip: Vous pouvez effectuer une recherche en utilisant une autre adresse, ou ajouter des photos de votre pièce d’identité pour essayer de vérifier à nouveau votre identité en ligne. + results_description: Il y a %{count} de bureaux de poste participants dans un rayon de 80 km autour de %{address}. + results_instructions: Sélectionnez un emplacement de bureau de poste ci-dessous, ou effectuez une nouvelle recherche en utilisant une autre adresse. Pour l’accessibilité des installations, utilisez les informations de contact indiquées pour l’emplacement du bureau de poste. post_office: Bureau de Poste retail_hours_heading: Heures de vente au détail retail_hours_sat: 'Sam:' From c0485eda6e6860885d5f19d02527207a97d5bc0f Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 10:31:38 -0500 Subject: [PATCH 09/15] changelog: Upcoming Features, In-Person Proofing, use SWR and refactor; prevent auto load From c751036a3d06d94e8f3d4ad94ba094e92996dfa1 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 10:36:36 -0500 Subject: [PATCH 10/15] Lint --- .../components/address-search.tsx | 2 -- .../components/in-person-location-step.spec.tsx | 4 ++-- config/locales/in_person_proofing/en.yml | 13 +++++++++---- config/locales/in_person_proofing/es.yml | 15 +++++++++++---- config/locales/in_person_proofing/fr.yml | 14 ++++++++++---- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/javascript/packages/document-capture/components/address-search.tsx b/app/javascript/packages/document-capture/components/address-search.tsx index 25cc1d1c8e7..92c80e4904b 100644 --- a/app/javascript/packages/document-capture/components/address-search.tsx +++ b/app/javascript/packages/document-capture/components/address-search.tsx @@ -22,7 +22,6 @@ export const ADDRESS_SEARCH_URL = '/api/addresses'; function AddressSearch({ onAddressFound = () => {}, registerField }: AddressSearchProps) { const validatedFieldRef = useRef(null); const [unvalidatedAddressInput, setUnvalidatedAddressInput] = useState(''); - const [addressQuery, setAddressQuery] = useState({} as Location); const { t } = useI18n(); const handleAddressSearch = useCallback( @@ -39,7 +38,6 @@ function AddressSearch({ onAddressFound = () => {}, registerField }: AddressSear }); const bestMatchedAddress = addressCandidates[0]; - setAddressQuery(bestMatchedAddress); onAddressFound(bestMatchedAddress); }, [unvalidatedAddressInput], diff --git a/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx index 5933730fb6c..a603e2ed93d 100644 --- a/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx +++ b/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx @@ -77,11 +77,11 @@ describe('InPersonLocationStep', () => { await userEvent.click( await findByText('in_person_proofing.body.location.po_search.search_button'), ); - await findByText('100 Main St, South Fulton, Tennessee, 38257'); + await findByText('in_person_proofing.body.location.po_search.results_description'); expect(window.fetch).to.have.been.calledWith( LOCATIONS_URL, sandbox.match({ - body: '{"address":{"street_address":"100 Main St","city":"South Fulton","state":"TN","zip_code":"38257"}}', + body: '{"address":{"street_address":"100 Main St","city":"South Fulton","state":"TN","zip_code":"38257","address":"100 Main St, South Fulton, Tennessee, 38257"}}', method: 'post', }), ); diff --git a/config/locales/in_person_proofing/en.yml b/config/locales/in_person_proofing/en.yml index dca42cc4111..6ffe01e42f2 100644 --- a/config/locales/in_person_proofing/en.yml +++ b/config/locales/in_person_proofing/en.yml @@ -35,13 +35,18 @@ en: po_search: address_search_hint: 'Example: 1960 W Chelsea Ave Allentown PA 18104' address_search_label: Enter an address to find a Post Office near you + none_found: Sorry, there are no participating Post Offices within 50 miles of + %{address}. + none_found_tip: You can search using a different address, or add photos of your + ID to try and verify your identity online again. po_search_about: If you are having trouble adding your ID, you may be able to verify your identity in person at a local United States Post Office. + results_description: There are %{count} participating Post Offices within 50 + miles of %{address}. + results_instructions: Select a Post Office location below, or search again using + a different address. For facility accessibility, use the contact + information listed for the Post Office location. search_button: Search - none_found: Sorry, there are no participating Post Offices within 50 miles of %{address}. - none_found_tip: You can search using a different address, or add photos of your ID to try and verify your identity online again. - results_description: There are %{count} participating Post Offices within 50 miles of %{address}. - results_instructions: Select a Post Office location below, or search again using a different address. For facility accessibility, use the contact information listed for the Post Office location. post_office: Post Office ™ retail_hours_heading: Retail Hours retail_hours_sat: 'Sat:' diff --git a/config/locales/in_person_proofing/es.yml b/config/locales/in_person_proofing/es.yml index bac25a834e5..b19428cbd0c 100644 --- a/config/locales/in_person_proofing/es.yml +++ b/config/locales/in_person_proofing/es.yml @@ -40,14 +40,21 @@ es: address_search_hint: 'Ejemplo: 1960 W Chelsea Ave Allentown PA 18104' address_search_label: Introduzca una dirección para encontrar una Oficina de Correos cercana a usted + none_found: Lo sentimos, no hay Oficinas de Correos participantes en un radio de + 50 millas de la %{address}. + none_found_tip: Puede buscar utilizando una dirección diferente, o añadir fotos + de su documento de identidad para intentar verificar su identidad en + línea de nuevo. po_search_about: Si tiene problemas para añadir su documento de identidad, es posible que pueda verificar su identidad en persona en una oficina de correos local de los Estados Unidos. + results_description: Hay %{count} de oficinas de correos participantes en un + radio de 50 millas de la %{address}. + results_instructions: Seleccione una ubicación de la Oficina de Correos a + continuación, o busque de nuevo utilizando una dirección diferente. + Para la accesibilidad de las instalaciones, utilice la información + de contacto que aparece para la ubicación de la Oficina de Correos. search_button: Búsqueda - none_found: Lo sentimos, no hay Oficinas de Correos participantes en un radio de 50 millas de la %{address}. - none_found_tip: Puede buscar utilizando una dirección diferente, o añadir fotos de su documento de identidad para intentar verificar su identidad en línea de nuevo. - results_description: Hay %{count} de oficinas de correos participantes en un radio de 50 millas de la %{address}. - results_instructions: Seleccione una ubicación de la Oficina de Correos a continuación, o busque de nuevo utilizando una dirección diferente. Para la accesibilidad de las instalaciones, utilice la información de contacto que aparece para la ubicación de la Oficina de Correos. post_office: Oficina de Correos retail_hours_heading: Horario de atención al público retail_hours_sat: 'Sáb:' diff --git a/config/locales/in_person_proofing/fr.yml b/config/locales/in_person_proofing/fr.yml index cb74741183a..1d356ab601e 100644 --- a/config/locales/in_person_proofing/fr.yml +++ b/config/locales/in_person_proofing/fr.yml @@ -40,14 +40,20 @@ fr: po_search: address_search_hint: 'Exemple : 1960 W Chelsea Ave Allentown PA 18104' address_search_label: Entrez une adresse pour trouver un bureau de poste près de chez vous. + none_found: + none_found_tip: Vous pouvez effectuer une recherche en utilisant une autre + adresse, ou ajouter des photos de votre pièce d’identité pour + essayer de vérifier à nouveau votre identité en ligne. po_search_about: Si vous avez des difficultés à ajouter votre pièce d’identité, vous pouvez vérifier votre identité en personne dans un bureau de poste américain proche. + results_description: Il y a %{count} de bureaux de poste participants dans un + rayon de 80 km autour de %{address}. + results_instructions: Sélectionnez un emplacement de bureau de poste ci-dessous, + ou effectuez une nouvelle recherche en utilisant une autre adresse. + Pour l’accessibilité des installations, utilisez les informations de + contact indiquées pour l’emplacement du bureau de poste. search_button: Rechercher - none_found: - none_found_tip: Vous pouvez effectuer une recherche en utilisant une autre adresse, ou ajouter des photos de votre pièce d’identité pour essayer de vérifier à nouveau votre identité en ligne. - results_description: Il y a %{count} de bureaux de poste participants dans un rayon de 80 km autour de %{address}. - results_instructions: Sélectionnez un emplacement de bureau de poste ci-dessous, ou effectuez une nouvelle recherche en utilisant une autre adresse. Pour l’accessibilité des installations, utilisez les informations de contact indiquées pour l’emplacement du bureau de poste. post_office: Bureau de Poste retail_hours_heading: Heures de vente au détail retail_hours_sat: 'Sam:' From caeb20a1d76dad9315f6b4d422448153715beb22 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 11:15:16 -0500 Subject: [PATCH 11/15] Add placeholder french translation --- config/locales/in_person_proofing/fr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/in_person_proofing/fr.yml b/config/locales/in_person_proofing/fr.yml index 1d356ab601e..30e154e4b95 100644 --- a/config/locales/in_person_proofing/fr.yml +++ b/config/locales/in_person_proofing/fr.yml @@ -40,7 +40,7 @@ fr: po_search: address_search_hint: 'Exemple : 1960 W Chelsea Ave Allentown PA 18104' address_search_label: Entrez une adresse pour trouver un bureau de poste près de chez vous. - none_found: + none_found: Placeholder %{address} none_found_tip: Vous pouvez effectuer une recherche en utilisant une autre adresse, ou ajouter des photos de votre pièce d’identité pour essayer de vérifier à nouveau votre identité en ligne. From 5b462f72c4c10ecbfbd8ae668fd2ba0279fac717 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 18:03:22 -0500 Subject: [PATCH 12/15] Drop the default transformKeys predicate and require consumer to specify --- .../in-person-location-post-office-search-step.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 01d239bd37f..b435cfcea86 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 @@ -56,7 +56,7 @@ const snakeCase = (value: string) => .toLowerCase(); // snake case the keys of the location -const transformKeys = (location: object, predicate = snakeCase) => { +const transformKeys = (location: object, predicate: (key: string) => string) => { const sendObject = {}; Object.keys(location).forEach((key) => { sendObject[predicate(key)] = location[key]; @@ -67,7 +67,7 @@ const transformKeys = (location: object, predicate = snakeCase) => { const requestUspsLocations = async (address: LocationQuery): Promise => { const response = await request(LOCATIONS_URL, { method: 'post', - json: { address: transformKeys(address) }, + json: { address: transformKeys(address, snakeCase) }, }); return formatLocation(response); @@ -108,7 +108,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist if (inProgress) { return; } - const selected = transformKeys(selectedLocation); + const selected = transformKeys(selectedLocation, snakeCase); setInProgress(true); await request(LOCATIONS_URL, { json: selected, From 01c10cbc16b592825557da888c89ac45015d0fee Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Wed, 14 Dec 2022 18:20:57 -0500 Subject: [PATCH 13/15] Rename prop to be more truthful --- .../in-person-location-post-office-search-step.tsx | 3 +-- .../document-capture/components/in-person-locations.tsx | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) 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 b435cfcea86..509180e3778 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 @@ -154,11 +154,10 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist {locationResults && ( )} -
); diff --git a/app/javascript/packages/document-capture/components/in-person-locations.tsx b/app/javascript/packages/document-capture/components/in-person-locations.tsx index 3d7ff79c25b..c9e10bf1a6b 100644 --- a/app/javascript/packages/document-capture/components/in-person-locations.tsx +++ b/app/javascript/packages/document-capture/components/in-person-locations.tsx @@ -15,11 +15,11 @@ export interface FormattedLocation { interface InPersonLocationsProps { locations: FormattedLocation[] | null | undefined; - didSelect; + onSelect; address: string; } -function InPersonLocations({ locations, didSelect, address }: InPersonLocationsProps) { +function InPersonLocations({ locations, onSelect, address }: InPersonLocationsProps) { const { t } = useI18n(); if (locations?.length === 0) { @@ -44,7 +44,7 @@ function InPersonLocations({ locations, didSelect, address }: InPersonLocationsP {(locations || []).map((item, index) => ( Date: Thu, 15 Dec 2022 13:32:12 -0500 Subject: [PATCH 14/15] Wrap back button in PageFooter --- .../in-person-location-post-office-search-step.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 509180e3778..d6f60a663e4 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 @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback, useRef, useContext } from 'react'; import { useI18n } from '@18f/identity-react-i18n'; -import { PageHeading } from '@18f/identity-components'; +import { PageFooter, PageHeading } from '@18f/identity-components'; import { request } from '@18f/identity-request'; import useSWR from 'swr'; import BackButton from './back-button'; @@ -158,7 +158,9 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist address={foundAddress?.address || ''} /> )} - + + + ); } From 1f87724ebf779634916c5c4eb0ee53a10786e1c2 Mon Sep 17 00:00:00 2001 From: Matt Gardner Date: Thu, 15 Dec 2022 13:41:51 -0500 Subject: [PATCH 15/15] Include formatting and divider --- .../in-person-location-post-office-search-step.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 d6f60a663e4..8350cef79e2 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 @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback, useRef, useContext } from 'react'; import { useI18n } from '@18f/identity-react-i18n'; -import { PageFooter, PageHeading } from '@18f/identity-components'; +import { PageHeading } from '@18f/identity-components'; import { request } from '@18f/identity-request'; import useSWR from 'swr'; import BackButton from './back-button'; @@ -158,9 +158,7 @@ function InPersonLocationPostOfficeSearchStep({ onChange, toPreviousStep, regist address={foundAddress?.address || ''} /> )} - - - + ); }