Merge branch 'MDL-70792-master-2' of https://github.com/junpataleta/moodle

This commit is contained in:
Andrew Nicols 2022-02-25 11:39:55 +08:00
commit 649705874d
9 changed files with 81 additions and 43 deletions

View File

@ -4340,6 +4340,7 @@ class action_menu implements renderable, templatable {
public function add_primary_action($action) {
if ($action instanceof action_link || $action instanceof pix_icon) {
$action->attributes['role'] = 'menuitem';
$action->attributes['tabindex'] = '-1';
if ($action instanceof action_menu_link) {
$action->actionmenu = $this;
}
@ -4355,6 +4356,7 @@ class action_menu implements renderable, templatable {
public function add_secondary_action($action) {
if ($action instanceof action_link || $action instanceof pix_icon) {
$action->attributes['role'] = 'menuitem';
$action->attributes['tabindex'] = '-1';
if ($action instanceof action_menu_link) {
$action->actionmenu = $this;
}
@ -4412,7 +4414,8 @@ class action_menu implements renderable, templatable {
'title' => $title,
'aria-label' => $label,
'id' => 'action-menu-toggle-'.$this->instance,
'role' => 'menuitem'
'role' => 'menuitem',
'tabindex' => '-1',
);
$link = html_writer::link('#', $string . $this->menutrigger . $pixicon, $attributes);
if ($this->prioritise) {

View File

@ -59,7 +59,7 @@
}}
{{#items}}
{{#link}}
<a href="{{{url}}}" class="dropdown-item" role="menuitem">
<a href="{{{url}}}" class="dropdown-item" role="menuitem" tabindex="-1">
{{#pixicon}}
{{#pix}}{{pixicon}}{{/pix}}
{{/pixicon}}
@ -70,7 +70,7 @@
</a>
{{/link}}
{{#submenulink}}
<a href="#" class="carousel-navigation-link dropdown-item" role="menuitem" data-carousel-target-id="carousel-item-{{submenuid}}">
<a href="#" class="carousel-navigation-link dropdown-item" role="menuitem" tabindex="-1" data-carousel-target-id="carousel-item-{{submenuid}}">
{{#pixicon}}
{{#pix}}{{pixicon}}{{/pix}}
{{/pixicon}}

View File

@ -41,7 +41,7 @@
}}
{{#items}}
{{#link}}
<a href="{{{url}}}" class="dropdown-item pl-5" role="menuitem" {{#isactive}}aria-current="true"{{/isactive}}>
<a href="{{{url}}}" class="dropdown-item pl-5" role="menuitem" tabindex="-1" {{#isactive}}aria-current="true"{{/isactive}}>
{{text}}
</a>
{{/link}}

View File

@ -5,6 +5,6 @@ define("theme_boost/aria",["exports","jquery","core/pending"],(function(_exports
* @module theme_boost/aria
* @copyright 2018 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const dropdownFix=()=>{let focusEnd=!1;document.addEventListener("keydown",(e=>{if(e.target.matches('[data-toggle="dropdown"]')){const trigger=e.key;"ArrowUp"==trigger&&(focusEnd=!0)," "!=trigger&&"Enter"!=trigger||(e.preventDefault(),e.target.click())}}));const shiftFocus=element=>{setTimeout((pendingPromise=>{element.focus(),pendingPromise.resolve()}),50,new _pending.default("core/aria:delayed-focus"))};(0,_jquery.default)(".dropdown").on("shown.bs.dropdown",(e=>{const menu=e.target.querySelector('[role="menu"]');let menuItems=!1,foundMenuItem=!1;menu&&(menuItems=menu.querySelectorAll('[role="menuitem"]')),menuItems&&menuItems.length>0&&(foundMenuItem=(()=>{const result=focusEnd;return focusEnd=!1,result})()?menuItems[menuItems.length-1]:menuItems[0]),foundMenuItem&&shiftFocus(foundMenuItem)})),document.addEventListener("keypress",(e=>{if(e.target.matches('.dropdown [role="menu"] [role="menuitem"]')){const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;const trigger=e.key.toLowerCase();for(let i=0;i<menuItems.length;i++){const item=menuItems[i];if(0==item.text.trim().toLowerCase().indexOf(trigger)){shiftFocus(item);break}}}})),document.addEventListener("keydown",(e=>{if(e.target.matches('.dropdown [role="menu"] [role="menuitem"]')){const trigger=e.key;let next=!1;const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;if("ArrowDown"==trigger){for(let i=0;i<menuItems.length-1;i++)if(menuItems[i]==e.target){next=menuItems[i+1];break}next||(next=menuItems[0])}else if("ArrowUp"==trigger){for(let i=1;i<menuItems.length;i++)if(menuItems[i]==e.target){next=menuItems[i-1];break}next||(next=menuItems[menuItems.length-1])}else"Home"==trigger?next=menuItems[0]:"End"==trigger&&(next=menuItems[menuItems.length-1]);next&&(e.preventDefault(),shiftFocus(next))}else;})),(0,_jquery.default)(".dropdown").on("hidden.bs.dropdown",(e=>{const trigger=e.target.querySelector('[data-toggle="dropdown"]');trigger&&shiftFocus(trigger)}))},tabElementFix=()=>{document.addEventListener("keydown",(e=>{["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(e.key)&&e.target.matches('[role="tablist"] [role="tab"]')&&(e=>{const tabList=e.target.closest('[role="tablist"]'),vertical="vertical"==tabList.getAttribute("aria-orientation"),rtl=window.right_to_left(),arrowNext=vertical?"ArrowDown":rtl?"ArrowLeft":"ArrowRight",arrowPrevious=vertical?"ArrowUp":rtl?"ArrowRight":"ArrowLeft",tabs=Array.prototype.filter.call(tabList.querySelectorAll('[role="tab"]'),(tab=>"none"!==getComputedStyle(tab).display));for(let i=0;i<tabs.length;i++)tabs[i].index=i;switch(e.key){case arrowNext:e.preventDefault(),void 0!==e.target.index&&tabs[e.target.index+1]?tabs[e.target.index+1].focus():tabs[0].focus();break;case arrowPrevious:e.preventDefault(),void 0!==e.target.index&&tabs[e.target.index-1]?tabs[e.target.index-1].focus():tabs[tabs.length-1].focus();break;case"Home":e.preventDefault(),tabs[0].focus();break;case"End":e.preventDefault(),tabs[tabs.length-1].focus();break;case"Enter":case" ":e.preventDefault(),(0,_jquery.default)(e.target).tab("show"),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}})(e)})),document.addEventListener("click",(e=>{if(e.target.matches('[role="tablist"] [role="tab"]')){const tabs=e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');e.preventDefault(),(0,_jquery.default)(e.target).tab("show"),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}}))};_exports.init=()=>{dropdownFix(),window.addEventListener("load",(()=>{const alerts=document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');Array.prototype.forEach.call(alerts,(autofocusElement=>{autofocusElement.innerHTML+=" ",autofocusElement.removeAttribute("data-aria-autofocus")}))})),tabElementFix()}}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const dropdownFix=()=>{let focusEnd=!1,focusBackToTrigger=!0;const setFocusEnd=function(){let end=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];focusEnd=end},shiftFocus=element=>{setTimeout((pendingPromise=>{element.focus(),pendingPromise.resolve()}),50,new _pending.default("core/aria:delayed-focus"))},handleMenuButton=e=>{const trigger=e.key;let fixFocus=!1;if(" "!==trigger&&"Enter"!==trigger||(fixFocus=!0,e.preventDefault(),e.target.click()),"ArrowUp"!==trigger&&"ArrowDown"!==trigger||(fixFocus=!0),"Tab"===trigger&&(focusBackToTrigger=!1),!fixFocus)return;const menu=e.target.parentElement.querySelector('[role="menu"]');let menuItems=!1,foundMenuItem=!1;menu&&(menuItems=menu.querySelectorAll('[role="menuitem"]')),menuItems&&menuItems.length>0&&("ArrowUp"===trigger?setFocusEnd():setFocusEnd(!1),foundMenuItem=(()=>{const result=focusEnd;return focusEnd=!1,result})()?menuItems[menuItems.length-1]:menuItems[0]),foundMenuItem&&shiftFocus(foundMenuItem)};document.addEventListener("keypress",(e=>{if(e.target.matches('.dropdown [role="menu"] [role="menuitem"]')){const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;const trigger=e.key.toLowerCase();for(let i=0;i<menuItems.length;i++){const item=menuItems[i];if(0==item.text.trim().toLowerCase().indexOf(trigger)){shiftFocus(item);break}}}})),document.addEventListener("keydown",(e=>{if(e.target.matches('[data-toggle="dropdown"]')&&handleMenuButton(e),e.target.matches('.dropdown [role="menu"] [role="menuitem"]')){const trigger=e.key;let next=!1;const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;if("ArrowDown"==trigger){for(let i=0;i<menuItems.length-1;i++)if(menuItems[i]==e.target){next=menuItems[i+1];break}next||(next=menuItems[0])}else if("ArrowUp"==trigger){for(let i=1;i<menuItems.length;i++)if(menuItems[i]==e.target){next=menuItems[i-1];break}next||(next=menuItems[menuItems.length-1])}else"Home"==trigger?next=menuItems[0]:"End"==trigger?next=menuItems[menuItems.length-1]:"Tab"==trigger&&(focusBackToTrigger=!1);next&&(e.preventDefault(),shiftFocus(next))}else;})),(0,_jquery.default)(".dropdown").on("hidden.bs.dropdown",(e=>{const trigger=e.target.querySelector('[data-toggle="dropdown"]');trigger&&focusBackToTrigger&&shiftFocus(trigger),focusBackToTrigger=!0}))},tabElementFix=()=>{document.addEventListener("keydown",(e=>{["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(e.key)&&e.target.matches('[role="tablist"] [role="tab"]')&&(e=>{const tabList=e.target.closest('[role="tablist"]'),vertical="vertical"==tabList.getAttribute("aria-orientation"),rtl=window.right_to_left(),arrowNext=vertical?"ArrowDown":rtl?"ArrowLeft":"ArrowRight",arrowPrevious=vertical?"ArrowUp":rtl?"ArrowRight":"ArrowLeft",tabs=Array.prototype.filter.call(tabList.querySelectorAll('[role="tab"]'),(tab=>"none"!==getComputedStyle(tab).display));for(let i=0;i<tabs.length;i++)tabs[i].index=i;switch(e.key){case arrowNext:e.preventDefault(),void 0!==e.target.index&&tabs[e.target.index+1]?tabs[e.target.index+1].focus():tabs[0].focus();break;case arrowPrevious:e.preventDefault(),void 0!==e.target.index&&tabs[e.target.index-1]?tabs[e.target.index-1].focus():tabs[tabs.length-1].focus();break;case"Home":e.preventDefault(),tabs[0].focus();break;case"End":e.preventDefault(),tabs[tabs.length-1].focus();break;case"Enter":case" ":e.preventDefault(),(0,_jquery.default)(e.target).tab("show"),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}})(e)})),document.addEventListener("click",(e=>{if(e.target.matches('[role="tablist"] [role="tab"]')){const tabs=e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');e.preventDefault(),(0,_jquery.default)(e.target).tab("show"),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}}))};_exports.init=()=>{dropdownFix(),window.addEventListener("load",(()=>{const alerts=document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');Array.prototype.forEach.call(alerts,(autofocusElement=>{autofocusElement.innerHTML+=" ",autofocusElement.removeAttribute("data-aria-autofocus")}))})),tabElementFix()}}));
//# sourceMappingURL=aria.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -29,8 +29,9 @@ import Pending from 'core/pending';
*/
const dropdownFix = () => {
let focusEnd = false;
const setFocusEnd = () => {
focusEnd = true;
let focusBackToTrigger = true;
const setFocusEnd = (end = true) => {
focusEnd = end;
};
const getFocusEnd = () => {
const result = focusEnd;
@ -38,27 +39,6 @@ const dropdownFix = () => {
return result;
};
// Special handling for "up" keyboard control.
document.addEventListener('keydown', e => {
if (e.target.matches('[data-toggle="dropdown"]')) {
const trigger = e.key;
// Up key opens the menu at the end.
if (trigger == 'ArrowUp') {
// Focus the end of the menu, not the beginning.
setFocusEnd();
}
// Space key or Enter key opens the menu.
if (trigger == ' ' || trigger == 'Enter') {
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
e.target.click();
}
}
});
// Special handling for navigation keys when menu is open.
const shiftFocus = element => {
const delayedFocus = pendingPromise => {
@ -68,9 +48,37 @@ const dropdownFix = () => {
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
};
$('.dropdown').on('shown.bs.dropdown', e => {
// We need to focus on the first menuitem.
const menu = e.target.querySelector('[role="menu"]');
// Event handling for the dropdown menu button.
const handleMenuButton = e => {
const trigger = e.key;
let fixFocus = false;
// Space key or Enter key opens the menu.
if (trigger === ' ' || trigger === 'Enter') {
fixFocus = true;
// Cancel random scroll.
e.preventDefault();
// Open the menu instead.
e.target.click();
}
// Up and Down keys also open the menu.
if (trigger === 'ArrowUp' || trigger === 'ArrowDown') {
fixFocus = true;
}
// Pressing tab on the menu button should focus on the next element in the DOM and not back to the menu trigger.
if (trigger === 'Tab') {
focusBackToTrigger = false;
}
if (!fixFocus) {
// No need to fix the focus. Return early.
return;
}
// Fix the focus on the menu items when the menu is opened.
const menu = e.target.parentElement.querySelector('[role="menu"]');
let menuItems = false;
let foundMenuItem = false;
@ -78,6 +86,13 @@ const dropdownFix = () => {
menuItems = menu.querySelectorAll('[role="menuitem"]');
}
if (menuItems && menuItems.length > 0) {
// Up key opens the menu at the end.
if (trigger === 'ArrowUp') {
setFocusEnd();
} else {
setFocusEnd(false);
}
if (getFocusEnd()) {
foundMenuItem = menuItems[menuItems.length - 1];
} else {
@ -85,10 +100,12 @@ const dropdownFix = () => {
foundMenuItem = menuItems[0];
}
}
if (foundMenuItem) {
shiftFocus(foundMenuItem);
}
});
};
// Search for menu items by finding the first item that has
// text starting with the typed character (case insensitive).
document.addEventListener('keypress', e => {
@ -117,6 +134,13 @@ const dropdownFix = () => {
// Keyboard navigation for arrow keys, home and end keys.
document.addEventListener('keydown', e => {
// We only want to set focus when users access the dropdown via keyboard as per
// guidelines defined in w3 aria practices 1.1 menu-button.
if (e.target.matches('[data-toggle="dropdown"]')) {
handleMenuButton(e);
}
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
const trigger = e.key;
let next = false;
@ -162,7 +186,11 @@ const dropdownFix = () => {
} else if (trigger == 'End') {
// End key.
next = menuItems[menuItems.length - 1];
} else if (trigger == 'Tab') {
// Pressing tab in the menu should focus on the next element in the DOM and not back to the menu trigger.
focusBackToTrigger = false;
}
// Variable next is set if we do want to act on the keypress.
if (next) {
e.preventDefault();
@ -175,9 +203,11 @@ const dropdownFix = () => {
$('.dropdown').on('hidden.bs.dropdown', e => {
// We need to focus on the menu trigger.
const trigger = e.target.querySelector('[data-toggle="dropdown"]');
if (trigger) {
if (trigger && focusBackToTrigger) {
shiftFocus(trigger);
}
// Reset flag to focus back to the menu trigger.
focusBackToTrigger = true;
});
};

View File

@ -91,7 +91,8 @@
.dropdown-item.active {
background-color: transparent;
color: $dropdown-link-color;
&:focus-within {
&:focus-within,
&:hover {
background-color: $dropdown-link-active-bg;
color: $dropdown-link-active-color;
a {

View File

@ -21515,12 +21515,14 @@ div.editor_atto_toolbar button .icon {
.moremenu .dropdown-item.active {
background-color: transparent;
color: #1d2125; }
.moremenu .dropdown-item[aria-current="true"]:focus-within,
.moremenu .dropdown-item.active:focus-within {
.moremenu .dropdown-item[aria-current="true"]:focus-within, .moremenu .dropdown-item[aria-current="true"]:hover,
.moremenu .dropdown-item.active:focus-within,
.moremenu .dropdown-item.active:hover {
background-color: #0f6cbf;
color: #fff; }
.moremenu .dropdown-item[aria-current="true"]:focus-within a,
.moremenu .dropdown-item.active:focus-within a {
.moremenu .dropdown-item[aria-current="true"]:focus-within a, .moremenu .dropdown-item[aria-current="true"]:hover a,
.moremenu .dropdown-item.active:focus-within a,
.moremenu .dropdown-item.active:hover a {
color: #fff; }
.moremenu .dropdown-item[aria-current="true"]:before,
.moremenu .dropdown-item.active:before {

View File

@ -21461,12 +21461,14 @@ div.editor_atto_toolbar button .icon {
.moremenu .dropdown-item.active {
background-color: transparent;
color: #1d2125; }
.moremenu .dropdown-item[aria-current="true"]:focus-within,
.moremenu .dropdown-item.active:focus-within {
.moremenu .dropdown-item[aria-current="true"]:focus-within, .moremenu .dropdown-item[aria-current="true"]:hover,
.moremenu .dropdown-item.active:focus-within,
.moremenu .dropdown-item.active:hover {
background-color: #0f6cbf;
color: #fff; }
.moremenu .dropdown-item[aria-current="true"]:focus-within a,
.moremenu .dropdown-item.active:focus-within a {
.moremenu .dropdown-item[aria-current="true"]:focus-within a, .moremenu .dropdown-item[aria-current="true"]:hover a,
.moremenu .dropdown-item.active:focus-within a,
.moremenu .dropdown-item.active:hover a {
color: #fff; }
.moremenu .dropdown-item[aria-current="true"]:before,
.moremenu .dropdown-item.active:before {