From 544947c9c75837bddfe752ae8d5faec2fd893f2e Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Fri, 24 Jun 2022 14:15:45 +1000 Subject: [PATCH 1/2] MDL-74741 javascript: A11y fix for dialogues visible from beginning --- ...moodle-core-notification-dialogue-debug.js | 66 +++++++++++-------- .../moodle-core-notification-dialogue-min.js | 4 +- .../moodle-core-notification-dialogue.js | 66 +++++++++++-------- lib/yui/src/notification/js/dialogue.js | 66 +++++++++++-------- 4 files changed, 122 insertions(+), 80 deletions(-) diff --git a/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js b/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js index d403dfbc675..4d1712207ae 100644 --- a/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js +++ b/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js @@ -176,6 +176,13 @@ Y.extend(DIALOGUE, Y.Panel, { if (this.get('visible')) { this.applyZIndex(); + this.applyAndTrapFocus(); + // Only do accessibility hiding for modals because the ARIA spec + // says that all ARIA dialogues should be modal. + if (this.get('modal')) { + // Make this dialogue visible to screen readers. + this.setAccessibilityVisible(); + } } // Recalculate the zIndex every time the modal is altered. this.on('maskShow', this.applyZIndex); @@ -420,35 +427,12 @@ Y.extend(DIALOGUE, Y.Panel, { }, show: function() { - var result = null, - content = this.bodyNode, - focusSelector = this.get('focusOnShowSelector'), - focusNode = null; - - result = DIALOGUE.superclass.show.call(this); - + var result = DIALOGUE.superclass.show.call(this); if (!this.get('center') && this._originalPosition) { // Restore the dialogue position to it's location before it was moved at show time. this.get('boundingBox').setXY(this._originalPosition); } - - // Try and find a node to focus on using the focusOnShowSelector attribute. - if (focusSelector !== null) { - focusNode = this.get('boundingBox').one(focusSelector); - } - if (!focusNode) { - // Fall back to the first focusable element in the body of the dialogue if no focus node was found yet. - if (content && content !== '') { - focusNode = content.one(CAN_RECEIVE_FOCUS_SELECTOR); - } - } - require(['core/local/aria/focuslock'], function(FocusLockManager) { - // Trap focus to the current bounding box. - FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode()); - if (focusNode) { - focusNode.focus(); - } - }.bind(this)); + this.applyAndTrapFocus(); return result; }, @@ -571,7 +555,37 @@ Y.extend(DIALOGUE, Y.Panel, { // Clear the cache. No longer need to store these. this._hiddenSiblings = []; - } + }, + + /** + * Focuses on the node specified by focusOnShowSelector, or the first focusable node if nothing is specified. + * It also traps the focus to the current bounding box. + * + * @method applyAndTrapFocus + */ + applyAndTrapFocus: function() { + var content = this.bodyNode; + var focusSelector = this.get('focusOnShowSelector'); + var focusNode = null; + + // Try and find a node to focus on using the focusOnShowSelector attribute. + if (focusSelector !== null) { + focusNode = this.get('boundingBox').one(focusSelector); + } + if (!focusNode) { + // Fall back to the first focusable element in the body of the dialogue if no focus node was found yet. + if (content && content !== '') { + focusNode = content.one(CAN_RECEIVE_FOCUS_SELECTOR); + } + } + require(['core/local/aria/focuslock'], function(FocusLockManager) { + // Trap focus to the current bounding box. + FocusLockManager.trapFocus(this.get('boundingBox').getDOMNode()); + if (focusNode) { + focusNode.focus(); + } + }.bind(this)); + }, }, { NAME: DIALOGUE_NAME, CSS_PREFIX: DIALOGUE_PREFIX, diff --git a/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js b/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js index 350106106dc..d5063eadbd1 100644 --- a/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js +++ b/lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-core-notification-dialogue",function(s,u){var n,t,a,l,d,e,i="moodle-dialogue",o="notificationBase",r={BASE:"moodle-dialogue-base",WRAP:"moodle-dialogue-wrap",HEADER:"moodle-dialogue-hd",BODY:"moodle-dialogue-bd",CONTENT:"moodle-dialogue-content",FOOTER:"moodle-dialogue-ft",HIDDEN:"hidden",LIGHTBOX:"moodle-dialogue-lightbox"};M.core=M.core||{},t=i+"-fullscreen",a=i+"-hidden",l="moodle-has-zindex",s.extend(n=function(e){var t="moodle-dialogue-"+s.stamp(this)+"-wrap";e.notificationBase=s.Node.create('
').append(s.Node.create('').append(s.Node.create('
')).append(s.Node.create('
')).append(s.Node.create('
'))),s.one(document.body).append(e.notificationBase),e.srcNode="#"+t,delete e.buttons,n.superclass.constructor.apply(this,[e])},s.Panel,{_resizeevent:null,_orientationevent:null,_calculatedzindex:!"form",_currentMaskNodeId:null,_originalPosition:null,_hiddenSiblings:null,hideIfNotForm:function(){null===this.get("boundingBox").one("form")&&this.hide()},initializer:function(){var e,t;!1!==this.get("closeButton")&&(e=this.get("closeButtonTitle"),this.get("buttons").header[0].setAttribute("title",e),this.get("buttons").header[0].setAttribute("aria-label",e)),this.setStdModContent(s.WidgetStdMod.HEADER,'
'+this.get("headerContent")+"
",s.WidgetStdMod.REPLACE),this._hiddenSiblings=[],this.get("render")&&this.render(),this.after("visibleChange",this.visibilityChanged,this),this.get("center")&&this.centerDialogue(),this.get("modal")&&(this.get(o).set("aria-hidden","true"),this.plug(s.M.core.LockScroll)),this.set("focusOn",s.Array(this.get("focusOn")).filter(function(e){return"focusoutside"!==e.eventName})),(e=this.get("boundingBox")).addClass(l),s.Array.each(this.get("extraClasses"),e.addClass,e),this.get("visible")&&this.applyZIndex(),this.on("maskShow",this.applyZIndex),this.on("maskShow",function(){var e,t=s.one(s.config.win),i=this.get("boundingBox");this.get("center")||(this._originalPosition=i.getXY()),e=this.get("maskNode"),this._currentMaskNodeId!==e.get("_yuid")&&(this._currentMaskNodeId=e.get("_yuid"),e.on("click",this.hideIfNotForm,this)),"fixed"!==i.getStyle("position")&&i.setStyles({top:t.get("scrollTop"),left:t.get("scrollLeft")})},this),e=this.get("notificationBase"),""!==(t=this.get("additionalBaseClass"))&&e.addClass(t),this.after("destroyedChange",function(){this.get(o).remove(!0)},this)},applyZIndex:function(){var e,t=1040,i=this.get("boundingBox"),o=this.get("maskNode"),n=this.get("zIndex");0===n||this._calculatedzindex?(s.all(" [role=dialog], [role=menubar], ."+l).each(function(e){e=this.findZIndex(e);t').append(n.Node.create('').append(n.Node.create('
')).append(n.Node.create('
')).append(n.Node.create('
'))),n.one(document.body).append(e.notificationBase),e.srcNode="#"+t,delete e.buttons,i.superclass.constructor.apply(this,[e])},n.Panel,{_resizeevent:null,_orientationevent:null,_calculatedzindex:!"form",_currentMaskNodeId:null,_originalPosition:null,_hiddenSiblings:null,hideIfNotForm:function(){null===this.get("boundingBox").one("form")&&this.hide()},initializer:function(){var e,t;!1!==this.get("closeButton")&&(e=this.get("closeButtonTitle"),this.get("buttons").header[0].setAttribute("title",e),this.get("buttons").header[0].setAttribute("aria-label",e)),this.setStdModContent(n.WidgetStdMod.HEADER,'
'+this.get("headerContent")+"
",n.WidgetStdMod.REPLACE),this._hiddenSiblings=[],this.get("render")&&this.render(),this.after("visibleChange",this.visibilityChanged,this),this.get("center")&&this.centerDialogue(),this.get("modal")&&(this.get(d).set("aria-hidden","true"),this.plug(n.M.core.LockScroll)),this.set("focusOn",n.Array(this.get("focusOn")).filter(function(e){return"focusoutside"!==e.eventName})),(e=this.get("boundingBox")).addClass(a),n.Array.each(this.get("extraClasses"),e.addClass,e),this.get("visible")&&(this.applyZIndex(),this.applyAndTrapFocus(),this.get("modal")&&this.setAccessibilityVisible()),this.on("maskShow",this.applyZIndex),this.on("maskShow",function(){var e,t=n.one(n.config.win),i=this.get("boundingBox");this.get("center")||(this._originalPosition=i.getXY()),e=this.get("maskNode"),this._currentMaskNodeId!==e.get("_yuid")&&(this._currentMaskNodeId=e.get("_yuid"),e.on("click",this.hideIfNotForm,this)),"fixed"!==i.getStyle("position")&&i.setStyles({top:t.get("scrollTop"),left:t.get("scrollLeft")})},this),e=this.get("notificationBase"),""!==(t=this.get("additionalBaseClass"))&&e.addClass(t),this.after("destroyedChange",function(){this.get(d).remove(!0)},this)},applyZIndex:function(){var e,t=1040,i=this.get("boundingBox"),o=this.get("maskNode"),s=this.get("zIndex");0===s||this._calculatedzindex?(n.all(" [role=dialog], [role=menubar], ."+a).each(function(e){e=this.findZIndex(e);t Date: Fri, 24 Jun 2022 14:21:51 +1000 Subject: [PATCH 2/2] MDL-74741 javascript: Set the role of d&d links to button --- .../build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js | 1 + .../build/moodle-core-dragdrop/moodle-core-dragdrop-min.js | 4 ++-- lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js | 1 + lib/yui/src/dragdrop/js/dragdrop.js | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js index dc7fe32f54a..a75b5c81060 100644 --- a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js +++ b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js @@ -471,6 +471,7 @@ Y.extend(DRAGDROP, Y.Base, { listlink.setAttribute('data-drop-target', node.get('id')); // Allow tabbing to the link. listlink.setAttribute('tabindex', '0'); + listlink.setAttribute('role', 'button'); // Set the event listeners for enter, space or click. listlink.on('click', this.global_keyboard_drop, this); diff --git a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js index 4233ca749f6..912bddff591 100644 --- a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js +++ b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-core-dragdrop",function(h,t){var i="moodle-core-dragdrop-draghandle",e=function(){e.superclass.constructor.apply(this,arguments)};h.extend(e,h.Base,{goingup:null,absgoingup:null,samenodeclass:null,parentnodeclass:null,samenodelabel:null,parentnodelabel:null,groups:[],lastdroptarget:null,detectkeyboarddirection:!1,listeners:null,initializer:function(){this.listeners=[],this.listeners.push(h.DD.DDM.on("drag:start",this.global_drag_start,this)),this.listeners.push(h.DD.DDM.on("drag:end",this.global_drag_end,this)),this.listeners.push(h.DD.DDM.on("drag:drag",this.global_drag_drag,this)),this.listeners.push(h.DD.DDM.on("drop:over",this.global_drop_over,this)),this.listeners.push(h.DD.DDM.on("drop:hit",this.global_drop_hit,this)),this.listeners.push(h.DD.DDM.on("drag:dropmiss",this.global_drag_dropmiss,this)),this.listeners.push(h.one(h.config.doc.body).delegate("key",this.global_keydown,"down:32, enter, esc","."+i,this)),this.listeners.push(h.one(h.config.doc.body).delegate("click",this.global_keydown,"."+i,this))},destructor:function(){new h.EventHandle(this.listeners).detach()},get_drag_handle:function(t,e,o){var r=h.Node.create("").addClass(e).setAttribute("title",t).setAttribute("tabIndex",0).setAttribute("data-draggroups",this.groups).setAttribute("role","button");return r.addClass(i),window.require(["core/templates"],function(t){t.renderPix("i/move_2d","core").then(function(t){t=h.Node.create(t);t.setStyle("cursor","move"),void 0!==o&&t.addClass(o),r.appendChild(t)})}),r},lock_drag_handle:function(t,e){t.removeHandle("."+e)},unlock_drag_handle:function(t,e){t.addHandle("."+e),t.get("activeHandle").focus()},ajax_failure:function(t){t={name:t.status+" "+t.statusText,message:t.responseText};return new M.core.exception(t)},in_group:function(e){var o=!1;return h.each(this.groups,function(t){e._groups[t]&&(o=!0)},this),o},global_drag_start:function(t){var e=t.target;this.in_group(e)&&(this.originalstyle=e.get("node").getAttribute("style"),e.get("node").setStyle("opacity",".25"),e.get("dragNode").setStyles({opacity:".75",borderColor:e.get("node").getStyle("borderColor"),backgroundColor:e.get("node").getStyle("backgroundColor")}),e.get("dragNode").empty(),this.drag_start(t))},global_drag_end:function(t){var e=t.target;this.in_group(e)&&(e.get("node").setAttribute("style",this.originalstyle),this.drag_end(t))},global_drag_drag:function(t){var e=t.target,o=t.info;this.in_group(e)&&(o.start[1]o.xy[1]&&(this.absgoingup=!1),o.delta[1]<0?this.goingup=!0:0")).addClass("dragdrop-keyboard-drag"),h.all("."+this.samenodeclass+", ."+this.parentnodeclass).each(function(t){var e,o,r,a=!1,n=t,d=t.getAttribute("class").split(" ").join(", .");if(t.drop&&t.drop.inGroup(this.groups)&&t.drop.get("node")!==i&&(t.next(d)!==i||this.detectkeyboarddirection))a=!0;else for(e=t.getAttribute("data-draggroups").split(" "),o=0;o"),p=h.Node.create(""),s=this.find_element_text(n),u=this.samenodelabel&&t.hasClass(this.samenodeclass)?M.util.get_string(this.samenodelabel.identifier,this.samenodelabel.component,s):this.parentnodelabel&&t.hasClass(this.parentnodeclass)?M.util.get_string(this.parentnodelabel.identifier,this.parentnodelabel.component,s):M.util.get_string("tocontent","moodle",s),p.setContent(u),p.setAttribute("data-drop-target",t.get("id")),p.setAttribute("tabindex","0"),p.on("click",this.global_keyboard_drop,this),p.on("key",this.global_keyboard_drop,"down:enter,32",this),g.append(p),l.append(g))},this),M.core.dragdrop.dropui=new M.core.dialogue({headerContent:e,bodyContent:l,draggable:!0,visible:!0,center:!0,modal:!0}),M.core.dragdrop.dropui.after("visibleChange",function(t){t.prevVal&&!t.newVal&&this.global_cancel_keyboard_drag()},this),l.one("a")&&l.one("a").focus()},simulated_drag_drop_event:function(t,e){var o=function(t){this.node=t};o.prototype.get=function(t){return"node"===t||"dragNode"===t||"dropNode"===t?this.node:"activeHandle"===t?this.node.one(".editing_move"):null},o.prototype.inGroup=function(){return!0},o.prototype.addHandle=function(){},o.prototype.removeHandle=function(){},this.drop=new o(e),this.drag=new o(t),this.target=this.drop},global_keyboard_drop:function(t){var e,o=M.core.dragdrop.keydragcontainer,r=h.one("#"+t.target.getAttribute("data-drop-target"));M.core.dragdrop.dropui.hide(),t.preventDefault(),this.detectkeyboarddirection&&o.getY()>r.getY()?(this.absgoingup=!0,this.goingup=!0):(this.absgoingup=!1, -this.goingup=!1),t=new this.simulated_drag_drop_event(o,o),e=new this.simulated_drag_drop_event(o,r),this.drag_start(t),this.global_drop_over(e),r.hasClass(this.parentnodeclass)&&r.contains(o)&&r.prepend(o),this.global_drop_hit(e)},global_cancel_keyboard_drag:function(){M.core.dragdrop.keydragcontainer&&(M.core.dragdrop.keydraghandle.focus(),M.core.dragdrop.keydragcontainer=null),M.core.dragdrop.dropui&&M.core.dragdrop.dropui.destroy()},global_keydown:function(t){var e,o,r,a,n,d=t.target.ancestor("."+i,!0);if(null!==d){if(27===t.keyCode)return this.global_cancel_keyboard_drag(),void t.preventDefault();if(d.hasClass(i)&&(13===t.keyCode||32===t.keyCode||"click"===t.type)){for(o=d.getAttribute("data-draggroups").split(" "),n=!1,r=0;r").addClass(e).setAttribute("title",t).setAttribute("tabIndex",0).setAttribute("data-draggroups",this.groups).setAttribute("role","button");return r.addClass(i),window.require(["core/templates"],function(t){t.renderPix("i/move_2d","core").then(function(t){t=h.Node.create(t);t.setStyle("cursor","move"),void 0!==o&&t.addClass(o),r.appendChild(t)})}),r},lock_drag_handle:function(t,e){t.removeHandle("."+e)},unlock_drag_handle:function(t,e){t.addHandle("."+e),t.get("activeHandle").focus()},ajax_failure:function(t){t={name:t.status+" "+t.statusText,message:t.responseText};return new M.core.exception(t)},in_group:function(e){var o=!1;return h.each(this.groups,function(t){e._groups[t]&&(o=!0)},this),o},global_drag_start:function(t){var e=t.target;this.in_group(e)&&(this.originalstyle=e.get("node").getAttribute("style"),e.get("node").setStyle("opacity",".25"),e.get("dragNode").setStyles({opacity:".75",borderColor:e.get("node").getStyle("borderColor"),backgroundColor:e.get("node").getStyle("backgroundColor")}),e.get("dragNode").empty(),this.drag_start(t))},global_drag_end:function(t){var e=t.target;this.in_group(e)&&(e.get("node").setAttribute("style",this.originalstyle),this.drag_end(t))},global_drag_drag:function(t){var e=t.target,o=t.info;this.in_group(e)&&(o.start[1]o.xy[1]&&(this.absgoingup=!1),o.delta[1]<0?this.goingup=!0:0")).addClass("dragdrop-keyboard-drag"),h.all("."+this.samenodeclass+", ."+this.parentnodeclass).each(function(t){var e,o,r,a=!1,n=t,d=t.getAttribute("class").split(" ").join(", .");if(t.drop&&t.drop.inGroup(this.groups)&&t.drop.get("node")!==i&&(t.next(d)!==i||this.detectkeyboarddirection))a=!0;else for(e=t.getAttribute("data-draggroups").split(" "),o=0;o"),p=h.Node.create(""),s=this.find_element_text(n),u=this.samenodelabel&&t.hasClass(this.samenodeclass)?M.util.get_string(this.samenodelabel.identifier,this.samenodelabel.component,s):this.parentnodelabel&&t.hasClass(this.parentnodeclass)?M.util.get_string(this.parentnodelabel.identifier,this.parentnodelabel.component,s):M.util.get_string("tocontent","moodle",s),p.setContent(u),p.setAttribute("data-drop-target",t.get("id")),p.setAttribute("tabindex","0"),p.setAttribute("role","button"),p.on("click",this.global_keyboard_drop,this),p.on("key",this.global_keyboard_drop,"down:enter,32",this),g.append(p),l.append(g))},this),M.core.dragdrop.dropui=new M.core.dialogue({headerContent:e,bodyContent:l,draggable:!0,visible:!0,center:!0,modal:!0}),M.core.dragdrop.dropui.after("visibleChange",function(t){t.prevVal&&!t.newVal&&this.global_cancel_keyboard_drag()},this),l.one("a")&&l.one("a").focus()},simulated_drag_drop_event:function(t,e){var o=function(t){this.node=t};o.prototype.get=function(t){return"node"===t||"dragNode"===t||"dropNode"===t?this.node:"activeHandle"===t?this.node.one(".editing_move"):null},o.prototype.inGroup=function(){return!0},o.prototype.addHandle=function(){},o.prototype.removeHandle=function(){},this.drop=new o(e),this.drag=new o(t),this.target=this.drop},global_keyboard_drop:function(t){var e,o=M.core.dragdrop.keydragcontainer,r=h.one("#"+t.target.getAttribute("data-drop-target"));M.core.dragdrop.dropui.hide(),t.preventDefault(),this.detectkeyboarddirection&&o.getY()>r.getY()?(this.absgoingup=!0, +this.goingup=!0):(this.absgoingup=!1,this.goingup=!1),t=new this.simulated_drag_drop_event(o,o),e=new this.simulated_drag_drop_event(o,r),this.drag_start(t),this.global_drop_over(e),r.hasClass(this.parentnodeclass)&&r.contains(o)&&r.prepend(o),this.global_drop_hit(e)},global_cancel_keyboard_drag:function(){M.core.dragdrop.keydragcontainer&&(M.core.dragdrop.keydraghandle.focus(),M.core.dragdrop.keydragcontainer=null),M.core.dragdrop.dropui&&M.core.dragdrop.dropui.destroy()},global_keydown:function(t){var e,o,r,a,n,d=t.target.ancestor("."+i,!0);if(null!==d){if(27===t.keyCode)return this.global_cancel_keyboard_drag(),void t.preventDefault();if(d.hasClass(i)&&(13===t.keyCode||32===t.keyCode||"click"===t.type)){for(o=d.getAttribute("data-draggroups").split(" "),n=!1,r=0;r