Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 36 additions & 0 deletions app/javascript/packages/components/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useRef } from 'react';
import type { ReactNode } from 'react';
import { useInstanceId } from '@18f/identity-react-hooks';

interface AccordionProps {
header: string;

children: ReactNode;
}

function Accordion({ header, children }: AccordionProps) {
const uniqueId = useInstanceId();
const ref = useRef(null as HTMLDivElement | null);

return (
<div ref={ref}>
<div className="usa-accordion usa-accordion--bordered">
<div className="usa-accordion__heading">
<button
type="button"
className="usa-accordion__button"
aria-expanded="false"
aria-controls={`accordion-${uniqueId}`}
>
{header}
</button>
</div>
<div id={`accordion-${uniqueId}`} className="usa-accordion__content usa-prose" hidden>
{children}
</div>
</div>
</div>
);
}

export default Accordion;
1 change: 1 addition & 0 deletions app/javascript/packages/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as Accordion } from './accordion';
export { default as Alert } from './alert';
export { default as Button } from './button';
export { default as ButtonTo } from './button-to';
Expand Down
5 changes: 3 additions & 2 deletions app/javascript/packages/verify-flow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "1.0.0",
"private": true,
"dependencies": {
"react": "^17.0.2",
"cleave.js": "^1.6.0"
"cleave.js": "^1.6.0",
"libphonenumber-js": "^1.9.53",
"react": "^17.0.2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import sinon from 'sinon';
import * as analytics from '@18f/identity-analytics';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { t } from '@18f/identity-i18n';
import { accordion } from 'identity-style-guide';
import PasswordConfirmStep from './password-confirm-step';

describe('PasswordConfirmStep', () => {
before(() => {
accordion.on();
});

after(() => {
accordion.off();
});

const sandbox = sinon.createSandbox();
const DEFAULT_PROPS = {
onChange() {},
onError() {},
errors: [],
toPreviousStep() {},
registerField: () => () => {},
unknownFieldErrors: [],
value: {},
};

beforeEach(() => {
sandbox.spy(analytics, 'trackEvent');
});

afterEach(() => {
sandbox.restore();
});

it('has a collapsed accordion by default', () => {
const { getByText } = render(<PasswordConfirmStep {...DEFAULT_PROPS} />);

const button = getByText(t('idv.messages.review.intro'));
expect(button.getAttribute('aria-expanded')).to.eq('false');
});

it('expands accordion when the accordion is clicked on', async () => {
const toPreviousStep = sinon.spy();
const { getByText } = render(
<PasswordConfirmStep {...DEFAULT_PROPS} toPreviousStep={toPreviousStep} />,
);

const button = getByText(t('idv.messages.review.intro'));
await userEvent.click(button);
expect(button.getAttribute('aria-expanded')).to.eq('true');
});

it('displays user information when the accordion is clicked on', async () => {
const toPreviousStep = sinon.spy();
const { getByText } = render(
<PasswordConfirmStep {...DEFAULT_PROPS} toPreviousStep={toPreviousStep} />,
);

const button = getByText(t('idv.messages.review.intro'));
await userEvent.click(button);

expect(getByText('idv.review.full_name')).to.exist();
expect(getByText('idv.review.mailing_address')).to.exist();
});
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
import type { ChangeEvent } from 'react';
import { PasswordToggle } from '@18f/identity-password-toggle';
import { PageHeading, Accordion, Alert } from '@18f/identity-components';
import { t } from '@18f/identity-i18n';
import { FormStepsButton } from '@18f/identity-form-steps';
import { Alert } from '@18f/identity-components';
import { PasswordToggle } from '@18f/identity-password-toggle';
import type { FormStepComponentProps } from '@18f/identity-form-steps';
import type { ChangeEvent } from 'react';
import { getConfigValue } from '@18f/identity-config';
import PersonalInfoSummary from './personal-info-summary';
import StartOverOrCancel from '../../start-over-or-cancel';
import type { VerifyFlowValues } from '../../verify-flow';
import type { VerifyFlowValues } from '../..';

interface PasswordConfirmStepStepProps extends FormStepComponentProps<VerifyFlowValues> {}
interface PasswordConfirmStepProps extends FormStepComponentProps<VerifyFlowValues> {}
Comment on lines -9 to +12
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch / fix on the duplication of "StepStep" 👍


function PasswordConfirmStep({ errors, registerField, onChange }: PasswordConfirmStepStepProps) {
function PasswordConfirmStep({ errors, registerField, onChange, value }: PasswordConfirmStepProps) {
return (
<>
{errors.map(({ error }) => (
<Alert key={error.message} type="error" className="margin-bottom-4">
{error.message}
</Alert>
))}
<PasswordToggle
ref={registerField('password')}
type="password"
onInput={(event: ChangeEvent<HTMLInputElement>) => {
onChange({ password: event.target.value });
}}
/>
<PageHeading>
{t('idv.titles.session.review', { app_name: getConfigValue('appName') })}
</PageHeading>
<div className="margin-top-6 margin-bottom-4">
<PasswordToggle
ref={registerField('password')}
type="password"
onInput={(event: ChangeEvent<HTMLInputElement>) => {
onChange({ password: event.target.value });
}}
/>
</div>
<Accordion header={t('idv.messages.review.intro')}>
<PersonalInfoSummary pii={value} />
</Accordion>
<FormStepsButton.Continue />
<StartOverOrCancel />
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { parse, format } from 'libphonenumber-js';
import { t } from '@18f/identity-i18n';

function PersonalInfoSummary({ pii }) {
const { firstName, lastName, dob, address1, address2, city, state, zipcode, ssn, phone } = pii;
const phoneNumber = parse(`+1${phone}`);
const formatted = format(phoneNumber, 'NATIONAL');

function getDateFormat(date) {
date = new Date(date);
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString(document.documentElement.lang, options);
}

return (
<div className="padding-x-4">
<div className="h6">{t('idv.review.full_name')}</div>
<div className="h4 text-bold ico-absolute ico-absolute-success">
{firstName} {lastName}
</div>
<div className="margin-top-4 h6">{t('idv.review.mailing_address')}</div>
<div className="h4 text-bold ico-absolute ico-absolute-success">
{address1} <br />
{address2 || ''}
<br />
{city && state ? `${city}, ${state} ${zipcode}` : ''}
</div>
<div className="margin-top-4 h6">{t('idv.review.dob')}</div>
<div className="h4 text-bold ico-absolute ico-absolute-success">{getDateFormat(dob)}</div>
<div className="margin-top-4 h6">{t('idv.review.ssn')}</div>
<div className="h4 text-bold ico-absolute ico-absolute-success">{ssn}</div>
{phone && (
<>
<div className="h6 margin-top-4"> {t('idv.messages.phone.phone_of_record')}</div>
<div className="h4 text-bold ico-absolute ico-absolute-success">{formatted}</div>
</>
)}
</div>
);
}

export default PersonalInfoSummary;
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4269,10 +4269,10 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"

libphonenumber-js@^1.9.6:
version "1.9.6"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.6.tgz#cdab0552a6705c5a5c1d69db15be9562b248591e"
integrity sha512-Ob419L88HxP3iVXPMI1Z/146izHrUPcV70ClnoP9WyNBgdy6mtlkCxx4ewMDmCzsdY5D4diDOUz8kboqLdKBoQ==
libphonenumber-js@^1.9.53, libphonenumber-js@^1.9.6:
version "1.9.53"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz#f4f3321f8fb0ee62952c2a8df4711236d2626088"
integrity sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==

lilconfig@^2.0.3:
version "2.0.5"
Expand Down