Skip to content

Add erroneous character checking to address search field#11224

Merged
WilliamBirdsall merged 1 commit intomainfrom
will/LG-14071
Sep 19, 2024
Merged

Add erroneous character checking to address search field#11224
WilliamBirdsall merged 1 commit intomainfrom
will/LG-14071

Conversation

@WilliamBirdsall
Copy link
Contributor

@WilliamBirdsall WilliamBirdsall commented Sep 10, 2024

🎫 Ticket

Link to the relevant ticket:
LG-14071

🛠 Summary of changes

Added client side character checking to PO search address field so that it aligns with other address field validations such as the ones associated with the state ID form.

Also added a validation function server side in case someone disables JS and submits. (It will fail as it used to instead of showing the new unsupported characters message since this is not how users should be interacting with the form, but it's technically possible.)

📜 Testing Plan

Provide a checklist of steps to confirm the changes.

  • Step 1: Create or login to an account that is at the starting step of the IPP flow.
  • Step 2: When on the PO search page add a comma and a hash/pound sign to the address field.
  • Step 3: On submission, an error should show regarding unsupported errors and specifically the comma. Allowed characters: alpha-numerics, spaces and the following: - ' . / #

(To see the dynamism of the error message you can also throw in a dollar sign and resubmit)

@WilliamBirdsall
Copy link
Contributor Author

WilliamBirdsall commented Sep 10, 2024

Hey @aduth sorry to bother you on this one again, but is there any reason you can think of as to why the test in: app/javascript/packages/address-search/components/full-address-search.spec.tsx is failing? Looks like the usa-error div is not being rendered in the mocked version :(

@WilliamBirdsall WilliamBirdsall marked this pull request as draft September 10, 2024 16:13
@aduth
Copy link
Contributor

aduth commented Sep 10, 2024

Hey @aduth sorry to bother you on this one again, but is there any reason you can think of as to why the test in: app/javascript/packages/address-search/components/full-address-search.spec.tsx is failing? Looks like the usa-error div is not being rendered in the mocked version :(

I think that for this test to be valuable (i.e. to verify that it actually shows the invalid characters), we should stub i18n data for the tests so that it has the unsupported_chars string, and then we can assert against that.

Example of where we've done this elsewhere:

<I18nContext.Provider
value={
new I18n({
strings: {
'doc_auth.errors.rate_limited_heading': 'We couldn’t verify your ID',
},
})
}
>

@WilliamBirdsall
Copy link
Contributor Author

Hey @aduth sorry to bother you on this one again, but is there any reason you can think of as to why the test in: app/javascript/packages/address-search/components/full-address-search.spec.tsx is failing? Looks like the usa-error div is not being rendered in the mocked version :(

I think that for this test to be valuable (i.e. to verify that it actually shows the invalid characters), we should stub i18n data for the tests so that it has the unsupported_chars string, and then we can assert against that.

Example of where we've done this elsewhere:

<I18nContext.Provider
value={
new I18n({
strings: {
'doc_auth.errors.rate_limited_heading': 'We couldn’t verify your ID',
},
})
}
>

I'm all for adding this but wouldn't we run into the same issue with the error markup itself not rendering in the mock?

@aduth
Copy link
Contributor

aduth commented Sep 10, 2024

I'm all for adding this but wouldn't we run into the same issue with the error markup itself not rendering in the mock?

Interestingly, I think the issue currently is that you have an invalid escape in your regular expression here (the \ before the #):

https://github.com/18F/identity-idp/blob/c42f10d7829aad7f356019295f7591c36457fdb0/app/javascript/packages/address-search/components/full-address-search-input.tsx#L110

# isn't a special character and generally you don't need to escape in character classes anyways. Still somewhat surprising how that makes a difference though.

@WilliamBirdsall
Copy link
Contributor Author

WilliamBirdsall commented Sep 10, 2024

I'm all for adding this but wouldn't we run into the same issue with the error markup itself not rendering in the mock?

Interestingly, I think the issue currently is that you have an invalid escape in your regular expression here (the \ before the #):

https://github.com/18F/identity-idp/blob/c42f10d7829aad7f356019295f7591c36457fdb0/app/javascript/packages/address-search/components/full-address-search-input.tsx#L110

# isn't a special character and generally you don't need to escape in character classes anyways. Still somewhat surprising how that makes a difference though.

Ah! Must have been a typo when I switched the regex from ruby to JS. UGH thank you I cant believe that was it!

@WilliamBirdsall WilliamBirdsall force-pushed the will/LG-14071 branch 2 times, most recently from 94c2676 to 0d82aa0 Compare September 11, 2024 17:11
@WilliamBirdsall
Copy link
Contributor Author

Note: Going to create a new ticket for the more robust tests as this ticket is currently blocking other tickets from starting.

@WilliamBirdsall WilliamBirdsall marked this pull request as ready for review September 11, 2024 18:11
@WilliamBirdsall WilliamBirdsall requested review from a team and shanechesnutt-ft September 11, 2024 18:12
@aduth
Copy link
Contributor

aduth commented Sep 11, 2024

I'd personally disagree with splitting tests to its own ticket. To me, test coverage is expected to be included for any change.

It's also written in the program Definition of Done:

Tests have been written. New or modified features have feature coverage. Code coverage of the entire test suite is not reduced

@aduth
Copy link
Contributor

aduth commented Sep 11, 2024

I had iterated on tests locally in an earlier version of the branch. In case it's helpful.

diff --git a/app/javascript/packages/address-search/components/full-address-search-input.tsx b/app/javascript/packages/address-search/components/full-address-search-input.tsx
index 757498981e..27401a1194 100644
--- a/app/javascript/packages/address-search/components/full-address-search-input.tsx
+++ b/app/javascript/packages/address-search/components/full-address-search-input.tsx
@@ -6,3 +6,3 @@ import { SpinnerButtonRefHandle, SpinnerButton } from '@18f/identity-spinner-but
 import { ValidatedField } from '@18f/identity-validated-field';
-import { t } from '@18f/identity-i18n';
+import { useI18n } from '@18f/identity-react-i18n';
 import { useCallback, useEffect, useRef, useState } from 'react';
@@ -32,2 +32,3 @@ export default function FullAddressSearchInput({
 }: FullAddressSearchInputProps) {
+  const { t } = useI18n();
   const spinnerButtonRef = useRef<SpinnerButtonRefHandle>(null);
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 82f0c5bcbe..85ab24cb21 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
@@ -8,2 +8,4 @@ import type { SetupServer } from 'msw/node';
 import { SWRConfig } from 'swr';
+import { I18nContext } from '@18f/identity-react-i18n';
+import { I18n } from '@18f/identity-i18n';
 import FullAddressSearch from './full-address-search';
@@ -137,13 +139,24 @@ describe('FullAddressSearch', () => {
       const handleLocationsFound = sandbox.stub();
-      const { findByText, findAllByText, findByLabelText } = render(
-        <SWRConfig value={{ provider: () => new Map() }}>
-          <FullAddressSearch
-            usStatesTerritories={usStatesTerritories}
-            onFoundLocations={handleLocationsFound}
-            locationsURL={locationsURL}
-            registerField={() => undefined}
-            handleLocationSelect={undefined}
-            disabled={false}
-          />
-        </SWRConfig>,
+      const { findByText, findByLabelText } = render(
+        <I18nContext.Provider
+          value={
+            new I18n({
+              strings: {
+                'in_person_proofing.form.address.errors.unsupported_chars':
+                  'Our system cannot read the following characters: %{char_list}.',
+              },
+            })
+          }
+        >
+          <SWRConfig value={{ provider: () => new Map() }}>
+            <FullAddressSearch
+              usStatesTerritories={usStatesTerritories}
+              onFoundLocations={handleLocationsFound}
+              locationsURL={locationsURL}
+              registerField={() => undefined}
+              handleLocationSelect={undefined}
+              disabled={false}
+            />
+          </SWRConfig>
+        </I18nContext.Provider>,
       );
@@ -170,8 +183,5 @@ describe('FullAddressSearch', () => {
 
-      const errors = await findAllByText(
-        'in_person_proofing.form.address.errors.unsupported_chars',
-        { exact: false },
-      );
+      const error = await findByText('Our system cannot read the following characters: ,.');
 
-      expect(errors).to.have.lengthOf(1);
+      expect(error).to.exist();
     });

@aduth
Copy link
Contributor

aduth commented Sep 11, 2024

Can you add the pull request template to the original comment?

@WilliamBirdsall
Copy link
Contributor Author

Will do x2! Thanks!

@WilliamBirdsall WilliamBirdsall force-pushed the will/LG-14071 branch 2 times, most recently from 39cae4e to 7fdebbf Compare September 11, 2024 19:24
@WilliamBirdsall WilliamBirdsall force-pushed the will/LG-14071 branch 3 times, most recently from 5673bbb to 7f0fff6 Compare September 13, 2024 19:45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we pull the pattern attribute off validatedAddressFieldRef.current rather than duplicating the regular expression? Or at least could we assign it once as a constant somewhere and reference it in the same place?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like both files would have access to validatedAddressFieldRef since this one creates it, and the other one just uses it. Can we arbitrarily set the pattern as a property on the ref in this file? Or is there a specific way of handling that? Not super familiar with ref uses.

Copy link
Contributor

@aduth aduth Sep 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting that we could keep the pattern assignment in the JSX for the <TextInput /> element, and then retrieve it off the ref here. validatedAddressFieldRef.current should be the rendered HTMLInputElement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! I was thinking about it the wrong way around. Great idea!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar point as previous, maybe we could assign the regular expression to a constant and reference it in both places in this file.

@WilliamBirdsall WilliamBirdsall changed the title WIP add erroneous character checking to address search field Add erroneous character checking to address search field Sep 17, 2024
@WilliamBirdsall
Copy link
Contributor Author

Looks like we need to have the regex itself in use-validated-usps-locations.ts since validatedAddressFieldRef is null there.

@aduth
Copy link
Contributor

aduth commented Sep 17, 2024

Looks like we need to have the regex itself in use-validated-usps-locations.ts since validatedAddressFieldRef is null there.

Should we be concerned that validatedAddressFieldRef is null ?

@WilliamBirdsall
Copy link
Contributor Author

WilliamBirdsall commented Sep 17, 2024

Looks like we need to have the regex itself in use-validated-usps-locations.ts since validatedAddressFieldRef is null there.

Should we be concerned that validatedAddressFieldRef is null ?

Its defined as null directly above that and then I think since its a test for the internal functionality of that file the element isn't created yet. That's how I'm reading it at least...

@aduth
Copy link
Contributor

aduth commented Sep 17, 2024

Typically useRef would be assigned to null initially, and expected to be populated with the element reference once the component is rendered. So while it makes sense that it would be null initially, I think we expect it to have an element reference by the time it's rendered. Otherwise, what's the point of the ref?

@WilliamBirdsall
Copy link
Contributor Author

Typically useRef would be assigned to null initially, and expected to be populated with the element reference once the component is rendered. So while it makes sense that it would be null initially, I think we expect it to have an element reference by the time it's rendered. Otherwise, what's the point of the ref?

That makes sense. But in the test it's only using renderHook which makes me think its only testing the hook logic and not the rendering side of things... So the ref would always be null in this case right? (Note the test is: use-validated-usps-locations.spec.ts and not the one in changed files)

@aduth
Copy link
Contributor

aduth commented Sep 17, 2024

That makes sense. But in the test it's only using renderHook which makes me think its only testing the hook logic and not the rendering side of things... So the ref would always be null in this case right? (Note the test is: use-validated-usps-locations.spec.ts and not the one in changed files)

Sounds like the test is lacking some test coverage for a scenario where those elements exist (i.e. the actual end-user behavior). But I do think it could make sense to use the optional chain operator to access the safely access address element properties like we're doing with other ref element references in that file.

Copy link
Contributor

@KeithNava KeithNava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing steps passed for me! 🔥

@WilliamBirdsall
Copy link
Contributor Author

WilliamBirdsall commented Sep 17, 2024

That makes sense. But in the test it's only using renderHook which makes me think its only testing the hook logic and not the rendering side of things... So the ref would always be null in this case right? (Note the test is: use-validated-usps-locations.spec.ts and not the one in changed files)

Sounds like the test is lacking some test coverage for a scenario where those elements exist (i.e. the actual end-user behavior). But I do think it could make sense to use the optional chain operator to access the safely access address element properties like we're doing with other ref element references in that file.

So the test that's in the PR changes handles the actual end user behavior (checking for proper error message) while this test (use-validated-usps-locations.spec.ts) is more of an internal thing? I originally was using that operator but since the ref is always null at that part of the code the test would be pointless if it was dependent on the ref.

Over all we have coverage for end user and the internal hook logic so I ~ think ~ its solid enough? Note that zipcode also repeats the regex in the use-validated-usps-locations.ts file instead of getting from a reference

@gina-yamada
Copy link
Contributor

gina-yamada commented Sep 17, 2024

  • I cannot submit form with error (I do not move on in the flow)
  • Call to USPS api is not made if there is an error
  • Unsupported chars display only once when the unsupported char is entered more than once in the input
  • Nil error still works as expected
  • languages all still supported

Some screenshots of testing

Screenshot 2024-09-17 at 12 25 11 PM Screenshot 2024-09-17 at 12 15 31 PM Screenshot 2024-09-17 at 1 18 32 PM Screenshot 2024-09-17 at 1 19 16 PM

@WilliamBirdsall
Copy link
Contributor Author

That makes sense. But in the test it's only using renderHook which makes me think its only testing the hook logic and not the rendering side of things... So the ref would always be null in this case right? (Note the test is: use-validated-usps-locations.spec.ts and not the one in changed files)

Sounds like the test is lacking some test coverage for a scenario where those elements exist (i.e. the actual end-user behavior). But I do think it could make sense to use the optional chain operator to access the safely access address element properties like we're doing with other ref element references in that file.

Oh wait are you saying we should also add extra tests for the upper part of the use-validated-usps-locations.ts in that file's spec instead of relying on the tests in full-address-search.spec? Sorry not trying to make this overly complex, just want to make sure we have everything needed since we are pushing package updates to fix this issue on the help center page and id rather measure twice and cut once so we don't need to re publish

@aduth
Copy link
Contributor

aduth commented Sep 17, 2024

Yes, I think if the hook has specific behaviors for how it interacts with elements, it'd be reasonable to expect that the specs for that hook would test those behaviors. The overarching issue with what you're seeing is that the specs currently don't reflect real-world usage, hence the issues you're running into.

@WilliamBirdsall
Copy link
Contributor Author

Hey @aduth, I tried pairing with another Joy engineer to figure out how to get the FullAddressSearch component rendering in the use-validated-usps-locations spec so that the various references are non-null. We tried copying code from the FullAddressSearch's spec file and got the render function working, but the references were still null. Also, is it weird that we are testing FullAddressSearch functionality in the hook's test file? I know they are interconnected now when actually in use, but it feels a little weird.

Is there any way you could provide some guidance on what is needed? Thanks!

@aduth
Copy link
Contributor

aduth commented Sep 19, 2024

I think if you could at least have test coverage somewhere demonstrating the expected behavior, that'd be fine, even if it's in the spec of the component using the hook. This is a case where the hook is so intrinsically tied to the component that it would be fine (and arguably there might not be much value in having the hook at all).

I'm guessing the expected behavior here is that we don't make a network request? We could check that the SWR cache doesn't expand on the invalid submission.

I actually think the repeated validation in the hook is unnecessary if we're also using native form validation. We can check the validity on the elements directly?

Something like this:

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 5e1688923b..a3a7ff018d 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
@@ -139,2 +139,3 @@ describe('FullAddressSearch', () => {
       const handleLocationsFound = sandbox.stub();
+      const locationCache = new Map();
       const { findByText, findByLabelText } = render(
@@ -150,3 +151,3 @@ describe('FullAddressSearch', () => {
         >
-          <SWRConfig value={{ provider: () => new Map() }}>
+          <SWRConfig value={{ provider: () => locationCache }}>
             <FullAddressSearch
@@ -172,3 +173,3 @@ describe('FullAddressSearch', () => {
       );
-      await userEvent.type(
+      await userEvent.selectOptions(
         await findByLabelText('in_person_proofing.body.location.po_search.state_label'),
@@ -178,3 +179,3 @@ describe('FullAddressSearch', () => {
         await findByLabelText('in_person_proofing.body.location.po_search.zipcode_label'),
-        '10',
+        '00010',
       );
@@ -189,2 +190,3 @@ describe('FullAddressSearch', () => {
       expect(error).to.exist();
+      expect(locationCache.size).to.equal(1);
     });
diff --git a/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts b/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
index f782c564fa..cd95c3f98b 100644
--- a/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
+++ b/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
@@ -8,6 +8,6 @@ export default function useValidatedUspsLocations(locationsURL: string) {
   const [locationQuery, setLocationQuery] = useState<LocationQuery | null>(null);
-  const validatedAddressFieldRef = useRef<HTMLFormElement>(null);
-  const validatedCityFieldRef = useRef<HTMLFormElement>(null);
-  const validatedStateFieldRef = useRef<HTMLFormElement>(null);
-  const validatedZipCodeFieldRef = useRef<HTMLFormElement>(null);
+  const validatedAddressFieldRef = useRef<HTMLInputElement>(null);
+  const validatedCityFieldRef = useRef<HTMLInputElement>(null);
+  const validatedStateFieldRef = useRef<HTMLInputElement>(null);
+  const validatedZipCodeFieldRef = useRef<HTMLInputElement>(null);
 
@@ -16,5 +16,2 @@ export default function useValidatedUspsLocations(locationsURL: string) {
     const zipCodeIsValid = zipCode.length === 5 && !!zipCode.match(/\d{5}/);
-    const addressReStr = "[A-Za-z0-9-' ./#]*";
-    const addressRegex = new RegExp(addressReStr, 'g');
-    const addressIsValid = address.replace(addressRegex, '') === '';
 
@@ -53,3 +50,10 @@ export default function useValidatedUspsLocations(locationsURL: string) {
 
-    return formIsValid && zipCodeIsValid && addressIsValid;
+    const hasInvalidFields = [
+      validatedAddressFieldRef,
+      validatedCityFieldRef,
+      validatedStateFieldRef,
+      validatedZipCodeFieldRef,
+    ].some((fieldRef) => fieldRef.current?.validity?.valid === false);
+
+    return formIsValid && zipCodeIsValid && !hasInvalidFields;
   };

@WilliamBirdsall
Copy link
Contributor Author

I think if you could at least have test coverage somewhere demonstrating the expected behavior, that'd be fine, even if it's in the spec of the component using the hook. This is a case where the hook is so intrinsically tied to the component that it would be fine (and arguably there might not be much value in having the hook at all).

I'm guessing the expected behavior here is that we don't make a network request? We could check that the SWR cache doesn't expand on the invalid submission.

I actually think the repeated validation in the hook is unnecessary if we're also using native form validation. We can check the validity on the elements directly?

Something like this:

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 5e1688923b..a3a7ff018d 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
@@ -139,2 +139,3 @@ describe('FullAddressSearch', () => {
       const handleLocationsFound = sandbox.stub();
+      const locationCache = new Map();
       const { findByText, findByLabelText } = render(
@@ -150,3 +151,3 @@ describe('FullAddressSearch', () => {
         >
-          <SWRConfig value={{ provider: () => new Map() }}>
+          <SWRConfig value={{ provider: () => locationCache }}>
             <FullAddressSearch
@@ -172,3 +173,3 @@ describe('FullAddressSearch', () => {
       );
-      await userEvent.type(
+      await userEvent.selectOptions(
         await findByLabelText('in_person_proofing.body.location.po_search.state_label'),
@@ -178,3 +179,3 @@ describe('FullAddressSearch', () => {
         await findByLabelText('in_person_proofing.body.location.po_search.zipcode_label'),
-        '10',
+        '00010',
       );
@@ -189,2 +190,3 @@ describe('FullAddressSearch', () => {
       expect(error).to.exist();
+      expect(locationCache.size).to.equal(1);
     });
diff --git a/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts b/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
index f782c564fa..cd95c3f98b 100644
--- a/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
+++ b/app/javascript/packages/address-search/hooks/use-validated-usps-locations.ts
@@ -8,6 +8,6 @@ export default function useValidatedUspsLocations(locationsURL: string) {
   const [locationQuery, setLocationQuery] = useState<LocationQuery | null>(null);
-  const validatedAddressFieldRef = useRef<HTMLFormElement>(null);
-  const validatedCityFieldRef = useRef<HTMLFormElement>(null);
-  const validatedStateFieldRef = useRef<HTMLFormElement>(null);
-  const validatedZipCodeFieldRef = useRef<HTMLFormElement>(null);
+  const validatedAddressFieldRef = useRef<HTMLInputElement>(null);
+  const validatedCityFieldRef = useRef<HTMLInputElement>(null);
+  const validatedStateFieldRef = useRef<HTMLInputElement>(null);
+  const validatedZipCodeFieldRef = useRef<HTMLInputElement>(null);
 
@@ -16,5 +16,2 @@ export default function useValidatedUspsLocations(locationsURL: string) {
     const zipCodeIsValid = zipCode.length === 5 && !!zipCode.match(/\d{5}/);
-    const addressReStr = "[A-Za-z0-9-' ./#]*";
-    const addressRegex = new RegExp(addressReStr, 'g');
-    const addressIsValid = address.replace(addressRegex, '') === '';
 
@@ -53,3 +50,10 @@ export default function useValidatedUspsLocations(locationsURL: string) {
 
-    return formIsValid && zipCodeIsValid && addressIsValid;
+    const hasInvalidFields = [
+      validatedAddressFieldRef,
+      validatedCityFieldRef,
+      validatedStateFieldRef,
+      validatedZipCodeFieldRef,
+    ].some((fieldRef) => fieldRef.current?.validity?.valid === false);
+
+    return formIsValid && zipCodeIsValid && !hasInvalidFields;
   };

Patched this into the branch and tested successfully. Was curious what the SWR bit was for that makes more sense now thanks!

…h that matches other address field validations
@WilliamBirdsall
Copy link
Contributor Author

WilliamBirdsall commented Sep 19, 2024

@gina-yamada Would you mind taking a quick look at the last couple small changes? Added an if check in the erroneous characters function and patched with Andrew's patch above. Then ill merge!

);

const error = await findByText(
'Our system cannot read the following characters: , . Please try again using substitutes for those characters.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified text here to prove test was working properly. Good

@gina-yamada
Copy link
Contributor

gina-yamada commented Sep 19, 2024

I tested same list as above and all behavior observed is what I'd expect after last update. LGTM

@WilliamBirdsall
Copy link
Contributor Author

merging!

@WilliamBirdsall WilliamBirdsall merged commit 3b61ba4 into main Sep 19, 2024
@WilliamBirdsall WilliamBirdsall deleted the will/LG-14071 branch September 19, 2024 17:15
AShukla-GSA pushed a commit that referenced this pull request Sep 30, 2024
…h that matches other address field validations (#11224)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants