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
16 changes: 3 additions & 13 deletions app/assets/stylesheets/components/_password-toggle.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
lg-password-toggle {
display: block;
position: relative;

&.password-toggle--toggle-top .password-toggle__toggle-label {
@include u-pin-right;
top: -24px;
.validated-field__input-wrapper {
margin-bottom: 0;
}

&.password-toggle--toggle-bottom .validated-field__input-wrapper {
margin-bottom: units(2);
}
}

.password-toggle__toggle-wrapper {
display: flex;
justify-content: end;
}

.password-toggle__toggle-label.usa-checkbox__label {
@include u-margin-y(0);
@include u-padding-y(1);
}
30 changes: 14 additions & 16 deletions app/components/password_toggle_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
<%= content_tag(:'lg-password-toggle', class: css_class) do %>
<%= content_tag(:'lg-password-toggle', **tag_options) do %>
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.

Previously this component would forward all additional initializer attributes to the ValidatedFieldComponent. This made it easier to apply options to the field as an optimized use-case (contrast with new field_options), but (a) was inconsistent with other components which would apply additional attributes to the root element and (b) made it difficult or impossible to assign additional attributes to the root element (e.g. extra CSS classes).

<%= render ValidatedFieldComponent.new(
form: form,
name: :password,
type: :password,
label: label,
label: default_label,
**field_options,
input_html: field_options[:input_html].to_h.merge(
id: input_id,
class: ['password-toggle__input', *field_options.dig(:input_html, :class)],
),
) %>
<div class="password-toggle__toggle-wrapper js">
<input
id="<%= toggle_id %>"
type="checkbox"
class="usa-checkbox__input usa-checkbox__input--bordered password-toggle__toggle"
aria-controls="<%= input_id %>"
>
<label
for="<%= toggle_id %>"
class="usa-checkbox__label password-toggle__toggle-label"
>
<%= toggle_label %>
</label>
</div>
<input
id="<%= toggle_id %>"
type="checkbox"
class="usa-checkbox__input password-toggle__toggle"
aria-controls="<%= input_id %>"
>
<label
for="<%= toggle_id %>"
class="usa-checkbox__label password-toggle__toggle-label"
>
<%= toggle_label %>
</label>
<% end %>
16 changes: 6 additions & 10 deletions app/components/password_toggle_component.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
class PasswordToggleComponent < BaseComponent
attr_reader :form, :label, :toggle_label, :toggle_position, :field_options
attr_reader :form, :label, :toggle_label, :field_options, :tag_options

def initialize(
form:,
label: t('components.password_toggle.label'),
toggle_label: t('components.password_toggle.toggle_label'),
toggle_position: :top,
**field_options
field_options: {},
**tag_options
)
@form = form
@label = label
@toggle_label = toggle_label
@toggle_position = toggle_position
@field_options = field_options
@tag_options = tag_options
end

def css_class
classes = []
classes << 'password-toggle--toggle-top' if toggle_position == :top
classes << 'password-toggle--toggle-bottom' if toggle_position == :bottom
classes
def default_label
t('components.password_toggle.label')
end

def toggle_id
Expand Down
8 changes: 5 additions & 3 deletions app/views/devise/passwords/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
<%= f.full_error :reset_password_token %>
<%= render PasswordToggleComponent.new(
form: f,
label: t('forms.passwords.edit.labels.password'),
required: true,
field_options: {
label: t('forms.passwords.edit.labels.password'),
required: true,
},
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'margin-bottom-4' %>
<%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'display-block margin-y-5' %>
<% end %>

<%= render 'shared/password_accordion' %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/devise/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
) %>
<%= render PasswordToggleComponent.new(
form: f,
required: true,
wrapper_html: { class: 'margin-top-6' },
class: 'margin-bottom-4',
field_options: { required: true },
) %>
<%= f.input :request_id, as: :hidden, input_html: { value: @request_id } %>
<div class='margin-bottom-4'>
Expand Down
2 changes: 1 addition & 1 deletion app/views/devise/shared/_password_strength.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div id='pw-strength-cntnr' class='display-none' aria-live='polite' aria-atomic='true'>
<div class='margin-top-neg-3 margin-bottom-4'>
<div class="margin-bottom-4">
<div class='clearfix margin-x-neg-05 padding-top-05'>
<div class='pw-bar'></div>
<div class='pw-bar'></div>
Expand Down
6 changes: 4 additions & 2 deletions app/views/event_disavowal/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
input_html: { value: @disavowal_token, name: :disavowal_token } %>
<%= render PasswordToggleComponent.new(
form: f,
label: t('forms.passwords.edit.labels.password'),
required: true,
field_options: {
label: t('forms.passwords.edit.labels.password'),
required: true,
},
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'margin-bottom-4' %>
Expand Down
11 changes: 6 additions & 5 deletions app/views/idv/review/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
<%= simple_form_for(
current_user,
url: idv_review_path,
html: { autocomplete: 'off', method: :put, class: 'margin-top-6' },
html: { autocomplete: 'off', method: :put, class: 'margin-top-4' },
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
label: t('idv.form.password'),
required: true,
wrapper_html: { class: 'margin-bottom-0' },
field_options: {
label: t('idv.form.password'),
required: true,
},
) %>
<div class="text-right margin-top-2 margin-bottom-4">
<div class="text-right margin-top-neg-4 margin-bottom-4">
<%= link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url, class: 'margin-left-1') %>
</div>
<%= render AccordionComponent.new do |c| %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/mfa_confirmation/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
<%= simple_form_for(
current_user,
url: reauthn_user_password_path,
html: { autocomplete: 'off', method: 'post', class: 'margin-top-6' },
html: { autocomplete: 'off', method: 'post', class: 'margin-top-4' },
) do |f| %>
<%= render PasswordToggleComponent.new(form: f, required: true) %>
<%= render PasswordToggleComponent.new(form: f, field_options: { required: true }) %>
<%= f.submit t('forms.buttons.continue'), class: 'display-block margin-y-5' %>
<% end %>
<%= render 'shared/cancel', link: account_path %>
8 changes: 5 additions & 3 deletions app/views/password_capture/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
as: :user,
url: capture_password_url,
method: :post,
html: { autocomplete: 'off', class: 'margin-top-6' },
html: { autocomplete: 'off', class: 'margin-top-4' },
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
label: t('account.index.password'),
required: true,
field_options: {
label: t('account.index.password'),
required: true,
},
) %>
<%= f.submit t('forms.buttons.submit.default'), class: 'display-block margin-y-5' %>
<% end %>
Expand Down
21 changes: 11 additions & 10 deletions app/views/shared/_ssn_field.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ locals:

<%# maxlength set and includes '-' delimiters to work around cleave bug %>
<%= render PasswordToggleComponent.new(
name: :ssn,
form: f,
as: :password,
label: t('idv.form.ssn_label_html'),
toggle_label: t('forms.ssn.show'),
toggle_position: :bottom,
hint: t('forms.example') + ' 123-45-6789',
required: true,
pattern: '^\d{3}-?\d{2}-?\d{4}$',
maxlength: 11,
input_html: { aria: { invalid: false }, class: 'ssn-toggle usa-input', value: '' },
error_messages: { patternMismatch: t('idv.errors.pattern_mismatch.ssn') },
field_options: {
name: :ssn,
as: :password,
label: t('idv.form.ssn_label_html'),
hint: t('forms.example') + ' 123-45-6789',
required: true,
pattern: '^\d{3}-?\d{2}-?\d{4}$',
maxlength: 11,
input_html: { aria: { invalid: false }, class: 'ssn-toggle usa-input', value: '' },
error_messages: { patternMismatch: t('idv.errors.pattern_mismatch.ssn') },
},
) %>

<%= javascript_packs_tag_once('ssn-field') %>
10 changes: 6 additions & 4 deletions app/views/sign_up/passwords/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
label: t('forms.password'),
required: true,
input_html: { aria: { describedby: 'password-description' } },
field_options: {
label: t('forms.password'),
required: true,
input_html: { aria: { describedby: 'password-description' } },
},
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= hidden_field_tag :confirmation_token, @confirmation_token, id: 'confirmation_token' %>
<%= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] || request_id } %>
<%= f.submit t('forms.buttons.continue'), class: 'margin-bottom-5' %>
<%= f.submit t('forms.buttons.continue'), class: 'display-block margin-y-5' %>
<% end %>

<%= render 'shared/password_accordion' %>
Expand Down
10 changes: 6 additions & 4 deletions app/views/users/delete/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@

<%= render PasswordToggleComponent.new(
form: f,
name: :password,
label: t('idv.form.password'),
required: true,
field_options: {
name: :password,
label: t('idv.form.password'),
required: true,
},
) %>

<%= f.submit t('users.delete.actions.delete'), danger: true, class: 'margin-top-1 margin-bottom-2' %>
<%= f.submit t('users.delete.actions.delete'), danger: true, class: 'margin-top-5 margin-bottom-2' %>
<% end %>

<%= link_to(
Expand Down
12 changes: 7 additions & 5 deletions app/views/users/passwords/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
<%= f.error_notification %>
<%= render PasswordToggleComponent.new(
form: f,
name: :password,
label: t('forms.passwords.edit.labels.password'),
required: true,
input_html: { aria: { describedby: 'password-description' } },
field_options: {
name: :password,
label: t('forms.passwords.edit.labels.password'),
required: true,
input_html: { aria: { describedby: 'password-description' } },
},
) %>
<%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %>
<%= f.submit t('forms.buttons.submit.update'), class: 'margin-top-2 margin-bottom-4' %>
<%= f.submit t('forms.buttons.submit.update'), class: 'display-block margin-top-5 margin-bottom-4' %>
<% end %>

<%= render 'shared/password_accordion' %>
Expand Down
10 changes: 6 additions & 4 deletions app/views/users/verify_password/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
) do |f| %>
<%= render PasswordToggleComponent.new(
form: f,
name: :password,
label: t('idv.form.password'),
required: true,
field_options: {
name: :password,
label: t('idv.form.password'),
required: true,
},
) %>
<%= f.submit t('forms.buttons.continue') %>
<%= f.submit t('forms.buttons.continue'), class: 'margin-top-5' %>
<% end %>

<div class='margin-top-6'>
Expand Down
59 changes: 19 additions & 40 deletions spec/components/password_toggle_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
subject(:rendered) { render_inline PasswordToggleComponent.new(form: form, **options) }

it 'renders default markup' do
expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-top')
expect(rendered).to have_css('lg-password-toggle')
expect(rendered).to have_field(t('components.password_toggle.label'), type: :password)
expect(rendered).to have_field(t('components.password_toggle.toggle_label'), type: :checkbox)
end
Expand All @@ -23,17 +23,6 @@
expect(rendered).to have_css("[aria-controls='#{input_id}']")
end

describe '#label' do
context 'with custom label' do
let(:label) { 'Custom Label' }
let(:options) { { label: label } }

it 'renders custom field label' do
expect(rendered).to have_field(label, type: :password)
end
end
end

describe '#toggle_label' do
context 'with custom label' do
let(:toggle_label) { 'Custom Toggle Label' }
Expand All @@ -45,24 +34,6 @@
end
end

describe '#toggle_position' do
context 'with top toggle position' do
let(:options) { { toggle_position: :top } }

it 'renders modifier class' do
expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-top')
end
end

context 'with bottom toggle position' do
let(:options) { { toggle_position: :bottom } }

it 'renders modifier class' do
expect(rendered).to have_css('lg-password-toggle.password-toggle--toggle-bottom')
end
end
end

describe '#toggle_id' do
it 'is unique across instances' do
toggle_one = PasswordToggleComponent.new(form: form)
Expand All @@ -85,17 +56,25 @@
end
end

describe '#field' do
context 'with field options' do
let(:options) do
{ input_html: { class: 'my-custom-field', data: { foo: 'bar' } }, required: true }
end
context 'with tag options' do
let(:options) do
{ class: 'my-custom-field', data: { foo: 'bar' } }
end

it 'forwards field options' do
expect(rendered).to have_css(
'.password-toggle__input.my-custom-field[data-foo="bar"][required]',
)
end
it 'forwards options to rendered tag' do
expect(rendered).to have_css('lg-password-toggle.my-custom-field[data-foo="bar"]')
end
end

context 'with field options' do
let(:label) { 'Custom Label' }
let(:options) do
{ field_options: { label: label, required: true } }
end

it 'forwards options to rendered field' do
expect(rendered).to have_css('.password-toggle__input[required]')
expect(rendered).to have_field(label, type: :password)
end
end
end
Loading