MDL-72263 courseformat: adding core/tree to course index
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
define ("core_courseformat/local/courseindex/keyboardnav",["exports","jquery","core/tree"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){e=function(a){return typeof a}}else{e=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return e(a)}function f(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function g(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function h(a,b,c){if(b)g(a.prototype,b);if(c)g(a,c);return a}function i(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)j(a,b)}function j(a,b){j=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return j(a,b)}function k(a){return function(){var b=p(a),c;if(n()){var d=p(this).constructor;c=Reflect.construct(b,arguments,d)}else{c=b.apply(this,arguments)}return l(this,c)}}function l(a,b){if(b&&("object"===e(b)||"function"==typeof b)){return b}return m(a)}function m(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function n(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return!0}catch(a){return!1}}function p(a){p=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return p(a)}var q=function(a){i(c,a);var d=k(c);function c(a){var b;f(this,c);b=d.call(this,a.element);b.selectors=a.selectors;if(a.reactive.isEditing){b._getVisibleItems=b.getVisibleItems;b.getVisibleItems=function(){b.refreshVisibleItemsCache();return b._getVisibleItems()}}b.treeRoot.on("hidden.bs.collapse shown.bs.collapse",function(){b.refreshVisibleItemsCache()});b.registerEnterCallback(b.enterCallback.bind(m(b)));return b}h(c,[{key:"getActiveItem",value:function getActiveItem(){var a=this.treeRoot.data("activeItem");if(a){return a.get(0)}}},{key:"enterCallback",value:function enterCallback(a){if(this.isGroupItem(a)){window.location.href=a.find(this.selectors.TOGGLER).first().attr("href");a.find(this.selectors.TOGGLER).get(0).click()}else{window.location.href=a.find("a").first().attr("href")}}},{key:"isGroupCollapsed",value:function isGroupCollapsed(a){var b=a.find("[aria-expanded]");return"false"===b.attr("aria-expanded")}},{key:"toggleGroup",value:function toggleGroup(a){var c,d=a.find(this.selectors.COLLAPSE),e=null!==(c=d.data("target"))&&void 0!==c?c:d.attr("href");if(!e){return}e=e.replace("#","");(0,b.default)("#".concat(e)).collapse("toggle")}},{key:"expandGroup",value:function expandGroup(a){if(this.isGroupCollapsed(a)){this.toggleGroup(a)}}},{key:"collapseGroup",value:function collapseGroup(a){if(!this.isGroupCollapsed(a)){this.toggleGroup(a)}}},{key:"expandAllGroups",value:function expandAllGroups(){var a=this,c=this.treeRoot.find(this.selectors.SECTION);c.each(function(c,d){a.expandGroup((0,b.default)(d))})}}]);return c}(c.default);a.default=q;return a.default});
|
||||
//# sourceMappingURL=keyboardnav.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -25,6 +25,7 @@
|
||||
import {BaseComponent} from 'core/reactive';
|
||||
import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
|
||||
import jQuery from 'jquery';
|
||||
import TreeNav from 'core_courseformat/local/courseindex/keyboardnav';
|
||||
|
||||
export default class Component extends BaseComponent {
|
||||
|
||||
@@ -41,6 +42,7 @@ export default class Component extends BaseComponent {
|
||||
CM: `[data-for='cm']`,
|
||||
TOGGLER: `[data-action="togglecourseindexsection"]`,
|
||||
COLLAPSE: `[data-toggle="collapse"]`,
|
||||
DRAWER: `.drawer`,
|
||||
};
|
||||
// Default classes to toggle on refresh.
|
||||
this.classes = {
|
||||
@@ -48,6 +50,7 @@ export default class Component extends BaseComponent {
|
||||
CMHIDDEN: 'dimmed',
|
||||
SECTIONCURRENT: 'current',
|
||||
COLLAPSED: `collapsed`,
|
||||
SHOW: `show`,
|
||||
};
|
||||
// Arrays to keep cms and sections elements.
|
||||
this.sections = {};
|
||||
@@ -85,6 +88,9 @@ export default class Component extends BaseComponent {
|
||||
cms.forEach((cm) => {
|
||||
this.cms[cm.dataset.id] = cm;
|
||||
});
|
||||
|
||||
// Configure Aria Tree.
|
||||
this.treeNav = new TreeNav(this);
|
||||
}
|
||||
|
||||
getWatchers() {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Course index keyboard navigation and aria-tree compatibility.
|
||||
*
|
||||
* Node tree and bootstrap collapsibles don't use the same HTML structure. However,
|
||||
* all keybindings and logic is compatible. This class translate the primitive opetations
|
||||
* to a bootstrap collapsible structure.
|
||||
*
|
||||
* @module core_courseformat/local/courseindex/keyboardnav
|
||||
* @class core_courseformat/local/courseindex/keyboardnav
|
||||
* @copyright 2021 Ferran Recio <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// The core/tree uses jQuery to expand all nodes.
|
||||
import $ from 'jquery';
|
||||
import Tree from 'core/tree';
|
||||
|
||||
export default class extends Tree {
|
||||
|
||||
/**
|
||||
* Setup the core/tree keyboard navigation.
|
||||
*
|
||||
* @param {CourseIndex} parent the parent component
|
||||
*/
|
||||
constructor(parent) {
|
||||
// Init this value with the parent DOM element.
|
||||
super(parent.element);
|
||||
|
||||
// Get selectors from parent.
|
||||
this.selectors = parent.selectors;
|
||||
|
||||
// The core/tree library saves the visible elements cache inside the main tree node.
|
||||
// However, in edit mode content can change suddenly so we need to refresh caches when needed.
|
||||
if (parent.reactive.isEditing) {
|
||||
this._getVisibleItems = this.getVisibleItems;
|
||||
this.getVisibleItems = () => {
|
||||
this.refreshVisibleItemsCache();
|
||||
return this._getVisibleItems();
|
||||
};
|
||||
}
|
||||
// Add jQuery events to detect boostrap collapse and uncollapse.
|
||||
this.treeRoot.on('hidden.bs.collapse shown.bs.collapse', () => {
|
||||
this.refreshVisibleItemsCache();
|
||||
});
|
||||
// Register a custom callback for pressing enter key.
|
||||
this.registerEnterCallback(this.enterCallback.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current active node.
|
||||
*
|
||||
* @return {Element|undefined} the active item if any
|
||||
*/
|
||||
getActiveItem() {
|
||||
const activeItem = this.treeRoot.data('activeItem');
|
||||
if (activeItem) {
|
||||
return activeItem.get(0);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle enter key on a collpasible node.
|
||||
*
|
||||
* @param {JQuery} item the jQuery object
|
||||
*/
|
||||
enterCallback(item) {
|
||||
if (this.isGroupItem(item)) {
|
||||
// Group elements is like clicking a topic but without loosing the focus.
|
||||
window.location.href = item.find(this.selectors.TOGGLER).first().attr('href');
|
||||
item.find(this.selectors.TOGGLER).get(0).click();
|
||||
} else {
|
||||
// Activity links just follow the link href.
|
||||
window.location.href = item.find('a').first().attr('href');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a gorup item is collapsed.
|
||||
*
|
||||
* @param {JQuery} item the jQuery object
|
||||
* @returns {boolean} if the element is collapsed
|
||||
*/
|
||||
isGroupCollapsed(item) {
|
||||
const toggler = item.find(`[aria-expanded]`);
|
||||
return toggler.attr('aria-expanded') === 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle a group item.
|
||||
*
|
||||
* @param {JQuery} item the jQuery object
|
||||
*/
|
||||
toggleGroup(item) {
|
||||
const toggler = item.find(this.selectors.COLLAPSE);
|
||||
let collapsibleId = toggler.data('target') ?? toggler.attr('href');
|
||||
if (!collapsibleId) {
|
||||
return;
|
||||
}
|
||||
collapsibleId = collapsibleId.replace('#', '');
|
||||
|
||||
// Bootstrap 4 uses jQuery to interact with collapsibles.
|
||||
$(`#${collapsibleId}`).collapse('toggle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a group item.
|
||||
*
|
||||
* @param {JQuery} item the jQuery object
|
||||
*/
|
||||
expandGroup(item) {
|
||||
if (this.isGroupCollapsed(item)) {
|
||||
this.toggleGroup(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collpase a group item.
|
||||
*
|
||||
* @param {JQuery} item the jQuery object
|
||||
*/
|
||||
collapseGroup(item) {
|
||||
if (!this.isGroupCollapsed(item)) {
|
||||
this.toggleGroup(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand all groups.
|
||||
*/
|
||||
expandAllGroups() {
|
||||
const togglers = this.treeRoot.find(this.selectors.SECTION);
|
||||
togglers.each((index, item) => {
|
||||
this.expandGroup($(item));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
"isactive": 1,
|
||||
"uniqid": "0",
|
||||
"isactive": 1,
|
||||
"accessvisible": 1,
|
||||
"accessvisible": 1
|
||||
}
|
||||
}}
|
||||
<li class="courseindex-item
|
||||
@@ -38,10 +38,11 @@
|
||||
id="{{uniqid}}-course-index-cm-{{id}}"
|
||||
data-for="cm"
|
||||
data-id="{{id}}"
|
||||
role="treeitem"
|
||||
>
|
||||
{{#url}}
|
||||
{{#uservisible}}
|
||||
<a class="courseindex-link text-truncate" href="{{{url}}}" data-for="cm_name">
|
||||
<a class="courseindex-link text-truncate" href="{{{url}}}" data-for="cm_name" tabindex="-1">
|
||||
{{{name}}}
|
||||
</a>
|
||||
{{/uservisible}}
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
data-for="section"
|
||||
data-id="{{id}}"
|
||||
data-number="{{number}}"
|
||||
role="treeitem"
|
||||
aria-owns="courseindexcollapse{{number}}"
|
||||
>
|
||||
<div class="courseindex-item d-flex {{^visible}}dimmed{{/visible}} courseindex-section-title"
|
||||
id="courseindexsection{{number}}"
|
||||
@@ -67,9 +69,10 @@
|
||||
>
|
||||
<a data-toggle="collapse"
|
||||
href="#courseindexcollapse{{number}}"
|
||||
class="courseindex-link icons-collapse-expand {{^isactive}}collapsed{{/isactive}}"
|
||||
aria-expanded="{{#isactive}}true{{/isactive}}{{^isactive}}false{{/isactive}}"
|
||||
aria-controls="courseindexcollapse{{number}}"
|
||||
class="courseindex-link icons-collapse-expand {{^isactive}}collapsed{{/isactive}}"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span class="collapsed-icon icon-no-margin mr-1"
|
||||
data-toggle="tooltip" title="{{#str}} expand, core {{/str}}">
|
||||
@@ -86,6 +89,7 @@
|
||||
class="courseindex-link text-truncate"
|
||||
data-action="togglecourseindexsection"
|
||||
data-for="section_title"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{{title}}}
|
||||
</a>
|
||||
@@ -96,7 +100,9 @@
|
||||
</div>
|
||||
<div id="courseindexcollapse{{number}}"
|
||||
class="courseindex-item-content collapse {{#isactive}}show{{/isactive}}"
|
||||
aria-labelledby="courseindexsection{{number}}">
|
||||
aria-labelledby="courseindexsection{{number}}"
|
||||
role="group"
|
||||
>
|
||||
<ul class="courseindex-sectioncontent unlist" data-for="cmlist" data-id="{{id}}">
|
||||
{{#cms}}
|
||||
{{> core_courseformat/local/courseindex/cm }}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
@core @core_courseformat
|
||||
Feature: Verify that courseindex is usable with the keyboard
|
||||
In order to use the course index
|
||||
As a user
|
||||
I need to be able to navigate it without a mouse
|
||||
|
||||
Background:
|
||||
Given the following "course" exists:
|
||||
| fullname | Course 1 |
|
||||
| shortname | C1 |
|
||||
| category | 0 |
|
||||
| enablecompletion | 1 |
|
||||
| numsections | 3 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | section |
|
||||
| assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 |
|
||||
| book | Activity sample 2 | Test book description | C1 | sample2 | 2 |
|
||||
| choice | Activity sample 3 | Test choice description | C1 | sample3 | 3 |
|
||||
Given I am on the "C1" "Course" page logged in as "admin"
|
||||
And I click on "Side panel" "button"
|
||||
And I click on "Open course index drawer" "button"
|
||||
And I should see "Topic 1" in the "courseindex-content" "region"
|
||||
And the focused element is ".drawertoggle" "css_element"
|
||||
And I press the tab key
|
||||
Then the focused element is ".courseindex-section" "css_element"
|
||||
|
||||
@javascript
|
||||
Scenario: General focus on open course index.
|
||||
When I press the shift tab key
|
||||
And the focused element is ".drawertoggle" "css_element"
|
||||
And I press enter
|
||||
Then I should not see "Topic 1" in the "courseindex-content" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Opening and closing sections.
|
||||
When I press the down key
|
||||
And I should see "Activity sample 1" in the "courseindex-content" "region"
|
||||
# Close section with left key.
|
||||
Then I press the left key
|
||||
And I should not see "Activity sample 1" in the "courseindex-content" "region"
|
||||
# Open a section with right key
|
||||
And I press the right key
|
||||
And I should see "Activity sample 1" in the "courseindex-content" "region"
|
||||
# Key down to focus the module and close the section with two left keys.
|
||||
And I press the down key
|
||||
And I press the left key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 1" in the "courseindex-content" "region"
|
||||
# Open a section using enter key.
|
||||
And I press the down key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 2" in the "courseindex-content" "region"
|
||||
And I press enter
|
||||
And I should see "Activity sample 2" in the "courseindex-content" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Navigate to an activity.
|
||||
When I press the down key
|
||||
And I press the right key
|
||||
And I press enter
|
||||
Then I should see "Activity sample 1" in the "page-header" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Navigate to first and last element.
|
||||
# Close sections 1 and 3.
|
||||
Given I press the down key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 1" in the "courseindex-content" "region"
|
||||
And I press the down key
|
||||
And I press the down key
|
||||
And I press the down key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 3" in the "courseindex-content" "region"
|
||||
# Use end key to go to the last element.
|
||||
When I press the end key
|
||||
And I press the right key
|
||||
And I should see "Activity sample 3" in the "courseindex-content" "region"
|
||||
And I press the left key
|
||||
Then I should not see "Activity sample 3" in the "courseindex-content" "region"
|
||||
# Use home key to go to the first element.
|
||||
And I press the home key
|
||||
And I press the down key
|
||||
And I press the right key
|
||||
And I should see "Activity sample 1" in the "courseindex-content" "region"
|
||||
|
||||
@javascript
|
||||
Scenario: Asterisc to open all sections.
|
||||
# Close sections 1 and 2.
|
||||
Given I press the down key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 1" in the "courseindex-content" "region"
|
||||
And I press the down key
|
||||
And I press the left key
|
||||
And I should not see "Activity sample 2" in the "courseindex-content" "region"
|
||||
And I should see "Activity sample 3" in the "courseindex-content" "region"
|
||||
When I press the multiply key
|
||||
Then I should see "Activity sample 1" in the "courseindex-content" "region"
|
||||
And I should see "Activity sample 2" in the "courseindex-content" "region"
|
||||
And I should see "Activity sample 3" in the "courseindex-content" "region"
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
define ("core/local/aria/aria-hidden",["exports","core/normalise","./selectors"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.unhideSiblings=a.hideSiblings=a.unhide=a.hide=void 0;c=function(a){return a&&a.__esModule?a:{default:a}}(c);var d=new Map,e=new Map,f=function(){return MutationObserver&&"function"==typeof MutationObserver},g=function(a){if(!(a instanceof HTMLElement)){return}if(a.matches(c.default.elements.focusable)){h(a)}a.querySelectorAll(c.default.elements.focusable).forEach(h)},h=function(a){if("undefined"!=typeof a.dataset.ariaHiddenTabIndex){return}if(a.getAttribute("tabindex")){a.dataset.ariaHiddenTabIndex=a.getAttribute("tabindex")}else{a.dataset.ariaHiddenTabIndex=""}a.setAttribute("tabindex",-1)},i=function(a){if(!(a instanceof HTMLElement)){return}if(a.matches(c.default.elements.focusableToUnhide)){j(a)}a.querySelectorAll(c.default.elements.focusableToUnhide).forEach(j)},j=function(a){if(a.closest(c.default.aria.hidden)){return}var b=a.dataset.ariaHiddenTabIndex;if(""===b){a.removeAttribute("tabindex")}else{a.setAttribute("tabindex",b)}delete a.dataset.ariaHiddenTabIndex},k=function(a){return(0,b.getList)(a).forEach(l)};a.hide=k;var l=function(a){if(!(a instanceof HTMLElement)){return}if(a.closest(c.default.aria.hidden)){return}a.setAttribute("aria-hidden",!0);g(a);if(f()){var b=new MutationObserver(function(a){a.forEach(function(a){a.addedNodes.forEach(g)})});b.observe(a,{childList:!0,subtree:!0});d.set(a,b)}},m=function(a){return(0,b.getList)(a).forEach(n)};a.unhide=m;var n=function(a){if(!(a instanceof HTMLElement)){return}a.removeAttribute("aria-hidden");i(a);if(d.has(a)){d.get(a).disconnect();d.delete(a)}};a.hideSiblings=function hideSiblings(a){return(0,b.getList)(a).forEach(o)};var o=function(a){if(!(a instanceof HTMLElement)){return}if(!a.parentElement){return}a.parentElement.childNodes.forEach(function(b){if(b===a){return}k(b)});if(f()){var b=new MutationObserver(function(b){b.forEach(function(b){b.addedNodes.forEach(function(b){if(a.contains(b)){return}k(b)})})});b.observe(a.parentElement,{childList:!0,subtree:!0});e.set(a.parentElement,b)}};a.unhideSiblings=function unhideSiblings(a){return(0,b.getList)(a).forEach(p)};var p=function(a){if(!(a instanceof HTMLElement)){return}if(!a.parentElement){return}a.parentElement.childNodes.forEach(function(b){if(b===a){return}m(b)});if(e.has(a.parentElement)){e.get(a.parentElement).disconnect();e.delete(a.parentElement)}}});
|
||||
define ("core/local/aria/aria-hidden",["exports","core/normalise","./selectors"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.unhideSiblings=a.hideSiblings=a.unhide=a.hide=void 0;c=function(a){return a&&a.__esModule?a:{default:a}}(c);var d=new Map,e=new Map,f=function(){return MutationObserver&&"function"==typeof MutationObserver},g=function(a){if(!(a instanceof HTMLElement)){return}if(a.matches(c.default.elements.focusable)){h(a)}a.querySelectorAll(c.default.elements.focusable).forEach(h)},h=function(a){if("undefined"!=typeof a.dataset.ariaHiddenTabIndex){return}if(a.getAttribute("tabindex")){a.dataset.ariaHiddenTabIndex=a.getAttribute("tabindex")}else{a.dataset.ariaHiddenTabIndex=""}a.setAttribute("tabindex",-1)},i=function(a){if(!(a instanceof HTMLElement)){return}if(a.matches(c.default.elements.focusableToUnhide)){j(a)}a.querySelectorAll(c.default.elements.focusableToUnhide).forEach(j)},j=function(a){if(a.closest(c.default.aria.hidden)){return}var b=a.dataset.ariaHiddenTabIndex;if(""===b){a.removeAttribute("tabindex")}else{a.setAttribute("tabindex",b)}delete a.dataset.ariaHiddenTabIndex},k=function(a){return(0,b.getList)(a).forEach(l)};a.hide=k;var l=function(a){if(!(a instanceof HTMLElement)){return}if(a.closest(c.default.aria.hidden)){return}a.setAttribute("aria-hidden",!0);g(a);if(f()){var b=new MutationObserver(function(a){a.forEach(function(a){if("childList"===a.type){a.addedNodes.forEach(g)}else if("attributes"===a.type){var b=a.target,c=b.getAttribute("tabindex");if("-1"!==c){b.dataset.ariaHiddenTabIndex=c;b.setAttribute("tabindex",-1)}}})});b.observe(a,{subtree:!0,childList:!0,attributes:!0,attributeFilter:["tabindex"]});d.set(a,b)}},m=function(a){return(0,b.getList)(a).forEach(n)};a.unhide=m;var n=function(a){if(!(a instanceof HTMLElement)){return}a.removeAttribute("aria-hidden");i(a);if(d.has(a)){d.get(a).disconnect();d.delete(a)}};a.hideSiblings=function hideSiblings(a){return(0,b.getList)(a).forEach(o)};var o=function(a){if(!(a instanceof HTMLElement)){return}if(!a.parentElement){return}a.parentElement.childNodes.forEach(function(b){if(b===a){return}k(b)});if(f()){var b=new MutationObserver(function(b){b.forEach(function(b){b.addedNodes.forEach(function(b){if(a.contains(b)){return}k(b)})})});b.observe(a.parentElement,{childList:!0,subtree:!0});e.set(a.parentElement,b)}};a.unhideSiblings=function unhideSiblings(a){return(0,b.getList)(a).forEach(p)};var p=function(a){if(!(a instanceof HTMLElement)){return}if(!a.parentElement){return}a.parentElement.childNodes.forEach(function(b){if(b===a){return}m(b)});if(e.has(a.parentElement)){e.get(a.parentElement).disconnect();e.delete(a.parentElement)}}});
|
||||
//# sourceMappingURL=aria-hidden.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -153,14 +153,36 @@ const _hide = target => {
|
||||
|
||||
if (supportsMutationObservers()) {
|
||||
// Add a MutationObserver to check for new children to the tree.
|
||||
const newNodeObserver = new MutationObserver(mutationList => {
|
||||
const mutationObserver = new MutationObserver(mutationList => {
|
||||
mutationList.forEach(mutation => {
|
||||
mutation.addedNodes.forEach(disableElementFocusability);
|
||||
if (mutation.type === 'childList') {
|
||||
mutation.addedNodes.forEach(disableElementFocusability);
|
||||
} else if (mutation.type === 'attributes') {
|
||||
// The tabindex has been updated on a hidden attribute.
|
||||
// Ensure that it is stored, ad set to -1 to prevent breakage.
|
||||
const element = mutation.target;
|
||||
const proposedTabIndex = element.getAttribute('tabindex');
|
||||
|
||||
if (proposedTabIndex !== "-1") {
|
||||
element.dataset.ariaHiddenTabIndex = proposedTabIndex;
|
||||
element.setAttribute('tabindex', -1);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
newNodeObserver.observe(target, {childList: true, subtree: true});
|
||||
childObserverMap.set(target, newNodeObserver);
|
||||
mutationObserver.observe(target, {
|
||||
// Watch for changes to the entire subtree.
|
||||
subtree: true,
|
||||
|
||||
// Watch for new nodes.
|
||||
childList: true,
|
||||
|
||||
// Watch for attribute changes to the tabindex.
|
||||
attributes: true,
|
||||
attributeFilter: ['tabindex'],
|
||||
});
|
||||
childObserverMap.set(target, mutationObserver);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
* Implement an accessible aria tree widget, from a nested unordered list.
|
||||
* Based on http://oaa-accessibility.org/example/41/.
|
||||
*
|
||||
* @module tool_lp/tree
|
||||
* @module core/tree
|
||||
* @copyright 2015 Damyon Wiese <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
@@ -1886,6 +1886,9 @@ EOF;
|
||||
case 'SPACE':
|
||||
$keys[] = behat_keys::SPACE;
|
||||
break;
|
||||
case 'MULTIPLY':
|
||||
$keys[] = behat_keys::MULTIPLY;
|
||||
break;
|
||||
default:
|
||||
// You can enter a single ASCII character (e.g. a letter) to directly type that key.
|
||||
if (strlen($key) === 1) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -155,10 +155,10 @@ export default class Drawers {
|
||||
this.drawerNode = drawerNode;
|
||||
|
||||
if (this.drawerNode.classList.contains('show')) {
|
||||
this.openDrawer();
|
||||
this.openDrawer(true);
|
||||
} else if (this.drawerNode.dataset.forceopen == 1) {
|
||||
if (!isSmall()) {
|
||||
this.openDrawer();
|
||||
this.openDrawer(true);
|
||||
}
|
||||
} else {
|
||||
Aria.hide(this.drawerNode);
|
||||
@@ -269,8 +269,15 @@ export default class Drawers {
|
||||
|
||||
/**
|
||||
* Open the drawer.
|
||||
*
|
||||
* By default, openDrawer move the page focus to the close drawer button.However, when
|
||||
* a drawer is opened by default this represents an accessibility problem as the initial
|
||||
* focus changes without any user interaction. Using the keepfocus param it is possible to
|
||||
* prevent the focus lose.
|
||||
*
|
||||
* @param {boolean} keepfocus true if the drawer has to open but don't alter the page focus.
|
||||
*/
|
||||
openDrawer() {
|
||||
openDrawer(keepfocus) {
|
||||
const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);
|
||||
if (showEvent.defaultPrevented) {
|
||||
return;
|
||||
@@ -301,8 +308,10 @@ export default class Drawers {
|
||||
.catch();
|
||||
}
|
||||
|
||||
const closeButton = this.drawerNode.querySelector('[data-toggle="drawers"][data-action="closedrawer"]');
|
||||
closeButton.focus();
|
||||
if (!keepfocus) {
|
||||
const closeButton = this.drawerNode.querySelector('[data-toggle="drawers"][data-action="closedrawer"]');
|
||||
closeButton.focus();
|
||||
}
|
||||
|
||||
this.dispatchEvent(Drawers.eventTypes.drawerShown);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"lib/tests/behat/action_menu.feature",
|
||||
"blocks/tests/behat/hide_blocks.feature",
|
||||
"blocks/tests/behat/move_blocks.feature",
|
||||
"course/format/tests/behat/course_courseindex.feature"
|
||||
"course/format/tests/behat/course_courseindex.feature",
|
||||
"course/format/tests/behat/courseindex_keyboardnav.feature"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user