diff --git a/app/assets/images/alert/error.svg b/app/assets/images/alert/error.svg deleted file mode 100644 index db7505a17f5..00000000000 --- a/app/assets/images/alert/error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/stylesheets/components/_form.scss b/app/assets/stylesheets/components/_form.scss index 0c3a308ac61..077e2d376df 100644 --- a/app/assets/stylesheets/components/_form.scss +++ b/app/assets/stylesheets/components/_form.scss @@ -45,7 +45,6 @@ textarea { // error states .has-error input { - background-image: url(image-path('alert/error.svg')); background-position: center right $form-field-padding-x; background-repeat: no-repeat; background-size: 1rem 1rem; @@ -74,6 +73,18 @@ input::-webkit-inner-spin-button { [aria-invalid="true"] ~ & { display: block; } + + [aria-invalid="value-missing"] ~ &.display-if-invalid--value-missing { + display: block; + } + + [aria-invalid="pattern-mismatch"] ~ &.display-if-invalid--pattern-mismatch { + display: block; + } +} + +.usa-input--error.field:invalid { + @include u-border($theme-input-state-border-width, 'error'); } .usa-hint { diff --git a/app/javascript/packs/form-validation.js b/app/javascript/packs/form-validation.js index 5b2fdb38846..4b04d30bb9c 100644 --- a/app/javascript/packs/form-validation.js +++ b/app/javascript/packs/form-validation.js @@ -19,25 +19,41 @@ function disableFormSubmit(event) { }); } +function kebabCase(string) { + return string.replace(/(.)([A-Z])/g, '$1-$2').toLowerCase(); +} + +function resetInput(input) { + input.setCustomValidity(''); + input.setAttribute('aria-invalid', 'false'); + input.classList.remove('usa-input--error'); +} + /** * Given an `input` or `invalid` event, updates custom validity of the given input. * * @param {Event} event Input or invalid event. */ + function checkInputValidity(event) { const input = /** @type {HTMLInputElement} */ (event.target); - input.setCustomValidity(''); - input.setAttribute('aria-invalid', String(!input.validity.valid)); + resetInput(input); if ( event.type === 'invalid' && !input.validity.valid && input.parentNode?.querySelector('.display-if-invalid') ) { event.preventDefault(); + const errors = Object.keys(ValidityState.prototype) + .filter((key) => key !== 'valid') + .filter((key) => input.validity[key]); + + input.setAttribute('aria-invalid', errors.length ? kebabCase(errors[0]) : 'false'); + input.classList.add('usa-input--error'); input.focus(); } - const { I18n } = /** @type {typeof window & LoginGovGlobal} */ (window).LoginGov; + const { I18n } = /** @type {typeof window & LoginGovGlobal} */ (window).LoginGov; if (input.validity.valueMissing) { input.setCustomValidity(I18n.t('simple_form.required.text')); } else if (input.validity.patternMismatch) { diff --git a/app/javascript/packs/ial2-consent-button.js b/app/javascript/packs/ial2-consent-button.js index 409ddbc8485..345108c0481 100644 --- a/app/javascript/packs/ial2-consent-button.js +++ b/app/javascript/packs/ial2-consent-button.js @@ -5,7 +5,10 @@ function toggleButton() { function sync() { continueButton.classList.toggle('usa-button--disabled', !checkbox.checked); - errorMessage.classList.toggle('display-none', checkbox.getAttribute('aria-invalid') !== 'true'); + errorMessage.classList.toggle( + 'display-none', + checkbox.getAttribute('aria-invalid') !== 'value-missing', + ); } sync(); diff --git a/app/views/idv/doc_auth/agreement.html.erb b/app/views/idv/doc_auth/agreement.html.erb index 0f9a3ed0ba5..d88e745257e 100644 --- a/app/views/idv/doc_auth/agreement.html.erb +++ b/app/views/idv/doc_auth/agreement.html.erb @@ -34,7 +34,8 @@ <%= t('doc_auth.instructions.consent', app_name: APP_NAME) %> <%= new_window_link_to t('doc_auth.instructions.learn_more'), MarketingSite.security_and_privacy_practices_url %> -
diff --git a/app/views/idv/doc_auth/send_link.html.erb b/app/views/idv/doc_auth/send_link.html.erb index f7a8bedad3e..45d8745acc2 100644 --- a/app/views/idv/doc_auth/send_link.html.erb +++ b/app/views/idv/doc_auth/send_link.html.erb @@ -17,7 +17,6 @@<%= t('doc_auth.info.camera_required') %>
<%= t('doc_auth.instructions.send_sms') %>
- <%= validated_form_for( :doc_auth, url: url_for, diff --git a/app/views/idv/phone/new.html.erb b/app/views/idv/phone/new.html.erb index b4882668d1e..6e9f8606096 100644 --- a/app/views/idv/phone/new.html.erb +++ b/app/views/idv/phone/new.html.erb @@ -57,10 +57,15 @@ method: :put, class: 'margin-top-2', }) do |f| %> - <%= f.label :phone, label: t('idv.form.phone'), class: 'bold' %> - <%= f.input :phone, required: true, input_html: { aria: { invalid: false }, class: 'sm-col-8' }, label: false, - wrapper_html: { class: 'margin-right-2' } %> - + <%= f.input :phone, required: true, + input_html: { aria: { invalid: false }, + class: 'sm-col-8' }, + label: t('idv.form.phone'), + wrapper: false, + label_html: { class: 'bold' } %> +