Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6f39a40
LG-10288: Remove duplication of doc_auth_* threshold variables from a…
dawei-nava Jul 19, 2023
1103ca9
Remove enabled rewrite_oidc_request_prompt feature flag (#8754)
Jul 19, 2023
0cb42b9
LG-10255: Display inline error messages when Acuant SDK recognizes a …
eileen-nava Jul 19, 2023
2e265a1
LG-10405: Add new placeholder manual address entry page behind a flag…
Jul 19, 2023
738f4aa
changelog: User-Facing Improvements, In-person proofing, This changes…
charleyf Jul 19, 2023
0d08e90
LG-7355 new Getting Started page (#8794)
soniaconnolly Jul 19, 2023
1048e1c
Sloppy working search prototype
Jul 19, 2023
2f2b5ad
Fix labels and error messages
Jul 20, 2023
3d3e6b9
Clear `fraud_pending_reason` when activating fraud pending profiles w…
jmhooper Jul 20, 2023
03ba8cb
Don't search if fields are missing
Jul 20, 2023
b86b3e2
LG-10167: Delete notification phone no when associated enrollment can…
night-jellyfish Jul 20, 2023
fafef87
Basic feature test
Jul 20, 2023
2124826
Check for whitespace, not empty strings
Jul 20, 2023
774c011
initial commit of select component
Jul 20, 2023
b60acf0
use select component on page w/sample options
Jul 20, 2023
f4b6202
pass states list to react
Jul 20, 2023
eb3185b
Reuse exports from address-search component
Jul 20, 2023
3195240
Refactor feature flag to use in person context
Jul 20, 2023
d0e3e0d
Add ECR reporting to gitlab (#8781)
stephencshelton Jul 20, 2023
6ae6ebb
Refactor Acuant document detection to use more enum features (#8811)
zachmargolis Jul 20, 2023
d943ad7
Merge branch 'sbachstein/lg-10405-manual-address-entry-form' of https…
NavaTim Jul 20, 2023
652a87a
Fix type errors
Jul 20, 2023
b757e40
changelog: User-Facing Improvements, In-person full address entry, Ad…
Jul 20, 2023
90e65f0
LG-10405: Inject states into PO search dropdown; fix bug using select…
NavaTim Jul 20, 2023
de5fa81
Only let users who are fraud review pending visit the please call con…
jmhooper Jul 20, 2023
44e4332
LG-10405: Style PO search state dropdown error state
NavaTim Jul 20, 2023
f5814fb
LG-9968 Read from `fraud_pending_reason` to find fraud pending profil…
jmhooper Jul 20, 2023
0ad4d41
LG-10405: Update select input and validate field React component tests
NavaTim Jul 20, 2023
82bc9c7
LG-10405: Fix typo and type issue
NavaTim Jul 20, 2023
338e79e
Merge branch 'sbachstein/lg-10405-manual-address-entry-form' into tom…
Jul 20, 2023
c7a98a9
keep flag non-optional
Jul 20, 2023
f5d9d7d
Add EmailNormalizer (LG-10313) (#8818)
zachmargolis Jul 20, 2023
35912cf
LG-10405: Fix lint issues
NavaTim Jul 20, 2023
23ffeb5
Fix crasher in Telephony::AlertSender (#8823)
matthinz Jul 20, 2023
7e47bbe
LG-10405: Add support for using HTML select elements in document capt…
NavaTim Jul 20, 2023
a37b452
Merge branch 'main' of https://github.com/18F/identity-idp into tomas…
NavaTim Jul 20, 2023
ef8c1c0
Revert "Merge branch 'main' of https://github.com/18F/identity-idp in…
NavaTim Jul 20, 2023
c61abed
LG-10405: Add support for using HTML select elements in document capt…
NavaTim Jul 20, 2023
79ed45c
LG-10405: Fix type for registerField
NavaTim Jul 20, 2023
e928a77
LG-10405: Fix lint issue
NavaTim Jul 20, 2023
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
6 changes: 0 additions & 6 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,6 @@ def sp_session_request_url_with_updated_params
url = if request_url.path.match?('saml')
sp_session[:final_auth_request] = true
complete_saml_url
elsif IdentityConfig.store.rewrite_oidc_request_prompt
# Login.gov redirects to the orginal request_url after a user authenticates
# replace prompt=login with prompt=select_account to prevent sign_out
# which should only ever occur once when the user
# lands on Login.gov with prompt=login
sp_session[:request_url]&.gsub('prompt=login', 'prompt=select_account')
else
sp_session[:request_url]
end
Expand Down
1 change: 1 addition & 0 deletions app/javascript/packages/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { default as ProcessList } from './process-list/process-list';
export { default as ProcessListHeading } from './process-list/process-list-heading';
export { default as ProcessListItem } from './process-list/process-list-item';
export { default as ScrollIntoView } from './scroll-into-view';
export { default as SelectInput } from './select-input';
export { default as SpinnerDots } from './spinner-dots';
export { default as StatusPage } from './status-page';
export { default as Tag } from './tag';
Expand Down
94 changes: 94 additions & 0 deletions app/javascript/packages/components/select-input.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { createRef } from 'react';
import { render } from '@testing-library/react';
import { computeAccessibleDescription } from 'dom-accessibility-api';
import SelectInput from './select-input';

describe('SelectInput', () => {
it('renders with an associated label', () => {
const { getByLabelText } = render(<SelectInput label="Input">test</SelectInput>);

const input = getByLabelText('Input');

expect(input).to.be.an.instanceOf(HTMLSelectElement);
expect(input.classList.contains('usa-select')).to.be.true();
});

it('renders with child elements', () => {
const childElement = <option value="abc">def</option>;
const { getByText, getByLabelText } = render(
<SelectInput label="Input">{childElement}</SelectInput>,
);

const input = getByLabelText('Input');

expect(input).to.be.an.instanceOf(HTMLSelectElement);
const optionElement = getByText('def');
expect(optionElement).to.be.an.instanceOf(HTMLOptionElement);
expect((optionElement as HTMLOptionElement).selected).to.be.true();
});

it('uses an explicitly-provided ID', () => {
const customId = 'custom-id';
const { getByLabelText } = render(
<SelectInput label="Input" id={customId}>
test
</SelectInput>,
);

const input = getByLabelText('Input');

expect(input.id).to.equal(customId);
});

it('applies additional given classes', () => {
const customClass = 'custom-class';
const { getByLabelText } = render(
<SelectInput label="Input" className={customClass}>
test
</SelectInput>,
);

const input = getByLabelText('Input');

expect([...input.classList.values()]).to.have.all.members(['usa-select', customClass]);
});

it('applies additional input attributes', () => {
const value = 'password';
const { getByLabelText } = render(
<SelectInput label="Input" title={value}>
test
</SelectInput>,
);

const input = getByLabelText('Input');

expect(input.title).to.equal(value);
});

it('forwards ref', () => {
const ref = createRef<HTMLSelectElement>();
render(
<SelectInput label="Input" ref={ref}>
test
</SelectInput>,
);

expect(ref.current).to.be.an.instanceOf(HTMLSelectElement);
});

it('renders with a hint', () => {
const { getByLabelText, getByText } = render(
<SelectInput label="Input" hint="Something special">
test
</SelectInput>,
);

const input = getByLabelText('Input');
const description = computeAccessibleDescription(input);
const hint = getByText('Something special');

expect(description).to.equal('Something special');
expect(hint.classList.contains('usa-hint')).to.be.true();
});
});
64 changes: 64 additions & 0 deletions app/javascript/packages/components/select-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { forwardRef } from 'react';
import type { InputHTMLAttributes, ForwardedRef } from 'react';
import { useInstanceId } from '@18f/identity-react-hooks';

export interface SelectInputProps extends InputHTMLAttributes<HTMLSelectElement> {
/**
* Text of label associated with input.
*/
label: string;

/**
* Muted explainer text sitting below the label.
*/
hint?: string;

/**
* Optional explicit ID to use in place of default behavior.
*/
id?: string;

/**
* Additional class name to be applied to the input element.
*/
className?: string;

/**
* Child elements
*/
children: React.ReactNode;
}

function SelectInput(
{ label, hint, id, className, children, ...inputProps }: SelectInputProps,
ref: ForwardedRef<HTMLSelectElement>,
) {
const instanceId = useInstanceId();
const inputId = id ?? `select-input-${instanceId}`;
const hintId = id ?? `select-input-hint-${instanceId}`;
const classes = ['usa-select', className].filter(Boolean).join(' ');

return (
<>
<label className="usa-label" htmlFor={inputId}>
{label}
</label>
{hint && (
<div id={hintId} className="usa-hint">
{hint}
</div>
)}
<select
ref={ref}
className={classes}
id={inputId}
aria-describedby={hint && hintId}
{...inputProps}
>
{children}
</select>
</>
);
}

export default forwardRef(SelectInput);
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,11 @@ interface AcuantCameraUIOptions {
text: AcuantCameraUIText;
}

/**
* Document type.
*
* 0 = None
* 1 = ID
* 2 = Passport
*/
export type AcuantDocumentType = 0 | 1 | 2;
export enum AcuantDocumentType {
NONE = 0,
ID = 1,
PASSPORT = 2,
}

export type AcuantCaptureFailureError =
| undefined // Cropping failure (SDK v11.5.0, L1171)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type {
} from './acuant-camera';

type AcuantDocumentTypeLabel = 'id' | 'passport' | 'none';
type AcuantImageAssessment = 'success' | 'glare' | 'blurry';
type AcuantImageAssessment = 'success' | 'glare' | 'blurry' | 'unsupported';
type ImageSource = 'acuant' | 'upload';

interface ImageAnalyticsPayload {
Expand Down Expand Up @@ -438,15 +438,19 @@ function AcuantCapture(
const { image, cardtype, dpi, moire, glare, sharpness } = nextCapture;
const isAssessedAsGlare = glare < glareThreshold;
const isAssessedAsBlurry = sharpness < sharpnessThreshold;
const isAssessedAsUnsupported = cardtype !== 1;
const { width, height, data } = image;

let assessment: AcuantImageAssessment;
if (isAssessedAsGlare) {
setOwnErrorMessage(t('doc_auth.errors.glare.failed_short'));
assessment = 'glare';
} else if (isAssessedAsBlurry) {
if (isAssessedAsBlurry) {
setOwnErrorMessage(t('doc_auth.errors.sharpness.failed_short'));
assessment = 'blurry';
} else if (isAssessedAsGlare) {
setOwnErrorMessage(t('doc_auth.errors.glare.failed_short'));
assessment = 'glare';
} else if (isAssessedAsUnsupported) {
setOwnErrorMessage(t('doc_auth.errors.card_type'));
assessment = 'unsupported';
} else {
assessment = 'success';
}
Expand All @@ -456,6 +460,7 @@ function AcuantCapture(
height,
mimeType: 'image/jpeg', // Acuant Web SDK currently encodes all images as JPEG
source: 'acuant',
isAssessedAsUnsupported,
documentType: getDocumentTypeLabel(cardtype),
dpi,
moire,
Expand All @@ -475,7 +480,11 @@ function AcuantCapture(
onChangeAndResetError(data, analyticsPayload);
onResetFailedCaptureAttempts();
} else {
onFailedCaptureAttempt({ isAssessedAsGlare, isAssessedAsBlurry });
onFailedCaptureAttempt({
isAssessedAsGlare,
isAssessedAsBlurry,
isAssessedAsUnsupported,
});
}

setIsCapturingEnvironment(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { UploadFormEntriesError } from '../services/upload';
import DocumentsStep from './documents-step';
import InPersonPrepareStep from './in-person-prepare-step';
import InPersonLocationPostOfficeSearchStep from './in-person-location-post-office-search-step';
import InPersonLocationFullAddressEntryPostOfficeSearchStep from './in-person-location-full-address-entry-post-office-search-step';
import InPersonSwitchBackStep from './in-person-switch-back-step';
import ReviewIssuesStep from './review-issues-step';
import UploadContext from '../context/upload';
Expand All @@ -36,7 +37,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
const { t } = useI18n();
const { flowPath } = useContext(UploadContext);
const { trackSubmitEvent, trackVisitEvent } = useContext(AnalyticsContext);
const { inPersonURL } = useContext(InPersonContext);
const { inPersonFullAddressEntryEnabled, inPersonURL } = useContext(InPersonContext);
const appName = getConfigValue('appName');

useDidUpdateEffect(onStepChange, [stepName]);
Expand Down Expand Up @@ -78,6 +79,10 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
initialValues = formValues;
}

const inPersonLocationPostOfficeSearchForm = inPersonFullAddressEntryEnabled
? InPersonLocationFullAddressEntryPostOfficeSearchStep
: InPersonLocationPostOfficeSearchStep;

const inPersonSteps: FormStep[] =
inPersonURL === undefined
? []
Expand All @@ -89,7 +94,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
},
{
name: 'location',
form: InPersonLocationPostOfficeSearchStep,
form: inPersonLocationPostOfficeSearchForm,
title: t('in_person_proofing.headings.po_search.location'),
},
flowPath === 'hybrid' && {
Expand Down
Loading