MDL-81702 core_message: Add focustrap and backdrop
* Add focustrap and backdrop to message drawer.
This commit is contained in:
@@ -36,8 +36,9 @@
|
||||
class="{{$drawerclasses}}{{/drawerclasses}} drawer bg-white {{^show}}hidden{{/show}}"
|
||||
{{^show}}aria-hidden="true"{{/show}}
|
||||
data-region="right-hand-drawer"
|
||||
role="region"
|
||||
tabindex="0"
|
||||
role="{{$role}}region{{/role}}"
|
||||
tabindex="{{$tabindex}}0{{/tabindex}}"
|
||||
{{$drawerattributes}}{{/drawerattributes}}
|
||||
>
|
||||
{{$drawercontent}}{{/drawercontent}}
|
||||
</div>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define("core_message/message_popover",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_events"],(function($,CustomEvents,PubSub,MessageDrawerEvents){var SELECTORS_COUNT_CONTAINER='[data-region="count-container"]',handleDecrementConversationCount=function(button){return function(){var countContainer=button.find(SELECTORS_COUNT_CONTAINER),count=parseInt(countContainer.text(),10);isNaN(count)||!count||count<2?countContainer.addClass("hidden"):(count-=1,countContainer.text(count))}},registerEventListeners=function(button){CustomEvents.define(button,[CustomEvents.events.activate]),button.on(CustomEvents.events.activate,(function(e,data){var buttonid;buttonid=button.attr("id"),PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY,buttonid),button.focus(),data.originalEvent.preventDefault()})),PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,handleDecrementConversationCount(button))};return{init:function(button){button=$(button),registerEventListeners(button)}}}));
|
||||
define("core_message/message_popover",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_events"],(function($,CustomEvents,PubSub,MessageDrawerEvents){var SELECTORS_COUNT_CONTAINER='[data-region="count-container"]',handleDecrementConversationCount=function(button){return function(){var countContainer=button.find(SELECTORS_COUNT_CONTAINER),count=parseInt(countContainer.text(),10);isNaN(count)||!count||count<2?countContainer.addClass("hidden"):(count-=1,countContainer.text(count))}},registerEventListeners=function(button){CustomEvents.define(button,[CustomEvents.events.activate]),button.on(CustomEvents.events.activate,(function(e,data){var buttonid;buttonid=button.attr("id"),PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY,buttonid),data.originalEvent.preventDefault()})),PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,handleDecrementConversationCount(button))};return{init:function(button){button=$(button),registerEventListeners(button)}}}));
|
||||
|
||||
//# sourceMappingURL=message_popover.min.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"message_popover.min.js","sources":["../src/message_popover.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Controls the message popover in the nav bar.\n *\n * @module core_message/message_popover\n * @copyright 2018 Ryan Wyllie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_events'\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n MessageDrawerEvents\n) {\n var SELECTORS = {\n COUNT_CONTAINER: '[data-region=\"count-container\"]'\n };\n\n /**\n * Toggle the message drawer visibility.\n *\n * @param {String} buttonid The button id for the popover.\n */\n var toggleMessageDrawerVisibility = function(buttonid) {\n PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY, buttonid);\n };\n\n /**\n * Decrement the unread conversation count in the nav bar if a conversation\n * is read. When there are no unread conversations then hide the counter.\n *\n * @param {Object} button The button element for the popover.\n * @return {Function}\n */\n var handleDecrementConversationCount = function(button) {\n return function() {\n var countContainer = button.find(SELECTORS.COUNT_CONTAINER);\n var count = parseInt(countContainer.text(), 10);\n\n if (isNaN(count)) {\n countContainer.addClass('hidden');\n } else if (!count || count < 2) {\n countContainer.addClass('hidden');\n } else {\n count = count - 1;\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Add events listeners for when the popover icon is clicked and when conversations\n * are read.\n *\n * @param {Object} button The button element for the popover.\n */\n var registerEventListeners = function(button) {\n CustomEvents.define(button, [CustomEvents.events.activate]);\n\n button.on(CustomEvents.events.activate, function(e, data) {\n toggleMessageDrawerVisibility(button.attr('id'));\n button.focus();\n data.originalEvent.preventDefault();\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, handleDecrementConversationCount(button));\n };\n\n /**\n * Initialise the message popover.\n *\n * @param {Object} button The button element for the popover.\n */\n var init = function(button) {\n button = $(button);\n registerEventListeners(button);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","MessageDrawerEvents","SELECTORS","handleDecrementConversationCount","button","countContainer","find","count","parseInt","text","isNaN","addClass","registerEventListeners","events","activate","on","e","data","buttonid","attr","publish","TOGGLE_VISIBILITY","focus","originalEvent","preventDefault","subscribe","CONVERSATION_READ","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","init"],"mappings":";;;;;;;AAsBAA,sCACA,CACI,SACA,iCACA,cACA,uCAEJ,SACIC,EACAC,aACAC,OACAC,yBAEIC,0BACiB,kCAmBjBC,iCAAmC,SAASC,eACrC,eACCC,eAAiBD,OAAOE,KAAKJ,2BAC7BK,MAAQC,SAASH,eAAeI,OAAQ,IAExCC,MAAMH,SAEEA,OAASA,MAAQ,EADzBF,eAAeM,SAAS,WAIxBJ,OAAgB,EAChBF,eAAeI,KAAKF,UAW5BK,uBAAyB,SAASR,QAClCL,aAAaF,OAAOO,OAAQ,CAACL,aAAac,OAAOC,WAEjDV,OAAOW,GAAGhB,aAAac,OAAOC,UAAU,SAASE,EAAGC,MApCpB,IAASC,SAAAA,SAqCPd,OAAOe,KAAK,MApC9CnB,OAAOoB,QAAQnB,oBAAoBoB,kBAAmBH,UAqClDd,OAAOkB,QACPL,KAAKM,cAAcC,oBAGvBxB,OAAOyB,UAAUxB,oBAAoByB,kBAAmBvB,iCAAiCC,SACzFJ,OAAOyB,UAAUxB,oBAAoB0B,yBAA0BxB,iCAAiCC,SAChGJ,OAAOyB,UAAUxB,oBAAoB2B,yBAA0BzB,iCAAiCC,gBAa7F,CACHyB,KANO,SAASzB,QAChBA,OAASN,EAAEM,QACXQ,uBAAuBR"}
|
||||
{"version":3,"file":"message_popover.min.js","sources":["../src/message_popover.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Controls the message popover in the nav bar.\n *\n * @module core_message/message_popover\n * @copyright 2018 Ryan Wyllie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_events'\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n MessageDrawerEvents\n) {\n var SELECTORS = {\n COUNT_CONTAINER: '[data-region=\"count-container\"]'\n };\n\n /**\n * Toggle the message drawer visibility.\n *\n * @param {String} buttonid The button id for the popover.\n */\n var toggleMessageDrawerVisibility = function(buttonid) {\n PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY, buttonid);\n };\n\n /**\n * Decrement the unread conversation count in the nav bar if a conversation\n * is read. When there are no unread conversations then hide the counter.\n *\n * @param {Object} button The button element for the popover.\n * @return {Function}\n */\n var handleDecrementConversationCount = function(button) {\n return function() {\n var countContainer = button.find(SELECTORS.COUNT_CONTAINER);\n var count = parseInt(countContainer.text(), 10);\n\n if (isNaN(count)) {\n countContainer.addClass('hidden');\n } else if (!count || count < 2) {\n countContainer.addClass('hidden');\n } else {\n count = count - 1;\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Add events listeners for when the popover icon is clicked and when conversations\n * are read.\n *\n * @param {Object} button The button element for the popover.\n */\n var registerEventListeners = function(button) {\n CustomEvents.define(button, [CustomEvents.events.activate]);\n\n button.on(CustomEvents.events.activate, function(e, data) {\n toggleMessageDrawerVisibility(button.attr('id'));\n data.originalEvent.preventDefault();\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, handleDecrementConversationCount(button));\n };\n\n /**\n * Initialise the message popover.\n *\n * @param {Object} button The button element for the popover.\n */\n var init = function(button) {\n button = $(button);\n registerEventListeners(button);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","MessageDrawerEvents","SELECTORS","handleDecrementConversationCount","button","countContainer","find","count","parseInt","text","isNaN","addClass","registerEventListeners","events","activate","on","e","data","buttonid","attr","publish","TOGGLE_VISIBILITY","originalEvent","preventDefault","subscribe","CONVERSATION_READ","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","init"],"mappings":";;;;;;;AAsBAA,sCACA,CACI,SACA,iCACA,cACA,uCAEJ,SACIC,EACAC,aACAC,OACAC,yBAEIC,0BACiB,kCAmBjBC,iCAAmC,SAASC,eACrC,eACCC,eAAiBD,OAAOE,KAAKJ,2BAC7BK,MAAQC,SAASH,eAAeI,OAAQ,IAExCC,MAAMH,SAEEA,OAASA,MAAQ,EADzBF,eAAeM,SAAS,WAIxBJ,OAAgB,EAChBF,eAAeI,KAAKF,UAW5BK,uBAAyB,SAASR,QAClCL,aAAaF,OAAOO,OAAQ,CAACL,aAAac,OAAOC,WAEjDV,OAAOW,GAAGhB,aAAac,OAAOC,UAAU,SAASE,EAAGC,MApCpB,IAASC,SAAAA,SAqCPd,OAAOe,KAAK,MApC9CnB,OAAOoB,QAAQnB,oBAAoBoB,kBAAmBH,UAqClDD,KAAKK,cAAcC,oBAGvBvB,OAAOwB,UAAUvB,oBAAoBwB,kBAAmBtB,iCAAiCC,SACzFJ,OAAOwB,UAAUvB,oBAAoByB,yBAA0BvB,iCAAiCC,SAChGJ,OAAOwB,UAAUvB,oBAAoB0B,yBAA0BxB,iCAAiCC,gBAa7F,CACHwB,KANO,SAASxB,QAChBA,OAASN,EAAEM,QACXQ,uBAAuBR"}
|
||||
@@ -42,6 +42,9 @@ define(
|
||||
'core/str',
|
||||
'core/config',
|
||||
'core/ajax',
|
||||
'core/local/aria/focuslock',
|
||||
'core/modal_backdrop',
|
||||
'core/templates',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
@@ -64,11 +67,13 @@ function(
|
||||
Str,
|
||||
Config,
|
||||
Ajax,
|
||||
FocusLock,
|
||||
ModalBackdrop,
|
||||
Templates,
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
DRAWER: '[data-region="right-hand-drawer"]',
|
||||
JUMPTO: '.popover-region [data-region="jumpto"]',
|
||||
PANEL_BODY_CONTAINER: '[data-region="panel-body-container"]',
|
||||
PANEL_HEADER_CONTAINER: '[data-region="panel-header-container"]',
|
||||
VIEW_CONTACT: '[data-region="view-contact"]',
|
||||
@@ -141,6 +146,69 @@ function(
|
||||
});
|
||||
};
|
||||
|
||||
let backdropPromise = null;
|
||||
|
||||
/**
|
||||
* Set the focus on the drawer.
|
||||
*
|
||||
* This method also creates or destroy any necessary backdrop zone and focus trap.
|
||||
*
|
||||
* @param {Object} root The message drawer container.
|
||||
* @param {Boolean} hasFocus Whether the drawer has focus or not.
|
||||
*/
|
||||
var setFocus = function(root, hasFocus) {
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (!drawerRoot.length) {
|
||||
return;
|
||||
}
|
||||
if (!backdropPromise) {
|
||||
backdropPromise = Templates.render('core/modal_backdrop', {})
|
||||
.then(html => new ModalBackdrop(html));
|
||||
}
|
||||
const backdropWithAdjustments = backdropPromise.then(modalBackdrop => {
|
||||
const messageDrawerZIndex = window.getComputedStyle(drawerRoot[0]).zIndex;
|
||||
if (messageDrawerZIndex) {
|
||||
modalBackdrop.setZIndex(messageDrawerZIndex - 1);
|
||||
}
|
||||
modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {
|
||||
PubSub.publish(Events.HIDE, {});
|
||||
e.preventDefault();
|
||||
});
|
||||
return modalBackdrop;
|
||||
});
|
||||
if (hasFocus) {
|
||||
FocusLock.trapFocus(root[0]);
|
||||
// eslint-disable-next-line promise/catch-or-return
|
||||
backdropWithAdjustments.then(modalBackdrop => {
|
||||
if (modalBackdrop) {
|
||||
modalBackdrop.show();
|
||||
const pageWrapper = document.getElementById('page');
|
||||
pageWrapper.style.overflow = 'hidden';
|
||||
// Set the focus on the close button so when we press enter, it closes the drawer as it did before
|
||||
var closeButton = root.find(SELECTORS.CLOSE_BUTTON);
|
||||
if (closeButton.length) {
|
||||
closeButton.focus();
|
||||
}
|
||||
}
|
||||
return modalBackdrop;
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line promise/catch-or-return
|
||||
backdropWithAdjustments.then(modalBackdrop => {
|
||||
if (modalBackdrop) {
|
||||
FocusLock.untrapFocus();
|
||||
var button = $(SELECTORS.DRAWER).attr('data-origin');
|
||||
if (button) {
|
||||
$('#' + button).focus();
|
||||
}
|
||||
modalBackdrop.hide();
|
||||
const pageWrapper = document.getElementById('page');
|
||||
pageWrapper.style.overflow = 'visible';
|
||||
}
|
||||
return modalBackdrop;
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Show the message drawer.
|
||||
*
|
||||
@@ -155,6 +223,7 @@ function(
|
||||
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (drawerRoot.length) {
|
||||
setFocus(root, true);
|
||||
Drawer.show(drawerRoot);
|
||||
}
|
||||
};
|
||||
@@ -167,6 +236,7 @@ function(
|
||||
var hide = function(root) {
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (drawerRoot.length) {
|
||||
setFocus(root, false);
|
||||
Drawer.hide(drawerRoot);
|
||||
}
|
||||
};
|
||||
@@ -361,7 +431,7 @@ function(
|
||||
* @param {bool} alwaysVisible Is this messaging app always shown?
|
||||
*/
|
||||
var registerEventListeners = function(namespace, root, alwaysVisible) {
|
||||
CustomEvents.define(root, [CustomEvents.events.activate]);
|
||||
CustomEvents.define(root, [CustomEvents.events.activate, CustomEvents.events.escape]);
|
||||
var paramRegex = /^data-route-param-?(\d*)$/;
|
||||
|
||||
root.on(CustomEvents.events.activate, SELECTORS.ROUTES, function(e, data) {
|
||||
@@ -468,6 +538,9 @@ function(
|
||||
$(SELECTORS.JUMPTO).attr('tabindex', 0);
|
||||
}
|
||||
});
|
||||
root.on(CustomEvents.events.escape, function() {
|
||||
PubSub.publish(Events.HIDE, {});
|
||||
});
|
||||
}
|
||||
|
||||
PubSub.subscribe(Events.SHOW_CONVERSATION, function(args) {
|
||||
@@ -479,7 +552,7 @@ function(
|
||||
var closebutton = root.find(SELECTORS.CLOSE_BUTTON);
|
||||
closebutton.on(CustomEvents.events.activate, function(e, data) {
|
||||
data.originalEvent.preventDefault();
|
||||
|
||||
setFocus(root, false);
|
||||
var button = $(SELECTORS.DRAWER).attr('data-origin');
|
||||
if (button) {
|
||||
$('#' + button).focus();
|
||||
|
||||
@@ -80,7 +80,6 @@ function(
|
||||
|
||||
button.on(CustomEvents.events.activate, function(e, data) {
|
||||
toggleMessageDrawerVisibility(button.attr('id'));
|
||||
button.focus();
|
||||
data.originalEvent.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
@@ -34,9 +34,15 @@
|
||||
|
||||
}}
|
||||
{{< core/drawer}}
|
||||
{{$tabindex}}-1{{/tabindex}}
|
||||
{{$role}}dialog{{/role}}
|
||||
{{$drawerattributes}}
|
||||
aria-modal="true"
|
||||
aria-labelledby="message-drawer-header-{{uniqid}}"
|
||||
{{/drawerattributes}}
|
||||
{{$drawercontent}}
|
||||
<div id="message-drawer-{{uniqid}}" class="message-app" data-region="message-drawer" role="region">
|
||||
<h2 class="visually-hidden">{{#str}} messagingcategory,admin {{/str}}</h2>
|
||||
<div id="message-drawer-{{uniqid}}" class="message-app" data-region="message-drawer" role="region" tabindex="0">
|
||||
<h2 class="visually-hidden" id="message-drawer-header-{{uniqid}}">{{#str}} messagingcategory,admin {{/str}}</h2>
|
||||
<div class="closewidget text-end pe-2">
|
||||
<a class="text-dark btn-link" data-action="closedrawer" href="#"
|
||||
title="{{#cleanstr}} closebuttontitle {{/cleanstr}}" aria-label="{{#cleanstr}} closebuttontitle {{/cleanstr}}"
|
||||
|
||||
@@ -92,7 +92,7 @@ Feature: Message send messages
|
||||
And I send "Hi!" message to "Student 2" user
|
||||
And I select "Student 2" user in messaging
|
||||
And I set the field "Write a message..." to "What you doing?"
|
||||
When I follow "Dashboard"
|
||||
When I follow "Profile" in the user menu
|
||||
Then I should see "You have an unsent message. It will be lost if you leave this page."
|
||||
And I press "Send message"
|
||||
And I should see "What you doing?" in the "Student 2" "core_message > Message conversation"
|
||||
|
||||
Reference in New Issue
Block a user