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[][];
+}