From d0f8a42e5f88845b8765b8172161f01dc6a761ef Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Thu, 13 Feb 2020 00:35:38 +0800 Subject: [PATCH] MDL-67663 mod_forum: Make user switching more accessible * Add a more descriptive label for the next and previous buttons so that the users would know that clicking on the next or previous buttons will save the changes they made before proceeding to the next user. * Add an sr-only div with aria-live to the user picker to let the grader know the current user that is being graded. * Hide the user picture from screen readers --- .../local/grades/local/grader/user_picker.min.js | 2 +- .../grades/local/grader/user_picker.min.js.map | 2 +- .../local/grader/user_picker/selectors.min.js | 2 +- .../local/grader/user_picker/selectors.min.js.map | 2 +- .../src/local/grades/local/grader/user_picker.js | 5 +++++ .../grades/local/grader/user_picker/selectors.js | 1 + mod/forum/lang/en/forum.php | 5 +++-- .../local/grades/local/grader/user_picker.mustache | 13 +++++++------ .../grades/local/grader/user_picker/user.mustache | 3 +-- 9 files changed, 21 insertions(+), 14 deletions(-) diff --git a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js index 7eb4cbdded0..51468f98379 100644 --- a/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js +++ b/mod/forum/amd/build/local/grades/local/grader/user_picker.min.js @@ -1,2 +1,2 @@ -define ("mod_forum/local/grades/local/grader/user_picker",["exports","core/templates","./user_picker/selectors"],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){for(var b=1;bthis.currentUserIndex){this.currentUserIndex=this.userList.length-1}else if(this.currentUserIndex>this.userList.length-1){this.currentUserIndex=0}return this.currentUserIndex}},{key:"currentUser",get:function get(){return e({},this.userList[this.currentUserIndex],{total:this.userList.length,displayIndex:this.currentUserIndex+1})}},{key:"rootNode",get:function get(){return this.root}}]);return a}(),r=function(){var a=m(regeneratorRuntime.mark(function a(b,c,d){var e,f,g,h,i=arguments;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:e=3this.currentUserIndex){this.currentUserIndex=this.userList.length-1}else if(this.currentUserIndex>this.userList.length-1){this.currentUserIndex=0}return this.currentUserIndex}},{key:"currentUser",get:function get(){return f({},this.userList[this.currentUserIndex],{total:this.userList.length,displayIndex:this.currentUserIndex+1})}},{key:"rootNode",get:function get(){return this.root}}]);return a}(),s=function(){var a=n(regeneratorRuntime.mark(function a(b,c,d){var e,f,g,h,i=arguments;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:e=3.\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/local/grader/user_picker\n * @package mod_forum\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Selectors from './user_picker/selectors';\n\nconst templatePath = 'mod_forum/local/grades/local/grader';\n\nclass UserPicker {\n\n /**\n * Constructor for the User Picker.\n *\n * @param {Array} userList List of users\n * @param {Function} showUserCallback The callback used to display the user\n * @param {Function} preChangeUserCallback The callback to use before changing user\n */\n constructor(userList, showUserCallback, preChangeUserCallback) {\n this.userList = userList;\n this.showUserCallback = showUserCallback;\n this.preChangeUserCallback = preChangeUserCallback;\n this.currentUserIndex = 0;\n\n // Ensure that render is bound correctly.\n this.render = this.render.bind(this);\n this.setUserId = this.setUserId.bind(this);\n }\n\n /**\n * Set the current userid without rendering the change.\n * To show the user, call showUser too.\n *\n * @param {Number} userId\n */\n setUserId(userId) {\n // Determine the current index based on the user ID.\n const userIndex = this.userList.findIndex(user => {\n return user.id === parseInt(userId);\n });\n\n if (userIndex === -1) {\n throw Error(`User with id ${userId} not found`);\n }\n\n this.currentUserIndex = userIndex;\n }\n\n /**\n * Render the user picker.\n */\n async render() {\n // Create the root node.\n this.root = document.createElement('div');\n\n const {html, js} = await this.renderNavigator();\n Templates.replaceNodeContents(this.root, html, js);\n\n // Call the showUser function to show the first user immediately.\n await this.showUser(this.currentUser);\n\n // Ensure that the event listeners are all bound.\n this.registerEventListeners();\n }\n\n /**\n * Render the navigator itself.\n *\n * @returns {Promise}\n */\n renderNavigator() {\n return Templates.renderForPromise(`${templatePath}/user_picker`, {});\n }\n\n /**\n * Render the current user details for the picker.\n *\n * @param {Object} context The data used to render the user picker.\n * @returns {Promise}\n */\n renderUserChange(context) {\n return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);\n }\n\n /**\n * Show the specified user in the picker.\n *\n * @param {Object} user\n */\n async showUser(user) {\n const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);\n const userRegion = this.root.querySelector(Selectors.regions.userRegion);\n Templates.replaceNodeContents(userRegion, html, js);\n }\n\n /**\n * Register the event listeners for the user picker.\n */\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const button = e.target.closest(Selectors.actions.changeUser);\n\n if (button) {\n const result = await this.preChangeUserCallback(this.currentUser);\n\n if (!result.failed) {\n this.updateIndex(parseInt(button.dataset.direction));\n await this.showUser(this.currentUser);\n }\n }\n });\n }\n\n /**\n * Update the current user index.\n *\n * @param {Number} direction\n * @returns {Number}}\n */\n updateIndex(direction) {\n this.currentUserIndex += direction;\n\n // Loop around the edges.\n if (this.currentUserIndex < 0) {\n this.currentUserIndex = this.userList.length - 1;\n } else if (this.currentUserIndex > this.userList.length - 1) {\n this.currentUserIndex = 0;\n }\n\n return this.currentUserIndex;\n }\n\n /**\n * Get the details of the user currently shown with the total number of users, and the 1-indexed count of the\n * current user.\n *\n * @returns {Object}\n */\n get currentUser() {\n return {\n ...this.userList[this.currentUserIndex],\n total: this.userList.length,\n displayIndex: this.currentUserIndex + 1,\n };\n }\n\n /**\n * Get the root node for the User Picker.\n *\n * @returns {HTMLElement}\n */\n get rootNode() {\n return this.root;\n }\n}\n\n/**\n * Create a new user picker.\n *\n * @param {Array} users The list of users\n * @param {Function} showUserCallback The function to call to show a specific user\n * @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user\n * @param {Number} [currentUserID] The userid of the current user\n * @returns {UserPicker}\n */\nexport default async(\n users,\n showUserCallback,\n preChangeUserCallback,\n {\n initialUserId = null,\n } = {}\n) => {\n const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);\n if (initialUserId) {\n userPicker.setUserId(initialUserId);\n }\n await userPicker.render();\n\n return userPicker;\n};\n"],"file":"user_picker.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../../../src/local/grades/local/grader/user_picker.js"],"names":["UserPicker","userList","showUserCallback","preChangeUserCallback","currentUserIndex","render","bind","setUserId","userId","userIndex","findIndex","user","id","parseInt","Error","root","document","createElement","renderNavigator","html","js","Templates","replaceNodeContents","showUser","currentUser","registerEventListeners","renderForPromise","context","Promise","all","renderUserChange","userRegion","querySelector","Selectors","regions","currentUserRegion","fullname","textContent","addEventListener","e","button","target","closest","actions","changeUser","result","failed","updateIndex","dataset","direction","length","total","displayIndex","users","initialUserId","userPicker"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/local/grader/user_picker\n * @package mod_forum\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport Selectors from './user_picker/selectors';\nimport {get_string as getString} from 'core/str';\n\nconst templatePath = 'mod_forum/local/grades/local/grader';\n\nclass UserPicker {\n\n /**\n * Constructor for the User Picker.\n *\n * @param {Array} userList List of users\n * @param {Function} showUserCallback The callback used to display the user\n * @param {Function} preChangeUserCallback The callback to use before changing user\n */\n constructor(userList, showUserCallback, preChangeUserCallback) {\n this.userList = userList;\n this.showUserCallback = showUserCallback;\n this.preChangeUserCallback = preChangeUserCallback;\n this.currentUserIndex = 0;\n\n // Ensure that render is bound correctly.\n this.render = this.render.bind(this);\n this.setUserId = this.setUserId.bind(this);\n }\n\n /**\n * Set the current userid without rendering the change.\n * To show the user, call showUser too.\n *\n * @param {Number} userId\n */\n setUserId(userId) {\n // Determine the current index based on the user ID.\n const userIndex = this.userList.findIndex(user => {\n return user.id === parseInt(userId);\n });\n\n if (userIndex === -1) {\n throw Error(`User with id ${userId} not found`);\n }\n\n this.currentUserIndex = userIndex;\n }\n\n /**\n * Render the user picker.\n */\n async render() {\n // Create the root node.\n this.root = document.createElement('div');\n\n const {html, js} = await this.renderNavigator();\n Templates.replaceNodeContents(this.root, html, js);\n\n // Call the showUser function to show the first user immediately.\n await this.showUser(this.currentUser);\n\n // Ensure that the event listeners are all bound.\n this.registerEventListeners();\n }\n\n /**\n * Render the navigator itself.\n *\n * @returns {Promise}\n */\n renderNavigator() {\n return Templates.renderForPromise(`${templatePath}/user_picker`, {});\n }\n\n /**\n * Render the current user details for the picker.\n *\n * @param {Object} context The data used to render the user picker.\n * @returns {Promise}\n */\n renderUserChange(context) {\n return Templates.renderForPromise(`${templatePath}/user_picker/user`, context);\n }\n\n /**\n * Show the specified user in the picker.\n *\n * @param {Object} user\n */\n async showUser(user) {\n const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]);\n const userRegion = this.root.querySelector(Selectors.regions.userRegion);\n Templates.replaceNodeContents(userRegion, html, js);\n\n // Update the hidden now-grading region so screen readers can announce the user that's currently being graded.\n const currentUserRegion = this.root.querySelector(Selectors.regions.currentUser);\n currentUserRegion.textContent = await getString('nowgradinguser', 'mod_forum', user.fullname);\n }\n\n /**\n * Register the event listeners for the user picker.\n */\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const button = e.target.closest(Selectors.actions.changeUser);\n\n if (button) {\n const result = await this.preChangeUserCallback(this.currentUser);\n\n if (!result.failed) {\n this.updateIndex(parseInt(button.dataset.direction));\n await this.showUser(this.currentUser);\n }\n }\n });\n }\n\n /**\n * Update the current user index.\n *\n * @param {Number} direction\n * @returns {Number}}\n */\n updateIndex(direction) {\n this.currentUserIndex += direction;\n\n // Loop around the edges.\n if (this.currentUserIndex < 0) {\n this.currentUserIndex = this.userList.length - 1;\n } else if (this.currentUserIndex > this.userList.length - 1) {\n this.currentUserIndex = 0;\n }\n\n return this.currentUserIndex;\n }\n\n /**\n * Get the details of the user currently shown with the total number of users, and the 1-indexed count of the\n * current user.\n *\n * @returns {Object}\n */\n get currentUser() {\n return {\n ...this.userList[this.currentUserIndex],\n total: this.userList.length,\n displayIndex: this.currentUserIndex + 1,\n };\n }\n\n /**\n * Get the root node for the User Picker.\n *\n * @returns {HTMLElement}\n */\n get rootNode() {\n return this.root;\n }\n}\n\n/**\n * Create a new user picker.\n *\n * @param {Array} users The list of users\n * @param {Function} showUserCallback The function to call to show a specific user\n * @param {Function} preChangeUserCallback The fucntion to call to save the grade for the current user\n * @param {Number} [currentUserID] The userid of the current user\n * @returns {UserPicker}\n */\nexport default async(\n users,\n showUserCallback,\n preChangeUserCallback,\n {\n initialUserId = null,\n } = {}\n) => {\n const userPicker = new UserPicker(users, showUserCallback, preChangeUserCallback);\n if (initialUserId) {\n userPicker.setUserId(initialUserId);\n }\n await userPicker.render();\n\n return userPicker;\n};\n"],"file":"user_picker.min.js"} \ No newline at end of file diff --git a/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js b/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js index 99f20c9bf41..df7e5337a90 100644 --- a/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js +++ b/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js @@ -1,2 +1,2 @@ -define ("mod_forum/local/grades/local/grader/user_picker/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={regions:{userRegion:"[data-region=\"user_picker/user\"]"},actions:{changeUser:"[data-action=\"change-user\"]"}};return a.default}); +define ("mod_forum/local/grades/local/grader/user_picker/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={regions:{currentUser:"[data-region=\"user_picker/current_user\"]",userRegion:"[data-region=\"user_picker/user\"]"},actions:{changeUser:"[data-action=\"change-user\"]"}};return a.default}); //# sourceMappingURL=selectors.min.js.map diff --git a/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js.map b/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js.map index 9c9dddcec17..da9f5c8bccc 100644 --- a/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js.map +++ b/mod/forum/amd/build/local/grades/local/grader/user_picker/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../../../../../src/local/grades/local/grader/user_picker/selectors.js"],"names":["regions","userRegion","actions","changeUser"],"mappings":"qLAwBe,CACXA,OAAO,CAAE,CACLC,UAAU,CAAE,oCADP,CADE,CAIXC,OAAO,CAAE,CACLC,UAAU,CAAE,+BADP,CAJE,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Define all of the selectors we will be using on the grading interface.\n *\n * @module mod_forum/local/grades/local/grader/user_picker/selectors\n * @package mod_forum\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n regions: {\n userRegion: '[data-region=\"user_picker/user\"]',\n },\n actions: {\n changeUser: '[data-action=\"change-user\"]',\n }\n};\n\n"],"file":"selectors.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../../../../src/local/grades/local/grader/user_picker/selectors.js"],"names":["regions","currentUser","userRegion","actions","changeUser"],"mappings":"qLAwBe,CACXA,OAAO,CAAE,CACLC,WAAW,CAAE,4CADR,CAELC,UAAU,CAAE,oCAFP,CADE,CAKXC,OAAO,CAAE,CACLC,UAAU,CAAE,+BADP,CALE,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Define all of the selectors we will be using on the grading interface.\n *\n * @module mod_forum/local/grades/local/grader/user_picker/selectors\n * @package mod_forum\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n regions: {\n currentUser: '[data-region=\"user_picker/current_user\"]',\n userRegion: '[data-region=\"user_picker/user\"]',\n },\n actions: {\n changeUser: '[data-action=\"change-user\"]',\n }\n};\n\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/mod/forum/amd/src/local/grades/local/grader/user_picker.js b/mod/forum/amd/src/local/grades/local/grader/user_picker.js index e0946748d36..ba209a43e41 100644 --- a/mod/forum/amd/src/local/grades/local/grader/user_picker.js +++ b/mod/forum/amd/src/local/grades/local/grader/user_picker.js @@ -24,6 +24,7 @@ import Templates from 'core/templates'; import Selectors from './user_picker/selectors'; +import {get_string as getString} from 'core/str'; const templatePath = 'mod_forum/local/grades/local/grader'; @@ -111,6 +112,10 @@ class UserPicker { const [{html, js}] = await Promise.all([this.renderUserChange(user), this.showUserCallback(user)]); const userRegion = this.root.querySelector(Selectors.regions.userRegion); Templates.replaceNodeContents(userRegion, html, js); + + // Update the hidden now-grading region so screen readers can announce the user that's currently being graded. + const currentUserRegion = this.root.querySelector(Selectors.regions.currentUser); + currentUserRegion.textContent = await getString('nowgradinguser', 'mod_forum', user.fullname); } /** diff --git a/mod/forum/amd/src/local/grades/local/grader/user_picker/selectors.js b/mod/forum/amd/src/local/grades/local/grader/user_picker/selectors.js index 035240f0297..0ef84059c69 100644 --- a/mod/forum/amd/src/local/grades/local/grader/user_picker/selectors.js +++ b/mod/forum/amd/src/local/grades/local/grader/user_picker/selectors.js @@ -24,6 +24,7 @@ export default { regions: { + currentUser: '[data-region="user_picker/current_user"]', userRegion: '[data-region="user_picker/user"]', }, actions: { diff --git a/mod/forum/lang/en/forum.php b/mod/forum/lang/en/forum.php index ae4692acca1..bd5bf8edf89 100644 --- a/mod/forum/lang/en/forum.php +++ b/mod/forum/lang/en/forum.php @@ -443,7 +443,7 @@ $string['namenews_help'] = 'The course announcements forum is a special forum fo $string['namesocial'] = 'Social forum'; $string['nameteacher'] = 'Teacher forum'; $string['nextdiscussiona'] = 'Next discussion: {$a}'; -$string['nextuser'] = 'Next user'; +$string['nextuser'] = 'Save changes and proceed to the next user'; $string['newforumposts'] = 'New forum posts'; $string['noattachments'] = 'There are no attachments to this post'; $string['nodiscussions'] = 'There are no discussion topics yet in this forum'; @@ -522,7 +522,7 @@ $string['poststo'] = 'Posts to'; $string['posttoforum'] = 'Post to forum'; $string['postupdated'] = 'Your post was updated'; $string['potentialsubscribers'] = 'Potential subscribers'; -$string['previoususer'] = 'Previous user'; +$string['previoususer'] = 'Save changes and proceed to the previous user'; $string['privacy:digesttypenone'] = 'We do not hold any data relating to a preferred forum digest type for this forum.'; $string['privacy:digesttypepreference'] = 'You have chosen to receive the following forum digest type: "{$a->type}".'; $string['privacy:discussionsubscriptionpreference'] = 'You have chosen the following discussion subscription preference for this forum: "{$a->preference}"'; @@ -750,6 +750,7 @@ $string['gradeusers'] = 'Grade users'; $string['graded'] = 'Graded'; $string['gradedby'] = 'Graded by'; $string['notgraded'] = 'Not graded'; +$string['nowgradinguser'] = 'Now grading {$a}'; $string['gradeforrating'] = 'Grade for rating: {$a->str_long_grade}'; $string['gradeforratinghidden'] = 'Grade for rating hidden'; $string['gradeforwholeforum'] = 'Grade for forum: {$a->str_long_grade}'; diff --git a/mod/forum/templates/local/grades/local/grader/user_picker.mustache b/mod/forum/templates/local/grades/local/grader/user_picker.mustache index d81753c1f03..43b53868e7b 100644 --- a/mod/forum/templates/local/grades/local/grader/user_picker.mustache +++ b/mod/forum/templates/local/grades/local/grader/user_picker.mustache @@ -36,35 +36,36 @@ }}
+
diff --git a/mod/forum/templates/local/grades/local/grader/user_picker/user.mustache b/mod/forum/templates/local/grades/local/grader/user_picker/user.mustache index 144f2e9d810..ef04cfe2734 100644 --- a/mod/forum/templates/local/grades/local/grader/user_picker/user.mustache +++ b/mod/forum/templates/local/grades/local/grader/user_picker/user.mustache @@ -42,8 +42,7 @@ {{/profileimage}}