diff --git a/src/applications/simple-forms/20-10207/components/FileField.jsx b/src/applications/simple-forms/20-10207/components/FileField.jsx
deleted file mode 100644
index a28978894c6b..000000000000
--- a/src/applications/simple-forms/20-10207/components/FileField.jsx
+++ /dev/null
@@ -1,731 +0,0 @@
-/* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */
-/* Customized copy of Platform's FileField component
- * - Adds new optional prop `ariaLabelAdditionalText` to append additional
- * text to the upload button's aria-label attribute.
-*/
-import PropTypes from 'prop-types';
-import React, { useEffect, useState, useRef } from 'react';
-import { connect } from 'react-redux';
-import classNames from 'classnames';
-import {
- VaCard,
- VaModal,
-} from '@department-of-veterans-affairs/component-library/dist/react-bindings';
-
-import { toggleValues } from 'platform/site-wide/feature-toggles/selectors';
-import get from 'platform/utilities/data/get';
-import set from 'platform/utilities/data/set';
-import unset from 'platform/utilities/data/unset';
-import {
- displayFileSize,
- focusElement,
- scrollTo,
- scrollToFirstError,
-} from 'platform/utilities/ui';
-
-import { FILE_UPLOAD_NETWORK_ERROR_MESSAGE } from 'platform/forms-system/src/js/constants';
-import { $ } from 'platform/forms-system/src/js/utilities/ui';
-import {
- ShowPdfPassword,
- PasswordLabel,
- PasswordSuccess,
- readAndCheckFile,
- checkTypeAndExtensionMatches,
- checkIsEncryptedPdf,
- FILE_TYPE_MISMATCH_ERROR,
-} from 'platform/forms-system/src/js/utilities/file';
-import { usePreviousValue } from 'platform/forms-system/src/js/helpers';
-
-/**
- * Modal content callback
- * @typedef ModalContent
- * @type {function}
- * @property {string} fileName - name of file to be removed
- * @returns {JSX} - default='We’ll delete the uploaded file
- * {fileName}'
- */
-/**
- * UI options used in FileField
- * @typedef uiOptions
- * @type {object}
- * @property {string} buttonText='Upload' - upload button text
- * @property {string} addAnotherLabel='Upload another' - upload another text,
- * replaces upload button text when greater than one upload is showing
- * @property {string} ariaLabelAdditionalText='' - additional screen-reader text to be appended to upload button's aria-label attribute.
- * @property {string} tryAgain='Try again' - button in enableShortWorkflow
- * @property {string} newFile='Upload a new file' - button in enableShortWorkflow
- * @property {string} cancel='Cancel' - button visible while uploading & in enableShortWorkflow
- * @property {string} delete='Delete file' - delete button text
- * @property {string} modalTitle='Are you sure you want to delete this file?' -
- * delete confirmation modal title
- * @property {ModalContent} modalContent - delete confirmation modal content
- * @property {string} yesButton='Yes, delete this' - modal Yes button text
- * @property {string} noButton='No, keep this' - modal No button text
- */
-/**
- * FormData of supported files
- * @typeof Files
- * @type {object}
- * @property {string} name - file name
- * @property {boolean} uploading - flag indicating that an upload is in
- * progress
- * @property {string} confirmationCode - uuid of uploaded file
- * @property {string} attachmentId - form ID set by user
- * @property {string} errorMessage - error message string returned from API
- * @property {boolean} isEncrypted - (Encrypted PDF only; pre-upload only)
- * encrypted state of the file
- * @property {DOMFileObject} file - (Encrypted PDF only) File object, used
- * when user submits password
- */
-const FileField = props => {
- const {
- enableShortWorkflow,
- errorSchema,
- formContext,
- formData = [],
- idSchema,
- onBlur,
- onChange,
- registry,
- schema,
- uiSchema,
- } = props;
-
- const files = formData || [];
- const [progress, setProgress] = useState(0);
- const [uploadRequest, setUploadRequest] = useState(null);
- const [isUploading, setIsUploading] = useState(
- files.some(file => file.uploading),
- );
- const [showRemoveModal, setShowRemoveModal] = useState(false);
- const [removeIndex, setRemoveIndex] = useState(null);
- const [initialized, setInitialized] = useState(false);
-
- const previousValue = usePreviousValue(formData);
- const fileInputRef = useRef(null);
- const fileButtonRef = useRef(null);
-
- const uiOptions = uiSchema?.['ui:options'];
-
- const maxItems = schema.maxItems || Infinity;
- const { SchemaField } = registry.fields;
- const attachmentIdRequired = schema.additionalItems.required
- ? schema.additionalItems.required.includes('attachmentId')
- : false;
- const uswds = true;
-
- const content = {
- upload: uiOptions.buttonText || 'Upload',
- uploadAnother: uiOptions.addAnotherLabel || 'Upload another',
- ariaLabelAdditionalText: uiOptions.ariaLabelAdditionalText || '',
- passwordLabel: fileName => `Add a password for ${fileName}`,
- tryAgain: 'Try again',
- tryAgainLabel: fileName => `Try uploading ${fileName} again`,
- newFile: 'Upload a new file',
- cancel: 'Cancel',
- cancelLabel: fileName => `Cancel upload of ${fileName}`,
- delete: 'Delete file',
- deleteLabel: fileName => `Delete ${fileName}`,
- modalTitle:
- uiOptions.modalTitle || 'Are you sure you want to delete this file?',
- modalContent: fileName =>
- uiOptions.modalContent?.(fileName || 'Unknown') || (
-
- We’ll delete the uploaded file{' '}
- {fileName || 'Unknown'}
-
- ),
- yesButton: 'Yes, delete this file',
- noButton: 'No, keep this',
- error: 'Error',
- };
-
- const Tag = formContext.onReviewPage && formContext.reviewMode ? 'dl' : 'div';
-
- // hide upload & delete buttons on review & submit page when reviewing
- const showButtons = !formContext.reviewMode && !isUploading;
-
- const titleString =
- typeof uiSchema['ui:title'] === 'string'
- ? uiSchema['ui:title']
- : schema.title;
-
- const getFileListId = index => `${idSchema.$id}_file_${index}`;
-
- // This is always true if enableShortWorkflow is not enabled
- // If enabled, do not allow upload if any error exist
- const checkUploadVisibility = () =>
- !enableShortWorkflow ||
- (enableShortWorkflow &&
- !files.some((file, index) => {
- const errors =
- errorSchema?.[index]?.__errors ||
- [file.errorMessage].filter(error => error);
-
- return errors.length > 0;
- }));
-
- const focusAddAnotherButton = () => {
- // Add a timeout to allow for the upload button to reappear in the DOM
- // before trying to focus on it
- setTimeout(() => {
- // focus on upload button, not the label
- focusElement(
- // including `#upload-button` because RTL can't access the shadowRoot
- 'button, #upload-button',
- {},
- $(`#upload-button`)?.shadowRoot,
- );
- }, 100);
- };
-
- const updateProgress = percent => {
- setProgress(percent);
- };
-
- useEffect(
- () => {
- const prevFiles = previousValue || [];
- fileButtonRef?.current?.classList?.toggle(
- 'vads-u-display--none',
- !checkUploadVisibility(),
- );
- if (initialized && files.length !== prevFiles.length) {
- focusAddAnotherButton();
- }
-
- const hasUploading = files.some(file => file.uploading);
- const wasUploading = prevFiles.some(file => file.uploading);
- setIsUploading(hasUploading);
- if (hasUploading && !wasUploading) {
- setProgress(0);
- }
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [formData],
- );
-
- useEffect(
- () => {
- // The File object is not preserved in the save-in-progress data
- // We need to remove these entries; an empty `file` is included in the
- // entry, but if API File Object still exists (within the same session), we
- // can't use Object.keys() on it because it returns an empty array
- const newData = files.filter(
- // keep - file may not exist (already uploaded)
- // keep - file may contain File object; ensure name isn't empty
- // remove - file may be an empty object
- data => !data.file || (data.file?.name || '') !== '',
- );
- if (newData.length !== files.length) {
- onChange(newData);
- }
- setInitialized(true);
- },
- [files, onChange],
- );
-
- /**
- * Add file to list and upload
- * @param {Event} event - DOM File upload event
- * @param {number} index - uploaded file index, if already uploaded
- * @param {string} password - encrypted PDF password, only defined by
- * `onSubmitPassword` function
- * @listens
- */
- const onAddFile = async (event, index = null, password) => {
- if (event.target?.files?.length) {
- const currentFile = event.target.files[0];
- const allFiles = props.formData || [];
- const addUiOptions = props.uiSchema['ui:options'];
- // needed for FileField unit tests
- const { mockReadAndCheckFile } = uiOptions;
-
- let idx = index;
- if (idx === null) {
- idx = allFiles.length === 0 ? 0 : allFiles.length;
- }
-
- let checkResults;
- const checks = { checkTypeAndExtensionMatches, checkIsEncryptedPdf };
-
- if (currentFile.type === 'testing') {
- // Skip read file for Cypress testing
- checkResults = {
- checkTypeAndExtensionMatches: true,
- checkIsEncryptedPdf: false,
- };
- } else {
- // read file mock for unit testing
- checkResults =
- typeof mockReadAndCheckFile === 'function'
- ? mockReadAndCheckFile()
- : await readAndCheckFile(currentFile, checks);
- }
-
- if (!checkResults.checkTypeAndExtensionMatches) {
- allFiles[idx] = {
- file: currentFile,
- name: currentFile.name,
- errorMessage: FILE_TYPE_MISMATCH_ERROR,
- };
- props.onChange(allFiles);
- return;
- }
-
- // Check if the file is an encrypted PDF
- if (
- currentFile.name?.endsWith('pdf') &&
- !password &&
- checkResults.checkIsEncryptedPdf
- ) {
- allFiles[idx] = {
- file: currentFile,
- name: currentFile.name,
- isEncrypted: true,
- };
-
- props.onChange(allFiles);
- // wait for user to enter a password before uploading
- return;
- }
-
- setUploadRequest(
- props.formContext.uploadFile(
- currentFile,
- addUiOptions,
- updateProgress,
- file => {
- // formData is undefined initially
- const newData = props.formData || [];
- newData[idx] = { ...file, isEncrypted: !!password };
- onChange(newData);
- // Focus on the 'Cancel' button when a file is being uploaded
- if (file.uploading) {
- $('.schemaform-file-uploading .cancel-upload')?.focus();
- }
- // Focus on the file card after the file has finished uploading
- if (!file.uploading) {
- $(getFileListId(idx))?.focus();
- }
- setUploadRequest(null);
- },
- () => {
- setUploadRequest(null);
- },
- formContext.trackingPrefix,
- password,
- props.enableShortWorkflow,
- ),
- );
- }
- };
-
- const onSubmitPassword = (file, index, password) => {
- if (file && password) {
- onAddFile({ target: { files: [file] } }, index, password);
- }
- };
-
- const onAttachmentIdChange = (index, value) => {
- if (!value) {
- props.onChange(unset([index, 'attachmentId'], props.formData));
- } else {
- props.onChange(set([index, 'attachmentId'], value, props.formData));
- }
- };
-
- const onAttachmentNameChange = (index, value) => {
- if (!value) {
- props.onChange(unset([index, 'name'], props.formData));
- } else {
- props.onChange(set([index, 'name'], value, props.formData));
- }
- };
-
- const removeFile = (index, focusAddButton = true) => {
- const newFileList = props.formData.filter((__, idx) => index !== idx);
- if (!newFileList.length) {
- props.onChange();
- } else {
- props.onChange(newFileList);
- }
-
- // clear file input value; without this, the user won't be able to open the
- // upload file window
- if (fileInputRef.current) {
- fileInputRef.current.value = '';
- }
-
- // When other actions follow removeFile, we do not want to apply this focus
- if (focusAddButton) {
- focusAddAnotherButton();
- }
- };
-
- const openRemoveModal = index => {
- setRemoveIndex(index);
- setShowRemoveModal(true);
- };
-
- const closeRemoveModal = ({ remove = false } = {}) => {
- const idx = removeIndex;
- setRemoveIndex(null);
- setShowRemoveModal(false);
- if (remove) {
- removeFile(idx);
- } else {
- setTimeout(() => {
- focusElement(
- 'button, .delete-upload',
- {},
- $(`#${getFileListId(idx)} .delete-upload`)?.shadowRoot,
- );
- });
- }
- };
-
- const cancelUpload = index => {
- if (uploadRequest) {
- uploadRequest.abort();
- }
- removeFile(index);
- };
-
- const retryLastUpload = (index, file) => {
- onAddFile({ target: { files: [file] } }, index);
- };
-
- const deleteThenAddFile = index => {
- removeFile(index, false);
- fileInputRef.current?.click();
- };
-
- const getRetryFunction = (allowRetry, index, file) => {
- return allowRetry
- ? () => retryLastUpload(index, file)
- : () => deleteThenAddFile(index);
- };
-
- const uploadText = content[files.length > 0 ? 'uploadAnother' : 'upload'];
-
- return (
-
-
closeRemoveModal({ remove: true })}
- onSecondaryButtonClick={closeRemoveModal}
- visible={showRemoveModal}
- uswds={uswds}
- >
-
- {removeIndex !== null
- ? content.modalContent(files[removeIndex]?.name)
- : null}
-
-
- {files.length > 0 && (
-
- {files.map((file, index) => {
- const errors =
- errorSchema?.[index]?.__errors ||
- [file.errorMessage].filter(error => error);
- const hasErrors = errors.length > 0;
- const itemClasses = classNames(
- 'vads-u-margin-bottom--2 vads-u-padding--2',
- {
- 'schemaform-file-error usa-input-error':
- hasErrors && !file.uploading,
- },
- );
- const itemSchema = schema.items[index];
- const attachmentIdSchema = {
- $id: `${idSchema.$id}_${index}_attachmentId`,
- };
- const attachmentNameSchema = {
- $id: `${idSchema.$id}_${index}_attachmentName`,
- };
- const attachmentIdErrors = get(
- [index, 'attachmentId'],
- errorSchema,
- );
- const attachmentNameErrors = get([index, 'name'], errorSchema);
- const showPasswordInput =
- file.isEncrypted && !file.confirmationCode;
- const showPasswordSuccess =
- file.isEncrypted && file.confirmationCode;
- const description =
- (!file.uploading && uiOptions.itemDescription) || '';
-
- const fileListId = getFileListId(index);
- const fileNameId = `${idSchema.$id}_file_name_${index}`;
-
- if (hasErrors) {
- setTimeout(() => {
- scrollToFirstError();
- if (enableShortWorkflow) {
- const retryButton = $(`[name="retry_upload_${index}"]`);
- if (retryButton) {
- focusElement('button', {}, retryButton?.shadowRoot);
- }
- } else if (showPasswordInput) {
- focusElement(`#${fileListId} .usa-input-error-message`);
- } else {
- focusElement('.usa-input-error, .input-error-date, [error]');
- }
- }, 250);
- } else if (showPasswordInput) {
- setTimeout(() => {
- const passwordInput = $(`[name="get_password_${index}"]`);
- if (passwordInput) {
- focusElement('input', {}, passwordInput?.shadowRoot);
- scrollTo(`get_password_${index}"]`);
- }
- }, 100);
- }
-
- const allowRetry = errors[0] === FILE_UPLOAD_NETWORK_ERROR_MESSAGE;
-
- const retryButtonText =
- content[allowRetry ? 'tryAgain' : 'newFile'];
- const deleteButtonText =
- content[enableShortWorkflow && hasErrors ? 'cancel' : 'delete'];
-
- const getUiSchema = innerUiSchema =>
- typeof innerUiSchema === 'function'
- ? innerUiSchema({ fileId: fileNameId, index })
- : innerUiSchema;
-
- // make index available to widgets in attachment ui schema
- const indexedRegistry = {
- ...registry,
- formContext: {
- ...registry.formContext,
- pagePerItemIndex: index,
- },
- };
-
- return (
- -
-
- {file.uploading && (
-
-
- {file.name}
-
-
- {/* no USWDS v3 "activity progress bar" */}
-
- {
- cancelUpload(index);
- }}
- label={content.cancelLabel(file.name)}
- text={content.cancel}
- uswds={uswds}
- />
-
- )}
- {description && {description}
}
- {!file.uploading && (
- <>
-
- {file.name}
-
- {file?.size && {displayFileSize(file.size)}
}
- >
- )}
- {(showPasswordInput || showPasswordSuccess) && (
-
- )}
- {showPasswordSuccess && }
- {!hasErrors &&
- !showPasswordInput &&
- get('properties.attachmentId', itemSchema) && (
-
- onAttachmentIdChange(index, value)}
- onBlur={onBlur}
- registry={indexedRegistry}
- disabled={props.disabled}
- readonly={props.readonly}
- />
-
- )}
- {!hasErrors &&
- !showPasswordInput &&
- uiOptions.attachmentName && (
-
-
- onAttachmentNameChange(index, value)
- }
- onBlur={onBlur}
- registry={indexedRegistry}
- disabled={props.disabled}
- readonly={props.readonly}
- />
-
- )}
- {!file.uploading &&
- hasErrors && (
-
- Error {errors[0]}
-
- )}
- {showPasswordInput && (
-
- )}
- {!formContext.reviewMode &&
- !isUploading && (
-
- {hasErrors &&
- enableShortWorkflow && (
-
- )}
- {
- openRemoveModal(index);
- }}
- label={content.deleteLabel(file.name)}
- text={deleteButtonText}
- uswds={uswds}
- />
-
- )}
-
-
- );
- })}
-
- )}
- {// Don't render an upload button on review & submit page while in
- // review mode
- showButtons && (
- <>
- {(maxItems === null || files.length < maxItems) &&
- // Prevent additional upload if any upload has error state
- checkUploadVisibility() && (
-
- )}
-
`.${item}`).join(',')}
- className="vads-u-display--none"
- id={idSchema.$id}
- name={idSchema.$id}
- onChange={onAddFile}
- />
- >
- )}
-
- );
-};
-
-FileField.propTypes = {
- schema: PropTypes.object.isRequired,
- onChange: PropTypes.func.isRequired,
- disabled: PropTypes.bool,
- enableShortWorkflow: PropTypes.bool,
- errorSchema: PropTypes.object,
- formContext: PropTypes.shape({
- onReviewPage: PropTypes.bool,
- reviewMode: PropTypes.bool,
- trackingPrefix: PropTypes.string,
- uploadFile: PropTypes.func,
- }),
- formData: PropTypes.array,
- idSchema: PropTypes.object,
- readonly: PropTypes.bool,
- registry: PropTypes.shape({
- fields: PropTypes.shape({
- SchemaField: PropTypes.func,
- }),
- formContext: PropTypes.shape({}),
- }),
- requiredSchema: PropTypes.object,
- uiSchema: PropTypes.object,
- onBlur: PropTypes.func,
-};
-
-const mapStateToProps = state => ({
- enableShortWorkflow: toggleValues(state).file_upload_short_workflow_enabled,
-});
-
-export { FileField };
-
-export default connect(mapStateToProps)(FileField);
diff --git a/src/applications/simple-forms/20-10207/pages/evidenceALS.js b/src/applications/simple-forms/20-10207/pages/evidenceALS.js
index 4f19c297cda4..b2707399966b 100644
--- a/src/applications/simple-forms/20-10207/pages/evidenceALS.js
+++ b/src/applications/simple-forms/20-10207/pages/evidenceALS.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import AlsViewField from '../components/AlsViewField';
import { ALS_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/pages/evidenceFinancialHardship.js b/src/applications/simple-forms/20-10207/pages/evidenceFinancialHardship.js
index b5db1a06328b..6ca59025a135 100644
--- a/src/applications/simple-forms/20-10207/pages/evidenceFinancialHardship.js
+++ b/src/applications/simple-forms/20-10207/pages/evidenceFinancialHardship.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import FinancialHardshipViewField from '../components/FinancialHardshipViewField';
import { FINANCIAL_HARDSHIP_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/pages/evidenceMedalAward.js b/src/applications/simple-forms/20-10207/pages/evidenceMedalAward.js
index a87c0187756c..0716d68760ee 100644
--- a/src/applications/simple-forms/20-10207/pages/evidenceMedalAward.js
+++ b/src/applications/simple-forms/20-10207/pages/evidenceMedalAward.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import MedalAwardViewField from '../components/MedalAwardViewField';
import { MEDAL_AWARD_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/pages/evidencePowDocuments.js b/src/applications/simple-forms/20-10207/pages/evidencePowDocuments.js
index 4e351601845a..6f7568fa2927 100644
--- a/src/applications/simple-forms/20-10207/pages/evidencePowDocuments.js
+++ b/src/applications/simple-forms/20-10207/pages/evidencePowDocuments.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import PowViewField from '../components/PowViewField';
import { POW_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/pages/evidenceTerminalIllness.js b/src/applications/simple-forms/20-10207/pages/evidenceTerminalIllness.js
index aaa5c73b32ef..c143dd8858de 100644
--- a/src/applications/simple-forms/20-10207/pages/evidenceTerminalIllness.js
+++ b/src/applications/simple-forms/20-10207/pages/evidenceTerminalIllness.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import TerminalIllnessViewField from '../components/TerminalIllnessViewField';
import { TERMINAL_ILLNESS_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/pages/evidenceVSI.js b/src/applications/simple-forms/20-10207/pages/evidenceVSI.js
index e831da3af135..9f2486a0c73b 100644
--- a/src/applications/simple-forms/20-10207/pages/evidenceVSI.js
+++ b/src/applications/simple-forms/20-10207/pages/evidenceVSI.js
@@ -1,7 +1,7 @@
import environment from 'platform/utilities/environment';
import { titleUI } from 'platform/forms-system/src/js/web-component-patterns/titlePattern';
-import { FileField } from '../components/FileField';
+import FileField from 'platform/forms-system/src/js/fields/FileField';
import VsiViewField from '../components/VsiViewField';
import { VSI_DESCRIPTION } from '../config/constants';
diff --git a/src/applications/simple-forms/20-10207/tests/e2e/fixtures/data/veteran.json b/src/applications/simple-forms/20-10207/tests/e2e/fixtures/data/veteran.json
index 76cc03a71273..684734e18a83 100644
--- a/src/applications/simple-forms/20-10207/tests/e2e/fixtures/data/veteran.json
+++ b/src/applications/simple-forms/20-10207/tests/e2e/fixtures/data/veteran.json
@@ -22,7 +22,7 @@
},
"veteranPhone": "1234567890",
"otherReasons": {
- "OVER_85": true
+ "OVER_85": true
},
"view:hasReceivedMedicalTreatment": true,
"medicalTreatments": [
diff --git a/src/applications/simple-forms/20-10207/tests/e2e/fixtures/mocks/local-mock-responses.js b/src/applications/simple-forms/20-10207/tests/e2e/fixtures/mocks/local-mock-responses.js
index 9bb9bb54413f..67e1d4a8177f 100644
--- a/src/applications/simple-forms/20-10207/tests/e2e/fixtures/mocks/local-mock-responses.js
+++ b/src/applications/simple-forms/20-10207/tests/e2e/fixtures/mocks/local-mock-responses.js
@@ -16,14 +16,7 @@ const responses = {
'GET /v0/feature_toggles': mockFeatureToggles,
'GET /v0/in_progress_forms/20-10207': mockSipGet,
'PUT /v0/in_progress_forms/20-10207': mockSipPut,
- 'POST /simple_forms_api/v1/simple_forms/submit_financial_hardship_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_terminal_illness_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_als_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_vsi_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_pow_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_pow_documents2': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_medal_award_documents': mockUpload,
- 'POST /simple_forms_api/v1/simple_forms/submit_medal_award_documents2': mockUpload,
+ 'POST /simple_forms_api/v1/simple_forms/submit_supporting_documents': mockUpload,
'POST /simple_forms_api/v1/simple_forms': mockSubmit,
};
diff --git a/src/applications/simple-forms/20-10207/tests/unit/components/FileField.unit.spec.jsx b/src/applications/simple-forms/20-10207/tests/unit/components/FileField.unit.spec.jsx
deleted file mode 100644
index 1f74ea199733..000000000000
--- a/src/applications/simple-forms/20-10207/tests/unit/components/FileField.unit.spec.jsx
+++ /dev/null
@@ -1,1505 +0,0 @@
-import React from 'react';
-import { expect } from 'chai';
-import sinon from 'sinon';
-import { render, fireEvent, waitFor } from '@testing-library/react';
-import { Provider } from 'react-redux';
-
-import { uploadStore } from 'platform/forms-system/test/config/helpers';
-import {
- DefinitionTester,
- getFormDOM,
-} from 'platform/testing/unit/schemaform-utils';
-
-import { FILE_UPLOAD_NETWORK_ERROR_MESSAGE } from 'platform/forms-system/src/js/constants';
-import { fileTypeSignatures } from 'platform/forms-system/src/js/utilities/file';
-import fileUploadUI, {
- fileSchema,
-} from 'platform/forms-system/src/js/definitions/file';
-import { $, $$ } from 'platform/forms-system/src/js/utilities/ui';
-import { MISSING_PASSWORD_ERROR } from 'platform/forms-system/src/js/validation';
-import { FileField } from '../../../components/FileField';
-
-const formContext = {
- setTouched: sinon.spy(),
-};
-const requiredSchema = {};
-
-describe('Schemaform ', () => {
- it('should render', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- const uploadButton = $('#upload-button', container);
- expect(uploadButton).to.have.attribute('text', 'Upload');
- const fileInput = $('input[type="file"]', container);
- expect(fileInput).to.have.attribute('accept', '.pdf,.jpg,.jpeg,.png');
- });
-
- it('should render files', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('li', container).textContent).to.contain('Test file name.pdf');
- expect($('strong.dd-privacy-hidden[data-dd-action-name]', container)).to
- .exist;
- });
-
- it('should render uswds components', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files', { uswds: true });
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.delete-upload[uswds]', container)).to.exist;
- expect($('#upload-button[uswds]', container)).to.exist;
- });
-
- it('should remove files with empty file object when initializing', async () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test1.png',
- },
- {
- file: {},
- name: 'Test2.pdf',
- },
- {
- file: {
- name: 'fake', // should never happen
- },
- name: 'Test3.txt',
- },
- {
- file: new File([1, 2, 3], 'Test4.jpg'),
- name: 'Test4.jpg',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const onChange = sinon.spy();
- render(
- ,
- );
-
- await waitFor(() => {
- expect(onChange.calledOnce).to.be.true;
- expect(onChange.firstCall.args[0].length).to.equal(3);
- // empty file object was removed;
- expect(onChange.firstCall.args[0][1].name).to.equal('Test3.txt');
- });
- });
-
- it('should call onChange once when deleting files', async () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const onChange = sinon.spy();
-
- const { container } = render(
- ,
- );
-
- const modal = $('va-modal', container);
- expect(modal.getAttribute('visible')).to.eq('false');
-
- fireEvent.click($('.delete-upload', container));
- expect(modal.getAttribute('visible')).to.eq('true');
-
- // click yes in modal
- $('va-modal', container).__events.primaryButtonClick();
-
- await waitFor(() => {
- expect(onChange.calledOnce).to.be.true;
- expect(onChange.firstCall.args.length).to.equal(0);
- });
- });
-
- it('should render uploading', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- name: 'Test.pdf',
- uploading: true,
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('va-progress-bar', container)).to.exist;
- const button = $('.cancel-upload', container);
- expect(button).to.exist;
- expect(button.getAttribute('text')).to.eq('Cancel');
- expect(button.getAttribute('label')).to.eq('Cancel upload of Test.pdf');
- });
-
- it('should show progress', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- uploading: true,
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- const progressBar = $('va-progress-bar', container);
- expect(progressBar.getAttribute('percent')).to.equal('0');
-
- // How to call `updateProgress(20)`? This method doesn't work:
- // https://github.com/testing-library/react-testing-library/issues/638#issuecomment-615937561
- // expect(progressBar.getAttribute('percent')).to.equal('20');
- });
-
- it('should render error', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- errorMessage: 'some error',
- },
- ];
- const errorSchema = {
- 0: {
- __errors: ['Bad error'],
- },
- };
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // Prepend 'Error' for screenreader
- expect($('.schemaform-file-error', container).textContent).to.contain(
- 'Error Bad error',
- );
- expect($('span[role="alert"]', container)).to.exist;
- });
-
- it('should not render upload button if over max items', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- maxItems: 1,
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.upload-button-label', container)).to.not.exist;
- });
-
- it('should not render upload or delete button on review & submit page while in review mode', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.upload-button-label', container)).to.not.exist;
- expect($('.delete-upload', container)).to.not.exist;
- });
-
- it('should render upload or delete button on review & submit page while in edit mode', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- size: 12345678,
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.upload-button-label', container)).to.exist;
- expect($('.delete-upload', container)).to.exist;
-
- const text = $('li', container).textContent;
- expect(text).to.include('Test file name.pdf');
- expect(text).to.include('12MB');
- });
-
- it('should delete file', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const { container } = render(
-
-
- ,
- );
-
- const uploadButton = $('#upload-button', container);
- expect($$('li', container)).to.not.be.empty;
-
- const modal = $('va-modal', container);
- expect(modal.getAttribute('visible')).to.eq('false');
-
- fireEvent.click($('.delete-upload', container));
- expect(modal.getAttribute('visible')).to.eq('true');
-
- // click yes in modal
- $('va-modal', container).__events.primaryButtonClick();
-
- await waitFor(() => {
- expect($$('li', container)).to.be.empty;
- expect(document.activeElement).to.eq(uploadButton);
- });
- });
-
- it('should not delete file when "No" is selected in modal', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const { container } = render(
-
-
- ,
- );
-
- expect($$('li', container)).to.not.be.empty;
-
- const modal = $('va-modal', container);
- expect(modal.getAttribute('visible')).to.eq('false');
-
- const deleteButton = $('.delete-upload', container);
- fireEvent.click(deleteButton);
- expect(modal.getAttribute('visible')).to.eq('true');
-
- // click no in modal
- $('va-modal', container).__events.secondaryButtonClick();
-
- await waitFor(() => {
- expect($$('li', container)).to.not.be.empty;
- expect(document.activeElement).to.eq(deleteButton);
- });
- });
-
- it('should upload png file', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const mockFile = {
- name: 'test.png',
- type: fileTypeSignatures.png.mime,
- };
- const uiOptions = {
- ...uiSchema['ui:options'],
- mockReadAndCheckFile: () => ({
- checkIsEncryptedPdf: false,
- checkTypeAndExtensionMatches: true,
- }),
- };
- const uploadFile = sinon.spy();
- const form = render(
-
-
- ,
- );
- const formDOM = getFormDOM(form);
-
- formDOM.files('input[type=file]', [mockFile]);
-
- await waitFor(() => {
- expect(uploadFile.firstCall.args[0]).to.eql(mockFile);
- expect(uploadFile.firstCall.args[1]).to.eql(uiOptions);
- expect(uploadFile.firstCall.args[2]).to.be.a('function');
- expect(uploadFile.firstCall.args[3]).to.be.a('function');
- expect(uploadFile.firstCall.args[4]).to.be.a('function');
- });
- });
-
- it('should upload unencrypted pdf file', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const uploadFile = sinon.spy();
- const mockPDFFile = {
- name: 'test.PDF',
- type: fileTypeSignatures.pdf.mime,
- };
- const uiOptions = {
- ...uiSchema['ui:options'],
- mockReadAndCheckFile: () => ({
- checkIsEncryptedPdf: false,
- checkTypeAndExtensionMatches: true,
- }),
- };
- const fileField = {
- ...uiSchema,
- 'ui:options': uiOptions,
- };
-
- const form = render(
-
-
- ,
- );
- const formDOM = getFormDOM(form);
-
- formDOM.files('input[type=file]', [mockPDFFile]);
-
- await waitFor(() => {
- expect(uploadFile.firstCall.args[0]).to.eql(mockPDFFile);
- expect(uploadFile.firstCall.args[1]).to.eql(uiOptions);
- expect(uploadFile.firstCall.args[2]).to.be.a('function');
- expect(uploadFile.firstCall.args[3]).to.be.a('function');
- expect(uploadFile.firstCall.args[4]).to.be.a('function');
- });
- });
-
- it('should upload test file using "testing" file type to bypass checks', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const mockFile = {
- name: 'test.pdf',
- type: 'testing',
- };
- const uploadFile = sinon.spy();
- const form = render(
-
-
- ,
- );
- const formDOM = getFormDOM(form);
-
- formDOM.files('input[type=file]', [mockFile]);
-
- await waitFor(() => {
- expect(uploadFile.firstCall.args[0]).to.eql(mockFile);
- expect(uploadFile.firstCall.args[1]).to.eql(uiSchema['ui:options']);
- expect(uploadFile.firstCall.args[2]).to.be.a('function');
- expect(uploadFile.firstCall.args[3]).to.be.a('function');
- expect(uploadFile.firstCall.args[4]).to.be.a('function');
- });
- });
-
- it('should not call uploadFile when initially adding an encrypted PDF', async () => {
- const uiSchema = fileUploadUI('Files');
- const schema = {
- type: 'object',
- properties: {
- fileField: fileSchema,
- },
- };
- const uploadFile = sinon.spy();
- const isFileEncrypted = () => Promise.resolve(true);
- const fileField = {
- ...uiSchema,
- 'ui:options': {
- ...uiSchema['ui:options'],
- isFileEncrypted,
- },
- };
-
- const form = render(
-
-
- ,
- );
- const formDOM = getFormDOM(form);
-
- formDOM.files('input[type=file]', [{ name: 'test-pw.pdf' }]);
-
- await waitFor(() => {
- expect(uploadFile.notCalled).to.be.true;
- });
- });
-
- it('should render file with attachment type', () => {
- let testProps = null;
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- items: [
- {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- size: 54321,
- },
- ];
- const registry = {
- fields: {
- SchemaField: props => {
- testProps = props;
- return ;
- },
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- const text = $('li', container).textContent;
- expect(text).to.contain('Test file name.pdf');
- expect(text).to.contain('53KB');
-
- expect(testProps.schema.type).to.eq('string');
- });
-
- it('should render file with attachmentName', () => {
- let testProps = null;
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- items: [
- {
- type: 'object',
- properties: {
- name: {
- type: 'string',
- },
- },
- },
- ],
- };
- const uiSchema = fileUploadUI('Files', {
- attachmentName: ({ fileId, index }) => ({
- 'ui:title': 'Document name',
- 'ui:options': {
- widgetProps: {
- 'aria-describedby': fileId,
- 'data-index': index,
- },
- },
- }),
- });
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- size: 987654,
- },
- ];
- const registry = {
- fields: {
- SchemaField: props => {
- testProps = props;
- return ;
- },
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- const text = $('li').textContent;
- expect(text).to.contain('Test file name.pdf');
- expect(text).to.contain('965KB');
-
- const deleteButton = $('.delete-upload', container);
- expect(deleteButton?.getAttribute('label')).to.eq(
- 'Delete Test file name.pdf',
- );
-
- // check ids & index passed into SchemaField
- expect(testProps.schema).to.equal(schema.items[0].properties.name);
- expect(testProps.registry.formContext.pagePerItemIndex).to.eq(0);
-
- const { widgetProps } = testProps.uiSchema['ui:options'];
- expect(widgetProps['aria-describedby']).to.eq('field_file_name_0');
- expect(widgetProps['data-index']).to.eq(0);
- });
-
- it('should render file with attachmentSchema', () => {
- let testProps = null;
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- type: 'array',
- additionalItems: {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- items: [
- {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- ],
- };
- const uiSchema = fileUploadUI('Files', {
- attachmentName: false,
- attachmentSchema: ({ fileId, index }) => ({
- 'ui:title': 'Document type',
- 'ui:options': {
- widgetProps: {
- 'aria-describedby': fileId,
- 'data-index': index,
- },
- },
- }),
- });
- const formData = [
- {
- name: 'Test file name.pdf',
- attachmentId: '1234',
- },
- ];
- const registry = {
- fields: {
- SchemaField: props => {
- testProps = props;
- return ;
- },
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.delete-upload', container).getAttribute('label')).to.eq(
- 'Delete Test file name.pdf',
- );
-
- // check ids & index passed into SchemaField
- const { widgetProps } = testProps.uiSchema['ui:options'];
- expect(testProps.schema).to.equal(schema.items[0].properties.attachmentId);
- expect(testProps.registry.formContext.pagePerItemIndex).to.eq(0);
- expect(widgetProps['aria-describedby']).to.eq('field_file_name_0');
- expect(widgetProps['data-index']).to.eq(0);
- });
-
- // Accessibility checks
- it('should render a div wrapper when not on the review page', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- items: [
- {
- type: 'object',
- properties: {
- name: {
- type: 'string',
- },
- },
- },
- ],
- };
- const uiSchema = fileUploadUI('Files', {
- attachmentSchema: {
- 'ui:title': 'Document ID',
- },
- attachmentName: {
- 'ui:title': 'Document name',
- },
- });
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // expect dl wrapper on review page
- expect($('div.review', container)).to.exist;
- });
-
- it('should render a dl wrapper when on the review page', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {
- type: 'object',
- properties: {
- attachmentId: {
- type: 'string',
- },
- },
- },
- items: [
- {
- type: 'object',
- properties: {
- name: {
- type: 'string',
- },
- },
- },
- ],
- };
- const uiSchema = fileUploadUI('Files', {
- attachmentName: {
- 'ui:title': 'Document name',
- },
- });
- const formData = [
- {
- confirmationCode: 'abcdef',
- name: 'Test file name.pdf',
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // expect dl wrapper on review page
- expect($('dl.review', container)).to.exist;
- });
-
- it('should render schema title', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- title: 'schema title',
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI(uiSchema title
);
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('#upload-button', container).getAttribute('label')).to.contain(
- 'schema title',
- );
- });
-
- it('should render cancel button with secondary class', () => {
- const idSchema = {
- $id: 'field',
- };
- const schema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- };
- const uiSchema = fileUploadUI('Files');
- const formData = [
- {
- uploading: true,
- },
- ];
- const registry = {
- fields: {
- SchemaField: () => ,
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- const cancelButton = $('.cancel-upload', container);
- expect(cancelButton.getAttribute('text')).to.equal('Cancel');
- });
-
- describe('enableShortWorkflow is true', () => {
- const mockIdSchema = {
- $id: 'field',
- };
- const mockSchema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- maxItems: 4,
- };
- const mockUiSchema = fileUploadUI('Files');
- const mockFormDataWithError = [
- {
- errorMessage: 'some error message',
- },
- ];
- const mockErrorSchemaWithError = {
- 0: {
- __errors: ['ERROR-123'],
- },
- };
- const mockRegistry = {
- fields: {
- SchemaField: () => ,
- },
- };
-
- it('should not render main upload button while file has error', () => {
- const idSchema = {
- $id: 'myIdSchemaId',
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- enableShortWorkflow
- />,
- );
-
- // id for main upload button is interpolated {idSchema.$id}_add_label
- const mainUploadButton = $('#myIdSchemaId_add_label', container);
- expect(mainUploadButton).to.not.exist;
- });
-
- it('should render Upload a new file button for file with error', () => {
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- enableShortWorkflow
- />,
- );
-
- // This button is specific to the file that has the error
- const errorFileUploadButton = $('.retry-upload', container);
- expect(errorFileUploadButton.getAttribute('text')).to.equal(
- 'Upload a new file',
- );
- });
-
- it('should render Try again button for file with error', () => {
- const errorSchema = {
- 0: {
- __errors: [FILE_UPLOAD_NETWORK_ERROR_MESSAGE],
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- enableShortWorkflow
- />,
- );
-
- // This button is specific to the file that has the error
- const individualFileTryAgainButton = $('.retry-upload', container);
- expect(individualFileTryAgainButton.getAttribute('text')).to.equal(
- 'Try again',
- );
- });
-
- it('should render remove file button as cancel', () => {
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- enableShortWorkflow
- />,
- );
-
- // This button is specific to the file that has the error
- const cancelButton = $('.delete-upload', container);
- expect(cancelButton.getAttribute('text')).to.equal('Cancel');
- });
-
- it('should render delete button for successfully uploaded file', () => {
- const formData = [
- {
- uploading: false,
- },
- ];
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- enableShortWorkflow
- />,
- );
-
- // This button is specific to the file that was uploaded
- const deleteButton = $('.delete-upload', container);
- expect(deleteButton.getAttribute('text')).to.equal('Delete file');
- });
- });
-
- describe('enableShortWorkflow is false', () => {
- const mockIdSchema = {
- $id: 'field',
- };
- const mockSchema = {
- additionalItems: {},
- items: [
- {
- properties: {},
- },
- ],
- maxItems: 4,
- };
- const mockUiSchema = fileUploadUI('Files');
- const mockFormDataWithError = [
- {
- errorMessage: 'some error message',
- },
- ];
- const mockErrorSchemaWithError = {
- 0: {
- __errors: ['ERROR-123'],
- },
- };
- const mockRegistry = {
- fields: {
- SchemaField: () => ,
- },
- };
-
- it('should render main upload button while any file has error', () => {
- const idSchema = {
- $id: 'myIdSchemaId',
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // id for main upload button is interpolated {idSchema.$id}_add_label
- const mainUploadButton = $('#myIdSchemaId_add_label', container);
- expect(mainUploadButton).to.exist;
- expect($('.usa-input-error-message', container).textContent).to.eq(
- 'Error ERROR-123',
- );
- });
-
- it('should not render missing password error', () => {
- const idSchema = {
- $id: 'myIdSchemaId',
- };
- const mockFormDataWithPasswordError = [
- {
- errorMessage: MISSING_PASSWORD_ERROR,
- },
- ];
- const mockErrorSchemaWithPasswordError = [
- {
- __errors: [MISSING_PASSWORD_ERROR],
- },
- ];
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- expect($('.usa-input-error-message', container)).to.not.exist;
- });
-
- it('should render remove file button as Delete file', () => {
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // This button is specific to the file that has the error
- const deleteFileButton = $('.delete-upload', container);
- expect(deleteFileButton.getAttribute('text')).to.equal('Delete file');
- });
-
- it('should render delete button for successfully uploaded file', () => {
- const formData = [
- {
- uploading: false,
- },
- ];
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // This button is specific to the file that was uploaded
- const deleteButton = $('.delete-upload', container);
- expect(deleteButton.getAttribute('text')).to.equal('Delete file');
- });
-
- it('should not render individual file Try again button', () => {
- const errorSchema = {
- 0: {
- __errors: [FILE_UPLOAD_NETWORK_ERROR_MESSAGE],
- },
- };
- const { container } = render(
- f}
- requiredSchema={requiredSchema}
- />,
- );
-
- // The retry button should be the only primary button. Should not be present
- // with enableShortWorkflow not enabled
- const individualFileTryAgainButton = $('.retry-upload', container);
- expect(individualFileTryAgainButton).to.not.exist;
- });
- });
-});
diff --git a/src/platform/forms-system/src/js/fields/FileField.jsx b/src/platform/forms-system/src/js/fields/FileField.jsx
index f008553a1576..c78ede372512 100644
--- a/src/platform/forms-system/src/js/fields/FileField.jsx
+++ b/src/platform/forms-system/src/js/fields/FileField.jsx
@@ -46,6 +46,7 @@ import { MISSING_PASSWORD_ERROR } from '../validation';
* @property {string} buttonText='Upload' - upload button text
* @property {string} addAnotherLabel='Upload another' - upload another text,
* replaces upload button text when greater than one upload is showing
+ * @property {string} ariaLabelAdditionalText additional screen-reader text to be appended to upload button's aria-label attribute.
* @property {string} tryAgain='Try again' - button in enableShortWorkflow
* @property {string} newFile='Upload a new file' - button in enableShortWorkflow
* @property {string} cancel='Cancel' - button visible while uploading & in enableShortWorkflow
@@ -110,6 +111,7 @@ const FileField = props => {
const content = {
upload: uiOptions.buttonText || 'Upload',
uploadAnother: uiOptions.addAnotherLabel || 'Upload another',
+ ariaLabelAdditionalText: uiOptions.ariaLabelAdditionalText || '',
passwordLabel: fileName => `Add a password for ${fileName}`,
tryAgain: 'Try again',
tryAgainLabel: fileName => `Try uploading ${fileName} again`,
@@ -675,7 +677,10 @@ const FileField = props => {
secondary
class="vads-u-padding-x--0 vads-u-padding-y--1"
onClick={() => fileInputRef?.current?.click()}
- label={`${uploadText} ${titleString || ''}`}
+ // label is the aria-label
+ label={`${uploadText} ${titleString || ''}. ${
+ content.ariaLabelAdditionalText
+ }`}
text={uploadText}
uswds
/>