From 018eb8f30d8519cbd1fa87f1a457fa86008024e2 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 7 Dec 2023 11:08:00 -0500 Subject: [PATCH 01/17] LG-11577: Selfie ui content. Separate capture component for selfie capture to add tip list etc. --- .../document-capture-selfie-capture.jsx | 58 +++++++++++++++++++ .../components/documents-step.jsx | 30 ++++++++-- config/locales/doc_auth/en.yml | 10 +++- config/locales/doc_auth/es.yml | 8 +++ config/locales/doc_auth/fr.yml | 8 +++ 5 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx new file mode 100644 index 00000000000..0cfea19d254 --- /dev/null +++ b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx @@ -0,0 +1,58 @@ +import { t } from '@18f/identity-i18n'; +import DocumentSideAcuantCapture from './document-side-acuant-capture'; +import TipList from './tip-list'; + +/** @typedef {import('@18f/identity-form-steps').FormStepError<*>} FormStepError */ +/** @typedef {import('@18f/identity-form-steps').RegisterFieldCallback} RegisterFieldCallback */ +/** @typedef {import('@18f/identity-form-steps').OnErrorCallback} OnErrorCallback */ + +/** + * @typedef DocumentCaptureSelfieCaptureProps + * + * @prop {RegisterFieldCallback} registerField + * @prop {Blob|string|null|undefined} value + * @prop {(nextValues:{[key:string]: Blob|string|null|undefined})=>void} onChange Update values, + * merging with existing values. + * @prop {FormStepError[]} errors + * @prop {OnErrorCallback} onError + * @prop {string=} className + */ + +/** + * @param {DocumentCaptureSelfieCaptureProps} props Props object. + */ +function DocumentCaptureSelfieCapture({ + registerField, + value, + onChange, + errors, + onError, + className, +}) { + return ( + <> +

{t('doc_auth.headings.document_capture_subheader_selfie')}

+ + + + ); +} + +export default DocumentCaptureSelfieCapture; diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index d8b9492a171..de02e906a11 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -11,6 +11,7 @@ import TipList from './tip-list'; import DocumentCaptureNotReady from './document-capture-not-ready'; import { FeatureFlagContext } from '../context'; import DocumentCaptureAbandon from './document-capture-abandon'; +import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; /** * @typedef {'front'|'back'|'selfie'} DocumentSide @@ -47,16 +48,26 @@ function DocumentsStep({ * * @type {DocumentSide[]} */ - const documentSides = selfieCaptureEnabled ? ['front', 'back', 'selfie'] : ['front', 'back']; + const documentSides = ['front', 'back']; + const selfieSide = 'selfie'; + + const pageHeaderText = selfieCaptureEnabled + ? t('doc_auth.headings.document_capture_with_selfie') + : t('doc_auth.headings.document_capture'); + + const idTipListTitle = selfieCaptureEnabled + ? t('doc_auth.tips.document_capture_selfie_id_header_text') + : t('doc_auth.tips.document_capture_header_text'); return ( <> {flowPath === 'hybrid' && } - {t('doc_auth.headings.document_capture')} -

{t('doc_auth.info.document_capture_intro_acknowledgment')}

+ {pageHeaderText} + {!selfieCaptureEnabled &&

{t('doc_auth.info.document_capture_intro_acknowledgment')}

} + {selfieCaptureEnabled &&

{t('doc_auth.headings.document_capture_subheader_id')}

} ))} + {selfieCaptureEnabled && ( + + )} {isLastStep ? : } {notReadySectionEnabled && } {exitQuestionSectionEnabled && } diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index 2bb33c86a74..f552f406114 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -151,7 +151,10 @@ en: document_capture: Add photos of your ID document_capture_back: Back of your ID document_capture_front: Front of your ID - document_capture_selfie: Selfie + document_capture_selfie: A photo of yourself + document_capture_subheader_id: 1. Driver’s license or state ID card + document_capture_subheader_selfie: 2. Photo of yourself + document_capture_with_selfie: Add photos of your ID and a photo of yourself front: Front of your driver’s license or state ID getting_started: Let’s verify your identity for %{sp_name} how_to_verify: Choose how you want to verify your identity @@ -279,6 +282,11 @@ en: document_capture_id_text2: Take the photo on a flat surface document_capture_id_text3: Do not use the flash on your camera document_capture_id_text4: File size should be at least 2 MB + document_capture_selfie_id_header_text: Tips for taking clear photos + document_capture_selfie_selfie_text: Tips for taking a clear photo + document_capture_selfie_text1: Hold your device at eye level + document_capture_selfie_text2: Make sure your whole face is visible + document_capture_selfie_text3: Do not wear sunglasses most_common: Most Common review_issues_id_header_text: 'Review the images of your state‑issued ID:' review_issues_id_text1: Did you use a dark background? diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index de94d035b0f..5c83e71ab1a 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -182,6 +182,9 @@ es: document_capture_back: Parte trasera de su documento de identidad document_capture_front: Parte delantera de su documento de identidad document_capture_selfie: Selfi + document_capture_subheader_id: 1. Driver’s license or state ID card + document_capture_subheader_selfie: 2. Photo of yourself + document_capture_with_selfie: Add photos of your ID and a photo of yourself front: Anverso de su licencia de conducir o identificación estatal getting_started: Vamos a verificar su identidad para %{sp_name} how_to_verify: Elija cómo quiere verificar su identidad @@ -321,6 +324,11 @@ es: document_capture_id_text2: Tome la foto en una superficie plana document_capture_id_text3: No use el flash de su cámara document_capture_id_text4: El tamaño del archivo debe ser de al menos 2 MB + document_capture_selfie_id_header_text: Tips for taking clear photos + document_capture_selfie_selfie_text: Tips for taking a clear photo + document_capture_selfie_text1: Hold your device at eye level + document_capture_selfie_text2: Make sure your whole face is visible + document_capture_selfie_text3: Do not wear sunglasses most_common: Más común review_issues_id_header_text: 'Revise las imágenes de su documento de identidad expedido por el estado:' diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index 59e84585ce1..45b2c29165d 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -190,6 +190,9 @@ fr: document_capture_back: Verso de votre carte d’identité document_capture_front: Recto de votre carte d’identité document_capture_selfie: Égoportrait + document_capture_subheader_id: 1. Driver’s license or state ID card + document_capture_subheader_selfie: 2. Photo of yourself + document_capture_with_selfie: Add photos of your ID and a photo of yourself front: Recto de votre permis de conduire ou de votre carte d’identité de l’État getting_started: Vérifions votre identité pour %{sp_name} how_to_verify: Choisissez la manière dont vous souhaitez confirmer votre identité @@ -333,6 +336,11 @@ fr: document_capture_id_text2: Prenez la photo sur une surface plane document_capture_id_text3: N’utilisez pas le flash de votre appareil photo document_capture_id_text4: La taille du fichier doit être d’au moins 2 Mo + document_capture_selfie_id_header_text: Tips for taking clear photos + document_capture_selfie_selfie_text: Tips for taking a clear photo + document_capture_selfie_text1: Hold your device at eye level + document_capture_selfie_text2: Make sure your whole face is visible + document_capture_selfie_text3: Do not wear sunglasses most_common: Le plus commun review_issues_id_header_text: 'Examinez les images de votre carte d’identité délivrée par l’État:' review_issues_id_text1: Avez-vous utilisé un fond sombre? From 9ad7c90dd5183e20605c21b96860848eef1f2a4d Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 7 Dec 2023 11:40:13 -0500 Subject: [PATCH 02/17] LG-11577: test for new component. --- .../document-capture-selfie-capture-spec.jsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx diff --git a/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx new file mode 100644 index 00000000000..4b7700a4b03 --- /dev/null +++ b/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx @@ -0,0 +1,30 @@ +import { expect } from 'chai'; +import { within } from '@testing-library/react'; +import DocumentCaptureSelfieCapture from '@18f/identity-document-capture/components/document-capture-selfie-capture'; +import { render } from '../../../support/document-capture'; + +describe('document-capture/components/document-capture-selfie-capture', () => { + it('renders the form steps', () => { + const { getAllByRole, getByText } = render( + {}} + errors={[]} + onError={() => {}} + registerField={() => undefined} + />, + ); + + const header = getByText('doc_auth.headings.document_capture_subheader_selfie'); + expect(header).to.be.ok(); + const tipListHeader = getByText('doc_auth.tips.document_capture_selfie_selfie_text'); + expect(tipListHeader).to.be.ok(); + const lists = getAllByRole('list'); + const tipList = lists[0]; + expect(tipList).to.be.ok(); + const tipListItem = within(tipList).getAllByRole('listitem'); + tipListItem.forEach((li, idx) => { + expect(li.textContent).to.equals(`doc_auth.tips.document_capture_selfie_text${idx + 1}`); + }); + }); +}); From c2f74dba526331d6e7c3b04191e6347f58ff49dc Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 7 Dec 2023 12:04:59 -0500 Subject: [PATCH 03/17] LG-11577: test for new conditional content in document_step. --- .../components/documents-step-spec.jsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx index c54dcc0b1be..56601dc1684 100644 --- a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx @@ -13,7 +13,6 @@ import DocumentsStep from '@18f/identity-document-capture/components/documents-s import { composeComponents } from '@18f/identity-compose-components'; import { render } from '../../../support/document-capture'; import { getFixtureFile } from '../../../support/file'; - describe('document-capture/components/documents-step', () => { it('renders with only front and back inputs by default', () => { const { getByLabelText, queryByLabelText } = render(); @@ -164,15 +163,29 @@ describe('document-capture/components/documents-step', () => { ], [DocumentsStep], ); - const { getByLabelText, queryByLabelText } = render(); + const { getByRole, getByLabelText, queryByLabelText } = render(); const front = getByLabelText('doc_auth.headings.document_capture_front'); const back = getByLabelText('doc_auth.headings.document_capture_back'); const selfie = queryByLabelText('doc_auth.headings.document_capture_selfie'); - + const pageHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture_with_selfie', + level: 1, + }); + const idHeader = getByRole('heading', { + name: 'doc_auth.headdngs.document_capture_subheader_id', + level: 2, + }); + const selfieHeader = getByRole('heading', { + name: 'doc_auth.headdngs.document_capture_subheader_selfie', + level: 2, + }); expect(front).to.be.ok(); expect(back).to.be.ok(); expect(selfie).to.be.ok(); + expect(pageHeader).to.be.ok(); + expect(idHeader).to.be.ok(); + expect(selfieHeader).to.be.ok(); }); }); }); From ad735d2e4a56256b4b050286eedcccf56415f194 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 7 Dec 2023 12:05:50 -0500 Subject: [PATCH 04/17] LG-11577: test for new conditional content in document_step. --- .../document-capture/components/documents-step-spec.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx index 56601dc1684..723697eab34 100644 --- a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx @@ -13,6 +13,7 @@ import DocumentsStep from '@18f/identity-document-capture/components/documents-s import { composeComponents } from '@18f/identity-compose-components'; import { render } from '../../../support/document-capture'; import { getFixtureFile } from '../../../support/file'; + describe('document-capture/components/documents-step', () => { it('renders with only front and back inputs by default', () => { const { getByLabelText, queryByLabelText } = render(); @@ -173,11 +174,11 @@ describe('document-capture/components/documents-step', () => { level: 1, }); const idHeader = getByRole('heading', { - name: 'doc_auth.headdngs.document_capture_subheader_id', + name: 'doc_auth.headings.document_capture_subheader_id', level: 2, }); const selfieHeader = getByRole('heading', { - name: 'doc_auth.headdngs.document_capture_subheader_selfie', + name: 'doc_auth.headings.document_capture_subheader_selfie', level: 2, }); expect(front).to.be.ok(); From 148e52046b029edf3e0bf7c62fd70cfdb40c3dca Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 7 Dec 2023 13:12:39 -0500 Subject: [PATCH 05/17] LG-11577: update review issue page and test. --- .../document-capture-review-issues.tsx | 54 +++++++++---- .../document-capture-review-issues-spec.jsx | 77 +++++++++++++++++++ 2 files changed, 116 insertions(+), 15 deletions(-) diff --git a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx index e0edf5410b2..03399664ac6 100644 --- a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx +++ b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx @@ -14,6 +14,7 @@ import DocumentSideAcuantCapture from './document-side-acuant-capture'; import DocumentCaptureNotReady from './document-capture-not-ready'; import { FeatureFlagContext } from '../context'; import DocumentCaptureAbandon from './document-capture-abandon'; +import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; interface DocumentCaptureReviewIssuesProps { isFailedDocType: boolean; @@ -28,8 +29,6 @@ interface DocumentCaptureReviewIssuesProps { hasDismissed: boolean; } -type DocumentSide = 'front' | 'back' | 'selfie'; - function DocumentCaptureReviewIssues({ isFailedDocType, remainingAttempts = Infinity, @@ -47,13 +46,20 @@ function DocumentCaptureReviewIssues({ useContext(FeatureFlagContext); // Sides of document to present as file input. - const documentSides: DocumentSide[] = selfieCaptureEnabled - ? ['front', 'back', 'selfie'] - : ['front', 'back']; + const documentSides: ('front' | 'back')[] = ['front', 'back']; + const selfieSide = 'selfie'; + + const pageHeaderText = selfieCaptureEnabled + ? t('doc_auth.headings.document_capture_with_selfie') + : t('doc_auth.headings.review_issues'); + + const idTipListTitle = selfieCaptureEnabled + ? t('doc_auth.tips.document_capture_selfie_id_header_text') + : t('doc_auth.tips.review_issues_id_header_text'); return ( <> - {t('doc_auth.headings.review_issues')} + {pageHeaderText} - {!isFailedDocType && captureHints && ( + {selfieCaptureEnabled &&

{t('doc_auth.headings.document_capture_subheader_id')}

} + {(selfieCaptureEnabled || (!isFailedDocType && captureHints)) && ( )} {documentSides.map((side) => ( @@ -85,6 +100,15 @@ function DocumentCaptureReviewIssues({ className="document-capture-review-issues-step__input" /> ))} + {selfieCaptureEnabled && ( + + )} {notReadySectionEnabled && } {exitQuestionSectionEnabled && } diff --git a/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx index 5a679f07427..aafc05d530b 100644 --- a/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx +++ b/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx @@ -136,4 +136,81 @@ describe('DocumentCaptureReviewIssues', () => { expect(getByRole('button', { name: 'forms.buttons.submit.default' })).to.be.ok(); }); }); + + context('with selfie enabled', () => { + it('renders alternative content', () => { + const props = { + isFailedDocType: false, + unknownFieldErrors: [ + { + field: 'general', + error: toFormEntryError({ field: 'general', message: 'general error' }), + }, + ], + errors: [ + { + field: 'front', + error: toFormEntryError({ field: 'front', message: 'front side error' }), + }, + { + field: 'back', + error: toFormEntryError({ field: 'back', message: 'back side error' }), + }, + ], + }; + const App = composeComponents( + [ + FeatureFlagContext.Provider, + { + value: { + selfieCaptureEnabled: true, + }, + }, + ], + [ + InPersonContext.Provider, + { + value: { + inPersonURL: '/verify/doc_capture', + }, + }, + ], + [ + DocumentCaptureReviewIssues, + { + ...{ + ...DEFAULT_OPTIONS, + ...props, + }, + }, + ], + ); + const { getByRole, getByText, getAllByRole } = render(); + const pageHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture_with_selfie', + level: 1, + }); + const idHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture_subheader_id', + level: 2, + }); + const selfieHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture_subheader_selfie', + level: 2, + }); + expect(pageHeader).to.be.ok(); + expect(idHeader).to.be.ok(); + expect(selfieHeader).to.be.ok(); + + const idTipListHeader = getByText('doc_auth.tips.document_capture_selfie_id_header_text'); + expect(idTipListHeader).to.be.ok(); + const lists = getAllByRole('list'); + const tipList = lists[0]; + expect(tipList).to.be.ok(); + const tipListItem = within(tipList).getAllByRole('listitem'); + tipListItem.forEach((li, idx) => { + expect(li.textContent).to.equals(`doc_auth.tips.document_capture_id_text${idx + 1}`); + }); + }); + }); }); From 24556a67b1bf186b69a887466188b9ba4ea47349 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 8 Dec 2023 16:16:30 -0500 Subject: [PATCH 06/17] LG-11577: update feature test. --- .../idv/doc_auth/document_capture_spec.rb | 5 +++- .../doc_auth/redo_document_capture_spec.rb | 24 ++++++++++++++++++- spec/support/features/doc_capture_helper.rb | 12 ++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index dd62489e059..1253d95b2b5 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -3,6 +3,7 @@ RSpec.feature 'document capture step', :js do include IdvStepHelper include DocAuthHelper + include DocCaptureHelper include ActionView::Helpers::DateHelper let(:max_attempts) { IdentityConfig.store.doc_auth_max_attempts } @@ -204,7 +205,9 @@ expect(page).to have_current_path(idv_document_capture_url) expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - + expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) + expect_doc_capture_id_subheader + expect_doc_capture_selfie_subheader attach_images attach_selfie submit_images diff --git a/spec/features/idv/doc_auth/redo_document_capture_spec.rb b/spec/features/idv/doc_auth/redo_document_capture_spec.rb index 852e10407ec..203e61d652b 100644 --- a/spec/features/idv/doc_auth/redo_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/redo_document_capture_spec.rb @@ -3,6 +3,7 @@ RSpec.feature 'doc auth redo document capture', js: true do include IdvStepHelper include DocAuthHelper + include DocCaptureHelper let(:fake_analytics) { FakeAnalytics.new } @@ -168,7 +169,6 @@ ) end end - context 'error due to data issue with 2xx status code', allow_browser_log: true do before do sign_in_and_2fa_user @@ -239,4 +239,26 @@ it_behaves_like 'image re-upload not allowed' end + + context 'when selfie is enabled' do + context 'error due to data issue with 2xx status code', allow_browser_log: true do + before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture). + and_return({ enabled: true }) + sign_in_and_2fa_user + complete_doc_auth_steps_before_document_capture_step + mock_doc_auth_acuant_error_unknown + attach_images + attach_selfie + submit_images + click_try_again + end + it_behaves_like 'image re-upload not allowed' + it 'show headers with selfie' do + expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) + expect_doc_capture_id_subheader + expect_doc_capture_selfie_subheader + end + end + end end diff --git a/spec/support/features/doc_capture_helper.rb b/spec/support/features/doc_capture_helper.rb index a802ca49a09..89f2c633360 100644 --- a/spec/support/features/doc_capture_helper.rb +++ b/spec/support/features/doc_capture_helper.rb @@ -46,4 +46,16 @@ def mock_doc_captured(user_id, response = DocAuth::Response.new(success: true)) user = User.find(user_id) user.document_capture_sessions.last.store_result_from_response(response) end + + def expect_doc_capture_page_header(text) + expect(page).to have_css('.page-heading', text: text, wait: 5) + end + + def expect_doc_capture_id_subheader + expect(page).to have_text(t('doc_auth.headings.document_capture_subheader_id')) + end + + def expect_doc_capture_selfie_subheader + expect(page).to have_text(t('doc_auth.headings.document_capture_subheader_selfie')) + end end From 6bc5b712597caaff1a5b1bc372521c38e1da96dc Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 12 Dec 2023 12:54:19 -0500 Subject: [PATCH 07/17] LG-11577: update non-selfie UI. --- .../components/document-capture-selfie-capture.jsx | 2 +- .../document-capture/components/documents-step.jsx | 12 +++++------- config/locales/doc_auth/en.yml | 4 ++-- config/locales/doc_auth/es.yml | 4 ++-- config/locales/doc_auth/fr.yml | 4 ++-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx index 0cfea19d254..58e1f92b34c 100644 --- a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx +++ b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx @@ -31,7 +31,7 @@ function DocumentCaptureSelfieCapture({ }) { return ( <> -

{t('doc_auth.headings.document_capture_subheader_selfie')}

+

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

{flowPath === 'hybrid' && } {pageHeaderText} - {!selfieCaptureEnabled &&

{t('doc_auth.info.document_capture_intro_acknowledgment')}

} - {selfieCaptureEnabled &&

{t('doc_auth.headings.document_capture_subheader_id')}

} +

+ {selfieCaptureEnabled && '1.'} {t('doc_auth.headings.document_capture_subheader_id')} +

Date: Tue, 12 Dec 2023 13:40:30 -0500 Subject: [PATCH 08/17] LG-11577: revert review issue page. --- .../document-capture-review-issues.tsx | 54 ++++--------- config/locales/doc_auth/en.yml | 2 +- config/locales/doc_auth/es.yml | 16 ++-- config/locales/doc_auth/fr.yml | 16 ++-- .../document-capture-review-issues-spec.jsx | 77 ------------------- .../components/documents-step-spec.jsx | 4 +- 6 files changed, 34 insertions(+), 135 deletions(-) diff --git a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx index 03399664ac6..e0edf5410b2 100644 --- a/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx +++ b/app/javascript/packages/document-capture/components/document-capture-review-issues.tsx @@ -14,7 +14,6 @@ import DocumentSideAcuantCapture from './document-side-acuant-capture'; import DocumentCaptureNotReady from './document-capture-not-ready'; import { FeatureFlagContext } from '../context'; import DocumentCaptureAbandon from './document-capture-abandon'; -import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; interface DocumentCaptureReviewIssuesProps { isFailedDocType: boolean; @@ -29,6 +28,8 @@ interface DocumentCaptureReviewIssuesProps { hasDismissed: boolean; } +type DocumentSide = 'front' | 'back' | 'selfie'; + function DocumentCaptureReviewIssues({ isFailedDocType, remainingAttempts = Infinity, @@ -46,20 +47,13 @@ function DocumentCaptureReviewIssues({ useContext(FeatureFlagContext); // Sides of document to present as file input. - const documentSides: ('front' | 'back')[] = ['front', 'back']; - const selfieSide = 'selfie'; - - const pageHeaderText = selfieCaptureEnabled - ? t('doc_auth.headings.document_capture_with_selfie') - : t('doc_auth.headings.review_issues'); - - const idTipListTitle = selfieCaptureEnabled - ? t('doc_auth.tips.document_capture_selfie_id_header_text') - : t('doc_auth.tips.review_issues_id_header_text'); + const documentSides: DocumentSide[] = selfieCaptureEnabled + ? ['front', 'back', 'selfie'] + : ['front', 'back']; return ( <> - {pageHeaderText} + {t('doc_auth.headings.review_issues')} - {selfieCaptureEnabled &&

{t('doc_auth.headings.document_capture_subheader_id')}

} - {(selfieCaptureEnabled || (!isFailedDocType && captureHints)) && ( + {!isFailedDocType && captureHints && ( )} {documentSides.map((side) => ( @@ -100,15 +85,6 @@ function DocumentCaptureReviewIssues({ className="document-capture-review-issues-step__input" /> ))} - {selfieCaptureEnabled && ( - - )} {notReadySectionEnabled && } {exitQuestionSectionEnabled && } diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index fa60d8643b0..c7c1466ea94 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -286,7 +286,7 @@ en: document_capture_selfie_selfie_text: Tips for taking a clear photo document_capture_selfie_text1: Hold your device at eye level document_capture_selfie_text2: Make sure your whole face is visible - document_capture_selfie_text3: Do not wear sunglasses + document_capture_selfie_text3: Take your photo in a well-lit place most_common: Most Common review_issues_id_header_text: 'Review the images of your state‑issued ID:' review_issues_id_text1: Did you use a dark background? diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index 912066d1184..a779fab4846 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -182,9 +182,9 @@ es: document_capture_back: Parte trasera de su documento de identidad document_capture_front: Parte delantera de su documento de identidad document_capture_selfie: Selfi - document_capture_subheader_id: Driver’s license or state ID card - document_capture_subheader_selfie: Photo of yourself - document_capture_with_selfie: Add photos of your ID and a photo of yourself + document_capture_subheader_id: Licencia de conducir o con un documento de identidad estatal + document_capture_subheader_selfie: Foto suya + document_capture_with_selfie: Incluir fotos de su identificación y una foto suya front: Anverso de su licencia de conducir o identificación estatal getting_started: Vamos a verificar su identidad para %{sp_name} how_to_verify: Elija cómo quiere verificar su identidad @@ -324,11 +324,11 @@ es: document_capture_id_text2: Tome la foto en una superficie plana document_capture_id_text3: No use el flash de su cámara document_capture_id_text4: El tamaño del archivo debe ser de al menos 2 MB - document_capture_selfie_id_header_text: Tips for taking clear photos - document_capture_selfie_selfie_text: Tips for taking a clear photo - document_capture_selfie_text1: Hold your device at eye level - document_capture_selfie_text2: Make sure your whole face is visible - document_capture_selfie_text3: Do not wear sunglasses + document_capture_selfie_id_header_text: Consejos para obtener fotografías nítidas + document_capture_selfie_selfie_text: Consejos para obtener una foto nítidas + document_capture_selfie_text1: Mantenga el dispositivo al mismo nivel que los ojos + document_capture_selfie_text2: Asegúrese de que toda su cara sea visible + document_capture_selfie_text3: Tómese la foto en un sitio con buena iluminación most_common: Más común review_issues_id_header_text: 'Revise las imágenes de su documento de identidad expedido por el estado:' diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index 9c65197d7fa..cff8076dd02 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -190,9 +190,9 @@ fr: document_capture_back: Verso de votre carte d’identité document_capture_front: Recto de votre carte d’identité document_capture_selfie: Égoportrait - document_capture_subheader_id: Driver’s license or state ID card - document_capture_subheader_selfie: Photo of yourself - document_capture_with_selfie: Add photos of your ID and a photo of yourself + document_capture_subheader_id: Permis de conduire ou de carte d’identité d’État + document_capture_subheader_selfie: Photo de vous-même + document_capture_with_selfie: Ajoutez des photos de votre pièce d’identité et une photo de vous-même front: Recto de votre permis de conduire ou de votre carte d’identité de l’État getting_started: Vérifions votre identité pour %{sp_name} how_to_verify: Choisissez la manière dont vous souhaitez confirmer votre identité @@ -336,11 +336,11 @@ fr: document_capture_id_text2: Prenez la photo sur une surface plane document_capture_id_text3: N’utilisez pas le flash de votre appareil photo document_capture_id_text4: La taille du fichier doit être d’au moins 2 Mo - document_capture_selfie_id_header_text: Tips for taking clear photos - document_capture_selfie_selfie_text: Tips for taking a clear photo - document_capture_selfie_text1: Hold your device at eye level - document_capture_selfie_text2: Make sure your whole face is visible - document_capture_selfie_text3: Do not wear sunglasses + document_capture_selfie_id_header_text: Conseils pour prendre des photos claires + document_capture_selfie_selfie_text: Conseils pour prendre un photo claires + document_capture_selfie_text1: Tenez votre appareil à hauteur des yeux + document_capture_selfie_text2: Veillez à ce que l’ensemble de votre visage soit visible + document_capture_selfie_text3: Prenez votre photo dans un endroit bien éclairé most_common: Le plus commun review_issues_id_header_text: 'Examinez les images de votre carte d’identité délivrée par l’État:' review_issues_id_text1: Avez-vous utilisé un fond sombre? diff --git a/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx index aafc05d530b..5a679f07427 100644 --- a/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx +++ b/spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx @@ -136,81 +136,4 @@ describe('DocumentCaptureReviewIssues', () => { expect(getByRole('button', { name: 'forms.buttons.submit.default' })).to.be.ok(); }); }); - - context('with selfie enabled', () => { - it('renders alternative content', () => { - const props = { - isFailedDocType: false, - unknownFieldErrors: [ - { - field: 'general', - error: toFormEntryError({ field: 'general', message: 'general error' }), - }, - ], - errors: [ - { - field: 'front', - error: toFormEntryError({ field: 'front', message: 'front side error' }), - }, - { - field: 'back', - error: toFormEntryError({ field: 'back', message: 'back side error' }), - }, - ], - }; - const App = composeComponents( - [ - FeatureFlagContext.Provider, - { - value: { - selfieCaptureEnabled: true, - }, - }, - ], - [ - InPersonContext.Provider, - { - value: { - inPersonURL: '/verify/doc_capture', - }, - }, - ], - [ - DocumentCaptureReviewIssues, - { - ...{ - ...DEFAULT_OPTIONS, - ...props, - }, - }, - ], - ); - const { getByRole, getByText, getAllByRole } = render(); - const pageHeader = getByRole('heading', { - name: 'doc_auth.headings.document_capture_with_selfie', - level: 1, - }); - const idHeader = getByRole('heading', { - name: 'doc_auth.headings.document_capture_subheader_id', - level: 2, - }); - const selfieHeader = getByRole('heading', { - name: 'doc_auth.headings.document_capture_subheader_selfie', - level: 2, - }); - expect(pageHeader).to.be.ok(); - expect(idHeader).to.be.ok(); - expect(selfieHeader).to.be.ok(); - - const idTipListHeader = getByText('doc_auth.tips.document_capture_selfie_id_header_text'); - expect(idTipListHeader).to.be.ok(); - const lists = getAllByRole('list'); - const tipList = lists[0]; - expect(tipList).to.be.ok(); - const tipListItem = within(tipList).getAllByRole('listitem'); - tipListItem.forEach((li, idx) => { - expect(li.textContent).to.equals(`doc_auth.tips.document_capture_id_text${idx + 1}`); - }); - }); - }); }); diff --git a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx index 723697eab34..5ab1bd5afbf 100644 --- a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx @@ -174,11 +174,11 @@ describe('document-capture/components/documents-step', () => { level: 1, }); const idHeader = getByRole('heading', { - name: 'doc_auth.headings.document_capture_subheader_id', + name: '1. doc_auth.headings.document_capture_subheader_id', level: 2, }); const selfieHeader = getByRole('heading', { - name: 'doc_auth.headings.document_capture_subheader_selfie', + name: '2. doc_auth.headings.document_capture_subheader_selfie', level: 2, }); expect(front).to.be.ok(); From c43d3f13ff5a853a6844fb92a21dd0cd9eb785fa Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 13 Dec 2023 09:30:56 -0500 Subject: [PATCH 09/17] LG-11577: add missing hr for selfie section. --- .../components/document-capture-selfie-capture.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx index 58e1f92b34c..f4faabfa115 100644 --- a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx +++ b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx @@ -31,6 +31,7 @@ function DocumentCaptureSelfieCapture({ }) { return ( <> +

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

Date: Thu, 14 Dec 2023 09:36:27 -0500 Subject: [PATCH 10/17] LG-11577: update tests. changelog: User-facing Improvements, Doc Auth, Document capture UI update. --- .../document-capture-selfie-capture.jsx | 2 +- config/locales/doc_auth/en.yml | 4 --- config/locales/doc_auth/es.yml | 4 --- config/locales/doc_auth/fr.yml | 4 --- .../doc_auth/redo_document_capture_spec.rb | 7 ++--- .../document-capture-selfie-capture-spec.jsx | 2 +- .../components/documents-step-spec.jsx | 31 +++++++++++++++++++ 7 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx index f4faabfa115..5b5376f3046 100644 --- a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx +++ b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx @@ -32,7 +32,7 @@ function DocumentCaptureSelfieCapture({ return ( <>
-

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

+

{`2. ${t('doc_auth.headings.document_capture_subheader_selfie')}`}

{ />, ); - const header = getByText('doc_auth.headings.document_capture_subheader_selfie'); + const header = getByText('2. doc_auth.headings.document_capture_subheader_selfie'); expect(header).to.be.ok(); const tipListHeader = getByText('doc_auth.tips.document_capture_selfie_selfie_text'); expect(tipListHeader).to.be.ok(); diff --git a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx index 5ab1bd5afbf..7dcbdd16a1f 100644 --- a/spec/javascript/packages/document-capture/components/documents-step-spec.jsx +++ b/spec/javascript/packages/document-capture/components/documents-step-spec.jsx @@ -189,4 +189,35 @@ describe('document-capture/components/documents-step', () => { expect(selfieHeader).to.be.ok(); }); }); + + it('renders with front, back when featureflag is off', () => { + const App = composeComponents( + [ + FeatureFlagContext.Provider, + { + value: { + selfieCaptureEnabled: false, + }, + }, + ], + [DocumentsStep], + ); + const { getByRole, getByLabelText } = render(); + + const front = getByLabelText('doc_auth.headings.document_capture_front'); + const back = getByLabelText('doc_auth.headings.document_capture_back'); + const pageHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture', + level: 1, + }); + const idHeader = getByRole('heading', { + name: 'doc_auth.headings.document_capture_subheader_id', + level: 2, + }); + + expect(front).to.be.ok(); + expect(back).to.be.ok(); + expect(pageHeader).to.be.ok(); + expect(idHeader).to.be.ok(); + }); }); From 02758530043429543c83f61c91b3ae74ca3704cc Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 14 Dec 2023 10:06:07 -0500 Subject: [PATCH 11/17] LG-11577: refactor conditionally rendering of the header. --- .../packages/document-capture/components/documents-step.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index 108f254024b..3a1ee3982e4 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -61,7 +61,9 @@ function DocumentsStep({ {flowPath === 'hybrid' && } {pageHeaderText}

- {selfieCaptureEnabled && '1.'} {t('doc_auth.headings.document_capture_subheader_id')} + {selfieCaptureEnabled + ? `1. ${t('doc_auth.headings.document_capture_subheader_id')}` + : t('doc_auth.headings.document_capture_subheader_id')}

Date: Thu, 14 Dec 2023 10:18:20 -0500 Subject: [PATCH 12/17] changelog: User-facing Improvements, Doc Auth, Document capture UI update (LG-11577) From 057c7f445eaecd3ccca9aaf48c410ee846491bf5 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 14 Dec 2023 16:47:32 -0500 Subject: [PATCH 13/17] LG-11577: minor styling fix for buttons. --- .../document-capture/components/acuant-capture.tsx | 11 ++++++----- config/locales/doc_auth/en.yml | 4 ++-- config/locales/doc_auth/es.yml | 5 +++-- config/locales/doc_auth/fr.yml | 5 +++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index 438a56b75a9..6bc249fb945 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -702,12 +702,13 @@ function AcuantCapture( allowUpload && formatHTML(t('doc_auth.buttons.take_or_upload_picture_html'), { 'lg-take-photo': () => null, + 'lg-or': ({ children }) => ( + {children} + ), 'lg-upload': ({ children }) => ( - - - + ), })} diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index d914b2ef6e1..6334869c8bc 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -8,8 +8,8 @@ en: buttons: add_new_photos: Add new photos continue: Continue - take_or_upload_picture_html: 'Take photo or - Upload photo' + take_or_upload_picture_html: 'Take photo + or Upload photo' take_picture: Take photo take_picture_retry: Retake photo upload_picture: Upload photo diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index 49e71eed1b4..e56ada52b82 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -8,8 +8,9 @@ es: buttons: add_new_photos: Añadir nuevas fotos continue: Continuar - take_or_upload_picture_html: 'Toma una foto o - Sube una foto' + take_or_upload_picture_html: 'Toma una + foto o Sube una + foto' take_picture: Toma una foto take_picture_retry: Retirar la foto upload_picture: Subir foto diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index cd2e06429f4..bda506d3ecf 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -8,8 +8,9 @@ fr: buttons: add_new_photos: Ajoutez de nouvelles photos continue: Continuer - take_or_upload_picture_html: 'Prendre une photo - ou Télécharger une photo' + take_or_upload_picture_html: 'Prendre une + photo ou Télécharger une + photo' take_picture: Prendre une photo take_picture_retry: Reprendre la photo upload_picture: Télécharger une photo From e70448afc29777c42343062839f8f009a9a0a73a Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 15 Dec 2023 09:04:14 -0500 Subject: [PATCH 14/17] LG-11577: translation update and refactor on capturing components. --- .../document-capture-selfie-capture.jsx | 59 ------------- .../document-capture-selfie-capture.tsx | 58 +++++++++++++ .../components/documents-step.jsx | 87 ++++++++++--------- config/locales/doc_auth/es.yml | 4 +- config/locales/doc_auth/fr.yml | 2 +- ... document-capture-selfie-capture-spec.tsx} | 2 +- 6 files changed, 108 insertions(+), 104 deletions(-) delete mode 100644 app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx create mode 100644 app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx rename spec/javascript/packages/document-capture/components/{document-capture-selfie-capture-spec.jsx => document-capture-selfie-capture-spec.tsx} (98%) diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx deleted file mode 100644 index 5b5376f3046..00000000000 --- a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import { t } from '@18f/identity-i18n'; -import DocumentSideAcuantCapture from './document-side-acuant-capture'; -import TipList from './tip-list'; - -/** @typedef {import('@18f/identity-form-steps').FormStepError<*>} FormStepError */ -/** @typedef {import('@18f/identity-form-steps').RegisterFieldCallback} RegisterFieldCallback */ -/** @typedef {import('@18f/identity-form-steps').OnErrorCallback} OnErrorCallback */ - -/** - * @typedef DocumentCaptureSelfieCaptureProps - * - * @prop {RegisterFieldCallback} registerField - * @prop {Blob|string|null|undefined} value - * @prop {(nextValues:{[key:string]: Blob|string|null|undefined})=>void} onChange Update values, - * merging with existing values. - * @prop {FormStepError[]} errors - * @prop {OnErrorCallback} onError - * @prop {string=} className - */ - -/** - * @param {DocumentCaptureSelfieCaptureProps} props Props object. - */ -function DocumentCaptureSelfieCapture({ - registerField, - value, - onChange, - errors, - onError, - className, -}) { - return ( - <> -
-

{`2. ${t('doc_auth.headings.document_capture_subheader_selfie')}`}

- - - - ); -} - -export default DocumentCaptureSelfieCapture; diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx new file mode 100644 index 00000000000..a4242626a3f --- /dev/null +++ b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx @@ -0,0 +1,58 @@ +import { FormStepError, OnErrorCallback, RegisterFieldCallback } from '@18f/identity-form-steps'; +import { useI18n } from '@18f/identity-react-i18n'; +import DocumentSideAcuantCapture from './document-side-acuant-capture'; +import TipList from './tip-list'; +import withProps from '../higher-order/with-props'; + +interface DocumentCaptureSelfieCaptureProps { + registerField: RegisterFieldCallback; + value: Blob | string | null | undefined; + onChange: (nextValues: { [key: string]: Blob | string | null | undefined }) => void; + errors: FormStepError[]; + onError?: OnErrorCallback; + className?: string; +} + +/** + * @param {DocumentCaptureSelfieCaptureProps} props Props object. + */ +function DocumentCaptureSelfieCapture({ + registerField, + value, + onChange, + errors, + onError, + className, +}: DocumentCaptureSelfieCaptureProps) { + const { t } = useI18n(); + const SelfieTipList = withProps({ + title: t('doc_auth.tips.document_capture_selfie_selfie_text'), + titleClassName: 'margin-bottom-0 text-bold', + items: [ + t('doc_auth.tips.document_capture_selfie_text1'), + t('doc_auth.tips.document_capture_selfie_text2'), + t('doc_auth.tips.document_capture_selfie_text3'), + ], + })(TipList); + const Selfie = withProps({ + key: 'selfie', + side: 'selfie', + value, + registerField, + onChange, + errors, + onError, + className, + })(DocumentSideAcuantCapture); + + return ( + <> +
+

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

+ + + + ); +} + +export default DocumentCaptureSelfieCapture; diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index 3a1ee3982e4..896fac3c30c 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -12,6 +12,7 @@ import DocumentCaptureNotReady from './document-capture-not-ready'; import { FeatureFlagContext } from '../context'; import DocumentCaptureAbandon from './document-capture-abandon'; import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; +import withProps from '../higher-order/with-props'; /** * @typedef {'front'|'back'|'selfie'} DocumentSide @@ -22,6 +23,7 @@ import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; * * @prop {Blob|string|null|undefined} front Front image value. * @prop {Blob|string|null|undefined} back Back image value. + * @prop {Blob|string|null|undefined} selfie Selfie image value. * @prop {string=} front_image_metadata Front image metadata. * @prop {string=} back_image_metadata Back image metadata. */ @@ -43,57 +45,60 @@ function DocumentsStep({ const { notReadySectionEnabled, exitQuestionSectionEnabled, selfieCaptureEnabled } = useContext(FeatureFlagContext); - /** - * Sides of document to present as file input. - * - * @type {DocumentSide[]} - */ - const documentSides = ['front', 'back']; - const selfieSide = 'selfie'; - const pageHeaderText = selfieCaptureEnabled ? t('doc_auth.headings.document_capture_with_selfie') : t('doc_auth.headings.document_capture'); - const idTipListTitle = t('doc_auth.tips.document_capture_selfie_id_header_text'); + const DocumentTipList = withProps({ + title: t('doc_auth.tips.document_capture_selfie_id_header_text'), + titleClassName: 'margin-bottom-0 text-bold', + items: [ + t('doc_auth.tips.document_capture_id_text1'), + t('doc_auth.tips.document_capture_id_text2'), + t('doc_auth.tips.document_capture_id_text3'), + ].concat(!isMobile ? [t('doc_auth.tips.document_capture_id_text4')] : []), + })(TipList); + + const defaultSideProps = { + registerField, + onChange, + errors, + onError, + }; + + const DocumentFront = withProps({ + key: 'front', + side: 'front', + value: value.front, + ...defaultSideProps, + })(DocumentSideAcuantCapture); + + const DocumentBack = withProps({ + key: 'back', + side: 'back', + value: value.back, + ...defaultSideProps, + })(DocumentSideAcuantCapture); + + const SelfieSection = withProps({ + key: 'selfie', + side: 'selfie', + value: value.selfie, + ...defaultSideProps, + })(DocumentCaptureSelfieCapture); + return ( <> {flowPath === 'hybrid' && } {pageHeaderText}

- {selfieCaptureEnabled - ? `1. ${t('doc_auth.headings.document_capture_subheader_id')}` - : t('doc_auth.headings.document_capture_subheader_id')} + {selfieCaptureEnabled && '1. '} + {t('doc_auth.headings.document_capture_subheader_id')}

- - {documentSides.map((side) => ( - - ))} - {selfieCaptureEnabled && ( - - )} + + + + {selfieCaptureEnabled && } {isLastStep ? : } {notReadySectionEnabled && } {exitQuestionSectionEnabled && } diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index e56ada52b82..ac12ab0e65f 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -182,7 +182,7 @@ es: document_capture: Incluir fotos de su identificación document_capture_back: Parte trasera de su documento de identidad document_capture_front: Parte delantera de su documento de identidad - document_capture_selfie: Selfi + document_capture_selfie: Una foto tuya document_capture_subheader_id: Licencia de conducir o con un documento de identidad estatal document_capture_subheader_selfie: Foto suya document_capture_with_selfie: Incluir fotos de su identificación y una foto suya @@ -324,7 +324,7 @@ es: document_capture_selfie_id_header_text: Consejos para obtener fotografías nítidas document_capture_selfie_selfie_text: Consejos para obtener una foto nítidas document_capture_selfie_text1: Mantenga el dispositivo al mismo nivel que los ojos - document_capture_selfie_text2: Asegúrese de que toda su cara sea visible + document_capture_selfie_text2: Asegúrate de que tu rostro completo sea visible document_capture_selfie_text3: Tómese la foto en un sitio con buena iluminación most_common: Más común review_issues_id_header_text: 'Revise las imágenes de su documento de identidad diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index bda506d3ecf..846a3de8e9e 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -190,7 +190,7 @@ fr: document_capture: Ajoutez des photos de votre pièce d’identité document_capture_back: Verso de votre carte d’identité document_capture_front: Recto de votre carte d’identité - document_capture_selfie: Égoportrait + document_capture_selfie: Une photo de vous document_capture_subheader_id: Permis de conduire ou de carte d’identité d’État document_capture_subheader_selfie: Photo de vous-même document_capture_with_selfie: Ajoutez des photos de votre pièce d’identité et une photo de vous-même diff --git a/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx b/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx similarity index 98% rename from spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx rename to spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx index 46549ada8af..8155de7a277 100644 --- a/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.jsx +++ b/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx @@ -7,7 +7,7 @@ describe('document-capture/components/document-capture-selfie-capture', () => { it('renders the form steps', () => { const { getAllByRole, getByText } = render( {}} errors={[]} onError={() => {}} From a8db6c09c7dfef284a39df8b41f2b433fe3b5648 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 15 Dec 2023 10:41:38 -0500 Subject: [PATCH 15/17] LG-11577: revert how to render side capturing component, it cause some issues on active element on page during testing. --- .../components/documents-step.jsx | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index 896fac3c30c..d475d2dd471 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -28,6 +28,13 @@ import withProps from '../higher-order/with-props'; * @prop {string=} back_image_metadata Back image metadata. */ +/** + * Sides of the ID document to present as file input. + * + * @type {DocumentSide[]} + */ +const DOCUMENT_SIDES = ['front', 'back']; + /** * @param {import('@18f/identity-form-steps').FormStepComponentProps} props Props object. */ @@ -66,20 +73,6 @@ function DocumentsStep({ onError, }; - const DocumentFront = withProps({ - key: 'front', - side: 'front', - value: value.front, - ...defaultSideProps, - })(DocumentSideAcuantCapture); - - const DocumentBack = withProps({ - key: 'back', - side: 'back', - value: value.back, - ...defaultSideProps, - })(DocumentSideAcuantCapture); - const SelfieSection = withProps({ key: 'selfie', side: 'selfie', @@ -96,8 +89,17 @@ function DocumentsStep({ {t('doc_auth.headings.document_capture_subheader_id')} - - + {DOCUMENT_SIDES.map((side) => ( + + ))} {selfieCaptureEnabled && } {isLastStep ? : } {notReadySectionEnabled && } From bf1b8c69d309380b5b637074d20d29d388c76aa9 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 15 Dec 2023 12:38:51 -0500 Subject: [PATCH 16/17] LG-11577: refactor no subcomponent for selfie. --- .../document-capture-selfie-capture.tsx | 58 ------------------- .../components/documents-step.jsx | 48 +++++++-------- .../document-capture-selfie-capture-spec.tsx | 30 ---------- 3 files changed, 22 insertions(+), 114 deletions(-) delete mode 100644 app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx delete mode 100644 spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx diff --git a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx b/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx deleted file mode 100644 index a4242626a3f..00000000000 --- a/app/javascript/packages/document-capture/components/document-capture-selfie-capture.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { FormStepError, OnErrorCallback, RegisterFieldCallback } from '@18f/identity-form-steps'; -import { useI18n } from '@18f/identity-react-i18n'; -import DocumentSideAcuantCapture from './document-side-acuant-capture'; -import TipList from './tip-list'; -import withProps from '../higher-order/with-props'; - -interface DocumentCaptureSelfieCaptureProps { - registerField: RegisterFieldCallback; - value: Blob | string | null | undefined; - onChange: (nextValues: { [key: string]: Blob | string | null | undefined }) => void; - errors: FormStepError[]; - onError?: OnErrorCallback; - className?: string; -} - -/** - * @param {DocumentCaptureSelfieCaptureProps} props Props object. - */ -function DocumentCaptureSelfieCapture({ - registerField, - value, - onChange, - errors, - onError, - className, -}: DocumentCaptureSelfieCaptureProps) { - const { t } = useI18n(); - const SelfieTipList = withProps({ - title: t('doc_auth.tips.document_capture_selfie_selfie_text'), - titleClassName: 'margin-bottom-0 text-bold', - items: [ - t('doc_auth.tips.document_capture_selfie_text1'), - t('doc_auth.tips.document_capture_selfie_text2'), - t('doc_auth.tips.document_capture_selfie_text3'), - ], - })(TipList); - const Selfie = withProps({ - key: 'selfie', - side: 'selfie', - value, - registerField, - onChange, - errors, - onError, - className, - })(DocumentSideAcuantCapture); - - return ( - <> -
-

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

- - - - ); -} - -export default DocumentCaptureSelfieCapture; diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index d475d2dd471..f8ad6ac3eaa 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -11,8 +11,6 @@ import TipList from './tip-list'; import DocumentCaptureNotReady from './document-capture-not-ready'; import { FeatureFlagContext } from '../context'; import DocumentCaptureAbandon from './document-capture-abandon'; -import DocumentCaptureSelfieCapture from './document-capture-selfie-capture'; -import withProps from '../higher-order/with-props'; /** * @typedef {'front'|'back'|'selfie'} DocumentSide @@ -56,30 +54,12 @@ function DocumentsStep({ ? t('doc_auth.headings.document_capture_with_selfie') : t('doc_auth.headings.document_capture'); - const DocumentTipList = withProps({ - title: t('doc_auth.tips.document_capture_selfie_id_header_text'), - titleClassName: 'margin-bottom-0 text-bold', - items: [ - t('doc_auth.tips.document_capture_id_text1'), - t('doc_auth.tips.document_capture_id_text2'), - t('doc_auth.tips.document_capture_id_text3'), - ].concat(!isMobile ? [t('doc_auth.tips.document_capture_id_text4')] : []), - })(TipList); - const defaultSideProps = { registerField, onChange, errors, onError, }; - - const SelfieSection = withProps({ - key: 'selfie', - side: 'selfie', - value: value.selfie, - ...defaultSideProps, - })(DocumentCaptureSelfieCapture); - return ( <> {flowPath === 'hybrid' && } @@ -88,19 +68,35 @@ function DocumentsStep({ {selfieCaptureEnabled && '1. '} {t('doc_auth.headings.document_capture_subheader_id')} - + {DOCUMENT_SIDES.map((side) => ( ))} - {selfieCaptureEnabled && } + {selfieCaptureEnabled && ( + <> +
+

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

+ + + )} {isLastStep ? : } {notReadySectionEnabled && } {exitQuestionSectionEnabled && } diff --git a/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx b/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx deleted file mode 100644 index 8155de7a277..00000000000 --- a/spec/javascript/packages/document-capture/components/document-capture-selfie-capture-spec.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { expect } from 'chai'; -import { within } from '@testing-library/react'; -import DocumentCaptureSelfieCapture from '@18f/identity-document-capture/components/document-capture-selfie-capture'; -import { render } from '../../../support/document-capture'; - -describe('document-capture/components/document-capture-selfie-capture', () => { - it('renders the form steps', () => { - const { getAllByRole, getByText } = render( - {}} - errors={[]} - onError={() => {}} - registerField={() => undefined} - />, - ); - - const header = getByText('2. doc_auth.headings.document_capture_subheader_selfie'); - expect(header).to.be.ok(); - const tipListHeader = getByText('doc_auth.tips.document_capture_selfie_selfie_text'); - expect(tipListHeader).to.be.ok(); - const lists = getAllByRole('list'); - const tipList = lists[0]; - expect(tipList).to.be.ok(); - const tipListItem = within(tipList).getAllByRole('listitem'); - tipListItem.forEach((li, idx) => { - expect(li.textContent).to.equals(`doc_auth.tips.document_capture_selfie_text${idx + 1}`); - }); - }); -}); From 01789d21c77b9f9a3924e4f7ab3a632650af78aa Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 15 Dec 2023 12:53:25 -0500 Subject: [PATCH 17/17] LG-11577: add missing tips and update tests. --- .../components/documents-step.jsx | 25 +++++++++++++------ .../components/documents-step-spec.jsx | 15 ++++++++++- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/javascript/packages/document-capture/components/documents-step.jsx b/app/javascript/packages/document-capture/components/documents-step.jsx index f8ad6ac3eaa..239cbfb2c23 100644 --- a/app/javascript/packages/document-capture/components/documents-step.jsx +++ b/app/javascript/packages/document-capture/components/documents-step.jsx @@ -26,13 +26,6 @@ import DocumentCaptureAbandon from './document-capture-abandon'; * @prop {string=} back_image_metadata Back image metadata. */ -/** - * Sides of the ID document to present as file input. - * - * @type {DocumentSide[]} - */ -const DOCUMENT_SIDES = ['front', 'back']; - /** * @param {import('@18f/identity-form-steps').FormStepComponentProps} props Props object. */ @@ -43,6 +36,13 @@ function DocumentsStep({ onError = () => {}, registerField = () => undefined, }) { + /** + * Sides of the ID document to present as file input. + * + * @type {DocumentSide[]} + */ + const documentsSides = ['front', 'back']; + const { t } = useI18n(); const { isMobile } = useContext(DeviceContext); const { isLastStep } = useContext(FormStepsContext); @@ -77,7 +77,7 @@ function DocumentsStep({ t('doc_auth.tips.document_capture_id_text3'), ].concat(!isMobile ? [t('doc_auth.tips.document_capture_id_text4')] : [])} /> - {DOCUMENT_SIDES.map((side) => ( + {documentsSides.map((side) => (

2. {t('doc_auth.headings.document_capture_subheader_selfie')}

+ { ], [DocumentsStep], ); - const { getByRole, getByLabelText, queryByLabelText } = render(); + const { getAllByRole, getByText, getByRole, getByLabelText, queryByLabelText } = render( + , + ); const front = getByLabelText('doc_auth.headings.document_capture_front'); const back = getByLabelText('doc_auth.headings.document_capture_back'); @@ -187,6 +190,16 @@ describe('document-capture/components/documents-step', () => { expect(pageHeader).to.be.ok(); expect(idHeader).to.be.ok(); expect(selfieHeader).to.be.ok(); + + const tipListHeader = getByText('doc_auth.tips.document_capture_selfie_selfie_text'); + expect(tipListHeader).to.be.ok(); + const lists = getAllByRole('list'); + const tipList = lists[1]; + expect(tipList).to.be.ok(); + const tipListItem = within(tipList).getAllByRole('listitem'); + tipListItem.forEach((li, idx) => { + expect(li.textContent).to.equals(`doc_auth.tips.document_capture_selfie_text${idx + 1}`); + }); }); });