Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.

Commit f27fdc2

Browse files
authored
va-file-input/va-file-input-multiple: Add support for backwards compatibility of password change/submission (#2027)
* Revert "Revert changes for file-input pw button (#2024)" This reverts commit 3a9d6f0. * va-file-input flag functionality & stories updates * Move pw handlers in source order * Clean prop comment * Update va-file-input-multiple and stories * Add check for pending pw submission after error * Update handler for va-file-input-multiple story * Update README for file input multiple * Updates tests/fix failing tests * Fix margin for password va-text-input on default pattern * Fix password state reset on change file * Update args for both pw submit button stories Removes duplicate controls and allows component to reflect toggled values for `use-password-submit-button-pattern` prop * Add onVaChange callback for pw button w/ min length story * Add va-alert to default password variant * Normalize labels and aria-describedby for pw inputs * Fix template logic for default min pw length * Use meaningful error message for min pw length * Match error message to hint (pw min stories) * Update min pw length error message again * Make usePasswordSubmitButtonPattern default to true * Rename prop: usePasswordSubmittButtonPattern -> disablePasswordSubmitButtonPattern * Remove console log from story template * Update docs to reflect changed prop name * Update missed comment to reflect prop name change
1 parent 58f5c9b commit f27fdc2

9 files changed

Lines changed: 882 additions & 53 deletions

File tree

packages/storybook/stories/va-file-input-multiple-uswds.stories.tsx

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const defaultArgs = {
3636
'value': null,
3737
'read-only': false,
3838
'slotFieldIndexes': null,
39+
'disablePasswordSubmitButtonPattern': false,
3940
};
4041

4142
const Template = ({
@@ -53,6 +54,7 @@ const Template = ({
5354
readOnly,
5455
children,
5556
slotFieldIndexes,
57+
disablePasswordSubmitButton,
5658
}) => {
5759
return (
5860
<VaFileInputMultiple
@@ -70,6 +72,7 @@ const Template = ({
7072
read-only={readOnly}
7173
children={children}
7274
slot-field-indexes={slotFieldIndexes}
75+
disablePasswordSubmitButtonPattern={disablePasswordSubmitButton}
7376
/>
7477
);
7578
};
@@ -453,9 +456,11 @@ CustomValidation.parameters = {
453456
chromatic: { disableSnapshot: true },
454457
};
455458

456-
const EncryptedTemplate = ({ label, name }) => {
459+
const EncryptedTemplate = ( {label, name, disablePasswordSubmitButtonPattern }) => {
457460
const [trackedFiles, setTrackedFiles] = useState([]);
458461
const [encryptedList, setEncryptedList] = useState([]);
462+
const [passwordSubmissionSuccessList, setPasswordSubmissionSuccessList] = useState<boolean[]>([]);
463+
const [derivedPasswordErrorList, setDerivedPasswordErrorList] = useState<string[]>([]);
459464

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

475480
const trackedFilesToSet = [...trackedFiles];
481+
const trackedPasswordSubmissionSuccessList = [
482+
...passwordSubmissionSuccessList
483+
];
484+
const currentDerivedPasswordErrorList = [...derivedPasswordErrorList];
476485

477486
// Add new file to trackedFiles if action is FILE_ADDED
478487
if (detail.action === 'FILE_ADDED') {
479488
trackedFilesToSet.push(detail.file);
489+
490+
if (detail.file.type === 'application/pdf') {
491+
trackedPasswordSubmissionSuccessList.push(null);
492+
}
480493
} else if (detail.action === 'FILE_UPDATED' && detail.file) {
481494
// If an existing file is updated successfully, replace the corresponding
482495
// error object in trackedFiles with the file
@@ -485,6 +498,7 @@ const EncryptedTemplate = ({ label, name }) => {
485498
);
486499
if (indexToUpdate !== -1) {
487500
trackedFilesToSet[indexToUpdate] = detail.file;
501+
trackedPasswordSubmissionSuccessList[indexToUpdate] = null;
488502
}
489503
} else if (detail.action === 'FILE_REMOVED' && detail.file) {
490504
// Remove file from trackedFiles if action is FILE_REMOVED when a valid
@@ -494,6 +508,10 @@ const EncryptedTemplate = ({ label, name }) => {
494508
);
495509
if (indexToRemove !== -1) {
496510
trackedFilesToSet.splice(indexToRemove, 1);
511+
// Remove index from success submission list and derived error list to
512+
// ensure password submission/error state is cleared for deleted file
513+
trackedPasswordSubmissionSuccessList.splice(indexToRemove, 1);
514+
currentDerivedPasswordErrorList.splice(indexToRemove, 1);
497515
}
498516
} else if (detail.action === 'FILE_REMOVED' && !detail.file) {
499517
// If an error file was deleted, remove the corresponding file from
@@ -503,13 +521,29 @@ const EncryptedTemplate = ({ label, name }) => {
503521
);
504522
if (indexToRemove !== -1) {
505523
trackedFilesToSet.splice(indexToRemove, 1);
524+
// Remove index from success submission list and derived error list to
525+
// ensure password submission/error state is cleared for deleted file
526+
trackedPasswordSubmissionSuccessList.splice(indexToRemove, 1);
527+
currentDerivedPasswordErrorList.splice(indexToRemove, 1);
528+
}
529+
} else if (detail.action === 'PASSWORD_UPDATE') {
530+
// Update value at index of file that password was updated to null to
531+
// enable validation to take place again at va-file-input level.
532+
const indexToUpdate = trackedFilesToSet.findIndex(
533+
file => file.name === detail.file.name,
534+
);
535+
536+
if (indexToUpdate !== -1) {
537+
trackedPasswordSubmissionSuccessList[indexToUpdate] = null;
506538
}
507539
}
508540

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

545+
setPasswordSubmissionSuccessList(trackedPasswordSubmissionSuccessList);
546+
setDerivedPasswordErrorList(currentDerivedPasswordErrorList);
513547
setEncryptedList(pdfFiles);
514548
setTrackedFiles(trackedFilesToSet);
515549
}
@@ -543,6 +577,19 @@ const EncryptedTemplate = ({ label, name }) => {
543577
setTrackedFiles(trackedFilesToSet);
544578
}
545579

580+
const handlePasswordSubmissionSuccessClick = (index: number, isSuccess: boolean = false) => {
581+
const currentState = [...passwordSubmissionSuccessList];
582+
currentState[index] = isSuccess;
583+
584+
if (!isSuccess) {
585+
const currentDerivedPasswordErrorList = [...derivedPasswordErrorList];
586+
currentDerivedPasswordErrorList[index] = 'Incorrect password. Try again or delete file.';
587+
setDerivedPasswordErrorList(currentDerivedPasswordErrorList);
588+
}
589+
590+
setPasswordSubmissionSuccessList(currentState);
591+
}
592+
546593
return (
547594
<>
548595
To learn how to check for an encrypted PDF <va-link
@@ -554,10 +601,55 @@ const EncryptedTemplate = ({ label, name }) => {
554601
name={name}
555602
hint={"This example shows a password field when a .pdf file is uploaded."}
556603
encrypted={encryptedList}
604+
passwordSubmissionSuccessList={passwordSubmissionSuccessList}
605+
passwordErrors={derivedPasswordErrorList}
557606
onVaMultipleChange={handleChange}
558607
onVaMultipleError={handleError}
608+
disablePasswordSubmitButtonPattern={disablePasswordSubmitButtonPattern}
559609
/>
560610
<hr />
611+
612+
<p>Password submission list: {JSON.stringify(passwordSubmissionSuccessList)}</p>
613+
614+
{ !disablePasswordSubmitButtonPattern &&
615+
(<div
616+
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"
617+
style={{ width: 'fit-content' }}
618+
>
619+
<p className="vads-u-margin-y--0">
620+
Simulate checking of submitted password for uploaded <strong>encrypted (PDF)</strong> files. Simulation is done
621+
by updating the <code>passwordSubmissionSuccessList</code> prop based on button clicks below.
622+
</p>
623+
<em>Each pair of buttons controls the password submission status for the corresponding encrypted file in the file list.</em>
624+
{
625+
encryptedList?.length ? (
626+
<ul>
627+
{encryptedList.map((isEncrypted, index) => {
628+
if (isEncrypted) {
629+
return (
630+
<li key={index}>
631+
<div className="vads-u-display--flex vads-u-align-items--center vads-u-gap--2 vads-u-margin-bottom--1">
632+
<p className="vads-u-margin-y--0 vads-u-margin-right--2">File {index + 1}</p>
633+
<va-button
634+
class="vads-u-margin-y--1"
635+
text="Set success"
636+
onClick={() => handlePasswordSubmissionSuccessClick(index, true)}
637+
/>
638+
<va-button
639+
text="Set error"
640+
onClick={() => handlePasswordSubmissionSuccessClick(index, false)}
641+
/>
642+
</div>
643+
</li>
644+
);
645+
}
646+
})}
647+
</ul>
648+
) : null
649+
}
650+
</div>)
651+
}
652+
561653
<div>
562654
<p>
563655
Parent components are responsible for managing if a password
@@ -614,6 +706,17 @@ AcceptsFilePassword.parameters = {
614706
chromatic: { disableSnapshot: true },
615707
};
616708

709+
export const AcceptsFilePasswordWithoutSubmitButton = EncryptedTemplate.bind(null);
710+
AcceptsFilePasswordWithoutSubmitButton.args = {
711+
...defaultArgs,
712+
disablePasswordSubmitButtonPattern: true,
713+
};
714+
// Snapshots disabled because visual difference is only apparent after interaction.
715+
// TODO: Enable snapshots after integrating Storybook play function
716+
AcceptsFilePasswordWithoutSubmitButton.parameters = {
717+
chromatic: { disableSnapshot: true },
718+
};
719+
617720
const FilesUploadedTemplate = args => {
618721
const [mockFiles, setMockFiles] = useState(null);
619722

0 commit comments

Comments
 (0)