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
19 changes: 16 additions & 3 deletions app/components/validated_field_component.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class ValidatedFieldComponent < BaseComponent
attr_reader :form, :name, :tag_options
attr_reader :form, :name, :tag_options, :input_type

alias_method :f, :form

Expand All @@ -8,23 +8,36 @@ def initialize(form:, name:, error_messages: {}, **tag_options)
@name = name
@error_messages = error_messages
@tag_options = tag_options
@input_type = inferred_input_type
end

def error_messages
{
valueMissing: value_missing_error_message,
typeMismatch: type_mismatch_error_message,
**@error_messages,
}
}.compact
end

private

def value_missing_error_message
case form.send(:default_input_type, name, form.send(:find_attribute_column, name), tag_options)
case input_type
when :boolean
t('forms.validation.required_checkbox')
else
t('simple_form.required.text')
end
end

def type_mismatch_error_message
case input_type
when :email
t('valid_email.validations.email.invalid')
end
end

def inferred_input_type
form.send(:default_input_type, name, form.send(:find_attribute_column, name), tag_options)
end
end
2 changes: 1 addition & 1 deletion app/forms/register_user_email_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def validate_terms_accepted
end

def submit(params, instructions = nil)
@terms_accepted = params[:terms_accepted] == 'true'
@terms_accepted = params[:terms_accepted] == '1'
build_user_and_email_address_with_email(
email: params[:email],
email_language: params[:email_language],
Expand Down
4 changes: 3 additions & 1 deletion app/javascript/packages/validated-field/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ export class ValidatedField extends HTMLElement {
setErrorMessage(message) {
if (message) {
this.getOrCreateErrorMessageElement().textContent = message;
this.input?.focus();
if (!document.activeElement?.classList.contains('usa-input--error')) {
this.input?.focus();
}
} else if (this.errorMessage) {
this.inputWrapper?.removeChild(this.errorMessage);
this.errorMessage = null;
Expand Down
22 changes: 20 additions & 2 deletions app/javascript/packages/validated-field/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ describe('ValidatedField', () => {
customElements.define('lg-validated-field', ValidatedField);
});

let idCounter = 0;

function createAndConnectElement({ hasInitialError = false } = {}) {
const element = document.createElement('lg-validated-field');
const errorMessageId = ++idCounter;
element.innerHTML = `
<script type="application/json" class="validated-field__error-strings">
{
Expand All @@ -19,14 +22,14 @@ describe('ValidatedField', () => {
<div class="validated-field__input-wrapper">
<input
aria-invalid="false"
aria-describedby="validated-field-error-298658fb"
aria-describedby="validated-field-error-${errorMessageId}"
required="required"
aria-required="true"
class="validated-field__input${hasInitialError ? ' usa-input--error' : ''}"
/>
${
hasInitialError
? '<div class="usa-error-message" id="validated-field-error-298658fb">Invalid value</div>'
? `<div class="usa-error-message" id="validated-field-error-${errorMessageId}">Invalid value</div>`
: ''
}
</div>
Expand Down Expand Up @@ -89,6 +92,21 @@ describe('ValidatedField', () => {
expect(() => getByText(element, 'This field is required')).to.throw();
});

it('focuses the first element with an error', () => {
const firstElement = createAndConnectElement();
createAndConnectElement();

/** @type {HTMLInputElement} */
const firstInput = getByRole(firstElement, 'textbox');

/** @type {HTMLFormElement} */
const form = document.querySelector('form');

form.checkValidity();

expect(document.activeElement).to.equal(firstInput);
});

context('with initial error message', () => {
it('clears existing validation state on input', () => {
const element = createAndConnectElement();
Expand Down
44 changes: 0 additions & 44 deletions app/javascript/packs/email-validation.js

This file was deleted.

2 changes: 1 addition & 1 deletion app/services/request_password_reset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
def perform
if user_should_receive_registration_email?
form = RegisterUserEmailForm.new(password_reset_requested: true, analytics: analytics)
result = form.submit({ email: email, terms_accepted: 'true' }, instructions)
result = form.submit({ email: email, terms_accepted: '1' }, instructions)
[form.user, result]
else
send_reset_password_instructions
Expand Down
100 changes: 46 additions & 54 deletions app/views/sign_up/registrations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,60 @@

<%= render 'shared/sp_alert' %>

<%= render AlertComponent.new(
type: :error,
class: 'email-invalid-alert margin-bottom-4 display-none',
message: t('sign_up.email.invalid_email_alert_head'),
) %>

<h1 class='margin-y-0'><%= t('titles.registrations.new') %></h1>

<div class='margin-bottom-6'>
<%= validated_form_for(
@register_user_email_form,
html: { autocomplete: 'off' },
url: sign_up_register_path,
) do |f| %>
<%= f.input :email,
label: t('forms.registration.labels.email'),
required: true,
input_html: { autocorrect: 'off', aria: { invalid: false } } %>

<span class='email-invalid-alert-inline usa-error-message margin-top-neg-3 margin-bottom-3 display-none' role='alert'>
<%= t('sign_up.email.invalid_email_alert_inline') %>
</span>
<%= simple_form_for(
@register_user_email_form,
html: { autocomplete: 'off' },
url: sign_up_register_path,
) do |f| %>
<%= render ValidatedFieldComponent.new(
form: f,
name: :email,
as: :email,
label: t('forms.registration.labels.email'),
required: true,
input_html: { autocorrect: 'off' },
) %>

<fieldset class="usa-fieldset">
<legend class="margin-bottom-1">
<%= t('forms.registration.labels.email_language') %>
</legend>
<fieldset class="usa-fieldset">
<legend class="margin-bottom-1">
<%= t('forms.registration.labels.email_language') %>
</legend>

<p class="margin-bottom-4">
<%= t(
'account.email_language.languages_list',
app_name: APP_NAME,
list: I18n.available_locales.
map { |locale| t("account.email_language.name.#{locale}") }.
to_sentence(last_word_connector: " #{t('account.email_language.sentence_connector')} "),
) %>
</p>
<p class="margin-bottom-4">
<%= t(
'account.email_language.languages_list',
app_name: APP_NAME,
list: I18n.available_locales.
map { |locale| t("account.email_language.name.#{locale}") }.
to_sentence(last_word_connector: " #{t('account.email_language.sentence_connector')} "),
) %>
</p>

<%= render partial: 'shared/email_languages',
locals: { f: f, selection: @register_user_email_form.email_language } %>
</fieldset>
<%= render partial: 'shared/email_languages',
locals: { f: f, selection: @register_user_email_form.email_language } %>
</fieldset>

<div class="margin-bottom-3">
<%= f.check_box :terms_accepted, { class: 'usa-checkbox__input',
required: true, aria: { invalid: false } }, true, false %>
<label for="user_terms_accepted" class="usa-checkbox__label">
<%= t('sign_up.terms', app_name: APP_NAME) %>
<%= new_window_link_to(t('titles.rules_of_use'), MarketingSite.rules_of_use_url) %>
</label>
<div class="usa-error-message display-if-invalid display-if-invalid--value-missing" role="alert">
<%= t('forms.validation.required_checkbox') %>
</div>
</div>
<%= render ValidatedFieldComponent.new(
form: f,
name: :terms_accepted,
as: :boolean,
label: capture do %>
<%= t('sign_up.terms', app_name: APP_NAME) %>
<%= new_window_link_to(t('titles.rules_of_use'), MarketingSite.rules_of_use_url) %>
<% end,
required: true,
) %>

<%= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] || request_id } %>
<%= f.button :button, t('forms.buttons.submit.default'), type: :submit,
class: 'usa-button--big grid-col-8 mobile-lg:grid-col-6' %>
<%= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] || request_id } %>
<%= f.button(
:button,
t('forms.buttons.submit.default'),
type: :submit,
class: 'usa-button usa-button--big usa-button--wide margin-y-5',
) %>
<% end %>
</div>

<%= render 'shared/cancel', link: decorated_session.cancel_link_url %>

Expand All @@ -77,6 +72,3 @@
MarketingSite.privacy_act_statement_url,
) %>
</p>


<%= javascript_packs_tag_once 'email-validation', 'accept-terms-button' %>
4 changes: 0 additions & 4 deletions config/locales/sign_up/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,4 @@ en:
cancel:
success: Your account has been deleted. We did not save your information.
warning_header: 'If you cancel now:'
email:
invalid_email_alert_head: 'Error: This is not a real email address. Make sure it
includes an @ and a domain name.'
invalid_email_alert_inline: Enter a real email address
terms: Check this box to accept the %{app_name}
4 changes: 0 additions & 4 deletions config/locales/sign_up/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,4 @@ es:
cancel:
success: Su cuenta ha sido eliminada. No guardamos su información.
warning_header: 'Si cancela ahora:'
email:
invalid_email_alert_head: 'Error: No es una dirección de correo electrónico
real. Asegúrese de que incluye una @ y un nombre de dominio.'
invalid_email_alert_inline: Ingrese una dirección de correo electrónico real
terms: Marque esta casilla para aceptar las reglas de uso de %{app_name}
4 changes: 0 additions & 4 deletions config/locales/sign_up/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,4 @@ fr:
cancel:
success: Le dossier contenant votre information a été effacé
warning_header: 'Si vous annulez maintenant:'
email:
invalid_email_alert_head: 'Erreur: Ce n’est pas une vraie adresse email.
Assurez-vous qu’il comprend un @ et un nom de domaine.'
invalid_email_alert_inline: Entrez une adresse email réelle
terms: Cochez cette case pour accepter les règles d’utilisation de %{app_name}
3 changes: 1 addition & 2 deletions config/locales/valid_email/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ en:
valid_email:
validations:
email:
invalid: Invalid email address format or domain entered. Please enter a valid
email address.
invalid: Email address is not valid
3 changes: 1 addition & 2 deletions config/locales/valid_email/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ es:
valid_email:
validations:
email:
invalid: El formato de email o dominio ingresado no es válido. Corrija su email
y vuelva a intentarlo.
invalid: La dirección de correo electrónico no es válida
3 changes: 1 addition & 2 deletions config/locales/valid_email/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ fr:
valid_email:
validations:
email:
invalid: Format d’adresse courriel ou domaine entré non valide. Corrigez
l’adresse et entrez-la de nouveau.
invalid: L’adresse électronique n’est pas valide
Loading