Skip to content

Commit

Permalink
Show/Hide SSN field (LG-3641) (#4342)
Browse files Browse the repository at this point in the history
* specify which password inputs have password toggle

* SSN field show/hide toggle

* Update other SSN fields

* js lint

* add width to inputs

* update comments

* ruby lint

* fix password edit class

* allow 11 characters for SSN always

* fix aria attributes

* remove formatting entirely on hidden input

* sync before adding event listener
  • Loading branch information
mitchellhenke committed Oct 27, 2020
1 parent c756a84 commit fb558a2
Show file tree
Hide file tree
Showing 22 changed files with 86 additions and 27 deletions.
8 changes: 0 additions & 8 deletions app/javascript/app/form-field-format.js
Expand Up @@ -23,14 +23,6 @@ function formatForm() {
});
}

if (document.querySelector('.ssn')) {
new Cleave('.ssn', {
numericOnly: true,
blocks: [3, 2, 4],
delimiter: '-',
});
}

if (document.querySelector('.zipcode')) {
new Cleave('.zipcode', {
numericOnly: true,
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/app/pw-toggle.js
@@ -1,7 +1,7 @@
const { I18n } = window.LoginGov;

function togglePw() {
const inputs = document.querySelectorAll('input[type="password"]');
const inputs = document.querySelectorAll('input.password-toggle[type="password"]');

if (inputs) {
[].slice.call(inputs).forEach((input, i) => {
Expand Down
52 changes: 52 additions & 0 deletions app/javascript/app/ssn-field.js
@@ -0,0 +1,52 @@
import Cleave from 'cleave.js';

const { I18n } = window.LoginGov;

function sync(input, toggle, ssnCleave) {
input.type = toggle.checked ? 'text' : 'password';

ssnCleave?.destroy();

if (toggle.checked) {
return new Cleave(input, {
numericOnly: true,
blocks: [3, 2, 4],
delimiter: '-',
});
}
input.value = input.value.replace(/-/g, '');

return null;
}

/* eslint-disable no-new */
function formatSSNField() {
const inputs = document.querySelectorAll('input.ssn-toggle[type="password"]');

if (inputs) {
[].slice.call(inputs).forEach((input, i) => {
const el = `
<div class="mt1 right">
<label class="btn-border" for="ssn-toggle-${i}">
<div class="checkbox">
<input id="ssn-toggle-${i}" type="checkbox">
<span class="indicator"></span>
${I18n.t('forms.ssn.show')}
</div>
</label>
</div>`;
input.insertAdjacentHTML('afterend', el);

const toggle = document.getElementById(`ssn-toggle-${i}`);
let ssnCleave;

ssnCleave = sync(input, toggle, ssnCleave);

toggle.addEventListener('change', function () {
ssnCleave = sync(input, toggle, ssnCleave);
});
});
}
}

document.addEventListener('DOMContentLoaded', formatSSNField);
1 change: 1 addition & 0 deletions app/javascript/packs/application.js
Expand Up @@ -10,3 +10,4 @@ require('../app/print-personal-key');
require('../app/i18n-dropdown');
require('../app/platform-authenticator');
require('../app/accessible-forms');
require('../app/ssn-field');
3 changes: 2 additions & 1 deletion app/views/devise/passwords/edit.html.erb
Expand Up @@ -11,7 +11,8 @@
role: 'form' }) do |f| %>
<%= f.input :reset_password_token, as: :hidden %>
<%= f.full_error :reset_password_token %>
<%= f.input :password, label: t('forms.passwords.edit.labels.password'), required: true %>
<%= f.input :password, label: t('forms.passwords.edit.labels.password'), required: true,
input_html: { class: 'password-toggle' } %>
<%= render 'devise/shared/password_strength' %>
<%= f.button :submit, t('forms.passwords.edit.buttons.submit'), class: 'mb3' %>
<% end %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/devise/sessions/new.html.erb
Expand Up @@ -29,7 +29,7 @@
<%= f.input :password,
label: t('account.index.password'),
required: true,
input_html: { aria: { invalid: false } } %>
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<%= f.input :request_id, as: :hidden, input_html: { value: request_id } %>
<div class='mb3'>
<%= submit_tag t('links.next'), class: 'usa-button usa-button--primary grid-col-12 mb2' %>
Expand Down
3 changes: 2 additions & 1 deletion app/views/event_disavowal/new.html.erb
Expand Up @@ -11,7 +11,8 @@
role: 'form' }) do |f| %>
<%= f.input :disavowal_token, as: :hidden,
input_html: { value: @disavowal_token, name: :disavowal_token } %>
<%= f.input :password, label: t('forms.passwords.edit.labels.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('forms.passwords.edit.labels.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<%= render 'devise/shared/password_strength' %>
<%= f.button :submit, t('forms.passwords.edit.buttons.submit'), class: 'mb3' %>
<% end %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/idv/cac/enter_info.html.erb
Expand Up @@ -36,12 +36,12 @@
</div>
</div>
<div class="clearfix mxn1">
<div class="sm-col sm-col-6 px1">
<div class="sm-col sm-col-8 px1">
<div class="mb3 tel required profile_ssn">
<label class="bold tel required" for="profile_ssn"><abbr title="required" class="red display-none">*</abbr>
<%= t('in_person_proofing.forms.ssn') %>
</label>
<input class="block col-12 field string tel required ssn" required="required" aria-invalid="false" aria-required="true" pattern="^\d{3}-?\d{2}-?\d{4}$" type="tel" name="doc_auth[ssn]" id="profile_ssn"/>
<input class="block col-12 field string tel required ssn ssn-toggle" required="required" aria-invalid="false" aria-required="true" pattern="^\d{3}-?\d{2}-?\d{4}$" type="password" name="doc_auth[ssn]" id="profile_ssn"/>
</div>
</div>
</div>
Expand Down
7 changes: 3 additions & 4 deletions app/views/idv/doc_auth/ssn.html.erb
Expand Up @@ -18,17 +18,16 @@
html: { autocomplete: 'off', role: 'form', class: 'mt2' }
) do |f| %>
<div class='clearfix mxn1'>
<div class='sm-col sm-col-6 px1 mt2'>
<!-- using :tel for mobile numeric keypad -->
<div class='sm-col sm-col-8 px1 mt2'>
<!-- maxlength set and includes '-' delimiters to work around cleave bug -->
<%= f.input(
:ssn,
as: :tel,
as: :password,
label: t('idv.form.ssn_label_html'),
required: true,
pattern: '^\d{3}-?\d{2}-?\d{4}$',
maxlength: 11,
input_html: { aria: { invalid: false }, class: 'ssn', value: '' }
input_html: { aria: { invalid: false }, class: 'ssn ssn-toggle', value: '' }
) %>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion app/views/idv/in_person/encrypt.html.erb
Expand Up @@ -19,7 +19,8 @@
<%= validated_form_for(:in_person_proofing, url: url_for, method: 'PUT',
html: { autocomplete: 'off', method: :put, role: 'form' }) do |f| %>
<%= f.input :password, label: t('idv.form.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('idv.form.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<div class="right-align mtn2 mb4">
<%= t('idv.forgot_password.link_html',
link: link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url,
Expand Down
2 changes: 1 addition & 1 deletion app/views/idv/in_person/enter_info.html.erb
Expand Up @@ -33,7 +33,7 @@
<label class="bold tel required" for="profile_ssn"><abbr title="required" class="red display-none">*</abbr>
<%= t('in_person_proofing.forms.ssn') %>
</label>
<input class="block col-12 field string tel required ssn" required="required" aria-invalid="false" aria-required="true" pattern="^\d{3}-?\d{2}-?\d{4}$" type="tel" name="in_person[ssn]" id="profile_ssn"/>
<input class="block col-12 field string tel required ssn ssn-toggle" required="required" aria-invalid="false" aria-required="true" pattern="^\d{3}-?\d{2}-?\d{4}$" type="password" name="in_person[ssn]" id="profile_ssn"/>
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion app/views/idv/review/new.html.erb
Expand Up @@ -13,7 +13,8 @@
<%= validated_form_for(current_user, url: idv_review_path,
html: { autocomplete: 'off', method: :put, role: 'form' }) do |f| %>
<%= f.input :password, label: t('idv.form.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('idv.form.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<div class="right-align mtn2 mb4">
<%= t('idv.forgot_password.link_html',
link: link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url,
Expand Down
2 changes: 1 addition & 1 deletion app/views/mfa_confirmation/new.html.erb
Expand Up @@ -10,7 +10,7 @@
<%= validated_form_for(current_user,
url: reauthn_user_password_path,
html: { autocomplete: 'off', role: 'form', method: 'post' }) do |f| %>
<%= f.input :password, required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, required: true, input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<%= f.button :submit, t('forms.buttons.continue'), class: 'btn-wide mt2' %>
<% end %>
<%= render 'shared/cancel', link: account_path %>
3 changes: 2 additions & 1 deletion app/views/password_capture/new.html.erb
Expand Up @@ -5,7 +5,8 @@
url: capture_password_url,
method: :post,
html: { autocomplete: 'off', role: 'form' }) do |f| %>
<%= f.input :password, label: t('account.index.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('account.index.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<div>
<%= f.button :submit, t('links.next'), class: 'sm-col-6 col-12 btn-wide mb3' %>
</div>
Expand Down
3 changes: 2 additions & 1 deletion app/views/sign_up/passwords/new.html.slim
Expand Up @@ -8,7 +8,8 @@ p.mt2.mb0#password-description
method: :post,
html: { role: 'form', autocomplete: 'off' }) do |f|
= f.input :password, required: true,
input_html: { aria: { invalid: false, describedby: 'password-description' } }
input_html: { aria: { invalid: false, describedby: 'password-description' },
class: 'password-toggle' }
= render 'devise/shared/password_strength'
= hidden_field_tag :confirmation_token, @confirmation_token, id: 'confirmation_token'
= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] || request_id }
Expand Down
3 changes: 2 additions & 1 deletion app/views/users/delete/show.html.erb
Expand Up @@ -17,7 +17,8 @@
<div class="mb3">
<%= t('users.delete.instructions') %>
</div>
<%= f.input :password, label: t('idv.form.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('idv.form.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<%= f.button :submit,
t('users.delete.actions.delete'),
class: 'btn btn-primary col-12 mb2 p2 rounded-lg' %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/users/passwords/edit.html.erb
Expand Up @@ -12,7 +12,7 @@
html: { autocomplete: 'off', method: :patch, role: 'form' }) do |f| %>
<%= f.error_notification %>
<%= f.input :password, label: t('forms.passwords.edit.labels.password'), required: true,
input_html: { aria: { invalid: false, describedby: 'password-description' } } %>
input_html: { aria: { invalid: false, describedby: 'password-description' }, class: 'password-toggle' } %>
<%= render 'devise/shared/password_strength' %>
<%= f.button :submit, t('forms.buttons.submit.update'), class: 'mt2 mb3' %>
<% end %>
Expand Down
3 changes: 2 additions & 1 deletion app/views/users/verify_password/new.html.erb
Expand Up @@ -10,7 +10,8 @@

<%= validated_form_for(current_user, url: update_verify_password_path,
html: { autocomplete: 'off', method: :put, role: 'form' }) do |f| %>
<%= f.input :password, label: t('idv.form.password'), required: true, input_html: { aria: { invalid: false } } %>
<%= f.input :password, label: t('idv.form.password'), required: true,
input_html: { aria: { invalid: false }, class: 'password-toggle' } %>
<%= f.button :submit, t('forms.buttons.continue'), class: 'btn btn-primary btn-wide sm-col-6 col-12' %>
<% end %>

Expand Down
1 change: 1 addition & 0 deletions config/js_locale_strings.yml
Expand Up @@ -60,6 +60,7 @@
- forms.buttons.continue
- forms.buttons.submit.default
- forms.passwords.show
- forms.ssn.show
- idv.errors.pattern_mismatch.dob
- idv.errors.pattern_mismatch.personal_key
- idv.errors.pattern_mismatch.ssn
Expand Down
2 changes: 2 additions & 0 deletions config/locales/forms/en.yml
Expand Up @@ -118,6 +118,8 @@ en:
registration:
labels:
email: Email address
ssn:
show: Show Social Security Number
totp_delete:
caution: If you remove your authentication app you won't be able to use it to
access your login.gov account.
Expand Down
2 changes: 2 additions & 0 deletions config/locales/forms/es.yml
Expand Up @@ -126,6 +126,8 @@ es:
registration:
labels:
email: Email
ssn:
show: Mostrar Número de Seguro Social
totp_delete:
caution: Si elimina su aplicación de autenticación, no podrá usarla para acceder
a su cuenta login.gov.
Expand Down
2 changes: 2 additions & 0 deletions config/locales/forms/fr.yml
Expand Up @@ -131,6 +131,8 @@ fr:
registration:
labels:
email: Adresse courriel
ssn:
show: Afficher le numéro de sécurité sociale
totp_delete:
caution: Si vous supprimez votre application d'authentification, vous ne pourrez
plus l'utiliser pour accéder à votre compte login.gov.
Expand Down

0 comments on commit fb558a2

Please sign in to comment.