MDL-78885 core_user: A11y fixes for user combobox search
Co-authored-by: Shamim Rezaie <[email protected]>
This commit is contained in:
committed by
Shamim Rezaie
co-authored by
Shamim Rezaie
parent
27c1692867
commit
e4083441ec
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("core_grades/comboboxsearch/grade",["exports","core/comboboxsearch/search_combobox","core_grades/searchwidget/repository","core/templates","core/utils"],(function(_exports,_search_combobox,Repository,_templates,_utils){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=(obj=_search_combobox)&&obj.__esModule?obj:{default:obj},Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository);class GradeItemSearch extends _search_combobox.default{constructor(){super(),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"courseID",void 0),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.gradesearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.renderDefault()}static init(){return new GradeItemSearch}componentSelector(){return".grade-search"}dropdownSelector(){return".gradesearchdropdown"}triggerSelector(){return".gradesearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core/local/comboboxsearch/resultset",{results:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js)}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async fetchDataset(){return await Repository.gradeitemFetch(this.courseID).then((r=>r.gradeitems))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((grade=>Object.keys(grade).some((key=>""!==grade[key]&&grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((grade=>({id:grade.id,name:grade.name,link:this.selectOneLink(grade.id)}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.dropdown)&&e.stopImmediatePropagation(),this.clearSearchButton.addEventListener("click",(async()=>{this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),e.target.closest(".dropdown-item")&&0===e.button&&(window.location=e.target.closest(".dropdown-item").href)}keyHandler(e){switch(super.keyHandler(e),e.key){case"Tab":e.target.closest(this.selectors.input)&&(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0}));break;case"Escape":if("option"===document.activeElement.getAttribute("role"))e.stopPropagation(),this.searchInput.focus({preventScroll:!0});else if(e.target.closest(this.selectors.input)){this.component.querySelector(this.selectors.trigger).focus({preventScroll:!0})}}}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(gradeID){throw new Error("selectOneLink(".concat(gradeID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GradeItemSearch,_exports.default}));
|
||||
define("core_grades/comboboxsearch/grade",["exports","core/comboboxsearch/search_combobox","core_grades/searchwidget/repository","core/templates","core/utils"],(function(_exports,_search_combobox,Repository,_templates,_utils){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=(obj=_search_combobox)&&obj.__esModule?obj:{default:obj},Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository);class GradeItemSearch extends _search_combobox.default{constructor(){super(),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"courseID",void 0),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.gradesearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.renderDefault()}static init(){return new GradeItemSearch}componentSelector(){return".grade-search"}dropdownSelector(){return".gradesearchdropdown"}triggerSelector(){return".gradesearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core/local/comboboxsearch/resultset",{results:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js)}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async fetchDataset(){return await Repository.gradeitemFetch(this.courseID).then((r=>r.gradeitems))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((grade=>Object.keys(grade).some((key=>""!==grade[key]&&grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((grade=>({id:grade.id,name:grade.name,link:this.selectOneLink(grade.id)}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.dropdown)&&e.stopImmediatePropagation(),this.clearSearchButton.addEventListener("click",(async()=>{this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),e.target.closest(".dropdown-item")&&0===e.button&&(window.location=e.target.closest(".dropdown-item").href)}keyHandler(e){if(super.keyHandler(e),"Escape"===e.key)if("option"===document.activeElement.getAttribute("role"))e.stopPropagation(),this.searchInput.focus({preventScroll:!0});else if(e.target.closest(this.selectors.input)){this.component.querySelector(this.selectors.trigger).focus({preventScroll:!0})}}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(gradeID){throw new Error("selectOneLink(".concat(gradeID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GradeItemSearch,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=grade.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -199,12 +199,6 @@ export default class GradeItemSearch extends search_combobox {
|
||||
super.keyHandler(e);
|
||||
// Switch the key presses to handle keyboard nav.
|
||||
switch (e.key) {
|
||||
case 'Tab':
|
||||
if (e.target.closest(this.selectors.input)) {
|
||||
e.preventDefault();
|
||||
this.clearSearchButton.focus({preventScroll: true});
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
if (document.activeElement.getAttribute('role') === 'option') {
|
||||
e.stopPropagation();
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -218,27 +218,6 @@ export default class ColumnSearch extends search_combobox {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The handler for when a user presses a key within the component.
|
||||
*
|
||||
* @param {KeyboardEvent} e The triggering event that we are working with.
|
||||
*/
|
||||
async keyHandler(e) {
|
||||
super.keyHandler(e);
|
||||
|
||||
// Switch the key presses to handle keyboard nav.
|
||||
switch (e.key) {
|
||||
case 'Tab':
|
||||
if (e.target.closest(this.selectors.input)) {
|
||||
if (!this.clearSearchButton.classList.contains('d-none')) {
|
||||
e.preventDefault();
|
||||
this.clearSearchButton.focus({preventScroll: true});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any keyboard inputs.
|
||||
*/
|
||||
|
||||
@@ -209,11 +209,7 @@ Feature: Within the grader report, test that we can collapse columns
|
||||
And I wait until "Mobile phone" "option_role" exists
|
||||
And I press the tab key
|
||||
And the focused element is "Clear search input" "button" in the ".dropdown-menu.show" "css_element"
|
||||
And I press the tab key
|
||||
And I press the tab key
|
||||
And I press the tab key
|
||||
And I press the tab key
|
||||
And the focused element is "Close" "button" in the ".dropdown-menu.show" "css_element"
|
||||
And I press the escape key
|
||||
And I press the tab key
|
||||
# The course grade category menu.
|
||||
And the focused element is "Cell actions" "button"
|
||||
|
||||
@@ -159,10 +159,9 @@ Feature: Within the grader report, test that we can search for users
|
||||
Given I click on "Turtle" in the "user" search widget
|
||||
# The search input remains in the field on reload this is in keeping with other search implementations.
|
||||
When the field "Search users" matches value "Turtle"
|
||||
And I wait until "View all results (1)" "link" does not exist
|
||||
# Test if we can then further retain the turtle result set and further filter from there.
|
||||
Then I set the field "Search users" to "Turtle plagiarism"
|
||||
And "Turtle Manatee" "list_item" should not exist
|
||||
And "Turtle Manatee" "list_item" should not be visible
|
||||
And I should see "No results for \"Turtle plagiarism\""
|
||||
|
||||
Scenario: A teacher can search for values besides the users' name
|
||||
@@ -244,15 +243,15 @@ Feature: Within the grader report, test that we can search for users
|
||||
And the page should meet "wcag131, wcag141, wcag412" accessibility standards
|
||||
And the page should meet accessibility standards with "wcag131, wcag141, wcag412" extra tests
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the end key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And ".active" "css_element" should exist in the "View all results (5)" "option_role"
|
||||
And I press the home key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the up key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And ".active" "css_element" should exist in the "View all results (5)" "option_role"
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the escape key
|
||||
And the focused element is "Search users" "field"
|
||||
Then I set the field "Search users" to "Goodmeme"
|
||||
@@ -263,16 +262,15 @@ Feature: Within the grader report, test that we can search for users
|
||||
And I set the field "Search users" to "ABC"
|
||||
And I wait until "Turtle Manatee" "option_role" exists
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
|
||||
# Lets check the tabbing order.
|
||||
And I set the field "Search users" to "ABC"
|
||||
And I wait until "View all results (5)" "option_role" exists
|
||||
And I click on "Search users" "field"
|
||||
And I wait until "Turtle Manatee" "option_role" exists
|
||||
And I press the tab key
|
||||
And the focused element is "Clear search input" "button"
|
||||
And I press the tab key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And I press the tab key
|
||||
And ".groupsearchwidget" "css_element" should exist
|
||||
# Ensure we can interact with the input & clear search options with the keyboard.
|
||||
# Space & Enter have the same handling for triggering the two functionalities.
|
||||
@@ -289,15 +287,6 @@ Feature: Within the grader report, test that we can search for users
|
||||
| Teacher 1 |
|
||||
| Student 1 |
|
||||
| Turtle Manatee |
|
||||
# Sometimes with behat we get unattached nodes causing spurious failures.
|
||||
And I wait "1" seconds
|
||||
And I set the field "Search users" to "ABC"
|
||||
And I wait until "Turtle Manatee" "option_role" exists
|
||||
And I press the tab key
|
||||
And the focused element is "Clear search input" "button"
|
||||
And I press the enter key
|
||||
And I wait until the page is ready
|
||||
And I confirm "Turtle Manatee" in "user" search within the gradebook widget does not exist
|
||||
|
||||
Scenario: Once a teacher searches, it'll apply the currently set filters and inform the teacher as such
|
||||
# Set up a basic filtering case.
|
||||
|
||||
@@ -128,7 +128,7 @@ Feature: Within the User report, a teacher can search for users.
|
||||
And I wait until "View all results (5)" "link" does not exist
|
||||
# Test if we can then further retain the turtle result set and further filter from there.
|
||||
Then I set the field "Search users" to "Turtle plagiarism"
|
||||
And "Turtle Manatee" "list_item" should not exist
|
||||
And I wait until "Turtle Manatee" "list_item" does not exist
|
||||
And I should see "No results for \"Turtle plagiarism\""
|
||||
|
||||
Scenario: A teacher can search for values besides the users' name
|
||||
@@ -200,21 +200,19 @@ Feature: Within the User report, a teacher can search for users.
|
||||
Scenario: A teacher can set focus and search using the input are with a keyboard
|
||||
Given I set the field "Search users" to "ABC"
|
||||
# Basic tests for the page.
|
||||
And the page should meet accessibility standards
|
||||
And the page should meet "wcag131, wcag141, wcag412" accessibility standards
|
||||
And the page should meet accessibility standards with "wcag131, wcag141, wcag412" extra tests
|
||||
# Move onto general keyboard navigation testing.
|
||||
When "Turtle Manatee" "option_role" should exist
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the end key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And ".active" "css_element" should exist in the "View all results (5)" "option_role"
|
||||
And I press the home key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the up key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And ".active" "css_element" should exist in the "View all results (5)" "option_role"
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
And I press the escape key
|
||||
And the focused element is "Search users" "field"
|
||||
Then I set the field "Search users" to "Goodmeme"
|
||||
@@ -225,16 +223,15 @@ Feature: Within the User report, a teacher can search for users.
|
||||
And I set the field "Search users" to "ABC"
|
||||
And "Turtle Manatee" "option_role" should exist
|
||||
And I press the down key
|
||||
And the focused element is "Student 1" "option_role"
|
||||
And ".active" "css_element" should exist in the "Student 1" "option_role"
|
||||
|
||||
# Lets check the tabbing order.
|
||||
And I set the field "Search users" to "ABC"
|
||||
And "View all results (5)" "option_role" should exist
|
||||
And I wait until "Clear search input" "button" exists
|
||||
And I click on "Search users" "field"
|
||||
And I press the tab key
|
||||
And the focused element is "Clear search input" "button" in the ".user-search" "css_element"
|
||||
And I press the tab key
|
||||
And the focused element is "View all results (5)" "option_role"
|
||||
And I press the tab key
|
||||
And ".groupsearchwidget" "css_element" should exist
|
||||
# Ensure we can interact with the input & clear search options with the keyboard.
|
||||
# Space & Enter have the same handling for triggering the two functionalities.
|
||||
@@ -248,12 +245,3 @@ Feature: Within the User report, a teacher can search for users.
|
||||
And "User Test" "heading" should not exist
|
||||
And "Teacher 1" "heading" should not exist
|
||||
And "Turtle Manatee" "heading" should not exist
|
||||
# Sometimes with behat we get unattached nodes causing spurious failures.
|
||||
And I wait "1" seconds
|
||||
And I set the field "Search users" to "ABC"
|
||||
And "Turtle Manatee" "option_role" should exist
|
||||
And I press the tab key
|
||||
And the focused element is "Clear search input" "button" in the ".user-search" "css_element"
|
||||
And I press the enter key
|
||||
And I wait until the page is ready
|
||||
And I confirm "Turtle Manatee" in "user" search within the gradebook widget does not exist
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}triggerSelector(){return".groupsearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js)}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,link:this.selectOneLink(group.id),groupimageurl:group.groupimageurl}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.dropdown)&&e.stopImmediatePropagation(),this.clearSearchButton.addEventListener("click",(async()=>{this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),e.target.closest(".dropdown-item")&&0===e.button&&(window.location=e.target.closest(".dropdown-item").href)}keyHandler(e){switch(super.keyHandler(e),e.key){case"Tab":e.target.closest(this.selectors.input)&&(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0}));break;case"Escape":if("option"===document.activeElement.getAttribute("role"))e.stopPropagation(),this.searchInput.focus({preventScroll:!0});else if(e.target.closest(this.selectors.input)){this.component.querySelector(this.selectors.trigger).focus({preventScroll:!0})}}}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default}));
|
||||
define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}triggerSelector(){return".groupsearchwidget"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js)}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,link:this.selectOneLink(group.id),groupimageurl:group.groupimageurl}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.dropdown)&&e.stopImmediatePropagation(),this.clearSearchButton.addEventListener("click",(async()=>{this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),e.target.closest(".dropdown-item")&&0===e.button&&(window.location=e.target.closest(".dropdown-item").href)}keyHandler(e){if(super.keyHandler(e),"Escape"===e.key)if("option"===document.activeElement.getAttribute("role"))e.stopPropagation(),this.searchInput.focus({preventScroll:!0});else if(e.target.closest(this.selectors.input)){this.component.querySelector(this.selectors.trigger).focus({preventScroll:!0})}}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=group.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -198,12 +198,6 @@ export default class GroupSearch extends search_combobox {
|
||||
super.keyHandler(e);
|
||||
// Switch the key presses to handle keyboard nav.
|
||||
switch (e.key) {
|
||||
case 'Tab':
|
||||
if (e.target.closest(this.selectors.input)) {
|
||||
e.preventDefault();
|
||||
this.clearSearchButton.focus({preventScroll: true});
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
if (document.activeElement.getAttribute('role') === 'option') {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -142,3 +142,5 @@ gradeitemadvanced,core_grades
|
||||
gradeitemadvanced_help,core_grades
|
||||
to,core
|
||||
from,core
|
||||
aria-toggledropdown,core_grades
|
||||
aria:dropdowngrades,core_grades
|
||||
|
||||
+2
-3
@@ -889,9 +889,6 @@ $string['xml'] = 'XML';
|
||||
$string['yes'] = 'Yes';
|
||||
$string['yourgrade'] = 'Your grade';
|
||||
|
||||
$string['aria-toggledropdown'] = 'Toggle the following dropdown';
|
||||
$string['aria:dropdowngrades'] = 'Grade items found';
|
||||
|
||||
// Deprecated since Moodle 4.0.
|
||||
$string['navmethod'] = 'Navigation method';
|
||||
$string['dropdown'] = 'Drop-down menu';
|
||||
@@ -924,3 +921,5 @@ $string['studentsperpage_help'] = 'This setting determines the number of student
|
||||
$string['grade'] = 'Grade';
|
||||
$string['gradeitemadvanced'] = 'Advanced grade item options';
|
||||
$string['gradeitemadvanced_help'] = 'Select all elements that should be displayed as advanced when editing grade items.';
|
||||
$string['aria-toggledropdown'] = 'Toggle the following dropdown';
|
||||
$string['aria:dropdowngrades'] = 'Grade items found';
|
||||
|
||||
@@ -159,6 +159,9 @@ $string['applyfilters'] = 'Apply filters';
|
||||
$string['areyousure'] = 'Are you sure?';
|
||||
$string['areyousuretorestorethis'] = 'Do you want to continue?';
|
||||
$string['areyousuretorestorethisinfo'] = 'Later in this process you will have a choice of adding this backup to an existing course or creating a completely new course.';
|
||||
$string['aria-toggledropdown'] = 'Toggle the following dropdown';
|
||||
$string['aria-comboboxsearchdialogue'] = 'Search or select an option';
|
||||
$string['aria-comboboxsearchitems'] = 'Select an option';
|
||||
$string['asc'] = 'Ascending';
|
||||
$string['assessment'] = 'Assessment';
|
||||
$string['assignadmins'] = 'Assign admins';
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -17,6 +17,7 @@ import $ from 'jquery';
|
||||
import CustomEvents from "core/custom_interaction_events";
|
||||
import {debounce} from 'core/utils';
|
||||
import Pending from 'core/pending';
|
||||
import * as FocusLockManager from 'core/local/aria/focuslock';
|
||||
|
||||
/**
|
||||
* The class that manages the state of the search within a combobox.
|
||||
@@ -85,6 +86,14 @@ export default class {
|
||||
if (this.searchInput !== null) {
|
||||
this.registerInputHandlers();
|
||||
}
|
||||
|
||||
// Handle the dropdown opening and closing trap focus.
|
||||
this.$component.on('shown.bs.dropdown', () => {
|
||||
FocusLockManager.trapFocus(this.component);
|
||||
});
|
||||
this.$component.on('hidden.bs.dropdown', () => {
|
||||
FocusLockManager.untrapFocus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,9 +237,9 @@ export default class {
|
||||
*/
|
||||
closeSearch(clear = false) {
|
||||
this.toggleDropdown();
|
||||
// Hide the "clear" search button search bar.
|
||||
this.clearSearchButton.classList.add('d-none');
|
||||
if (clear) {
|
||||
// Hide the "clear" search button search bar.
|
||||
this.clearSearchButton.classList.add('d-none');
|
||||
// Clear the entered search query in the search bar and hide the search results container.
|
||||
this.setSearchTerms('');
|
||||
this.searchInput.value = "";
|
||||
@@ -442,12 +451,6 @@ export default class {
|
||||
e.preventDefault();
|
||||
this.moveToLastNode();
|
||||
break;
|
||||
case 'Tab':
|
||||
// If the current focus is on the view all link, then close the widget then set focus on the next tertiary nav item.
|
||||
if (e.target.closest(this.selectors.viewall)) {
|
||||
this.closeSearch();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
|
||||
'group_message' => 'group_message',
|
||||
'autocomplete' => 'autocomplete',
|
||||
'iframe' => 'iframe',
|
||||
'option_role' => 'option_role',
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,12 +46,6 @@
|
||||
<div class="{{#parentclasses}}{{.}}{{/parentclasses}}"
|
||||
{{^usebutton}}
|
||||
tabindex="-1"
|
||||
data-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
role="combobox"
|
||||
aria-haspopup="dialog"
|
||||
aria-controls="dialog-{{instance}}-{{uniqid}}"
|
||||
aria-label="{{#cleanstr}} aria-toggledropdown, core_grades {{/cleanstr}}"
|
||||
data-input-element="input-{{uniqid}}"
|
||||
{{/usebutton}}>
|
||||
|
||||
@@ -63,7 +57,7 @@
|
||||
aria-haspopup="dialog"
|
||||
aria-controls="dialog-{{instance}}-{{uniqid}}"
|
||||
class="{{#buttonclasses}}{{.}}{{/buttonclasses}} btn dropdown-toggle keep-open d-flex text-left align-items-center p-0 font-weight-bold"
|
||||
aria-label="{{#cleanstr}} aria-toggledropdown, core_grades {{/cleanstr}}"
|
||||
aria-label="{{#cleanstr}} aria-toggledropdown, core {{/cleanstr}}"
|
||||
data-input-element="input-{{uniqid}}">
|
||||
{{{buttoncontent}}}
|
||||
</div>
|
||||
@@ -72,10 +66,12 @@
|
||||
{{^usebutton}}{{{buttoncontent}}}{{/usebutton}}
|
||||
|
||||
<div class="{{#dropdownclasses}}{{.}}{{/dropdownclasses}} dropdown-menu {{#rtl}}dropdown-menu-right{{/rtl}}"
|
||||
id="dialog-{{instance}}-{{uniqid}}"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="{{#cleanstr}} selectagroup, core {{/cleanstr}}"
|
||||
id="dialog-{{instance}}-{{uniqid}}"
|
||||
{{#usebutton}}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="{{#cleanstr}} aria-comboboxsearchdialogue, core {{/cleanstr}}"
|
||||
{{/usebutton}}
|
||||
>
|
||||
<div class="w-100 p-3" data-region="placeholder">
|
||||
{{#renderlater}}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
}}
|
||||
<div class="d-flex flex-column mh-100 h-100">
|
||||
{{#hasresults}}
|
||||
<ul id="{{$listid}}list{{/listid}}-result-listbox" class="searchresultitemscontainer d-flex flex-column mw-100 position-relative py-2 list-group h-100 mx-0 {{$listclasses}}{{/listclasses}}" role="listbox" data-region="search-result-items-container" tabindex="-1" aria-label="{{#cleanstr}} aria:dropdowngrades, core_grades {{/cleanstr}}">
|
||||
<ul id="{{$listid}}list{{/listid}}-result-listbox" class="searchresultitemscontainer d-flex flex-column mw-100 position-relative py-2 list-group h-100 mx-0 {{$listclasses}}{{/listclasses}}" role="listbox" data-region="search-result-items-container" tabindex="-1" aria-label="{{#cleanstr}} aria-comboboxsearchitems, core {{/cleanstr}}">
|
||||
{{$results}}
|
||||
{{#results}}
|
||||
{{>core/local/comboboxsearch/resultitem}}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -54,6 +54,9 @@ export default class UserSearch extends search_combobox {
|
||||
const component = document.querySelector(this.componentSelector());
|
||||
this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;
|
||||
this.groupID = document.querySelector(this.selectors.groupid)?.dataset?.groupid;
|
||||
|
||||
// We need to render some content by default for ARIA purposes.
|
||||
this.renderDefault();
|
||||
}
|
||||
|
||||
static init() {
|
||||
@@ -101,6 +104,23 @@ export default class UserSearch extends search_combobox {
|
||||
replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the content then replace the node by default we want our form to exist.
|
||||
*/
|
||||
async renderDefault() {
|
||||
this.setMatchedResults(await this.filterDataset(await this.getDataset()));
|
||||
this.filterMatchDataset();
|
||||
|
||||
const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {
|
||||
users: this.getMatchedResults().slice(0, 5),
|
||||
hasresults: this.getMatchedResults().length > 0,
|
||||
matches: this.getMatchedResults().length,
|
||||
searchterm: this.getSearchTerm(),
|
||||
selectall: this.selectAllResultsLink(),
|
||||
});
|
||||
replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data we will be searching against in this component.
|
||||
*
|
||||
@@ -193,7 +213,7 @@ export default class UserSearch extends search_combobox {
|
||||
* @param {KeyboardEvent} e The triggering event that we are working with.
|
||||
*/
|
||||
keyHandler(e) {
|
||||
super.keyHandler(e);
|
||||
// We don't call the super here because we want to let aria.js handle the key presses mostly.
|
||||
|
||||
if (e.target === this.getHTMLElements().currentViewAll && (e.key === 'Enter' || e.key === 'Space')) {
|
||||
window.location = this.selectAllResultsLink();
|
||||
@@ -203,6 +223,7 @@ export default class UserSearch extends search_combobox {
|
||||
switch (e.key) {
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
e.stopPropagation();
|
||||
if (document.activeElement === this.getHTMLElements().searchInput) {
|
||||
if (e.key === 'Enter' && this.selectAllResultsLink() !== null) {
|
||||
window.location = this.selectAllResultsLink();
|
||||
@@ -230,9 +251,6 @@ export default class UserSearch extends search_combobox {
|
||||
// If the current focus is on clear search, then check if viewall exists then around tab to it.
|
||||
if (e.target.closest(this.selectors.clearSearch)) {
|
||||
if (this.currentViewAll && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
this.currentViewAll.focus({preventScroll: true});
|
||||
} else {
|
||||
this.closeSearch();
|
||||
}
|
||||
}
|
||||
@@ -249,11 +267,11 @@ export default class UserSearch extends search_combobox {
|
||||
if (on) {
|
||||
this.searchDropdown.classList.add('show');
|
||||
$(this.searchDropdown).show();
|
||||
this.component.setAttribute('aria-expanded', 'true');
|
||||
this.getHTMLElements().searchInput.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
this.searchDropdown.classList.remove('show');
|
||||
$(this.searchDropdown).hide();
|
||||
this.component.setAttribute('aria-expanded', 'false');
|
||||
this.getHTMLElements().searchInput.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,15 @@
|
||||
<span class="d-none" data-region="groupid" data-groupid="{{group}}" aria-hidden="true"></span>
|
||||
{{#currentvalue}}
|
||||
{{< core/search_input_auto }}
|
||||
{{$label}}{{#str}}
|
||||
searchusers, core
|
||||
{{/str}}{{/label}}
|
||||
{{$label}}{{#str}}searchusers, core{{/str}}{{/label}}
|
||||
{{$value}}{{currentvalue}}{{/value}}
|
||||
{{$additionalattributes}}
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-controls="user-result-listbox"
|
||||
aria-autocomplete="list"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="{{#cleanstr}} aria-toggledropdown, core {{/cleanstr}}"
|
||||
data-input-element="user-input-{{uniqid}}"
|
||||
{{/additionalattributes}}
|
||||
{{/ core/search_input_auto }}
|
||||
@@ -52,14 +55,15 @@
|
||||
{{/currentvalue}}
|
||||
{{^currentvalue}}
|
||||
{{< core/search_input_auto }}
|
||||
{{$label}}{{#str}}
|
||||
searchusers, core
|
||||
{{/str}}{{/label}}
|
||||
{{$placeholder}}{{#str}}
|
||||
searchusers, core
|
||||
{{/str}}{{/placeholder}}
|
||||
{{$label}}{{#str}}searchusers, core{{/str}}{{/label}}
|
||||
{{$placeholder}}{{#str}}searchusers, core{{/str}}{{/placeholder}}
|
||||
{{$additionalattributes}}
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-controls="user-result-listbox"
|
||||
aria-autocomplete="list"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="{{#cleanstr}} aria-toggledropdown, core {{/cleanstr}}"
|
||||
data-input-element="user-input-{{uniqid}}"
|
||||
{{/additionalattributes}}
|
||||
{{/ core/search_input_auto }}
|
||||
|
||||
Reference in New Issue
Block a user