@@ -36,6 +36,7 @@ const defaultArgs = {
3636 'value' : null ,
3737 'read-only' : false ,
3838 'slotFieldIndexes' : null ,
39+ 'disablePasswordSubmitButtonPattern' : false ,
3940} ;
4041
4142const 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+
617720const FilesUploadedTemplate = args => {
618721 const [ mockFiles , setMockFiles ] = useState ( null ) ;
619722
0 commit comments