Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
17cf133
Revert "Revert changes for file-input pw button (#2024)"
RyanMunsch Mar 5, 2026
f43b038
va-file-input flag functionality & stories updates
RyanMunsch Mar 6, 2026
3b29cba
Move pw handlers in source order
RyanMunsch Mar 6, 2026
7b8d6b8
Clean prop comment
RyanMunsch Mar 6, 2026
1f8b96c
Update va-file-input-multiple and stories
RyanMunsch Mar 6, 2026
fa594ac
Add check for pending pw submission after error
RyanMunsch Mar 11, 2026
49d0419
Update handler for va-file-input-multiple story
RyanMunsch Mar 11, 2026
295ea3a
Merge branch 'main' into 5851-file-input-pw-flag
RyanMunsch Mar 11, 2026
30cef79
Update README for file input multiple
RyanMunsch Mar 11, 2026
24bc435
Merge branch '5851-file-input-pw-flag' of https://github.com/departme…
RyanMunsch Mar 11, 2026
c4bd6d5
Updates tests/fix failing tests
RyanMunsch Mar 11, 2026
6ed07eb
Fix margin for password va-text-input on default pattern
RyanMunsch Mar 12, 2026
fe487e3
Fix password state reset on change file
RyanMunsch Mar 12, 2026
9bc42e6
Merge branch 'main' into 5851-file-input-pw-flag
RyanMunsch Mar 12, 2026
ce43fed
Merge branch 'main' into 5851-file-input-pw-flag
RyanMunsch Mar 13, 2026
c4598ed
Update args for both pw submit button stories
RyanMunsch Mar 17, 2026
89e294e
Merge branch 'main' into 5851-file-input-pw-flag
RyanMunsch Mar 17, 2026
a9d68f5
Add onVaChange callback for pw button w/ min length story
RyanMunsch Mar 17, 2026
84fcca2
Add va-alert to default password variant
RyanMunsch Mar 17, 2026
cdf3b52
Normalize labels and aria-describedby for pw inputs
RyanMunsch Mar 17, 2026
ac96d5f
Fix template logic for default min pw length
RyanMunsch Mar 18, 2026
919f404
Use meaningful error message for min pw length
RyanMunsch Mar 18, 2026
0ed897f
Match error message to hint (pw min stories)
RyanMunsch Mar 18, 2026
c77d481
Update min pw length error message again
RyanMunsch Mar 18, 2026
7806dc8
Make usePasswordSubmitButtonPattern default to true
RyanMunsch Mar 19, 2026
e4e11bd
Rename prop: usePasswordSubmittButtonPattern -> disablePasswordSubmit…
RyanMunsch Mar 19, 2026
5be2adb
Remove console log from story template
RyanMunsch Mar 19, 2026
734a847
Update docs to reflect changed prop name
RyanMunsch Mar 19, 2026
fb0b66d
Update missed comment to reflect prop name change
RyanMunsch Mar 19, 2026
990814d
Merge branch 'main' into 5851-file-input-pw-flag
RyanMunsch Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const defaultArgs = {
'value': null,
'read-only': false,
'slotFieldIndexes': null,
'disablePasswordSubmitButtonPattern': false,
};

const Template = ({
Expand All @@ -53,6 +54,7 @@ const Template = ({
readOnly,
children,
slotFieldIndexes,
disablePasswordSubmitButton,
}) => {
return (
<VaFileInputMultiple
Expand All @@ -70,6 +72,7 @@ const Template = ({
read-only={readOnly}
children={children}
slot-field-indexes={slotFieldIndexes}
disablePasswordSubmitButtonPattern={disablePasswordSubmitButton}
/>
);
};
Expand Down Expand Up @@ -453,9 +456,11 @@ CustomValidation.parameters = {
chromatic: { disableSnapshot: true },
};

const EncryptedTemplate = ({ label, name }) => {
const EncryptedTemplate = ( {label, name, disablePasswordSubmitButtonPattern }) => {
const [trackedFiles, setTrackedFiles] = useState([]);
const [encryptedList, setEncryptedList] = useState([]);
const [passwordSubmissionSuccessList, setPasswordSubmissionSuccessList] = useState<boolean[]>([]);
const [derivedPasswordErrorList, setDerivedPasswordErrorList] = useState<string[]>([]);

/**
* Callback passed to onVaMultipleChange to track the file objects for each
Expand All @@ -473,10 +478,18 @@ const EncryptedTemplate = ({ label, name }) => {
const { detail } = event;

const trackedFilesToSet = [...trackedFiles];
const trackedPasswordSubmissionSuccessList = [
...passwordSubmissionSuccessList
];
const currentDerivedPasswordErrorList = [...derivedPasswordErrorList];

// Add new file to trackedFiles if action is FILE_ADDED
if (detail.action === 'FILE_ADDED') {
trackedFilesToSet.push(detail.file);

if (detail.file.type === 'application/pdf') {
trackedPasswordSubmissionSuccessList.push(null);
}
} else if (detail.action === 'FILE_UPDATED' && detail.file) {
// If an existing file is updated successfully, replace the corresponding
// error object in trackedFiles with the file
Expand All @@ -485,6 +498,7 @@ const EncryptedTemplate = ({ label, name }) => {
);
if (indexToUpdate !== -1) {
trackedFilesToSet[indexToUpdate] = detail.file;
trackedPasswordSubmissionSuccessList[indexToUpdate] = null;
}
} else if (detail.action === 'FILE_REMOVED' && detail.file) {
// Remove file from trackedFiles if action is FILE_REMOVED when a valid
Expand All @@ -494,6 +508,10 @@ const EncryptedTemplate = ({ label, name }) => {
);
if (indexToRemove !== -1) {
trackedFilesToSet.splice(indexToRemove, 1);
// Remove index from success submission list and derived error list to
// ensure password submission/error state is cleared for deleted file
trackedPasswordSubmissionSuccessList.splice(indexToRemove, 1);
currentDerivedPasswordErrorList.splice(indexToRemove, 1);
}
} else if (detail.action === 'FILE_REMOVED' && !detail.file) {
// If an error file was deleted, remove the corresponding file from
Expand All @@ -503,13 +521,29 @@ const EncryptedTemplate = ({ label, name }) => {
);
if (indexToRemove !== -1) {
trackedFilesToSet.splice(indexToRemove, 1);
// Remove index from success submission list and derived error list to
// ensure password submission/error state is cleared for deleted file
trackedPasswordSubmissionSuccessList.splice(indexToRemove, 1);
currentDerivedPasswordErrorList.splice(indexToRemove, 1);
}
} else if (detail.action === 'PASSWORD_UPDATE') {
// Update value at index of file that password was updated to null to
// enable validation to take place again at va-file-input level.
const indexToUpdate = trackedFilesToSet.findIndex(
file => file.name === detail.file.name,
);

if (indexToUpdate !== -1) {
trackedPasswordSubmissionSuccessList[indexToUpdate] = null;
}
}

const pdfFiles = trackedFilesToSet.map((file) => {
return file.type === 'application/pdf'
});

setPasswordSubmissionSuccessList(trackedPasswordSubmissionSuccessList);
setDerivedPasswordErrorList(currentDerivedPasswordErrorList);
setEncryptedList(pdfFiles);
setTrackedFiles(trackedFilesToSet);
}
Expand Down Expand Up @@ -543,6 +577,19 @@ const EncryptedTemplate = ({ label, name }) => {
setTrackedFiles(trackedFilesToSet);
}

const handlePasswordSubmissionSuccessClick = (index: number, isSuccess: boolean = false) => {
const currentState = [...passwordSubmissionSuccessList];
currentState[index] = isSuccess;

if (!isSuccess) {
const currentDerivedPasswordErrorList = [...derivedPasswordErrorList];
currentDerivedPasswordErrorList[index] = 'Incorrect password. Try again or delete file.';
setDerivedPasswordErrorList(currentDerivedPasswordErrorList);
}

setPasswordSubmissionSuccessList(currentState);
}

return (
<>
To learn how to check for an encrypted PDF <va-link
Expand All @@ -554,10 +601,55 @@ const EncryptedTemplate = ({ label, name }) => {
name={name}
hint={"This example shows a password field when a .pdf file is uploaded."}
encrypted={encryptedList}
passwordSubmissionSuccessList={passwordSubmissionSuccessList}
passwordErrors={derivedPasswordErrorList}
onVaMultipleChange={handleChange}
onVaMultipleError={handleError}
disablePasswordSubmitButtonPattern={disablePasswordSubmitButtonPattern}
/>
<hr />

<p>Password submission list: {JSON.stringify(passwordSubmissionSuccessList)}</p>

{ !disablePasswordSubmitButtonPattern &&
(<div
className="vads-u-display--flex vads-u-flex-direction--column vads-u-margin--2 vads-u-border--1px vads-u-border-color--gray-light vads-u-padding--2"
style={{ width: 'fit-content' }}
>
<p className="vads-u-margin-y--0">
Simulate checking of submitted password for uploaded <strong>encrypted (PDF)</strong> files. Simulation is done
by updating the <code>passwordSubmissionSuccessList</code> prop based on button clicks below.
</p>
<em>Each pair of buttons controls the password submission status for the corresponding encrypted file in the file list.</em>
{
encryptedList?.length ? (
<ul>
{encryptedList.map((isEncrypted, index) => {
if (isEncrypted) {
return (
<li key={index}>
<div className="vads-u-display--flex vads-u-align-items--center vads-u-gap--2 vads-u-margin-bottom--1">
<p className="vads-u-margin-y--0 vads-u-margin-right--2">File {index + 1}</p>
<va-button
class="vads-u-margin-y--1"
text="Set success"
onClick={() => handlePasswordSubmissionSuccessClick(index, true)}
/>
<va-button
text="Set error"
onClick={() => handlePasswordSubmissionSuccessClick(index, false)}
/>
</div>
</li>
);
}
})}
</ul>
) : null
}
</div>)
}

<div>
<p>
Parent components are responsible for managing if a password
Expand Down Expand Up @@ -614,6 +706,17 @@ AcceptsFilePassword.parameters = {
chromatic: { disableSnapshot: true },
};

export const AcceptsFilePasswordWithoutSubmitButton = EncryptedTemplate.bind(null);
AcceptsFilePasswordWithoutSubmitButton.args = {
...defaultArgs,
disablePasswordSubmitButtonPattern: true,
};
// Snapshots disabled because visual difference is only apparent after interaction.
// TODO: Enable snapshots after integrating Storybook play function
AcceptsFilePasswordWithoutSubmitButton.parameters = {
chromatic: { disableSnapshot: true },
};

const FilesUploadedTemplate = args => {
const [mockFiles, setMockFiles] = useState(null);

Expand Down
Loading
Loading