From 2adef77861d41509e3cb713cacc78a2e71dde893 Mon Sep 17 00:00:00 2001 From: "Maxence Coulibaly (via MelvinBot)" Date: Tue, 21 Apr 2026 12:43:32 +0000 Subject: [PATCH 1/5] Add 2MB per-file size limit for Corpay onboarding document uploads The UploadFile component had a totalFilesSizeLimit prop but no per-file size validation, allowing individual files larger than the backend's limit to be uploaded. This adds a maxFileSize prop to UploadFile and passes it (2MB) in both the Signer and BeneficialOwner upload pages. Co-authored-by: madmax330 Co-authored-by: Maxence Coulibaly --- src/CONST/index.ts | 1 + src/components/UploadFile.tsx | 12 ++++++++++++ .../BeneficialOwnerDetailsFormSubSteps/Documents.tsx | 4 ++++ .../NonUSD/SignerInfo/subSteps/UploadDocuments.tsx | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index aaac07e5d513..a8f31ced1e0a 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -683,6 +683,7 @@ const CONST = { NON_USD_BANK_ACCOUNT: { ALLOWED_FILE_TYPES: ['pdf', 'jpg', 'jpeg', 'png'], FILE_LIMIT: 1, + MAX_FILE_SIZE: 2097152, TOTAL_FILES_SIZE_LIMIT: 5242880, PURPOSE_OF_TRANSACTION_ID: 'Intercompany_Payment', CURRENT_USER_KEY: 'currentUser', diff --git a/src/components/UploadFile.tsx b/src/components/UploadFile.tsx index ca2e9eead5fc..a465dd76636f 100644 --- a/src/components/UploadFile.tsx +++ b/src/components/UploadFile.tsx @@ -49,6 +49,9 @@ type UploadFileProps = { /** The total size limit of the files that can be selected. */ totalFilesSizeLimit?: number; + + /** The maximum size of a single file that can be selected. */ + maxFileSize?: number; }; function UploadFile({ @@ -63,6 +66,7 @@ function UploadFile({ onInputChange = () => {}, totalFilesSizeLimit = 0, fileLimit = 0, + maxFileSize = 0, }: UploadFileProps) { const icons = useMemoizedLazyExpensifyIcons(['Close', 'Paperclip']); const {translate} = useLocalize(); @@ -73,6 +77,14 @@ function UploadFile({ const totalSize = resultedFiles.reduce((sum, file) => sum + (file.size ?? 0), 0); + if (maxFileSize) { + const oversizedFile = files.find((file) => (file.size ?? 0) > maxFileSize); + if (oversizedFile) { + setError(translate('attachmentPicker.sizeExceededWithLimit', maxFileSize / (1024 * 1024))); + return; + } + } + if (totalFilesSizeLimit) { if (totalSize > totalFilesSizeLimit) { setError(translate('attachmentPicker.sizeExceededWithValue', totalFilesSizeLimit / (1024 * 1024))); diff --git a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx index 214a419118e6..f0909bec25d1 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx @@ -125,6 +125,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setUploadError(error, proofOfOwnershipInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} value={defaultValues[proofOfOwnershipInputID]} inputID={proofOfOwnershipInputID} @@ -153,6 +154,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setUploadError(error, copyOfIDInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} value={defaultValues[copyOfIDInputID]} inputID={copyOfIDInputID} @@ -179,6 +181,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setUploadError(error, addressProofInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} value={defaultValues[addressProofInputID]} inputID={addressProofInputID} @@ -205,6 +208,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setUploadError(error, codiceFiscaleInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} value={defaultValues[codiceFiscaleInputID]} inputID={codiceFiscaleInputID} diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx index b8ca7467e107..3518bebfb215 100644 --- a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx @@ -149,6 +149,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { setUploadError(error, copyOfIDInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} /> {translate('ownershipInfoStep.copyOfIDDescription')} {(isDocumentNeededStatus.isAddressProofNeeded || @@ -177,6 +178,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { setUploadError(error, addressProofInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} /> {translate('ownershipInfoStep.proofOfAddressDescription')} {(isDocumentNeededStatus.isProofOfDirectorsNeeded || isDocumentNeededStatus.isCodiceFiscaleNeeded || isDocumentNeededStatus.isPRDAndFSGNeeded) && ( @@ -204,6 +206,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { setUploadError(error, directorsProofInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} /> {translate('signerInfoStep.proofOfDirectorsDescription')} {(isDocumentNeededStatus.isCodiceFiscaleNeeded || isDocumentNeededStatus.isPRDAndFSGNeeded) && } @@ -229,6 +232,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { setUploadError(error, codiceFiscaleInputID); }} fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} /> {translate('signerInfoStep.codiceFiscaleDescription')} {isDocumentNeededStatus.isPRDAndFSGNeeded && } From 7747c4e2b3edbd8fe00844c2e38f684d51fd3c5d Mon Sep 17 00:00:00 2001 From: "Maxence Coulibaly (via MelvinBot)" Date: Wed, 6 May 2026 19:02:51 +0000 Subject: [PATCH 2/5] Add maxFileSize to DocusignFullStep UploadFile Co-authored-by: Maxence Coulibaly --- src/components/SubStepForms/DocusignFullStep.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/SubStepForms/DocusignFullStep.tsx b/src/components/SubStepForms/DocusignFullStep.tsx index 0ff243359d99..475bf27bc8f6 100644 --- a/src/components/SubStepForms/DocusignFullStep.tsx +++ b/src/components/SubStepForms/DocusignFullStep.tsx @@ -155,6 +155,7 @@ function DocusignFullStep({ setUploadError(error); }} fileLimit={1} + maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} /> From 1839dd820f43c6cb70c7b45fbd00b21e5c159e18 Mon Sep 17 00:00:00 2001 From: "Maxence Coulibaly (via MelvinBot)" Date: Thu, 7 May 2026 09:26:45 +0000 Subject: [PATCH 3/5] Rename file upload consts from NON_USD_BANK_ACCOUNT to CORPAY_DOCUMENT Move ALLOWED_FILE_TYPES, FILE_LIMIT, MAX_FILE_SIZE, and TOTAL_FILES_SIZE_LIMIT to a new CORPAY_DOCUMENT const group since these consts are used in both USD and non-USD flows (e.g., DocusignFullStep is also used on the wallet page for global reimbursements). Co-authored-by: Maxence Coulibaly --- src/CONST/index.ts | 4 +++- .../SubStepForms/DocusignFullStep.tsx | 4 ++-- .../subSteps/UploadDocuments.tsx | 6 ++--- .../Documents.tsx | 24 +++++++++---------- .../SignerInfo/subSteps/UploadDocuments.tsx | 24 +++++++++---------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 9343313b7d65..2dc6668a213b 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -680,11 +680,13 @@ const CONST = { PERSONAL: 'PERSONAL', }, }, - NON_USD_BANK_ACCOUNT: { + CORPAY_DOCUMENT: { ALLOWED_FILE_TYPES: ['pdf', 'jpg', 'jpeg', 'png'], FILE_LIMIT: 1, MAX_FILE_SIZE: 2097152, TOTAL_FILES_SIZE_LIMIT: 5242880, + }, + NON_USD_BANK_ACCOUNT: { PURPOSE_OF_TRANSACTION_ID: 'Intercompany_Payment', CURRENT_USER_KEY: 'currentUser', CORPAY_UNDEFINED_OPTION_VALUE: 'Undefined', diff --git a/src/components/SubStepForms/DocusignFullStep.tsx b/src/components/SubStepForms/DocusignFullStep.tsx index 475bf27bc8f6..b07a52272b92 100644 --- a/src/components/SubStepForms/DocusignFullStep.tsx +++ b/src/components/SubStepForms/DocusignFullStep.tsx @@ -148,14 +148,14 @@ function DocusignFullStep({ onRemove={(fileName) => { handleRemoveFile(fileName); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedFiles} inputID={inputID as string} setError={(error) => { setUploadError(error); }} fileLimit={1} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} /> diff --git a/src/pages/ReimbursementAccount/EnterSignerInfo/subSteps/UploadDocuments.tsx b/src/pages/ReimbursementAccount/EnterSignerInfo/subSteps/UploadDocuments.tsx index e3d63c5dfbb5..a5027689fb32 100644 --- a/src/pages/ReimbursementAccount/EnterSignerInfo/subSteps/UploadDocuments.tsx +++ b/src/pages/ReimbursementAccount/EnterSignerInfo/subSteps/UploadDocuments.tsx @@ -140,7 +140,7 @@ function UploadDocuments({onNext, isEditing, policyID}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedIDs, copyOfIDInputID, setUploadedID); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedIDs} inputID={copyOfIDInputID} setError={(error) => { @@ -168,7 +168,7 @@ function UploadDocuments({onNext, isEditing, policyID}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedProofsOfAddress, addressProofInputID, setUploadedProofOfAddress); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedProofsOfAddress} inputID={addressProofInputID} setError={(error) => { @@ -195,7 +195,7 @@ function UploadDocuments({onNext, isEditing, policyID}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedProofsOfDirectors, directorsProofInputID, setUploadedProofsOfDirectors); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedProofsOfDirectors} inputID={directorsProofInputID} setError={(error) => { diff --git a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx index f0909bec25d1..b06ebf34e7e4 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx @@ -124,9 +124,9 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setError={(error) => { setUploadError(error, proofOfOwnershipInputID); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={defaultValues[proofOfOwnershipInputID]} inputID={proofOfOwnershipInputID} /> @@ -153,9 +153,9 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setError={(error) => { setUploadError(error, copyOfIDInputID); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={defaultValues[copyOfIDInputID]} inputID={copyOfIDInputID} /> @@ -180,9 +180,9 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setError={(error) => { setUploadError(error, addressProofInputID); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={defaultValues[addressProofInputID]} inputID={addressProofInputID} /> @@ -207,9 +207,9 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { setError={(error) => { setUploadError(error, codiceFiscaleInputID); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={defaultValues[codiceFiscaleInputID]} inputID={codiceFiscaleInputID} /> diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx index bb80ceea71fa..b8964b51b368 100644 --- a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx @@ -131,14 +131,14 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedIDs, COPY_OF_ID, setUploadedID); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedIDs} inputID={COPY_OF_ID} setError={(error) => { setUploadError(error, COPY_OF_ID); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} /> {translate('ownershipInfoStep.copyOfIDDescription')} {(isDocumentNeededStatus.isAddressProofNeeded || @@ -160,14 +160,14 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedProofsOfAddress, ADDRESS_PROOF, setUploadedProofOfAddress); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedProofsOfAddress} inputID={ADDRESS_PROOF} setError={(error) => { setUploadError(error, ADDRESS_PROOF); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} /> {translate('ownershipInfoStep.proofOfAddressDescription')} {(isDocumentNeededStatus.isProofOfDirectorsNeeded || isDocumentNeededStatus.isCodiceFiscaleNeeded || isDocumentNeededStatus.isPRDAndFSGNeeded) && ( @@ -188,14 +188,14 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedProofsOfDirectors, PROOF_OF_DIRECTORS, setUploadedProofsOfDirectors); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedProofsOfDirectors} inputID={PROOF_OF_DIRECTORS} setError={(error) => { setUploadError(error, PROOF_OF_DIRECTORS); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} /> {translate('signerInfoStep.proofOfDirectorsDescription')} {(isDocumentNeededStatus.isCodiceFiscaleNeeded || isDocumentNeededStatus.isPRDAndFSGNeeded) && } @@ -214,14 +214,14 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { onRemove={(fileName) => { handleRemoveFile(fileName, uploadedCodiceFiscale, CODICE_FISCALE, setUploadedCodiceFiscale); }} - acceptedFileTypes={[...CONST.NON_USD_BANK_ACCOUNT.ALLOWED_FILE_TYPES]} + acceptedFileTypes={[...CONST.CORPAY_DOCUMENT.ALLOWED_FILE_TYPES]} value={uploadedCodiceFiscale} inputID={CODICE_FISCALE} setError={(error) => { setUploadError(error, CODICE_FISCALE); }} - fileLimit={CONST.NON_USD_BANK_ACCOUNT.FILE_LIMIT} - maxFileSize={CONST.NON_USD_BANK_ACCOUNT.MAX_FILE_SIZE} + fileLimit={CONST.CORPAY_DOCUMENT.FILE_LIMIT} + maxFileSize={CONST.CORPAY_DOCUMENT.MAX_FILE_SIZE} /> {translate('signerInfoStep.codiceFiscaleDescription')} {isDocumentNeededStatus.isPRDAndFSGNeeded && } From cf666f71e0386b43c1eca5b2dbe0d504d3f157b2 Mon Sep 17 00:00:00 2001 From: "Maxence Coulibaly (via MelvinBot)" Date: Thu, 7 May 2026 13:20:16 +0000 Subject: [PATCH 4/5] Fix upload error not showing when form validation error already present When a form has a 'Field is required' validation error and the user then picks an oversized file, the size error was hidden because form validation errors (errors[inputID]) take precedence over errorFields in FormProvider. Clear form validation errors before setting upload-specific error fields. Co-authored-by: Maxence Coulibaly --- src/components/SubStepForms/DocusignFullStep.tsx | 1 + .../BeneficialOwnerDetailsFormSubSteps/Documents.tsx | 3 ++- .../NonUSD/SignerInfo/subSteps/UploadDocuments.tsx | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/SubStepForms/DocusignFullStep.tsx b/src/components/SubStepForms/DocusignFullStep.tsx index b07a52272b92..ffe41a3984e8 100644 --- a/src/components/SubStepForms/DocusignFullStep.tsx +++ b/src/components/SubStepForms/DocusignFullStep.tsx @@ -87,6 +87,7 @@ function DocusignFullStep({ return; } + clearErrors(formID); setErrorFields(formID, {[inputID]: {onUpload: error}}); }; diff --git a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx index b06ebf34e7e4..0be181053d56 100644 --- a/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/BeneficialOwnerInfo/BeneficialOwnerDetailsFormSubSteps/Documents.tsx @@ -13,7 +13,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import getCurrencyForNonUSDBankAccount from '@pages/ReimbursementAccount/NonUSD/utils/getCurrencyForNonUSDBankAccount'; import getNeededDocumentsStatusForBeneficialOwner from '@pages/ReimbursementAccount/NonUSD/utils/getNeededDocumentsStatusForBeneficialOwner'; -import {clearErrorFields, setDraftValues, setErrorFields} from '@userActions/FormActions'; +import {clearErrorFields, clearErrors, setDraftValues, setErrorFields} from '@userActions/FormActions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {FileObject} from '@src/types/utils/Attachment'; @@ -78,6 +78,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { return; } + clearErrors(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM); setErrorFields(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[inputID]: {onUpload: error}}); }; diff --git a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx index b8964b51b368..f413e08a6df2 100644 --- a/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/SignerInfo/subSteps/UploadDocuments.tsx @@ -17,7 +17,7 @@ import {getFieldRequiredErrors} from '@libs/ValidationUtils'; import getCurrencyForNonUSDBankAccount from '@pages/ReimbursementAccount/NonUSD/utils/getCurrencyForNonUSDBankAccount'; import getNeededDocumentsStatusForSignerInfo from '@pages/ReimbursementAccount/utils/getNeededDocumentsStatusForSignerInfo'; import WhyLink from '@pages/ReimbursementAccount/WhyLink'; -import {clearErrorFields, setDraftValues, setErrorFields} from '@userActions/FormActions'; +import {clearErrorFields, clearErrors, setDraftValues, setErrorFields} from '@userActions/FormActions'; import {openExternalLink} from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -97,6 +97,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { return; } + clearErrors(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM); setErrorFields(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[inputID]: {onUpload: error}}); }; From fe2f998ad51234fb7b6f9176a3b5fec4b28e8a54 Mon Sep 17 00:00:00 2001 From: "Maxence Coulibaly (via MelvinBot)" Date: Thu, 7 May 2026 14:19:23 +0000 Subject: [PATCH 5/5] Use formRef.resetFormFieldError to clear validation errors before showing upload errors Replace the clearErrors-only approach with formRef.resetFormFieldError to properly clear per-field validation errors (e.g. 'Field is required') before setting upload-specific errorFields. This ensures the upload size error is displayed when a form validation error was already present. Diff provided by MrMuzyk. Co-authored-by: Maxence Coulibaly --- src/components/SubStepForms/DocusignFullStep.tsx | 8 ++++++-- .../BeneficialOwnerDetailsFormSubSteps/Documents.tsx | 8 ++++++-- .../NonUSD/SignerInfo/subSteps/UploadDocuments.tsx | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/SubStepForms/DocusignFullStep.tsx b/src/components/SubStepForms/DocusignFullStep.tsx index ffe41a3984e8..cb461095c5b3 100644 --- a/src/components/SubStepForms/DocusignFullStep.tsx +++ b/src/components/SubStepForms/DocusignFullStep.tsx @@ -1,8 +1,8 @@ -import React, {useCallback, useState} from 'react'; +import React, {useCallback, useRef, useState} from 'react'; import Button from '@components/Button'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import type {FormInputErrors, FormOnyxKeys, FormOnyxValues} from '@components/Form/types'; +import type {FormInputErrors, FormOnyxKeys, FormOnyxValues, FormRef} from '@components/Form/types'; import InteractiveStepWrapper from '@components/InteractiveStepWrapper'; import Text from '@components/Text'; import UploadFile from '@components/UploadFile'; @@ -61,6 +61,8 @@ function DocusignFullStep({ const styles = useThemeStyles(); const {environmentURL} = useEnvironment(); + const formRef = useRef(null); + const [uploadedFiles, setUploadedFiles] = useState(defaultValue); const validate = useCallback( @@ -87,6 +89,7 @@ function DocusignFullStep({ return; } + formRef.current?.resetFormFieldError(inputID as string); clearErrors(formID); setErrorFields(formID, {[inputID]: {onUpload: error}}); }; @@ -107,6 +110,7 @@ function DocusignFullStep({ startStepIndex={startStepIndex} > (null); + const [uploadedProofOfOwnership, setUploadedProofOfOwnership] = useState(defaultValues[proofOfOwnershipInputID]); const [uploadedCopyOfID, setUploadedCopyOfID] = useState(defaultValues[copyOfIDInputID]); const [uploadedAddressProof, setUploadedAddressProof] = useState(defaultValues[addressProofInputID]); @@ -78,6 +80,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { return; } + formRef.current?.resetFormFieldError(inputID); clearErrors(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM); setErrorFields(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[inputID]: {onUpload: error}}); }; @@ -98,6 +101,7 @@ function Documents({onNext, isEditing, ownerBeingModifiedID}: DocumentsProps) { return ( (null); + const [uploadedIDs, setUploadedID] = useState(defaultValues[COPY_OF_ID]); const [uploadedProofsOfAddress, setUploadedProofOfAddress] = useState(defaultValues[ADDRESS_PROOF]); const [uploadedProofsOfDirectors, setUploadedProofsOfDirectors] = useState(defaultValues[PROOF_OF_DIRECTORS]); @@ -97,6 +99,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { return; } + formRef.current?.resetFormFieldError(inputID); clearErrors(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM); setErrorFields(ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM, {[inputID]: {onUpload: error}}); }; @@ -109,6 +112,7 @@ function UploadDocuments({onNext, isEditing}: UploadDocumentsProps) { return (