Skip to content

Commit

Permalink
LG-6066: Implement print button on FSMv2 Personal Key step (#6205)
Browse files Browse the repository at this point in the history
* LG-6066: Implement print button on FSMv2 Personal Key step

**Why**:

- So that we can retain feature parity with the existing screen.
- To reduce size and scope of common application bundle
- To create a more rigid connection between print JavaScript functionality and server-side render logic
- To improve test coverage for existing print button behavior

changelog: Upcoming Features, Identity Verification, Add personal key step screen

* Create README.md
  • Loading branch information
aduth authored Apr 15, 2022
1 parent 6b733cf commit b8db75b
Show file tree
Hide file tree
Showing 24 changed files with 206 additions and 31 deletions.
15 changes: 15 additions & 0 deletions app/components/print_button_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class PrintButtonComponent < ButtonComponent
attr_reader :tag_options

def initialize(**tag_options)
super(**tag_options, type: :button, icon: :print)
end

def call
content_tag(:'lg-print-button', super)
end

def content
t('components.print_button.label')
end
end
1 change: 1 addition & 0 deletions app/components/print_button_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@18f/identity-print-button';
15 changes: 0 additions & 15 deletions app/javascript/app/print-personal-key.js

This file was deleted.

35 changes: 35 additions & 0 deletions app/javascript/packages/print-button/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# `@18f/identity-print-button`

Custom element and React implementation for a print button component.

## Usage

### Custom Element

Importing the package will register the `<lg-print-button>` custom element:

```ts
import '@18f/identity-print-button';
```

The custom element will implement the behavior to show a print dialog upon click, but all markup must already exist, rendered server-side or by the included React component.

```html
<lg-print-button>
<button type="button">Print</button>
</lg-print-button>
```

### React

The package exports a `PrintButton` component, which extends the `Button` component from `@18f/identity-components`.

```tsx
import { PrintButton } from '@18f/identity-print-button';

export function Example() {
return (
<PrintButton isOutline />
);
}
```
3 changes: 3 additions & 0 deletions app/javascript/packages/print-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './print-button-element';

export { default as PrintButton } from './print-button';
13 changes: 13 additions & 0 deletions app/javascript/packages/print-button/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@18f/identity-print-button",
"version": "1.0.0",
"private": true,
"peerDependencies": {
"react": "*"
},
"peerDependenciesMeta": {
"react": {
"optional": true
}
}
}
25 changes: 25 additions & 0 deletions app/javascript/packages/print-button/print-button-element.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import sinon from 'sinon';
import { screen } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import './print-button-element';

describe('PrintButtonElement', () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
sandbox.stub(window, 'print');
});

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

it('prints when clicked', () => {
document.body.innerHTML = `<lg-print-button><button type="button">Print</button><lg-print-button>`;
const button = screen.getByRole('button');

userEvent.click(button);

expect(window.print).to.have.been.called();
});
});
17 changes: 17 additions & 0 deletions app/javascript/packages/print-button/print-button-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class PrintButtonElement extends HTMLElement {
connectedCallback() {
this.addEventListener('click', () => window.print());
}
}

declare global {
interface HTMLElementTagNameMap {
'lg-print-button': PrintButtonElement;
}
}

if (!customElements.get('lg-print-button')) {
customElements.define('lg-print-button', PrintButtonElement);
}

export default PrintButtonElement;
35 changes: 35 additions & 0 deletions app/javascript/packages/print-button/print-button.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sinon from 'sinon';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import PrintButton from './print-button';

describe('PrintButton', () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
sandbox.stub(window, 'print');
});

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

it('renders a button that prints when clicked', () => {
const { getByRole } = render(<PrintButton />);

const button = getByRole('button', { name: 'components.print_button.label' });

userEvent.click(button);

expect(window.print).to.have.been.called();
});

it('forwards all other props to the button child', () => {
const { getByRole } = render(<PrintButton isOutline />);

const button = getByRole('button', { name: 'components.print_button.label' });

expect(button.closest('lg-print-button')).to.exist();
expect(button.classList.contains('usa-button--outline')).to.be.true();
});
});
24 changes: 24 additions & 0 deletions app/javascript/packages/print-button/print-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { HTMLAttributes } from 'react';
import { t } from '@18f/identity-i18n';
import { Button } from '@18f/identity-components';
import type { ButtonProps } from '@18f/identity-components';
import type PrintButtonElement from './print-button-element';
import './print-button-element';

declare global {
namespace JSX {
interface IntrinsicElements {
'lg-print-button': HTMLAttributes<PrintButtonElement> & { class?: string };
}
}
}

function PrintButton(buttonProps: ButtonProps) {
return (
<lg-print-button>
<Button {...buttonProps}>{t('components.print_button.label')}</Button>
</lg-print-button>
);
}

export default PrintButton;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PageHeading, Button } from '@18f/identity-components';
import { ClipboardButton } from '@18f/identity-clipboard-button';
import { PrintButton } from '@18f/identity-print-button';
import { t } from '@18f/identity-i18n';
import { formatHTML } from '@18f/identity-react-i18n';
import { FormStepsContinueButton } from '@18f/identity-form-steps';
Expand Down Expand Up @@ -42,9 +43,7 @@ function PersonalKeyStep({ value }: PersonalKeyStepProps) {
<Button isOutline className="margin-right-2 margin-bottom-2 tablet:margin-bottom-0">
{t('forms.backup_code.download')}
</Button>
<Button isOutline className="margin-right-2 margin-bottom-2 tablet:margin-bottom-0">
{t('users.personal_key.print')}
</Button>
<PrintButton isOutline className="margin-right-2 margin-bottom-2 tablet:margin-bottom-0" />
<ClipboardButton
clipboardText={personalKey}
isOutline
Expand Down
1 change: 0 additions & 1 deletion app/javascript/packs/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
require('../app/components/index');
require('../app/print-personal-key');
require('../app/i18n-dropdown');
5 changes: 2 additions & 3 deletions app/views/shared/_personal_key.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@
outline: true,
class: 'margin-right-2 margin-bottom-2 tablet:margin-bottom-0',
).with_content(t('forms.backup_code.download')) %>
<%= render ButtonComponent.new(
<%= render PrintButtonComponent.new(
icon: :print,
outline: true,
type: :button,
data: { print: '' },
class: 'margin-right-2 margin-bottom-2 tablet:margin-bottom-0',
).with_content(t('users.personal_key.print')) %>
) %>
<%= render ClipboardButtonComponent.new(
clipboard_text: code,
outline: true,
Expand Down
5 changes: 2 additions & 3 deletions app/views/users/backup_code_setup/create.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@
icon: :file_download,
).with_content(t('forms.backup_code.download')) %>
<% end %>
<%= render ButtonComponent.new(
<%= render PrintButtonComponent.new(
icon: :print,
outline: true,
type: :button,
data: { print: '' },
class: 'margin-top-2 tablet:margin-top-0 tablet:margin-left-2',
).with_content(t('forms.backup_code.print')) %>
) %>
<%= render ClipboardButtonComponent.new(
clipboard_text: @codes.join(' '),
outline: true,
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ en:
toggle_label: Show password
phone_input:
country_code_label: Country code
print_button:
label: Print
status_page:
icons:
error: Error
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ es:
toggle_label: Mostrar contraseña
phone_input:
country_code_label: Código del país
print_button:
label: Imprima esta página
status_page:
icons:
error: Error
Expand Down
2 changes: 2 additions & 0 deletions config/locales/components/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ fr:
toggle_label: Afficher le mot de passe
phone_input:
country_code_label: Code pays
print_button:
label: Imprimer cette page
status_page:
icons:
error: Erreur
Expand Down
1 change: 0 additions & 1 deletion config/locales/forms/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ en:
generate: Get codes
last_code: You used your last backup code. Please print, copy or download the
codes below. You can use these new codes the next time you sign in.
print: Print
regenerate: Get new codes
subinfo_html: '<b>Don’t lose these codes</b>. Download, print, or copy them.
Each code can only be used once. After you’ve used all 10 codes, we’ll
Expand Down
1 change: 0 additions & 1 deletion config/locales/forms/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ es:
last_code: Usted utilizó el último código de seguridad. Imprima, copie o
descargue los códigos que aparecen a continuación. Puede introducir
estos nuevos códigos la próxima vez que se registre.
print: Impresión
regenerate: Obtener nuevos códigos
subinfo_html: '<b>No pierdas estos códigos</b>. Descarga, imprime, o copialos.
Cada código solo se puede usar una vez. Después de que haya utilizado el
Expand Down
1 change: 0 additions & 1 deletion config/locales/forms/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ fr:
last_code: Vous avez utilisé votre dernier code de sauvegarde. Veuillez
imprimer, copier ou télécharger les codes ci-dessous. Vous pourrez
utiliser ces nouveaux codes la prochaine fois que vous vous connecterez.
print: Impression
regenerate: Obtenir de nouveaux codes
subinfo_html: '<b>Ne pas perdre ces codes</b>. Téléchargez, imprimez ou
copiez-les. Chaque code ne peut être utilisé qu’une seule fois. Une fois
Expand Down
1 change: 0 additions & 1 deletion config/locales/users/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ en:
confirmation_error: You’ve entered an incorrect personal key.
generated_on_html: Generated on %{date}
header: Your personal key
print: Print
phones:
error_message: You’ve added the maximum number of phone numbers.
rules_of_use:
Expand Down
1 change: 0 additions & 1 deletion config/locales/users/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ es:
confirmation_error: Ha ingresado una clave personal incorrecta.
generated_on_html: Generado el %{date}
header: Su clave personal
print: Imprima esta página
phones:
error_message: Agregó el número máximo de números de teléfono.
rules_of_use:
Expand Down
1 change: 0 additions & 1 deletion config/locales/users/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ fr:
confirmation_error: Vous avez entré un clé personnelle erronée.
generated_on_html: Générée le %{date}
header: Votre clé personnelle
print: Imprimer cette page
phones:
error_message: Vous avez ajouté le nombre maximum de numéros de téléphone.
rules_of_use:
Expand Down
26 changes: 26 additions & 0 deletions spec/components/print_button_component_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'rails_helper'

RSpec.describe PrintButtonComponent, type: :component do
let(:tag_options) { {} }

subject(:rendered) do
render_inline PrintButtonComponent.new(**tag_options)
end

it 'renders custom element with button' do
expect(rendered).to have_css(
'lg-print-button button[type="button"]',
text: t('components.print_button.label'),
)
end

context 'with tag options' do
let(:tag_options) { { outline: true, data: { foo: 'bar' } } }

it 'renders with tag options forwarded to button' do
expect(rendered).to have_css(
'lg-print-button button.usa-button--outline:not([outline])[data-foo="bar"]',
)
end
end
end

0 comments on commit b8db75b

Please sign in to comment.