Merge branch 'MDL-84732-m500' of https://github.com/sammarshallou/moodle into MOODLE_500_STABLE
This commit is contained in:
+1
-1
@@ -8,6 +8,6 @@ define("core/local/aria/focuslock",["exports","./selectors"],(function(_exports,
|
||||
* @module core/local/aria/focuslock
|
||||
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.untrapFocus=_exports.trapFocus=void 0,_selectors=(obj=_selectors)&&obj.__esModule?obj:{default:obj};const lockRegionStack=[],initialFocusElementStack=[],finalFocusElementStack=[];let lastFocus=null,ignoreFocusChanges=!1,isLocked=!1;const lockHandler=event=>{if(ignoreFocusChanges)return;let lockRegion=getCurrentLockRegion();for(;lockRegion&&!document.contains(lockRegion);)untrapFocus(),lockRegion=getCurrentLockRegion();lockRegion&&(event&&lockRegion.contains(event.target)?lastFocus=event.target:(focusFirstDescendant(),lastFocus==document.activeElement&&focusLastDescendant(),lastFocus=document.activeElement))},keyDownHandler=event=>{if("Tab"===event.key&&getCurrentLockRegion())if(event.shiftKey){const firstFocusable=getCurrentLockRegion().querySelector(_selectors.default.elements.focusable);document.activeElement===firstFocusable&&(lockHandler(),event.preventDefault())}else{const allFocusable=document.querySelectorAll(_selectors.default.elements.focusable);document.activeElement===allFocusable[allFocusable.length-1]&&(lockHandler(),event.preventDefault())}},focusFirstDescendant=()=>{const lockRegion=getCurrentLockRegion(),focusableElements=Array.from(lockRegion.querySelectorAll(_selectors.default.elements.focusable));return focusableElements.unshift(lockRegion),focusableElements.some((focusableElement=>attemptFocus(focusableElement)))},focusLastDescendant=()=>{const lockRegion=getCurrentLockRegion(),focusableElements=Array.from(lockRegion.querySelectorAll(_selectors.default.elements.focusable)).reverse();return focusableElements.push(lockRegion),focusableElements.some((focusableElement=>attemptFocus(focusableElement)))},attemptFocus=focusTarget=>{if(!(focusTarget=>{if(focusTarget.tabIndex>0||0===focusTarget.tabIndex&&null!==focusTarget.getAttribute("tabIndex"))return!0;if(focusTarget.disabled)return!1;switch(focusTarget.nodeName){case"A":return!!focusTarget.href&&"ignore"!=focusTarget.rel;case"INPUT":return"hidden"!=focusTarget.type&&"file"!=focusTarget.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}})(focusTarget))return!1;ignoreFocusChanges=!0;try{focusTarget.focus()}catch(e){}return ignoreFocusChanges=!1,document.activeElement===focusTarget},getCurrentLockRegion=()=>lockRegionStack[lockRegionStack.length-1];_exports.trapFocus=newLockRegion=>{if((newLockRegion=>{if(newLockRegion===getCurrentLockRegion())return;lockRegionStack.push(newLockRegion);const currentLockRegion=getCurrentLockRegion(),element=document.createElement("div");element.tabIndex=0,element.style.position="fixed",element.style.top=0,element.style.left=0;const initialNode=element.cloneNode();currentLockRegion.parentNode.insertBefore(initialNode,currentLockRegion),initialFocusElementStack.push(initialNode);const finalNode=element.cloneNode();currentLockRegion.parentNode.insertBefore(finalNode,currentLockRegion.nextSibling),finalFocusElementStack.push(finalNode)})(newLockRegion),isLocked||(document.addEventListener("focus",lockHandler,!0),document.addEventListener("keydown",keyDownHandler,!0)),!focusFirstDescendant()){const currentLockRegion=getCurrentLockRegion(),originalRegionTabIndex=currentLockRegion.tabIndex;currentLockRegion.tabIndex=0,attemptFocus(currentLockRegion),currentLockRegion.tabIndex=originalRegionTabIndex}lastFocus=document.activeElement,isLocked=!0};const untrapFocus=()=>{(()=>{lockRegionStack.pop();const finalNode=finalFocusElementStack.pop();finalNode&&finalNode.remove();const initialNode=initialFocusElementStack.pop();initialNode&&initialNode.remove()})(),lockRegionStack.length||(document.removeEventListener("focus",lockHandler,!0),document.removeEventListener("keydown",keyDownHandler,!0),lastFocus=null,ignoreFocusChanges=!1,isLocked=!1)};_exports.untrapFocus=untrapFocus}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.untrapFocus=_exports.trapFocus=void 0,_selectors=(obj=_selectors)&&obj.__esModule?obj:{default:obj};const lockRegionStack=[],initialFocusElementStack=[],finalFocusElementStack=[];let lastFocus=null,ignoreFocusChanges=!1,isLocked=!1;const lockHandler=event=>{if(ignoreFocusChanges)return;let lockRegion=getCurrentLockRegion();for(;lockRegion&&!document.contains(lockRegion);)untrapFocus(),lockRegion=getCurrentLockRegion();lockRegion&&(event&&lockRegion.contains(event.target)?lastFocus=event.target:(focusFirstDescendant(),lastFocus==document.activeElement&&focusLastDescendant(),lastFocus=document.activeElement))},keyDownHandler=event=>{if("Tab"===event.key&&getCurrentLockRegion())if(event.shiftKey){const firstFocusable=getCurrentLockRegion().querySelector(_selectors.default.elements.focusable);document.activeElement===firstFocusable&&(lockHandler(),event.preventDefault())}else{const allFocusable=(()=>{const allFocusable=document.querySelectorAll(_selectors.default.elements.focusable);return Array.from(allFocusable).filter((focusable=>!!focusable.offsetParent))})();document.activeElement===allFocusable[allFocusable.length-1]&&(lockHandler(),event.preventDefault())}},focusFirstDescendant=()=>{const lockRegion=getCurrentLockRegion(),focusableElements=Array.from(lockRegion.querySelectorAll(_selectors.default.elements.focusable));return focusableElements.unshift(lockRegion),focusableElements.some((focusableElement=>attemptFocus(focusableElement)))},focusLastDescendant=()=>{const lockRegion=getCurrentLockRegion(),focusableElements=Array.from(lockRegion.querySelectorAll(_selectors.default.elements.focusable)).reverse();return focusableElements.push(lockRegion),focusableElements.some((focusableElement=>attemptFocus(focusableElement)))},attemptFocus=focusTarget=>{if(!(focusTarget=>{if(focusTarget.tabIndex>0||0===focusTarget.tabIndex&&null!==focusTarget.getAttribute("tabIndex"))return!0;if(focusTarget.disabled)return!1;switch(focusTarget.nodeName){case"A":return!!focusTarget.href&&"ignore"!=focusTarget.rel;case"INPUT":return"hidden"!=focusTarget.type&&"file"!=focusTarget.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}})(focusTarget))return!1;ignoreFocusChanges=!0;try{focusTarget.focus()}catch(e){}return ignoreFocusChanges=!1,document.activeElement===focusTarget},getCurrentLockRegion=()=>lockRegionStack[lockRegionStack.length-1];_exports.trapFocus=newLockRegion=>{if((newLockRegion=>{if(newLockRegion===getCurrentLockRegion())return;lockRegionStack.push(newLockRegion);const currentLockRegion=getCurrentLockRegion(),element=document.createElement("div");element.tabIndex=0,element.style.position="fixed",element.style.top=0,element.style.left=0;const initialNode=element.cloneNode();currentLockRegion.parentNode.insertBefore(initialNode,currentLockRegion),initialFocusElementStack.push(initialNode);const finalNode=element.cloneNode();currentLockRegion.parentNode.insertBefore(finalNode,currentLockRegion.nextSibling),finalFocusElementStack.push(finalNode)})(newLockRegion),isLocked||(document.addEventListener("focus",lockHandler,!0),document.addEventListener("keydown",keyDownHandler,!0)),!focusFirstDescendant()){const currentLockRegion=getCurrentLockRegion(),originalRegionTabIndex=currentLockRegion.tabIndex;currentLockRegion.tabIndex=0,attemptFocus(currentLockRegion),currentLockRegion.tabIndex=originalRegionTabIndex}lastFocus=document.activeElement,isLocked=!0};const untrapFocus=()=>{(()=>{lockRegionStack.pop();const finalNode=finalFocusElementStack.pop();finalNode&&finalNode.remove();const initialNode=initialFocusElementStack.pop();initialNode&&initialNode.remove()})(),lockRegionStack.length||(document.removeEventListener("focus",lockHandler,!0),document.removeEventListener("keydown",keyDownHandler,!0),lastFocus=null,ignoreFocusChanges=!1,isLocked=!1)};_exports.untrapFocus=untrapFocus}));
|
||||
|
||||
//# sourceMappingURL=focuslock.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -85,6 +85,20 @@ const lockHandler = event => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets all the focusable elements in the document that are not set to display:none. This is useful
|
||||
* because sometimes, a nested modal dialog may be left in the DOM but set to display:none, and you
|
||||
* can't actually focus display:none elements.
|
||||
*
|
||||
* @returns {HTMLElement[]} All focusable elements that aren't display:none, in DOM order
|
||||
*/
|
||||
const getAllFocusableElements = () => {
|
||||
const allFocusable = document.querySelectorAll(Selectors.elements.focusable);
|
||||
// The offsetParent check is a well-perfoming way to ensure that an element in the document
|
||||
// does not have display:none.
|
||||
return Array.from(allFocusable).filter(focusable => !!focusable.offsetParent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch event for any keydown during focus lock.
|
||||
*
|
||||
@@ -102,7 +116,7 @@ const keyDownHandler = event => {
|
||||
|
||||
if (!event.shiftKey) {
|
||||
// Have they already focused the last focusable element in the document?
|
||||
const allFocusable = document.querySelectorAll(Selectors.elements.focusable);
|
||||
const allFocusable = getAllFocusableElements();
|
||||
if (document.activeElement === allFocusable[allFocusable.length - 1]) {
|
||||
// When the last thing is focused, focus would go to browser UI next, instead use
|
||||
// lockHandler to put focus back on the first element in lock region.
|
||||
|
||||
@@ -4,8 +4,7 @@ Feature: Focus lock in modal popups
|
||||
As a user
|
||||
The tab key should cycle through elements in the form and not go outside it
|
||||
|
||||
@javascript
|
||||
Scenario: Tab cycles through elements in modal, using image popup in Tiny as an example
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
@@ -16,9 +15,11 @@ Feature: Focus lock in modal popups
|
||||
And I follow "Add discussion topic"
|
||||
And I click on "Image" "button"
|
||||
|
||||
@javascript
|
||||
Scenario: Tab cycles through elements in modal, using image popup in Tiny as an example
|
||||
# Repeated tabs just to get to the last element. This may need changing if controls are added
|
||||
# or removed to the form.
|
||||
And I press the tab key
|
||||
When I press the tab key
|
||||
And I press the tab key
|
||||
And I press the tab key
|
||||
And I press the tab key
|
||||
@@ -33,3 +34,15 @@ Feature: Focus lock in modal popups
|
||||
And I press the shift tab key
|
||||
And I press the shift tab key
|
||||
And the focused element is "Browse repositories" "button"
|
||||
|
||||
@javascript
|
||||
Scenario: Focus continues to be locked to modal even after closing nested modal
|
||||
# Open 'Browse repositories' nested modal, then close it again.
|
||||
When I press "Browse repositories"
|
||||
And I click on "Close" "button" in the "File picker" "dialogue"
|
||||
And the focused element is "Browse repositories" "button"
|
||||
|
||||
# Focus should still wrap around to the start of the image modal.
|
||||
When I press the tab key
|
||||
And I press the tab key
|
||||
Then the focused element is "Close" "button" in the "Insert image" "dialogue"
|
||||
|
||||
Reference in New Issue
Block a user