MDL-70288 theme_boost: Manage aria-describedby on form validation

* The aria-describedby attribute can be a list of element IDs that
describe the element. On form validation, the ID of the error message
container is added to this attribute which may already be containing
another ID. So we need to properly add/remove the error message ID
so that we don't delete any existing ID(s) in the aria-describedby
attribute during form validation.
This commit is contained in:
Jun Pataleta 2020-12-10 22:03:21 +08:00
parent 9dabd071fe
commit da237792ec
3 changed files with 35 additions and 4 deletions

View File

@ -1,2 +1,2 @@
define ("theme_boost/form-display-errors",["jquery","core/event"],function(a,b){return{enhance:function enhance(c){var d=document.getElementById(c);if(!d){return}a(d).on(b.Events.FORM_FIELD_VALIDATION,function(b,c){b.preventDefault();var e=a(d).closest(".form-group"),f=e.find(".form-control-feedback");if("TEXTAREA"==a(d).prop("tagName")&&e.find("[contenteditable]")){d=e.find("[contenteditable]")}if(""!==c){e.addClass("has-danger");e.data("client-validation-error",!0);a(d).addClass("is-invalid");a(d).attr("aria-describedby",f.attr("id"));a(d).attr("aria-invalid",!0);f.attr("tabindex",0);f.html(c);if(!f.is(":visible")){f.show();f.focus()}}else{if(!0===e.data("client-validation-error")){e.removeClass("has-danger");e.data("client-validation-error",!1);a(d).removeClass("is-invalid");a(d).removeAttr("aria-describedby");a(d).attr("aria-invalid",!1);f.hide()}}});var e=d.closest("form");if(e&&!("boostFormErrorsEnhanced"in e.dataset)){e.addEventListener("submit",function(){var b=a(".form-control-feedback:visible");if(b.length){b[0].focus()}});e.dataset.boostFormErrorsEnhanced=1}}}});
define ("theme_boost/form-display-errors",["jquery","core/event"],function(a,b){return{enhance:function enhance(c){var d=document.getElementById(c);if(!d){return}a(d).on(b.Events.FORM_FIELD_VALIDATION,function(b,c){b.preventDefault();var e=a(d).closest(".form-group"),f=e.find(".form-control-feedback"),g=f.attr("id"),h=a(d).attr("aria-describedby");if("undefined"==typeof h){h=""}var i=[];if(h.length){i=h.split(" ")}var j=i.indexOf(g);if("TEXTAREA"==a(d).prop("tagName")&&e.find("[contenteditable]")){d=e.find("[contenteditable]")}if(""!==c){e.addClass("has-danger");e.data("client-validation-error",!0);a(d).addClass("is-invalid");if(-1===j){i.push(g);a(d).attr("aria-describedby",i.join(" "))}a(d).attr("aria-invalid",!0);f.attr("tabindex",0);f.html(c);if(!f.is(":visible")){f.show();f.focus()}}else{if(!0===e.data("client-validation-error")){e.removeClass("has-danger");e.data("client-validation-error",!1);a(d).removeClass("is-invalid");if(-1<j){i.splice(j,1)}if(i.length){h=i.join(" ");a(d).attr("aria-describedby",h)}else{a(d).removeAttr("aria-describedby")}a(d).attr("aria-invalid",!1);f.hide()}}});var e=d.closest("form");if(e&&!("boostFormErrorsEnhanced"in e.dataset)){e.addEventListener("submit",function(){var b=a(".form-control-feedback:visible");if(b.length){b[0].focus()}});e.dataset.boostFormErrorsEnhanced=1}}}});
//# sourceMappingURL=form-display-errors.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,20 @@ define(['jquery', 'core/event'], function($, Event) {
event.preventDefault();
var parent = $(element).closest('.form-group');
var feedback = parent.find('.form-control-feedback');
const feedbackId = feedback.attr('id');
// Get current aria-describedby value.
let describedBy = $(element).attr('aria-describedby');
if (typeof describedBy === "undefined") {
describedBy = '';
}
// Split aria-describedby attribute into an array of IDs if necessary.
let describedByIds = [];
if (describedBy.length) {
describedByIds = describedBy.split(" ");
}
// Find the the feedback container in the aria-describedby attribute.
const feedbackIndex = describedByIds.indexOf(feedbackId);
// Sometimes (atto) we have a hidden textarea backed by a real contenteditable div.
if (($(element).prop("tagName") == 'TEXTAREA') && parent.find('[contenteditable]')) {
@ -44,7 +58,11 @@ define(['jquery', 'core/event'], function($, Event) {
parent.addClass('has-danger');
parent.data('client-validation-error', true);
$(element).addClass('is-invalid');
$(element).attr('aria-describedby', feedback.attr('id'));
// Append the feedback ID to the aria-describedby attribute if it doesn't exist yet.
if (feedbackIndex === -1) {
describedByIds.push(feedbackId);
$(element).attr('aria-describedby', describedByIds.join(" "));
}
$(element).attr('aria-invalid', true);
feedback.attr('tabindex', 0);
feedback.html(msg);
@ -61,7 +79,20 @@ define(['jquery', 'core/event'], function($, Event) {
parent.removeClass('has-danger');
parent.data('client-validation-error', false);
$(element).removeClass('is-invalid');
$(element).removeAttr('aria-describedby');
// If the aria-describedby attribute contains the error container's ID, remove it.
if (feedbackIndex > -1) {
describedByIds.splice(feedbackIndex, 1);
}
// Check the remaining element IDs in the aria-describedby attribute.
if (describedByIds.length) {
// If there's at least one, combine them with a blank space and update the aria-describedby attribute.
describedBy = describedByIds.join(" ");
// Put back the new describedby attribute.
$(element).attr('aria-describedby', describedBy);
} else {
// If there's none, remove the aria-describedby attribute.
$(element).removeAttr('aria-describedby');
}
$(element).attr('aria-invalid', false);
feedback.hide();
}