fix: update zip code validation when address country changes - #93137
Conversation
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c940e2b3d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const [countryInEditMode, setCountryInEditMode] = useState<string>(defaultValues?.country ?? CONST.COUNTRY.US); | ||
| // When draft values are not being saved we need to relay on local state to determine the currently selected country | ||
| const currentlySelectedCountry = shouldSaveDraft ? defaultValues?.country : countryInEditMode; | ||
| const currentlySelectedCountry = countryInEditMode || defaultValues?.country; | ||
| const zipSampleFormat = ((currentlySelectedCountry ? COMMON_CONST.COUNTRY_ZIP_REGEX_DATA[currentlySelectedCountry as Country] : undefined) as CountryZipRegex | undefined)?.samples ?? ''; |
There was a problem hiding this comment.
Derive the displayed country from updated defaults
When defaultValues.country changes after this component has mounted, countryInEditMode keeps its initial value (often US, from the empty initial Onyx value), so currentlySelectedCountry never reflects the loaded/default country unless the user manually changes the selector. In flows with a fixed or asynchronously loaded country, such as the non-USD business address step where shouldAllowCountryChange={false} and the country comes from Onyx, this leaves the postal hint/input mode and the CA-vs-US state/province list stuck on the wrong country.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed this feedback by making countryInEditMode only store a user-selected override so async defaultValues.country updates are reflected.
| import type {Country} from '@src/CONST'; | ||
| import type {TranslationPaths} from '@src/languages/types'; | ||
| import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; | ||
|
|
There was a problem hiding this comment.
❌ CONSISTENCY-3 (docs)
The CountryZipRegex type is defined here and also in src/components/SubStepForms/AddressStep.tsx (line 20). Both types serve the same purpose (typing the shape of COMMON_CONST.COUNTRY_ZIP_REGEX_DATA entries) but have slightly different fields, which is fragile. Additionally, the inline access pattern COMMON_CONST.COUNTRY_ZIP_REGEX_DATA[currentlySelectedCountry as Country] as CountryZipRegex duplicates the logic already encapsulated by getCountryZipRegexDetails in AddressStep.tsx.
Extract CountryZipRegex to a single shared location (e.g., a types file or alongside ValidationUtils) and reuse getCountryZipRegexDetails from AddressStep.tsx (or better yet, move it to ValidationUtils) instead of duplicating the lookup logic inline:
// In AddressFormFields.tsx, reuse the shared helper:
import {getCountryZipRegexDetails} from '@libs/ValidationUtils';
const zipSampleFormat = getCountryZipRegexDetails(currentlySelectedCountry as Country)?.samples ?? '';Reviewed at: 5c940e2 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
Hi @Pujan92, thank you for the review.
I addressed this by centralizing CountryZipRegex and getCountryZipRegexDetails in ValidationUtils. AddressFormFields now uses the shared helper instead of directly accessing COMMON_CONST.COUNTRY_ZIP_REGEX_DATA.
| samples?: string; | ||
| }; | ||
|
|
||
| function getCountryZipRegexDetails(country?: Country | ''): CountryZipRegex | undefined { |
There was a problem hiding this comment.
❌ CONSISTENCY-3 (docs)
getCountryZipRegexDetails and isValidZipCodeForCountry are validation utility functions placed inside a UI component file. The codebase already has isValidZipCode and the formerly-used isValidZipCodeInternational in @libs/ValidationUtils. These new helpers perform the same category of work (zip code validation) and should live alongside them for discoverability, reuse, and consistency.
Move getCountryZipRegexDetails, isValidZipCodeForCountry, and the shared CountryZipRegex type to src/libs/ValidationUtils.ts so both AddressStep.tsx and AddressFormFields.tsx can import them from the same location.
Reviewed at: 5c940e2 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
Hi @Pujan92, thank you for the review.
I moved getCountryZipRegexDetails, isValidZipCodeForCountry, and CountryZipRegex to src/libs/ValidationUtils.ts so the ZIP validation helpers live alongside the existing validation utilities and can be reused by both address components.
|
Hi @Pujan92, thank you for taking the time to review this. This PR only changes: So this looks like a global seatbelt threshold/baseline lint failure rather than an issue introduced by the ZIP validation changes in this PR. |
|
@Pujan92 Gentle bump. Thanks! |
|
@Pujan92 Friendly bump. Thanks! |
|
I will review today |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / SafariScreen.Recording.2026-06-23.at.18.26.21.mov |
|
@MelvinBot review |
ReviewNo correctness bugs in the core logic — the country-aware validation is sound. A few things worth confirming before approving, mostly around an intended behavior change and some now-dead code. 1. Behavior change to confirm (intended, but please verify with product)For the three NonUSD country‑selector flows (BusinessInfo, BeneficialOwnerInfo, SignerInfo), a non‑US postal code was previously not validated at all ( 2.
|
|
@nabi-ebrahimi could you plz confirm that earlier the zip validation wasn't applied to those screens where shouldValidateZipCodeFormat was false. Is it because of any specific reason? |
Hi @Pujan92, thank you for the feedback and for taking the time to review this. Yes, I confirmed that ZIP/postal code validation was previously skipped on screens where The reason appears to be that the previous validation logic was entirely gated by This PR changes that behavior by applying country-specific validation whenever a country selector is present, allowing non-US postal codes to be validated against |
|
@Pujan92, Gentle bump. thanks. |
|
@Pujan92 Hi, thank you for the review. During testing, I noticed that the ZIP code hint was no longer shown when no country was selected. I have updated the fallback logic so it now uses the US ZIP format when the country is unset, matching the previous behavior. |
|
@Pujan92 Hi, I have addressed the conflicts and kept both the latest address validation changes from |
|
@Pujan92, Gentle bump. thanks. |
marcochavezf
left a comment
There was a problem hiding this comment.
The latest changes LGTM
|
🚧 marcochavezf has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/marcochavezf in version: 9.4.25-0 🚀
|
|
@nabi-ebrahimi This PR is failing because of a regression issue #95021 The issue is reproducible in: Web, Android, iOS Bug7195883_1782854512302.WEB-repro.mp4 |
|
🚀 Deployed to production by https://github.com/cristipaval in version: 9.4.25-2 🚀
Bundle Size Analysis (Sentry): |
Explanation of Change
This change improves address entry for users outside the United States by validating postal codes against the selected country instead of applying a generic or US-focused ZIP code rule. This matters because reimbursement and account setup flows collect addresses across multiple countries, and rejecting valid local postal codes creates unnecessary friction and blocks users from completing setup.
The form now also shows country-specific postal code examples and only uses a numeric keyboard for US ZIP codes. That makes the expected input clearer for international users while preserving the simpler numeric experience for US addresses.
Fixed Issues
$ #90491
PROPOSAL: #90491 (comment)
Tests
Offline tests
Same as Tests.
QA Steps
Same as Tests.
// TODO: These must be filled out, or the issue title must include "[No QA]."
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Screen_Recording_20260610_070422_New.Expensify.Dev.mp4
Android: mWeb Chrome
Recording_20260610_065424.mp4
iOS: Native
Screen.Recording.2026-06-10.at.7.25.33.AM.mov
iOS: mWeb Safari
Screen.Recording.2026-06-10.at.7.00.21.AM.mov
MacOS: Chrome / Safari
Screen.Recording.2026-06-10.at.6.50.10.AM.mov