Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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 @@ -702,12 +702,13 @@ function AcuantCapture(
allowUpload &&
formatHTML(t('doc_auth.buttons.take_or_upload_picture_html'), {
'lg-take-photo': () => null,
'lg-or': ({ children }) => (
Comment thread
dawei-nava marked this conversation as resolved.
<span className="padding-left-1 padding-right-1">{children}</span>
),
'lg-upload': ({ children }) => (
<span className="padding-left-1">
<Button isUnstyled onClick={withLoggedClick('upload')(forceUpload)}>
{children}
</Button>
</span>
<Button isUnstyled onClick={withLoggedClick('upload')(forceUpload)}>
{children}
</Button>
),
})}
</div>
Expand Down
Comment thread
dawei-nava marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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 (
<>
<hr className="margin-y-5" />
<h2>{`2. ${t('doc_auth.headings.document_capture_subheader_selfie')}`}</h2>
Comment thread
dawei-nava marked this conversation as resolved.
Outdated
<TipList
titleClassName="margin-bottom-0 text-bold"
title={t('doc_auth.tips.document_capture_selfie_selfie_text')}
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'),
]}
/>
<DocumentSideAcuantCapture
key="selfie"
side="selfie"
registerField={registerField}
value={value}
onChange={onChange}
errors={errors}
onError={onError}
className={className}
/>
</>
);
}

export default DocumentCaptureSelfieCapture;
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -47,16 +48,26 @@ function DocumentsStep({
*
* @type {DocumentSide[]}
*/
const documentSides = selfieCaptureEnabled ? ['front', 'back', 'selfie'] : ['front', 'back'];
const documentSides = ['front', 'back'];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see why you went this direction. I agree that we should be splitting things out into components. Instead of doing it this way, I'd recommend you return a component structure like this from this file. Some of my reasoning:

  • With the approach I'm suggesting all the headers are in the same file.
  • With this approach we avoid the need for another component that mostly passes props through.
  • I think naming the various instances of is a good idea.
return (
    <>
      {flowpath === ...}
      <PageHeading>...
      <DocumentCaptureSubheader> // returns the h2 starting with "1."
      <TipList>...
      <DocumentFront> // a new component, wraps DocumentSideAcuantCapture
      <DocumentBack> // a new component, wraps DocumentSideAcuantCapture
      <SelfieCaptureSubheader> // returns the h2 starting with "2."
      <Selfie> // a new component, wraps DocumentSideAcuantCapture
      {isLastStep ...}
      {notReadySectionEnabled ...}
      {exisQuestionSectionEnabled ...}
      <Cancel>
    </>
)

And for the <DocumentFront>, <DocumentBack>, and <Selfie> components, something like this in this same file (probably don't need a new file for each of these components):

const DocumentFront = ({registerField, onChange, onError}: {...new type...}) = {
  const side: DocumentSide = 'front'
  return (
    <DocumentSideAcuantCapture
          key={side}
          side={side}
          registerField={registerField}
          value={value[side]}
          onChange={onChange}
          errors={errors}
          onError={onError}
    />
  )
}

@dawei-nava dawei-nava Dec 15, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charleyf , refactored with HOC for document sides. Also I kept selfie part as a separate component, since it contains multiple parts, it feels easier to test as a whole piece.

@charleyf charleyf Dec 15, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels easier to test as a whole piece

That makes sense to me. I do still think that everything in document-capture-selfie-capture.tsx should move into documents-step.jsx. I think that if you move everything up into documents-step.jsx your tests will continue to work (with minor setup changes) in documents-step-spec.jsx?

A few pieces of evidence:

  • The two h2s ( 1. ... and 2. ...) should appear in the same file since they're at the same level on the same page.
  • The props you're sending to DocumentCaptureSelfieCapture are identical to the props received by DocumentsStep.
  • The two tip lists have identical props: SelfieTipList and DocumentTipList
  • The props for SelfieSection (in the existing section) are identical to the props for Selfie (in the new file)

Another way I'm thinking about this: I can't describe in words what the purpose of document-capture-selfie-capture is, or how it's different from what documents-step accomplishes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert how front, back sides rendered, otherwise it cause some document active element issues during testing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charleyf agree most of the bullet points, and seems backtracked from previously components etc.

Also, in general it may be callsed document-capture-selfi-section? Anyway I can pull into document-steps, separate it because later we will have it with review-issue-step page.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charleyf , pulled all stuff in document step.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Having all the TipList, <h2> and <DocumentSideAcuantCapture> in the same file makes it much clearer to me.

and seems backtracked from previously components etc

I'd be interested to hear more about this. The pseudocode for my suggested component structure with an example component is earlier in this thread. I'm happy to help you implement that, but I think the way the code is currently is close enough.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charleyf , yes basically following is the same with the HOC withProps, where the element focus seems not behaves the same with rendering in places, I too was baffled by it since the change has nothing to do with functionality, but that feels quite elusive and can consume quite some time.

const DocumentFront = ({registerField, onChange, onError}: {...new type...}) = {
  const side: DocumentSide = 'front'
  return (
    <DocumentSideAcuantCapture
          key={side}
          side={side}
          registerField={registerField}
          value={value[side]}
          onChange={onChange}
          errors={errors}
          onError={onError}
    />
  )
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ just approved, since I agree it doesn't necessarily make much sense to keep polishing this.

Agreed, I'm also quite surprised that extracting those components would change anything. Can you tell me what you mean about the element focus changing? Do you mean the outline or some other aspect? A screenshot would help too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charleyf in the document-capture-spec test, checks for document.activeElement

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');
return (
<>
{flowPath === 'hybrid' && <HybridDocCaptureWarning className="margin-bottom-4" />}
<PageHeading>{t('doc_auth.headings.document_capture')}</PageHeading>
<p>{t('doc_auth.info.document_capture_intro_acknowledgment')}</p>
<PageHeading>{pageHeaderText}</PageHeading>
<h2>
{selfieCaptureEnabled
? `1. ${t('doc_auth.headings.document_capture_subheader_id')}`
Comment thread
charleyf marked this conversation as resolved.
Outdated
: t('doc_auth.headings.document_capture_subheader_id')}
</h2>
<TipList
titleClassName="margin-bottom-0"
title={t('doc_auth.tips.document_capture_header_text')}
titleClassName="margin-bottom-0 text-bold"
title={idTipListTitle}
items={[
t('doc_auth.tips.document_capture_id_text1'),
t('doc_auth.tips.document_capture_id_text2'),
Expand All @@ -74,6 +85,15 @@ function DocumentsStep({
onError={onError}
/>
))}
{selfieCaptureEnabled && (
<DocumentCaptureSelfieCapture
registerField={registerField}
value={value[selfieSide]}
onChange={onChange}
errors={errors}
onError={onError}
/>
)}
{isLastStep ? <FormStepsButton.Submit /> : <FormStepsButton.Continue />}
{notReadySectionEnabled && <DocumentCaptureNotReady />}
{exitQuestionSectionEnabled && <DocumentCaptureAbandon />}
Expand Down
18 changes: 11 additions & 7 deletions config/locales/doc_auth/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ en:
buttons:
add_new_photos: Add new photos
continue: Continue
take_or_upload_picture_html: '<lg-take-photo>Take photo</lg-take-photo> or
<lg-upload>Upload photo</lg-upload>'
take_or_upload_picture_html: '<lg-take-photo>Take photo</lg-take-photo><lg-or>
or </lg-or> <lg-upload>Upload photo</lg-upload>'
take_picture: Take photo
take_picture_retry: Retake photo
upload_picture: Upload photo
Expand Down Expand Up @@ -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: 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
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
Expand Down Expand Up @@ -189,9 +192,6 @@ en:
capture_status_none: Align
capture_status_small_document: Move Closer
capture_status_tap_to_capture: Tap to Capture
document_capture_intro_acknowledgment: We’ll collect information about you by
reading your driver’s license or state ID card. We use this information
to verify your identity.
exit:
with_sp: Exit %{app_name} and return to %{sp_name}
without_sp: Exit identity verification and go to your account page
Expand Down Expand Up @@ -273,12 +273,16 @@ en:
process.
header: Not ready to add photos?
tips:
document_capture_header_text: 'For best results:'
document_capture_hint: Must be a JPG or PNG
document_capture_id_text1: Use a dark background
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: 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?
Expand Down
17 changes: 11 additions & 6 deletions config/locales/doc_auth/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ es:
buttons:
add_new_photos: Añadir nuevas fotos
continue: Continuar
take_or_upload_picture_html: '<lg-take-photo>Toma una foto</lg-take-photo> o
<lg-upload>Sube una foto</lg-upload>'
take_or_upload_picture_html: '<lg-take-photo>Toma una
foto</lg-take-photo><lg-or> o </lg-or> <lg-upload>Sube una
foto</lg-upload>'
take_picture: Toma una foto
take_picture_retry: Retirar la foto
upload_picture: Subir foto
Expand Down Expand Up @@ -182,6 +183,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
Comment thread
dawei-nava marked this conversation as resolved.
Outdated
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
Expand Down Expand Up @@ -222,9 +226,6 @@ es:
capture_status_none: Alinea
capture_status_small_document: Muévete mas cerca
capture_status_tap_to_capture: Toque para capturar
document_capture_intro_acknowledgment: Recopilaremos información sobre usted
leyendo su licencia de conducir o identificación estatal. Usamos esta
información para verificar su identidad.
exit:
with_sp: Salir de %{app_name} y volver a %{sp_name}
without_sp: Salir de la verificación de identidad e ir a la página de su cuenta
Expand Down Expand Up @@ -315,12 +316,16 @@ es:
proceso.
header: ¿No está listo para enviar las fotos?
tips:
document_capture_header_text: 'Para obtener los mejores resultados:'
document_capture_hint: Debe ser un JPG o PNG
document_capture_id_text1: Use un fondo oscuro
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: 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
Comment thread
dawei-nava marked this conversation as resolved.
Outdated
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:'
Expand Down
17 changes: 11 additions & 6 deletions config/locales/doc_auth/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ fr:
buttons:
add_new_photos: Ajoutez de nouvelles photos
continue: Continuer
take_or_upload_picture_html: '<lg-take-photo>Prendre une photo</lg-take-photo>
ou <lg-upload>Télécharger une photo</lg-upload>'
take_or_upload_picture_html: '<lg-take-photo>Prendre une
photo</lg-take-photo><lg-or> ou </lg-or><lg-upload>Télécharger une
photo</lg-upload>'
take_picture: Prendre une photo
take_picture_retry: Reprendre la photo
upload_picture: Télécharger une photo
Expand Down Expand Up @@ -190,6 +191,9 @@ fr:
document_capture_back: Verso de votre carte d’identité
document_capture_front: Recto de votre carte d’identité
document_capture_selfie: Égoportrait
Comment thread
dawei-nava marked this conversation as resolved.
Outdated
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é
Expand Down Expand Up @@ -229,9 +233,6 @@ fr:
capture_status_none: Alignez
capture_status_small_document: Approchez-vous
capture_status_tap_to_capture: Appuyez pour capturer
document_capture_intro_acknowledgment: Nous recueillons des informations sur
vous en lisant votre permis de conduire ou votre carte d’identité de
l’État. Nous utilisons ces informations pour vérifier votre identité.
exit:
with_sp: Quittez %{app_name} et retournez à %{sp_name}
without_sp: Quittez la vérification d’identité et accédez à la page de votre compte
Expand Down Expand Up @@ -327,12 +328,16 @@ fr:
pour terminer ce processus.
header: Vous n’êtes pas prêt à ajouter des photos?
tips:
document_capture_header_text: 'Pour obtenir les meilleurs résultats:'
document_capture_hint: Doit être un JPG ou PNG
document_capture_id_text1: Utilisez un fond sombre
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: 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?
Expand Down
5 changes: 4 additions & 1 deletion spec/features/idv/doc_auth/document_capture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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
Expand Down
23 changes: 22 additions & 1 deletion spec/features/idv/doc_auth/redo_document_capture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -239,4 +239,25 @@

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
sleep(10)
end
it_behaves_like 'image re-upload not allowed'
it 'shows current existing header' do
expect_doc_capture_page_header(t('doc_auth.headings.review_issues'))
end
end
end
end
Original file line number Diff line number Diff line change
@@ -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(
<DocumentCaptureSelfieCapture
value={{}}
onChange={() => {}}
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}`);
});
});
});
Loading