Merge branch 'MDL-75837-master-4' of https://github.com/mihailges/moodle
This commit is contained in:
+3
-3
@@ -1,10 +1,10 @@
|
||||
define("core_grades/searchwidget/basewidget",["exports","core/modal_factory","core/modal_events","core/utils","core/templates","core/aria"],(function(_exports,ModalFactory,ModalEvents,_utils,Templates,_aria){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
define("core_grades/searchwidget/basewidget",["exports","core/utils","core/templates","core_grades/searchwidget/selectors","core/notification"],(function(_exports,_utils,Templates,Selectors,_notification){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
/**
|
||||
* A small modal to search users or grade items within the gradebook.
|
||||
* A widget to search users or grade items within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/basewidget
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.promisesAndResolvers=_exports.init=void 0,ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),Templates=_interopRequireWildcard(Templates);_exports.init=function(bodyPromise,data,searchFunc,modalTitle){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;const modal=buildModal(bodyPromise,modalTitle);registerListenerEvents(modal,data,searchFunc,unsearchableContent)};const registerListenerEvents=(modal,data,searchFunc,unsearchableContent)=>{modal.then((modal=>{modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal.getBodyPromise().then((body=>body[0])).then((body=>{const searchInput=body.querySelector('input[data-action="search"]'),searchResultsContainer=body.querySelector('[data-region="search-results-container-widget"]');if(renderSearchResults(searchResultsContainer,data),unsearchableContent){body.querySelector('[data-region="unsearchable-content-container-widget"]').innerHTML+=unsearchableContent}return searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),(0,_aria.comboBox)(searchInput),body})).catch()})).catch()},buildModal=(bodyPromise,modalTitle)=>ModalFactory.create({type:ModalFactory.types.DEFAULT,title:modalTitle,body:bodyPromise,small:!0,scrollable:!1,templateContext:{classes:"reportdatasearch modal-sm"}}).then((modal=>(modal.show(),modal))),debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showLoader=_exports.registerListenerEvents=_exports.promisesAndResolvers=_exports.init=void 0,Templates=_interopRequireWildcard(Templates),Selectors=_interopRequireWildcard(Selectors),_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};_exports.init=async function(widgetContentContainer,bodyPromise,data,searchFunc){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;bodyPromise.then((async bodyContent=>{if(widgetContentContainer.innerHTML=bodyContent,unsearchableContent){widgetContentContainer.querySelector(Selectors.regions.unsearchableContent).innerHTML+=unsearchableContent}const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults);await showLoader(searchResultsContainer),await renderSearchResults(searchResultsContainer,data),registerListenerEvents(widgetContentContainer,data,searchFunc)})).catch(_notification.default.exception)};const registerListenerEvents=(widgetContentContainer,data,searchFunc)=>{const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults),searchInput=widgetContentContainer.querySelector(Selectors.actions.search);searchInput.focus();const clearSearchButton=widgetContentContainer.querySelector(Selectors.actions.clearSearch);searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{searchInput.value.length>0?clearSearchButton.classList.remove("d-none"):clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),clearSearchButton.addEventListener("click",(async e=>{e.stopPropagation(),searchInput.value="",searchInput.focus(),clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}))};_exports.registerListenerEvents=registerListenerEvents;const showLoader=async container=>{const{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/loading",{});Templates.replaceNodeContents(container,html,js)};_exports.showLoader=showLoader;const debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}}));
|
||||
|
||||
//# sourceMappingURL=basewidget.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+3
-3
@@ -1,10 +1,10 @@
|
||||
define("core_grades/searchwidget/group",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("core_grades/searchwidget/group",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* A small modal to search groups within the gradebook.
|
||||
* A widget to search groups within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/group
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase);let registered=!1;_exports.init=()=>{if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve(),registered=!0};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".groupwidget");if(trigger){const courseID=trigger.dataset.courseid,actionBaseUrl=trigger.dataset.actionBaseUrl;e.preventDefault();const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;WidgetBase.init(bodyPromise,data.groups,searchGroups(),(0,_str.get_string)("selectagroup","core"))}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[]))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let registered=!1;_exports.init=()=>{if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve(),registered=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,actionBaseUrl=e.relatedTarget.dataset.actionBaseUrl;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups())})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults}}));
|
||||
|
||||
//# sourceMappingURL=group.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
define("core_grades/searchwidget/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={regions:{searchResults:'[data-region="search-results-container-widget"]',unsearchableContent:'[data-region="unsearchable-content-container-widget"]'},actions:{search:'[data-action="search"]',clearSearch:'[data-action="clearsearch"]'},elements:{getSearchWidgetSelector:searchtype=>'.search-widget[data-searchtype="'.concat(searchtype,'"]'),getSearchWidgetDropdownSelector:searchtype=>'.search-widget[data-searchtype="'.concat(searchtype,'"] .dropdown-menu')}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=selectors.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"selectors.min.js","sources":["../../src/searchwidget/selectors.js"],"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 <http://www.gnu.org/licenses/>.\n\n/**\n * Selectors for the search widget.\n *\n * @module core_grades/searchwidget/selectors\n * @copyright 2022 Mihail Geshoski <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n regions: {\n searchResults: '[data-region=\"search-results-container-widget\"]',\n unsearchableContent: '[data-region=\"unsearchable-content-container-widget\"]',\n },\n actions: {\n search: '[data-action=\"search\"]',\n clearSearch: '[data-action=\"clearsearch\"]',\n },\n elements: {\n getSearchWidgetSelector: searchtype => `.search-widget[data-searchtype=\"${searchtype}\"]`,\n getSearchWidgetDropdownSelector: searchtype => `.search-widget[data-searchtype=\"${searchtype}\"] .dropdown-menu`,\n },\n};\n"],"names":["regions","searchResults","unsearchableContent","actions","search","clearSearch","elements","getSearchWidgetSelector","searchtype","getSearchWidgetDropdownSelector"],"mappings":"oLAuBe,CACXA,QAAS,CACLC,cAAe,kDACfC,oBAAqB,yDAEzBC,QAAS,CACLC,OAAQ,yBACRC,YAAa,+BAEjBC,SAAU,CACNC,wBAAyBC,sDAAiDA,iBAC1EC,gCAAiCD,sDAAiDA"}
|
||||
@@ -14,105 +14,112 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A small modal to search users or grade items within the gradebook.
|
||||
* A widget to search users or grade items within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/basewidget
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import * as ModalFactory from 'core/modal_factory';
|
||||
import * as ModalEvents from 'core/modal_events';
|
||||
import {debounce} from 'core/utils';
|
||||
import * as Templates from 'core/templates';
|
||||
import {comboBox} from 'core/aria';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
import Notification from 'core/notification';
|
||||
|
||||
/**
|
||||
* Build the base searching widget.
|
||||
*
|
||||
* @method init
|
||||
* @param {Promise} bodyPromise The promise from the callee of the contents to place in the modal body.
|
||||
* @param {HTMLElement} widgetContentContainer The selector for the widget container element.
|
||||
* @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container.
|
||||
* @param {Array} data An array of all the data generated by the callee.
|
||||
* @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.
|
||||
* @param {string} modalTitle The name of the search widget.
|
||||
* @param {string|null} unsearchableContent The content rendered in a non-searchable area.
|
||||
*/
|
||||
export const init = (bodyPromise, data, searchFunc, modalTitle, unsearchableContent = null) => {
|
||||
const modal = buildModal(bodyPromise, modalTitle);
|
||||
registerListenerEvents(modal, data, searchFunc, unsearchableContent);
|
||||
export const init = async(widgetContentContainer, bodyPromise, data, searchFunc, unsearchableContent = null) => {
|
||||
bodyPromise.then(async(bodyContent) => {
|
||||
// Render the body content.
|
||||
widgetContentContainer.innerHTML = bodyContent;
|
||||
|
||||
// Render the unsearchable content if defined.
|
||||
if (unsearchableContent) {
|
||||
const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent);
|
||||
unsearchableContentContainer.innerHTML += unsearchableContent;
|
||||
}
|
||||
|
||||
const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);
|
||||
// Display a loader until the search results are rendered.
|
||||
await showLoader(searchResultsContainer);
|
||||
// Render the search results.
|
||||
await renderSearchResults(searchResultsContainer, data);
|
||||
|
||||
registerListenerEvents(widgetContentContainer, data, searchFunc);
|
||||
|
||||
}).catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register chooser related event listeners.
|
||||
* Register the event listeners for the search widget.
|
||||
*
|
||||
* @method registerListenerEvents
|
||||
* @param {Promise} modal Our modal that we are working with.
|
||||
* @param {HTMLElement} widgetContentContainer The selector for the widget container element.
|
||||
* @param {Array} data An array of all the data generated by the callee.
|
||||
* @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.
|
||||
* @param {string|null} unsearchableContent The content rendered in a non-searchable area.
|
||||
*/
|
||||
const registerListenerEvents = (modal, data, searchFunc, unsearchableContent) => {
|
||||
modal.then(modal => {
|
||||
// We want to destroy this when the dialog is closed.
|
||||
modal.getRoot().on(ModalEvents.hidden, () => {
|
||||
modal.destroy();
|
||||
});
|
||||
// Once the body of the modal has been resolved, add more features.
|
||||
modal.getBodyPromise()
|
||||
// The return value of getBodyPromise is a jquery object containing the body NodeElement.
|
||||
.then(body => body[0])
|
||||
.then(body => {
|
||||
const searchInput = body.querySelector('input[data-action="search"]');
|
||||
const searchResultsContainer = body.querySelector('[data-region="search-results-container-widget"]');
|
||||
export const registerListenerEvents = (widgetContentContainer, data, searchFunc) => {
|
||||
const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);
|
||||
const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);
|
||||
// We want to focus on the first known user interable element within the dropdown.
|
||||
searchInput.focus();
|
||||
const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch);
|
||||
|
||||
renderSearchResults(searchResultsContainer, data);
|
||||
// The search input is triggered.
|
||||
searchInput.addEventListener('input', debounce(async() => {
|
||||
// If search query is present display the 'clear search' button, otherwise hide it.
|
||||
if (searchInput.value.length > 0) {
|
||||
clearSearchButton.classList.remove('d-none');
|
||||
} else {
|
||||
clearSearchButton.classList.add('d-none');
|
||||
}
|
||||
// Display the search results.
|
||||
await renderSearchResults(
|
||||
searchResultsContainer,
|
||||
debounceCallee(
|
||||
searchInput.value,
|
||||
data,
|
||||
searchFunc()
|
||||
)
|
||||
);
|
||||
}, 300));
|
||||
|
||||
if (unsearchableContent) {
|
||||
const unsearchableContentContainer = body.querySelector(
|
||||
'[data-region="unsearchable-content-container-widget"]');
|
||||
unsearchableContentContainer.innerHTML += unsearchableContent;
|
||||
}
|
||||
// Clear search is triggered.
|
||||
clearSearchButton.addEventListener('click', async(e) => {
|
||||
e.stopPropagation();
|
||||
// Clear the entered search query in the search bar.
|
||||
searchInput.value = "";
|
||||
searchInput.focus();
|
||||
clearSearchButton.classList.add('d-none');
|
||||
|
||||
// The search input is triggered.
|
||||
searchInput.addEventListener('input', debounce(async() => {
|
||||
// Display the search results.
|
||||
await renderSearchResults(
|
||||
searchResultsContainer,
|
||||
debounceCallee(
|
||||
searchInput.value,
|
||||
data,
|
||||
searchFunc()
|
||||
)
|
||||
);
|
||||
}, 300));
|
||||
// Trigger event handling for the results in line with aria guidelines.
|
||||
comboBox(searchInput);
|
||||
return body;
|
||||
}).catch();
|
||||
}).catch();
|
||||
// Display all results.
|
||||
await renderSearchResults(
|
||||
searchResultsContainer,
|
||||
debounceCallee(
|
||||
searchInput.value,
|
||||
data,
|
||||
searchFunc()
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an object we want to build a modal ready to show.
|
||||
* Renders the loading placeholder for the search widget.
|
||||
*
|
||||
* @method buildModal
|
||||
* @param {Promise} bodyPromise Body promise that the caller should resolve.
|
||||
* @param {string} modalTitle The name of the search widget.
|
||||
* @return {Object} The modal ready to display immediately and render body in later.
|
||||
* @method showLoader
|
||||
* @param {HTMLElement} container The DOM node where we'll render the loading placeholder.
|
||||
*/
|
||||
const buildModal = (bodyPromise, modalTitle) => {
|
||||
return ModalFactory.create({
|
||||
type: ModalFactory.types.DEFAULT,
|
||||
title: modalTitle,
|
||||
body: bodyPromise,
|
||||
small: true,
|
||||
scrollable: false,
|
||||
templateContext: {
|
||||
classes: 'reportdatasearch modal-sm'
|
||||
}
|
||||
}).then(modal => {
|
||||
modal.show();
|
||||
return modal;
|
||||
});
|
||||
export const showLoader = async(container) => {
|
||||
const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {});
|
||||
Templates.replaceNodeContents(container, html, js);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -133,7 +140,7 @@ const debounceCallee = (searchValue, data, searchFunction) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Given the output of the callers' search function, render out the results into the modal.
|
||||
* Given the output of the callers' search function, render out the results into the search results container.
|
||||
*
|
||||
* @method renderSearchResults
|
||||
* @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.
|
||||
@@ -149,14 +156,14 @@ const renderSearchResults = async(searchResultsContainer, searchResultsData) =>
|
||||
};
|
||||
|
||||
/**
|
||||
* We want to create the basic promises and hooks that the caller will implement, so we can build modals
|
||||
* We want to create the basic promises and hooks that the caller will implement, so we can build the search widget
|
||||
* ahead of time and allow the caller to resolve their promises once complete.
|
||||
*
|
||||
* @method promisesAndResolvers
|
||||
* @returns {{bodyPromise: Promise, bodyPromiseResolver}}
|
||||
*/
|
||||
export const promisesAndResolvers = () => {
|
||||
// We want to show the modal instantly but loading whilst waiting for our data.
|
||||
// We want to show the widget instantly but loading whilst waiting for our data.
|
||||
let bodyPromiseResolver;
|
||||
const bodyPromise = new Promise(resolve => {
|
||||
bodyPromiseResolver = resolve;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A small modal to search groups within the gradebook.
|
||||
* A widget to search groups within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/group
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
import Pending from 'core/pending';
|
||||
import * as Templates from 'core/templates';
|
||||
import CustomEvents from "core/custom_interaction_events";
|
||||
import * as Repository from 'core_grades/searchwidget/repository';
|
||||
import * as WidgetBase from 'core_grades/searchwidget/basewidget';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import $ from 'jquery';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
|
||||
/**
|
||||
* Whether the event listener has already been registered for this module.
|
||||
@@ -58,51 +58,48 @@ export const init = () => {
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
const events = [
|
||||
'click',
|
||||
CustomEvents.events.activate,
|
||||
CustomEvents.events.keyboardActivate
|
||||
];
|
||||
CustomEvents.define(document, events);
|
||||
|
||||
let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();
|
||||
const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group'));
|
||||
|
||||
// Register events.
|
||||
events.forEach((event) => {
|
||||
document.addEventListener(event, async(e) => {
|
||||
const trigger = e.target.closest('.groupwidget');
|
||||
if (trigger) {
|
||||
const courseID = trigger.dataset.courseid;
|
||||
const actionBaseUrl = trigger.dataset.actionBaseUrl;
|
||||
e.preventDefault();
|
||||
// Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('group')).on('show.bs.dropdown', async(e) => {
|
||||
const courseID = e.relatedTarget.dataset.courseid;
|
||||
const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl;
|
||||
// Display a loading icon in the dropdown menu container until the body promise is resolved.
|
||||
await WidgetBase.showLoader(dropdownMenuContainer);
|
||||
|
||||
// If an error occurs while fetching the data, display the error within the modal.
|
||||
const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
WidgetBase.init(
|
||||
bodyPromise,
|
||||
data.groups,
|
||||
searchGroups(),
|
||||
getString('selectagroup', 'core')
|
||||
);
|
||||
}
|
||||
// If an error occurs while fetching the data, display the error within the dropdown menu.
|
||||
const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
await WidgetBase.init(
|
||||
dropdownMenuContainer,
|
||||
bodyPromise,
|
||||
data.groups,
|
||||
searchGroups(),
|
||||
);
|
||||
});
|
||||
// Resolvers for passed functions in the modal creation.
|
||||
|
||||
// Resolvers for passed functions in the dropdown creation.
|
||||
bodyPromiseResolver(Templates.render(
|
||||
'core_grades/searchwidget/group/groupsearch_body',
|
||||
[]
|
||||
));
|
||||
|
||||
// Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('group')).on('hide.bs.dropdown', () => {
|
||||
// Reset the state once the groups menu dropdown is closed.
|
||||
dropdownMenuContainer.innerHTML = '';
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Selectors for the search widget.
|
||||
*
|
||||
* @module core_grades/searchwidget/selectors
|
||||
* @copyright 2022 Mihail Geshoski <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
export default {
|
||||
regions: {
|
||||
searchResults: '[data-region="search-results-container-widget"]',
|
||||
unsearchableContent: '[data-region="unsearchable-content-container-widget"]',
|
||||
},
|
||||
actions: {
|
||||
search: '[data-action="search"]',
|
||||
clearSearch: '[data-action="clearsearch"]',
|
||||
},
|
||||
elements: {
|
||||
getSearchWidgetSelector: searchtype => `.search-widget[data-searchtype="${searchtype}"]`,
|
||||
getSearchWidgetDropdownSelector: searchtype => `.search-widget[data-searchtype="${searchtype}"] .dropdown-menu`,
|
||||
},
|
||||
};
|
||||
@@ -105,6 +105,7 @@ class get_enrolled_users_for_search_widget extends external_api {
|
||||
$userpicture->size = 1;
|
||||
$user->profileimage = $userpicture->get_url($PAGE)->out(false);
|
||||
$user->email = $guiuser->email;
|
||||
$user->active = false; // @TODO MDL-76246
|
||||
|
||||
$users[] = $user;
|
||||
}
|
||||
@@ -151,6 +152,7 @@ class get_enrolled_users_for_search_widget extends external_api {
|
||||
core_user::get_property_type('email'),
|
||||
'An email address - allow email as root@localhost',
|
||||
VALUE_OPTIONAL),
|
||||
'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED)
|
||||
];
|
||||
return new external_single_structure($userfields);
|
||||
}
|
||||
|
||||
@@ -116,8 +116,10 @@ class get_groups_for_search_widget extends external_api {
|
||||
'group' => $group->id
|
||||
]);
|
||||
return (object) [
|
||||
'id' => $group->id,
|
||||
'name' => $group->name,
|
||||
'url' => $url->out(false),
|
||||
'active' => false // @TODO MDL-76246
|
||||
];
|
||||
}, $groupsmenu);
|
||||
}
|
||||
@@ -147,8 +149,10 @@ class get_groups_for_search_widget extends external_api {
|
||||
*/
|
||||
public static function group_description(): external_description {
|
||||
$groupfields = [
|
||||
'id' => new external_value(PARAM_ALPHANUM, 'An ID for the group', VALUE_REQUIRED),
|
||||
'url' => new external_value(PARAM_URL, 'The link that applies the group action', VALUE_REQUIRED),
|
||||
'name' => new external_value(PARAM_TEXT, 'The full name of the group', VALUE_REQUIRED),
|
||||
'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED)
|
||||
];
|
||||
return new external_single_structure($groupfields);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
define("gradereport_singleview/grade",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("gradereport_singleview/grade",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* A small modal to search users within the gradebook.
|
||||
* A small modal to search grade items within the gradebook.
|
||||
*
|
||||
* @module gradereport_singleview
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".gradewidget");if(trigger){const courseID=trigger.dataset.courseid;e.preventDefault();const data=await Repository.gradeitemFetch(courseID).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[]||void 0===data)return;WidgetBase.init(bodyPromise,data.gradeitems,searchGradeitems(),(0,_str.get_string)("selectagrade","gradereport_singleview"))}}))})),bodyPromiseResolver(Templates.render("gradereport_singleview/gradesearch_body",[]))},searchGradeitems=()=>()=>(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((module=>{module.name.toLowerCase().includes(searchTerm)&&searchResults.push(module)})),searchResults}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("grade"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("grade")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.gradeitemFetch(courseID).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.gradeitems,searchGradeitems()),bodyPromiseResolver(Templates.render("gradereport_singleview/gradesearch_body",[])))})),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("grade")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchGradeitems=()=>()=>(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((module=>{module.name.toLowerCase().includes(searchTerm)&&searchResults.push(module)})),searchResults}}));
|
||||
|
||||
//# sourceMappingURL=grade.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,10 +1,10 @@
|
||||
define("gradereport_singleview/user",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str","core/url"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str,_url){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("gradereport_singleview/user",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/url","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_url,_jquery,Selectors){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* A small modal to search users within the gradebook.
|
||||
*
|
||||
* @module gradereport_singleview/user
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".userwidget");if(trigger){const courseID=trigger.dataset.courseid,groupId=trigger.dataset.groupid;e.preventDefault();const actionBaseUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{item:"user"},!1),data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;WidgetBase.init(bodyPromise,data.users,searchUsers(),(0,_str.get_string)("selectauser","grades"))}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[]))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid,actionBaseUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{item:"user"},!1);await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers()),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[])))})),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}}));
|
||||
|
||||
//# sourceMappingURL=user.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A small modal to search users within the gradebook.
|
||||
* A small modal to search grade items within the gradebook.
|
||||
*
|
||||
* @module gradereport_singleview
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
import Pending from 'core/pending';
|
||||
import * as Templates from 'core/templates';
|
||||
import CustomEvents from "core/custom_interaction_events";
|
||||
import * as Repository from 'core_grades/searchwidget/repository';
|
||||
import * as WidgetBase from 'core_grades/searchwidget/basewidget';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import $ from 'jquery';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
|
||||
/**
|
||||
* Our entry point into starting to build the search widget.
|
||||
@@ -46,50 +46,46 @@ export const init = () => {
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
const events = [
|
||||
'click',
|
||||
CustomEvents.events.activate,
|
||||
CustomEvents.events.keyboardActivate
|
||||
];
|
||||
CustomEvents.define(document, events);
|
||||
|
||||
let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();
|
||||
const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('grade'));
|
||||
|
||||
// Register events.
|
||||
events.forEach((event) => {
|
||||
document.addEventListener(event, async(e) => {
|
||||
const trigger = e.target.closest('.gradewidget');
|
||||
if (trigger) {
|
||||
const courseID = trigger.dataset.courseid;
|
||||
e.preventDefault();
|
||||
// Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('grade')).on('show.bs.dropdown', async(e) => {
|
||||
const courseID = e.relatedTarget.dataset.courseid;
|
||||
// Display a loading icon in the dropdown menu container until the body promise is resolved.
|
||||
await WidgetBase.showLoader(dropdownMenuContainer);
|
||||
|
||||
// If an error occurs while fetching the data, display the error within the modal.
|
||||
const data = await Repository.gradeitemFetch(courseID).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
// Early return if there is no module data.
|
||||
if (data === [] || data === undefined) {
|
||||
return;
|
||||
}
|
||||
WidgetBase.init(
|
||||
bodyPromise,
|
||||
data.gradeitems,
|
||||
searchGradeitems(),
|
||||
getString('selectagrade', 'gradereport_singleview')
|
||||
);
|
||||
}
|
||||
// If an error occurs while fetching the data, display the error within the modal.
|
||||
const data = await Repository.gradeitemFetch(courseID).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
await WidgetBase.init(
|
||||
dropdownMenuContainer,
|
||||
bodyPromise,
|
||||
data.gradeitems,
|
||||
searchGradeitems()
|
||||
);
|
||||
|
||||
// Resolvers for passed functions in the modal creation.
|
||||
bodyPromiseResolver(Templates.render('gradereport_singleview/gradesearch_body', []));
|
||||
});
|
||||
|
||||
// Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('grade')).on('hide.bs.dropdown', () => {
|
||||
// Reset the state once the grade item menu dropdown is closed.
|
||||
dropdownMenuContainer.innerHTML = '';
|
||||
});
|
||||
// Resolvers for passed functions in the modal creation.
|
||||
bodyPromiseResolver(Templates.render(
|
||||
'gradereport_singleview/gradesearch_body',
|
||||
[]
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
|
||||
import Pending from 'core/pending';
|
||||
import * as Templates from 'core/templates';
|
||||
import CustomEvents from "core/custom_interaction_events";
|
||||
import * as Repository from 'core_grades/searchwidget/repository';
|
||||
import * as WidgetBase from 'core_grades/searchwidget/basewidget';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import Url from 'core/url';
|
||||
import $ from 'jquery';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
|
||||
/**
|
||||
* Our entry point into starting to build the search widget.
|
||||
@@ -47,52 +47,48 @@ export const init = () => {
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
const events = [
|
||||
'click',
|
||||
CustomEvents.events.activate,
|
||||
CustomEvents.events.keyboardActivate
|
||||
];
|
||||
CustomEvents.define(document, events);
|
||||
|
||||
let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();
|
||||
const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));
|
||||
|
||||
// Register events.
|
||||
events.forEach((event) => {
|
||||
document.addEventListener(event, async(e) => {
|
||||
const trigger = e.target.closest('.userwidget');
|
||||
if (trigger) {
|
||||
const courseID = trigger.dataset.courseid;
|
||||
const groupId = trigger.dataset.groupid;
|
||||
e.preventDefault();
|
||||
// Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('user')).on('show.bs.dropdown', async(e) => {
|
||||
const courseID = e.relatedTarget.dataset.courseid;
|
||||
const groupId = e.relatedTarget.dataset.groupid;
|
||||
const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false);
|
||||
// Display a loading icon in the dropdown menu container until the body promise is resolved.
|
||||
await WidgetBase.showLoader(dropdownMenuContainer);
|
||||
|
||||
const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false);
|
||||
// If an error occurs while fetching the data, display the error within the modal.
|
||||
const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
WidgetBase.init(
|
||||
bodyPromise,
|
||||
data.users,
|
||||
searchUsers(),
|
||||
getString('selectauser', 'grades')
|
||||
);
|
||||
}
|
||||
// If an error occurs while fetching the data, display the error within the dropdown menu.
|
||||
const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
await WidgetBase.init(
|
||||
dropdownMenuContainer,
|
||||
bodyPromise,
|
||||
data.users,
|
||||
searchUsers()
|
||||
);
|
||||
|
||||
// Resolvers for passed functions in the dropdown menu creation.
|
||||
bodyPromiseResolver(Templates.render('core_grades/searchwidget/user/usersearch_body', []));
|
||||
});
|
||||
|
||||
// Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('user')).on('hide.bs.dropdown', () => {
|
||||
// Reset the state once the user menu dropdown is closed.
|
||||
dropdownMenuContainer.innerHTML = '';
|
||||
});
|
||||
// Resolvers for passed functions in the modal creation.
|
||||
bodyPromiseResolver(Templates.render(
|
||||
'core_grades/searchwidget/user/usersearch_body',
|
||||
[]
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+4
-2
@@ -85,7 +85,7 @@ class singleview extends core_course_external {
|
||||
|
||||
$gradeitems = array_map(function ($gradeitem) use ($PAGE, $USER, $params) {
|
||||
$item = new \stdClass();
|
||||
$item->gid = $gradeitem->id;
|
||||
$item->id = $gradeitem->id;
|
||||
$url = new moodle_url('/grade/report/singleview/index.php', [
|
||||
'id' => $params['courseid'],
|
||||
'itemid' => $gradeitem->id,
|
||||
@@ -94,6 +94,7 @@ class singleview extends core_course_external {
|
||||
);
|
||||
$item->name = $gradeitem->get_name(true);
|
||||
$item->url = $url->out(false);
|
||||
$item->active = false; // @TODO MDL-76246
|
||||
|
||||
return $item;
|
||||
}, $gradeableitems);
|
||||
@@ -114,13 +115,14 @@ class singleview extends core_course_external {
|
||||
return new external_single_structure([
|
||||
'gradeitems' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'gid' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL),
|
||||
'id' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL),
|
||||
'url' => new external_value(
|
||||
PARAM_URL,
|
||||
'The link to the grade report',
|
||||
VALUE_OPTIONAL
|
||||
),
|
||||
'name' => new external_value(PARAM_TEXT, 'The full name of the grade item', VALUE_OPTIONAL),
|
||||
'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED)
|
||||
])
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
|
||||
@@ -61,7 +61,7 @@ class gradereport_singleview_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$this->page->requires->js_call_amd('gradereport_singleview/user', 'init');
|
||||
return $this->render_from_template('gradereport_singleview/user_selector', $data);
|
||||
return $this->render_from_template('core_grades/user_selector', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.path-grade-report-singleview .user-selector .userinitials {
|
||||
.path-grade-report-singleview .search-widget[data-searchtype="user"] .userinitials {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
}
|
||||
}
|
||||
}}
|
||||
<div class="grade-selector d-flex">
|
||||
<div role="button" class="gradewidget btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-searchtype="grade" tabindex="0" aria-label="{{#str}} selectagrade, gradereport_singleview {{/str}}">
|
||||
<div class="search-widget dropdown d-flex" data-searchtype="grade">
|
||||
<button aria-expanded="false" data-toggle="dropdown" class="btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" aria-label="{{#str}} selectagrade, gradereport_singleview {{/str}}">
|
||||
<div class="align-items-center d-flex">
|
||||
{{#selectedoption}}
|
||||
<div class="d-block pr-3">
|
||||
@@ -48,5 +48,7 @@
|
||||
</div>
|
||||
{{/selectedoption}}
|
||||
</div>
|
||||
</button>
|
||||
<div class="dropdown-menu wide">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template gradereport_singleview/user_selector
|
||||
|
||||
The user selector trigger element.
|
||||
|
||||
Context variables required for this template:
|
||||
* courseid - The course ID.
|
||||
* groupid - The group ID.
|
||||
* selectedoption - (optional) Object containing information about the selected option.
|
||||
* image - The image corresponding to the selected option.
|
||||
* text - The text of the selected option.
|
||||
* additionaltext - (optional) Additional text displayed below the selected option (e.g. the user email)
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"courseid": "2",
|
||||
"groupid": "2",
|
||||
"selectedoption": {
|
||||
"image": "<img src=\"http://example.com/pluginfile.php/14/user/icon/boost/f2\" class=\"userpicture\" width=\"40\" alt=\"\">",
|
||||
"text": "John Doe",
|
||||
"additionaltext": "[email protected]"
|
||||
}
|
||||
}
|
||||
}}
|
||||
<div class="user-selector d-flex">
|
||||
<div role="button" class="userwidget btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-groupid="{{groupid}}" data-searchtype="user" tabindex="0" aria-label="{{#str}} selctauser, core {{/str}}">
|
||||
<div class="align-items-center d-flex">
|
||||
{{#selectedoption}}
|
||||
<div class="selected-option-img d-block pr-2">
|
||||
{{#image}}
|
||||
{{{image}}}
|
||||
{{/image}}
|
||||
{{^image}}
|
||||
<span class="userinitials"></span>
|
||||
{{/image}}
|
||||
</div>
|
||||
<div class="selected-option-info d-block pr-3">
|
||||
<span class="selected-option-text p-0 font-weight-bold">
|
||||
{{text}}
|
||||
</span>
|
||||
{{#additionaltext}}
|
||||
<span class="d-block small">
|
||||
{{additionaltext}}
|
||||
</span>
|
||||
{{/additionaltext}}
|
||||
</div>
|
||||
{{/selectedoption}}
|
||||
{{^selectedoption}}
|
||||
<div class="d-block pr-2">
|
||||
<span class="userinitials"></span>
|
||||
</div>
|
||||
<div class="user-info d-block pr-3">
|
||||
{{#str}} selctauser, core {{/str}}
|
||||
</div>
|
||||
{{/selectedoption}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
}}
|
||||
<div class="zero-state mx-auto w-50 text-center my-6">
|
||||
<img src="{{imglink}}" alt="{{#str}}pluginname, gradereport_singleview{{/str}}" role="presentation" class="my-5">
|
||||
<img src="{{imglink}}" alt="{{#str}}pluginname, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
|
||||
<h3>{{#str}}pluginname, gradereport_singleview{{/str}}</h3>
|
||||
<p>{{#str}}viewsingleuserorgradeitem, gradereport_singleview{{/str}}</p>
|
||||
<div class="justify-content-center d-flex">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
}}
|
||||
<div class="zero-state mx-auto w-50 text-center my-6">
|
||||
<img src="{{imglink}}" alt="{{#str}}viewsinglegradeitem, gradereport_singleview{{/str}}" role="presentation" class="my-5">
|
||||
<img src="{{imglink}}" alt="{{#str}}viewsinglegradeitem, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
|
||||
<h3>{{#str}}viewsinglegradeitem, gradereport_singleview{{/str}}</h3>
|
||||
<p>{{#str}}singleviewdescription, gradereport_singleview{{/str}}</p>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
}}
|
||||
<div class="zero-state mx-auto w-50 text-center my-6">
|
||||
<img src="{{imglink}}" alt="{{#str}}viewsingleuser, gradereport_singleview{{/str}}" role="presentation" class="my-5">
|
||||
<img src="{{imglink}}" alt="{{#str}}viewsingleuser, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
|
||||
<h3>{{#str}}viewsingleuser, gradereport_singleview{{/str}}</h3>
|
||||
<p>{{#str}}singleviewdescription, gradereport_singleview{{/str}}</p>
|
||||
</div>
|
||||
|
||||
@@ -22,10 +22,10 @@ Feature: Given we have opted to search for a grade item, Lets find and search th
|
||||
Scenario: A teacher can search for and find a grade item to view
|
||||
Given I navigate to "View > Single view" in the course gradebook
|
||||
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
|
||||
When I click on ".gradewidget" "css_element"
|
||||
Then I confirm "Test assignment one" in "Select a grade item" search within the gradebook widget exists
|
||||
And I confirm "Test assignment two" in "Select a grade item" search within the gradebook widget exists
|
||||
When I click on ".search-widget[data-searchtype='grade']" "css_element"
|
||||
Then I confirm "Test assignment one" in "grade" search within the gradebook widget exists
|
||||
And I confirm "Test assignment two" in "grade" search within the gradebook widget exists
|
||||
And I set the field "searchinput" to "two"
|
||||
And I wait "1" seconds
|
||||
And I confirm "Test assignment two" in "Select a grade item" search within the gradebook widget exists
|
||||
And I confirm "Test assignment one" in "Select a grade item" search within the gradebook widget does not exist
|
||||
And I confirm "Test assignment two" in "grade" search within the gradebook widget exists
|
||||
And I confirm "Test assignment one" in "grade" search within the gradebook widget does not exist
|
||||
|
||||
@@ -21,9 +21,10 @@ Feature: Given we land on the index page, select what type of report we wish to
|
||||
Scenario: I switch between the two report types within singleview
|
||||
Given I navigate to "View > Single view" in the course gradebook
|
||||
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
|
||||
When I click on ".gradewidget" "css_element"
|
||||
Then I wait until "Select a grade item" "dialogue" exists
|
||||
And I click on "Close" "button" in the "Select a grade item" "dialogue"
|
||||
And I click on "Users" "link" in the ".page-toggler" "css_element"
|
||||
And I click on ".userwidget" "css_element"
|
||||
And I wait until "Select a user" "dialogue" exists
|
||||
And ".search-widget[data-searchtype='user']" "css_element" should not exist
|
||||
And ".search-widget[data-searchtype='grade']" "css_element" should exist
|
||||
And I confirm "assign" in "grade" search within the gradebook widget exists
|
||||
When I click on "Users" "link" in the ".page-toggler" "css_element"
|
||||
Then ".search-widget[data-searchtype='grade']" "css_element" should not exist
|
||||
And ".search-widget[data-searchtype='user']" "css_element" should exist
|
||||
And I confirm "student1" in "user" search within the gradebook widget exists
|
||||
|
||||
@@ -59,7 +59,7 @@ Feature: We can use Single view
|
||||
|
||||
Scenario: I can update grades, add feedback and exclude grades.
|
||||
Given I navigate to "View > Single view" in the course gradebook
|
||||
And I click on "Users" "link"
|
||||
And I click on "Users" "link" in the ".page-toggler" "css_element"
|
||||
And I click on "Student" in the "user" search widget
|
||||
And I turn editing mode on
|
||||
And I set the field "Override for Test assignment one" to "1"
|
||||
@@ -99,7 +99,7 @@ Feature: We can use Single view
|
||||
And I log in as "teacher2"
|
||||
And I am on "Course 1" course homepage
|
||||
Given I navigate to "View > Single view" in the course gradebook
|
||||
And I click on "Users" "link"
|
||||
And I click on "Users" "link" in the ".page-toggler" "css_element"
|
||||
And I click on "Student" in the "user" search widget
|
||||
And I turn editing mode on
|
||||
And the "Exclude for Test assignment one" "checkbox" should be disabled
|
||||
|
||||
@@ -23,12 +23,12 @@ Feature: Within the singleview report, a teacher can search for users.
|
||||
Given I navigate to "View > Single view" in the course gradebook
|
||||
When I click on "Users" "link" in the ".page-toggler" "css_element"
|
||||
And I wait until the page is ready
|
||||
And I click on ".userwidget" "css_element"
|
||||
Then I confirm "Student 1" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "Select a user" search within the gradebook widget exists
|
||||
And I click on ".search-widget[data-searchtype='user']" "css_element"
|
||||
Then I confirm "Student 1" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "user" search within the gradebook widget exists
|
||||
And I set the field "searchinput" to "2"
|
||||
And I wait "1" seconds
|
||||
And I confirm "Student 2" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 1" in "Select a user" search within the gradebook widget does not exist
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget does not exist
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
define("gradereport_user/user",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str","core/url"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str,_url){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("gradereport_user/user",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str","core/url","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_str,_url,_jquery,Selectors){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* A small modal to search users within the gradebook.
|
||||
* A widget to search users within the gradebook.
|
||||
*
|
||||
* @module gradereport_user/user
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".userwidget");if(trigger){const courseID=trigger.dataset.courseid,groupId=trigger.dataset.groupid;e.preventDefault();const actionBaseUrl=_url.default.relativeUrl("/grade/report/user/index.php",{},!1),data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;const allUsersOptionName=await(0,_str.get_string)("allusersnum","gradereport_user",data.users.length),allUsersOption=await Templates.render("core_grades/searchwidget/searchitem",{id:0,name:allUsersOptionName,url:_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:0},!1)});WidgetBase.init(bodyPromise,data.users,searchUsers(),(0,_str.get_string)("selectauser","grades"),allUsersOption)}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",{displayunsearchablecontent:!0}))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid,actionBaseUrl=_url.default.relativeUrl("/grade/report/user/index.php",{},!1);await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseID,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;const allUsersOptionName=await(0,_str.get_string)("allusersnum","gradereport_user",data.users.length),allUsersOption=await Templates.render("gradereport_user/all_users_item",{id:0,name:allUsersOptionName,url:_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:0},!1)});await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers(),allUsersOption),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",{displayunsearchablecontent:!0}))})),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("user")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}}));
|
||||
|
||||
//# sourceMappingURL=user.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* A small modal to search users within the gradebook.
|
||||
* A widget to search users within the gradebook.
|
||||
*
|
||||
* @module gradereport_user/user
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
@@ -23,11 +23,12 @@
|
||||
|
||||
import Pending from 'core/pending';
|
||||
import * as Templates from 'core/templates';
|
||||
import CustomEvents from "core/custom_interaction_events";
|
||||
import * as Repository from 'core_grades/searchwidget/repository';
|
||||
import * as WidgetBase from 'core_grades/searchwidget/basewidget';
|
||||
import {get_string as getString} from 'core/str';
|
||||
import Url from 'core/url';
|
||||
import $ from 'jquery';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
|
||||
/**
|
||||
* Our entry point into starting to build the search widget.
|
||||
@@ -47,62 +48,59 @@ export const init = () => {
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
const events = [
|
||||
'click',
|
||||
CustomEvents.events.activate,
|
||||
CustomEvents.events.keyboardActivate
|
||||
];
|
||||
CustomEvents.define(document, events);
|
||||
|
||||
let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();
|
||||
const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));
|
||||
|
||||
// Register events.
|
||||
events.forEach((event) => {
|
||||
document.addEventListener(event, async(e) => {
|
||||
const trigger = e.target.closest('.userwidget');
|
||||
if (trigger) {
|
||||
const courseID = trigger.dataset.courseid;
|
||||
const groupId = trigger.dataset.groupid;
|
||||
e.preventDefault();
|
||||
// Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('user')).on('show.bs.dropdown', async(e) => {
|
||||
const courseID = e.relatedTarget.dataset.courseid;
|
||||
const groupId = e.relatedTarget.dataset.groupid;
|
||||
const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false);
|
||||
// Display a loading icon in the dropdown menu container until the body promise is resolved.
|
||||
await WidgetBase.showLoader(dropdownMenuContainer);
|
||||
|
||||
const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false);
|
||||
// If an error occurs while fetching the data, display the error within the modal.
|
||||
const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The HTML for the 'All users' option which will be rendered in the non-searchable content are of the widget.
|
||||
const allUsersOptionName = await getString('allusersnum', 'gradereport_user', data.users.length);
|
||||
const allUsersOption = await Templates.render('core_grades/searchwidget/searchitem', {
|
||||
id: 0,
|
||||
name: allUsersOptionName,
|
||||
url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false),
|
||||
});
|
||||
|
||||
WidgetBase.init(
|
||||
bodyPromise,
|
||||
data.users,
|
||||
searchUsers(),
|
||||
getString('selectauser', 'grades'),
|
||||
allUsersOption
|
||||
);
|
||||
}
|
||||
// If an error occurs while fetching the data, display the error within the dropdown menu.
|
||||
const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => {
|
||||
const errorTemplateData = {
|
||||
'errormessage': e.message
|
||||
};
|
||||
bodyPromiseResolver(
|
||||
await Templates.render('core_grades/searchwidget/error', errorTemplateData)
|
||||
);
|
||||
});
|
||||
|
||||
// Early return if there is no module data.
|
||||
if (data === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The HTML for the 'All users' option which will be rendered in the non-searchable content are of the widget.
|
||||
const allUsersOptionName = await getString('allusersnum', 'gradereport_user', data.users.length);
|
||||
const allUsersOption = await Templates.render('gradereport_user/all_users_item', {
|
||||
id: 0,
|
||||
name: allUsersOptionName,
|
||||
url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false),
|
||||
});
|
||||
|
||||
await WidgetBase.init(
|
||||
dropdownMenuContainer,
|
||||
bodyPromise,
|
||||
data.users,
|
||||
searchUsers(),
|
||||
allUsersOption
|
||||
);
|
||||
|
||||
// Resolvers for passed functions in the dropdown menu creation.
|
||||
bodyPromiseResolver(Templates.render(
|
||||
'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true}
|
||||
));
|
||||
});
|
||||
|
||||
// Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('user')).on('hide.bs.dropdown', () => {
|
||||
// Reset the state once the user menu dropdown is closed.
|
||||
dropdownMenuContainer.innerHTML = '';
|
||||
});
|
||||
// Resolvers for passed functions in the modal creation.
|
||||
bodyPromiseResolver(Templates.render(
|
||||
'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true}
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ $string['gotonextreport'] = 'Go to next user report';
|
||||
$string['gotopreviousreport'] = 'Go to previous user report';
|
||||
$string['pluginname'] = 'User report';
|
||||
$string['user:view'] = 'View your own grade report';
|
||||
$string['userreportdesc'] = 'View a user\'s grades, any feedback and the course total.';
|
||||
$string['userreports'] = 'User reports';
|
||||
$string['privacy:metadata:preference:gradereport_user_view_user'] = 'Whether to view report as current user or another user in the gradebook reports';
|
||||
$string['selectuser'] = 'Select a user to view their grades';
|
||||
$string['selectuserinstructions'] = 'By selecting a user you can view the grades by activity';
|
||||
$string['tablesummary'] = 'The table is arranged as a list of graded items including categories of graded items. When items are in a category they will be indicated as such.';
|
||||
|
||||
@@ -127,7 +127,7 @@ class gradereport_user_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$this->page->requires->js_call_amd('gradereport_user/user', 'init');
|
||||
return $this->render_from_template('gradereport_user/user_selector', $data);
|
||||
return $this->render_from_template('core_grades/user_selector', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.path-grade-report-user .user-selector .userinitials {
|
||||
.path-grade-report-user .search-widget[data-searchtype="user"] .userinitials {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template gradereport_user/all_users_item
|
||||
|
||||
Search result line items.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": "0",
|
||||
"name": "All users(10)",
|
||||
"url": "http://foo.bar/gradereport/?userid=0&id=2"
|
||||
}
|
||||
}}
|
||||
<a role="menuitem" id="item-{{id}}" href="{{url}}" class="dropdown-item d-flex px-2 py-1" tabindex="0">
|
||||
<span class="pull-left result-cell text-truncate mr-2">
|
||||
{{name}}
|
||||
</span>
|
||||
</a>
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
}}
|
||||
<div class="zero-state mx-auto w-50 text-center my-6">
|
||||
<img src="{{imglink}}" alt="{{#str}}selectuser, gradereport_user{{/str}}" role="presentation" class="my-5">
|
||||
<h3>{{#str}}selectuser, gradereport_user{{/str}}</h3>
|
||||
<p>{{#str}}selectuserinstructions, gradereport_user{{/str}}</p>
|
||||
<img src="{{imglink}}" alt="{{#str}}userreports, gradereport_user{{/str}}" aria-hidden="true" class="my-5">
|
||||
<h3>{{#str}}userreports, gradereport_user{{/str}}</h3>
|
||||
<p>{{#str}}userreportdesc, gradereport_user{{/str}}</p>
|
||||
</div>
|
||||
|
||||
@@ -28,50 +28,38 @@ Feature: Group searching functionality within the user report.
|
||||
|
||||
Scenario: A teacher can see the 'group' search widget only when group mode is enabled in the course
|
||||
Given I navigate to "View > User report" in the course gradebook
|
||||
And ".groupwidget" "css_element" should exist
|
||||
And ".search-widget[data-searchtype='group']" "css_element" should exist
|
||||
And I am on "Course 1" course homepage
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| id_groupmode | No groups |
|
||||
And I press "Save and display"
|
||||
When I navigate to "View > User report" in the course gradebook
|
||||
Then ".groupwidget" "css_element" should not exist
|
||||
Then ".search-widget[data-searchtype='group']" "css_element" should not exist
|
||||
|
||||
Scenario: A teacher can search for and find a group to find a user in
|
||||
Given I navigate to "View > User report" in the course gradebook
|
||||
And I click on ".groupwidget" "css_element"
|
||||
And I wait until "Select a group" "dialogue" exists
|
||||
And I should see "Tutor group" in the "Select a group" "dialogue"
|
||||
And I should see "Marker group" in the "Select a group" "dialogue"
|
||||
And I click on ".search-widget[data-searchtype='group']" "css_element"
|
||||
And I confirm "Tutor group" in "group" search within the gradebook widget exists
|
||||
And I confirm "Marker group" in "group" search within the gradebook widget exists
|
||||
When I set the field "searchinput" to "tutor"
|
||||
And I wait "1" seconds
|
||||
Then I should see "Tutor group" in the "Select a group" "dialogue"
|
||||
And I should not see "Marker group" in the "Select a group" "dialogue"
|
||||
Then I confirm "Tutor group" in "group" search within the gradebook widget exists
|
||||
And I confirm "Marker group" in "group" search within the gradebook widget does not exist
|
||||
|
||||
Scenario: A teacher can only see the group members in the 'user' search widget after selecting a group option
|
||||
Given I navigate to "View > User report" in the course gradebook
|
||||
# Confirm that all users are initially displayed in the 'user' search widget.
|
||||
And I click on ".userwidget" "css_element"
|
||||
And I wait until "Select a user" "dialogue" exists
|
||||
And I should see "Student 1" in the "Select a user" "dialogue"
|
||||
And I should see "Student 2" in the "Select a user" "dialogue"
|
||||
And I click on "Close" "button" in the "Select a user" "dialogue"
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
# Select a particular group from the 'group' search widget.
|
||||
When I click on "Default group" in the "group" search widget
|
||||
# Confirm that only users which are members of the selected group are displayed in the 'user' search widget.
|
||||
And I click on ".userwidget" "css_element"
|
||||
And I wait until "Select a user" "dialogue" exists
|
||||
Then I should see "Student 1" in the "Select a user" "dialogue"
|
||||
And I should not see "Student 2" in the "Select a user" "dialogue"
|
||||
And I click on "Close" "button" in the "Select a user" "dialogue"
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget does not exist
|
||||
And I click on "Tutor group" in the "group" search widget
|
||||
And I click on ".userwidget" "css_element"
|
||||
And I wait until "Select a user" "dialogue" exists
|
||||
And I should not see "Student 1" in the "Select a user" "dialogue"
|
||||
And I should not see "Student 2" in the "Select a user" "dialogue"
|
||||
And I click on "Close" "button" in the "Select a user" "dialogue"
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget does not exist
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget does not exist
|
||||
And I click on "All participants" in the "group" search widget
|
||||
And I click on ".userwidget" "css_element"
|
||||
And I wait until "Select a user" "dialogue" exists
|
||||
And I should see "Student 1" in the "Select a user" "dialogue"
|
||||
And I should see "Student 2" in the "Select a user" "dialogue"
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
|
||||
@@ -21,12 +21,12 @@ Feature: Within the User report, a teacher can search for users.
|
||||
|
||||
Scenario: A teacher can search for and find a user to view
|
||||
Given I navigate to "View > User report" in the course gradebook
|
||||
When I click on ".userwidget" "css_element"
|
||||
Then I confirm "Student 1" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "Select a user" search within the gradebook widget exists
|
||||
When I click on ".search-widget[data-searchtype='user']" "css_element"
|
||||
Then I confirm "Student 1" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "user" search within the gradebook widget exists
|
||||
And I set the field "searchinput" to "2"
|
||||
And I wait "1" seconds
|
||||
And I confirm "Student 2" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "Select a user" search within the gradebook widget exists
|
||||
And I confirm "Student 1" in "Select a user" search within the gradebook widget does not exist
|
||||
And I confirm "Student 2" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 32" in "user" search within the gradebook widget exists
|
||||
And I confirm "Student 1" in "user" search within the gradebook widget does not exist
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
"selectedgroup": "Group 1"
|
||||
}
|
||||
}}
|
||||
<div class="group-selector d-flex">
|
||||
<div role="button" class="groupwidget btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-action-base-url="{{groupactionbaseurl}}" data-searchtype="group" tabindex="0">
|
||||
<div class="search-widget d-flex dropdown" data-searchtype="group">
|
||||
<button aria-expanded="false" data-toggle="dropdown" class="btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-action-base-url="{{groupactionbaseurl}}">
|
||||
<div class="align-items-center d-flex">
|
||||
<div class="d-block pr-3">
|
||||
<span class="d-block small">
|
||||
@@ -45,5 +45,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="dropdown-menu narrow">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_grades/searchwidget/loading
|
||||
|
||||
This template renders the loading placeholder for the search widget.
|
||||
|
||||
Example context (json):
|
||||
{}
|
||||
}}
|
||||
<div class="d-flex w-100 h-100 justify-content-center align-items-center">
|
||||
{{> core/loading }}
|
||||
</div>
|
||||
@@ -24,25 +24,26 @@
|
||||
"id": "1",
|
||||
"name": "Quiz 1",
|
||||
"url": "http://foo.bar/gradereport/?userid=25",
|
||||
"fullname": "Cameron Greeve"
|
||||
"fullname": "Cameron Greeve",
|
||||
"active": true
|
||||
}
|
||||
}}
|
||||
<div class="w-100 result-row" role="row" id="result-row-{{id}}">
|
||||
<li class="w-100 result-row" role="none" id="result-row-{{id}}">
|
||||
{{#name}}
|
||||
<a role="option" id="result-item-{{id}}x{{id}}" href="{{url}}" class="d-flex px-2 py-1">
|
||||
<span class="w-50 pull-left result-cell text-truncate text-body mr-2">
|
||||
<a role="menuitem" id="item-{{id}}" href="{{url}}" class="dropdown-item d-flex px-2 py-1" tabindex="-1"">
|
||||
<span class="pull-left result-cell text-truncate mr-2">
|
||||
{{name}}
|
||||
</span>
|
||||
</a>
|
||||
{{/name}}
|
||||
{{^name}}
|
||||
<a role="option" id="user-{{id}}" href="{{url}}" class="d-flex px-2 py-1">
|
||||
<span class="w-50 pull-left result-cell text-truncate text-body mr-2">
|
||||
<a role="menuitem" id="user-{{id}}" href="{{url}}" class="dropdown-item d-flex px-2 py-1 align-items-center" tabindex="-1">
|
||||
<span class="w-50 pull-left text-truncate mr-2">
|
||||
{{fullname}}
|
||||
</span>
|
||||
<span class="w-50 pull-left result-cell text-truncate text-muted">
|
||||
<span id="email-user-{{id}}" role="gridcell">{{email}}</span>
|
||||
<span class="w-50 pull-left text-truncate small email">
|
||||
{{email}}
|
||||
</span>
|
||||
</a>
|
||||
{{/name}}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="searchresultitemscontainer-wrapper">
|
||||
<div class="searchresultitemscontainer d-flex flex-column mw-100 position-relative py-2" role="listbox" data-region="search-result-items-container">
|
||||
<div class="searchresultitemscontainer-wrapper dropdown">
|
||||
<ul class="searchresultitemscontainer d-flex flex-column mw-100 position-relative py-2 list-group" role="menu" data-region="search-result-items-container" tabindex="0">
|
||||
{{#searchresults}}
|
||||
{{>core_grades/searchwidget/searchitem}}
|
||||
{{/searchresults}}
|
||||
{{^searchresults}}
|
||||
<p>{{#str}} resultsfound, core, 0 {{/str}}</p>
|
||||
{{/searchresults}}
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -32,5 +32,5 @@
|
||||
|
||||
<div class="searchresultscontainer" data-region="search-results-container-widget" aria-live="polite"></div>
|
||||
{{#displayunsearchablecontent}}
|
||||
<div class="unsearchablecontentcontainer" data-region="unsearchable-content-container-widget" aria-live="polite"></div>
|
||||
<div class="unsearchablecontentcontainer" data-region="unsearchable-content-container-widget" role="menu" aria-live="polite"></div>
|
||||
{{/displayunsearchablecontent}}
|
||||
|
||||
+5
-3
@@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template gradereport_user/user_selector
|
||||
@template core_grades/user_selector
|
||||
|
||||
The user selector trigger element.
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
}
|
||||
}
|
||||
}}
|
||||
<div class="user-selector d-flex">
|
||||
<div role="button" class="userwidget btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-groupid="{{groupid}}" data-searchtype="user" tabindex="0" aria-label="{{#str}} selectauser, core_grades {{/str}}">
|
||||
<div class="search-widget dropdown d-flex" data-searchtype="user">
|
||||
<button aria-expanded="false" data-toggle="dropdown" class="btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-groupid="{{groupid}}" aria-label="{{#str}} selectauser, core_grades {{/str}}">
|
||||
<div class="align-items-center d-flex">
|
||||
{{#selectedoption}}
|
||||
<div class="selected-option-img d-block pr-2">
|
||||
@@ -70,5 +70,7 @@
|
||||
</div>
|
||||
{{/selectedoption}}
|
||||
</div>
|
||||
</button>
|
||||
<div class="dropdown-menu wide">
|
||||
</div>
|
||||
</div>
|
||||
@@ -426,38 +426,61 @@ class behat_grade extends behat_base {
|
||||
* Confirm if a value is within the search widget within the gradebook.
|
||||
*
|
||||
* Examples:
|
||||
* - I confirm "User1" in "User" search within the gradebook widget exists
|
||||
* - I confirm "User" in "user" search within the gradebook widget exists
|
||||
* - I confirm "Group" in "group" search within the gradebook widget exists
|
||||
* - I confirm "Grade item" in "grade" search within the gradebook widget exists
|
||||
*
|
||||
* @Given /^I confirm "(?P<needle>(?:[^"]|\\")*)" in "(?P<haystack>(?:[^"]|\\")*)" search within the gradebook widget exists$/
|
||||
* @param string $needle The value to search for.
|
||||
* @param string $haystack The selector to use within the zero state.
|
||||
* @param string $haystack The type of the search widget.
|
||||
*/
|
||||
public function i_confirm_in_search_within_the_gradebook_widget_exists($needle, $haystack) {
|
||||
$this->execute("behat_general::wait_until_exists", [$haystack, "dialogue"]);
|
||||
$this->execute("behat_general::assert_element_contains_text", [$needle, $haystack, "dialogue"]);
|
||||
$triggercssselector = ".search-widget[data-searchtype='{$haystack}']";
|
||||
|
||||
// Make sure that the dropdown menu is visible.
|
||||
$node = $this->find("css_element", "{$triggercssselector} .dropdown-menu");
|
||||
if (!$node->isVisible()) {
|
||||
$this->execute("behat_general::i_click_on", [$triggercssselector, "css_element"]);
|
||||
}
|
||||
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
$this->execute("behat_general::assert_element_contains_text",
|
||||
[$needle, "{$triggercssselector} .dropdown-menu", "css_element"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm if a value is not within the search widget within the gradebook.
|
||||
*
|
||||
* Examples:
|
||||
* - I confirm "User1" in "User" search within the gradebook widget does not exist
|
||||
* - I confirm "User" in "user" search within the gradebook widget does not exist
|
||||
* - I confirm "Group" in "group" search within the gradebook widget does not exist
|
||||
* - I confirm "Grade item" in "grade" search within the gradebook widget does not exist
|
||||
*
|
||||
* @Given /^I confirm "(?P<needle>(?:[^"]|\\")*)" in "(?P<haystack>(?:[^"]|\\")*)" search within the gradebook widget does not exist$/
|
||||
* @param string $needle The value to search for.
|
||||
* @param string $haystack The selector to use within the zero state.
|
||||
* @param string $haystack The type of the search widget.
|
||||
*/
|
||||
public function i_confirm_in_search_within_the_gradebook_widget_does_not_exist($needle, $haystack) {
|
||||
$this->execute("behat_general::wait_until_exists", [$haystack, "dialogue"]);
|
||||
$this->execute("behat_general::assert_element_not_contains_text", [$needle, $haystack, "dialogue"]);
|
||||
$triggercssselector = ".search-widget[data-searchtype='{$haystack}']";
|
||||
|
||||
// Make sure that the dropdown menu is visible.
|
||||
$node = $this->find("css_element", "{$triggercssselector} .dropdown-menu");
|
||||
if (!$node->isVisible()) {
|
||||
$this->execute("behat_general::i_click_on", [$triggercssselector, "css_element"]);
|
||||
}
|
||||
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
$this->execute("behat_general::assert_element_not_contains_text",
|
||||
[$needle, "{$triggercssselector} .dropdown-menu", "css_element"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on an option from the specified search widget in the current gradebook page.
|
||||
*
|
||||
* Examples:
|
||||
* - I click on "Student1" in the "user" search widget
|
||||
* - I click on "Group1" in the "group" search widget
|
||||
* - I click on "Student" in the "user" search widget
|
||||
* - I click on "Group" in the "group" search widget
|
||||
* - I click on "Grade item" in the "grade" search widget
|
||||
*
|
||||
* @Given /^I click on "(?P<needle>(?:[^"]|\\")*)" in the "(?P<haystack>(?:[^"]|\\")*)" search widget$/
|
||||
* @param string $needle The value to search for.
|
||||
@@ -466,26 +489,11 @@ class behat_grade extends behat_base {
|
||||
public function i_click_on_in_search_widget(string $needle, string $haystack) {
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
|
||||
$triggercssselector = '';
|
||||
$dialoguetitle = '';
|
||||
|
||||
switch ($haystack) {
|
||||
case 'user':
|
||||
$triggercssselector = '.userwidget';
|
||||
$dialoguetitle = 'Select a user';
|
||||
break;
|
||||
case 'grade':
|
||||
$triggercssselector = '.gradewidget';
|
||||
$dialoguetitle = 'Select a grade item';
|
||||
break;
|
||||
case 'group':
|
||||
$triggercssselector = '.groupwidget';
|
||||
$dialoguetitle = 'Select a group';
|
||||
break;
|
||||
}
|
||||
$triggercssselector = ".search-widget[data-searchtype='{$haystack}']";
|
||||
|
||||
$this->execute("behat_general::i_click_on", [$triggercssselector, "css_element"]);
|
||||
$this->execute("behat_general::wait_until_exists", [$dialoguetitle, "dialogue"]);
|
||||
$this->execute('behat_general::i_click_on_in_the', [$needle, "link", $dialoguetitle, 'dialogue']);
|
||||
$this->execute("behat_general::wait_until_the_page_is_ready");
|
||||
$this->execute('behat_general::i_click_on_in_the',
|
||||
[$needle, "link", "{$triggercssselector} .dropdown-menu", "css_element"]);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("core/aria",["exports","./local/aria/aria-hidden","./local/aria/aria-combobox"],(function(_exports,_ariaHidden,_ariaCombobox){Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"comboBox",{enumerable:!0,get:function(){return _ariaCombobox.comboBox}}),Object.defineProperty(_exports,"hide",{enumerable:!0,get:function(){return _ariaHidden.hide}}),Object.defineProperty(_exports,"hideSiblings",{enumerable:!0,get:function(){return _ariaHidden.hideSiblings}}),Object.defineProperty(_exports,"unhide",{enumerable:!0,get:function(){return _ariaHidden.unhide}}),Object.defineProperty(_exports,"unhideSiblings",{enumerable:!0,get:function(){return _ariaHidden.unhideSiblings}})}));
|
||||
define("core/aria",["exports","./local/aria/aria-hidden"],(function(_exports,_ariaHidden){Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"hide",{enumerable:!0,get:function(){return _ariaHidden.hide}}),Object.defineProperty(_exports,"hideSiblings",{enumerable:!0,get:function(){return _ariaHidden.hideSiblings}}),Object.defineProperty(_exports,"unhide",{enumerable:!0,get:function(){return _ariaHidden.unhide}}),Object.defineProperty(_exports,"unhideSiblings",{enumerable:!0,get:function(){return _ariaHidden.unhideSiblings}})}));
|
||||
|
||||
//# sourceMappingURL=aria.min.js.map
|
||||
@@ -1,3 +0,0 @@
|
||||
define("core/local/aria/aria-combobox",["exports","core/key_codes"],(function(_exports,_key_codes){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.comboBox=void 0;_exports.comboBox=comboInput=>{registerEventListeners(comboInput)};const registerEventListeners=comboInput=>{document.addEventListener("keydown",(e=>{if(e.target===comboInput){let next=null;const comboResultArea=document.querySelector('[data-region="search-result-items-container"]'),resultRows=Array.from(comboResultArea.querySelectorAll('[role="row"]')),resultCells=Array.from(comboResultArea.querySelectorAll('[role="gridcell"]')),activeResultRow=comboResultArea.querySelector('.active[role="row"]'),activeResultCell=comboResultArea.querySelector('.focused-cell[role="gridcell"]');switch(e.keyCode){case _key_codes.arrowUp:if(null===activeResultRow)next=setFirstActiveRow(next,resultRows,comboInput,resultRows.length-1);else for(let i=0;i<resultRows.length;i++)if(resultRows[i].id===activeResultRow.id){next=resultRows[i-1];break}break;case _key_codes.arrowDown:if(null===activeResultRow)next=setFirstActiveRow(next,resultRows,comboInput,0);else for(let i=0;i<resultRows.length-1;i++)if(resultRows[i].id===activeResultRow.id){next=resultRows[i+1];break}break;case _key_codes.home:next=resultRows[0];break;case _key_codes.end:next=resultRows[resultRows.length-1];break;case _key_codes.enter||_key_codes.space:window.location=activeResultCell.href;break;case _key_codes.arrowLeft:if(null===activeResultRow)next=setFirstActiveRow(next,resultRows,comboInput,0);else for(let i=0;i<resultCells.length;i++)if(resultCells[i].id===activeResultCell.id){if(void 0===resultCells[i-1]){resultCells[i].classList.remove("focused-cell"),resultCells[resultCells.length-1].classList.add("focused-cell");break}resultCells[i].classList.remove("focused-cell"),resultCells[i-1].classList.add("focused-cell");break}break;case _key_codes.arrowRight:if(null===activeResultRow)next=setFirstActiveRow(next,resultRows,comboInput,0);else for(let i=0;i<resultCells.length-1;i++){if(resultCells[i].id===activeResultCell.id){resultCells[i].classList.remove("focused-cell"),resultCells[i+1].classList.add("focused-cell");break}if(void 0===resultCells[i+2]){resultCells[i+1].classList.remove("focused-cell"),resultCells[0].classList.add("focused-cell");break}}break;default:window.console.log("nothing to see here!")}nextHandler(next,e,activeResultRow,comboInput)}}))},setFirstActiveRow=(next,resultRows,comboInput,val)=>((next=resultRows[val]).setAttribute("aria-selected","true"),next.classList.add("active"),comboInput.setAttribute("aria-activedescendant",next.id),next.querySelector(".result-cell").classList.add("focused-cell"),next),nextHandler=(next,e,activeResultRow,comboInput)=>{next&&(e.preventDefault(),null!==activeResultRow&&(activeResultRow.classList.remove("active"),activeResultRow.querySelector(".result-cell").classList.remove("focused-cell")),next.classList.add("active"),next.querySelector(".result-cell").classList.add("focused-cell"),comboInput.setAttribute("aria-activedescendant",next.id))}}));
|
||||
|
||||
//# sourceMappingURL=aria-combobox.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -27,6 +27,3 @@ export {
|
||||
hideSiblings,
|
||||
unhideSiblings,
|
||||
} from './local/aria/aria-hidden';
|
||||
export {
|
||||
comboBox
|
||||
} from './local/aria/aria-combobox';
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import {end, arrowLeft, arrowRight, arrowUp, arrowDown, home, enter, space} from 'core/key_codes';
|
||||
|
||||
/**
|
||||
* ARIA helpers related to the combobox role.
|
||||
*
|
||||
* @module core/local/aria/aria-combobox.
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Our entry point into adding accessibility handling for comboboxes.
|
||||
*
|
||||
* @param {Element} comboInput The combobox area to add aria handling to.
|
||||
*/
|
||||
export const comboBox = (comboInput) => {
|
||||
registerEventListeners(comboInput);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event management of the provided combobox.
|
||||
*
|
||||
* @param {Element} comboInput The combobox area to add aria handling to.
|
||||
*/
|
||||
const registerEventListeners = (comboInput) => {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.target === comboInput) {
|
||||
let next = null;
|
||||
const comboResultArea = document.querySelector('[data-region="search-result-items-container"]');
|
||||
const resultRows = Array.from(comboResultArea.querySelectorAll('[role="row"]'));
|
||||
const resultCells = Array.from(comboResultArea.querySelectorAll('[role="gridcell"]'));
|
||||
const activeResultRow = comboResultArea.querySelector('.active[role="row"]');
|
||||
const activeResultCell = comboResultArea.querySelector('.focused-cell[role="gridcell"]');
|
||||
switch (e.keyCode) {
|
||||
case arrowUp: {
|
||||
// TODO: Handle the wrapping.
|
||||
if (activeResultRow === null) {
|
||||
next = setFirstActiveRow(next, resultRows, comboInput, resultRows.length - 1);
|
||||
} else {
|
||||
for (let i = 0; i < resultRows.length; i++) {
|
||||
if (resultRows[i].id === activeResultRow.id) {
|
||||
next = resultRows[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case arrowDown: {
|
||||
if (activeResultRow === null) {
|
||||
next = setFirstActiveRow(next, resultRows, comboInput, 0);
|
||||
} else {
|
||||
for (let i = 0; i < resultRows.length - 1; i++) {
|
||||
if (resultRows[i].id === activeResultRow.id) {
|
||||
next = resultRows[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case home: {
|
||||
next = resultRows[0];
|
||||
break;
|
||||
}
|
||||
case end: {
|
||||
next = resultRows[resultRows.length - 1];
|
||||
break;
|
||||
}
|
||||
case enter || space: {
|
||||
// Redirect the user to the appropriate link.
|
||||
// TODO: Space does not work, special handler on the cell itself?
|
||||
window.location = activeResultCell.href;
|
||||
break;
|
||||
}
|
||||
case arrowLeft: {
|
||||
if (activeResultRow === null) {
|
||||
next = setFirstActiveRow(next, resultRows, comboInput, 0);
|
||||
} else {
|
||||
for (let i = 0; i < resultCells.length; i++) {
|
||||
if (resultCells[i].id === activeResultCell.id) {
|
||||
if (resultCells[i - 1] === undefined) {
|
||||
resultCells[i].classList.remove('focused-cell');
|
||||
resultCells[resultCells.length - 1].classList.add('focused-cell');
|
||||
break;
|
||||
} else {
|
||||
resultCells[i].classList.remove('focused-cell');
|
||||
resultCells[i - 1].classList.add('focused-cell');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case arrowRight: {
|
||||
if (activeResultRow === null) {
|
||||
next = setFirstActiveRow(next, resultRows, comboInput, 0);
|
||||
} else {
|
||||
for (let i = 0; i < resultCells.length - 1; i++) {
|
||||
if (resultCells[i].id === activeResultCell.id) {
|
||||
resultCells[i].classList.remove('focused-cell');
|
||||
resultCells[i + 1].classList.add('focused-cell');
|
||||
break;
|
||||
}
|
||||
if (resultCells[i + 2] === undefined) {
|
||||
resultCells[i + 1].classList.remove('focused-cell');
|
||||
resultCells[0].classList.add('focused-cell');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
window.console.log('nothing to see here!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Variable next is set if we do want to act on the keypress.
|
||||
nextHandler(next, e, activeResultRow, comboInput);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* With search, we can't automatically set aria elements in the results field, so we do it here.
|
||||
*
|
||||
* @param {Element} next
|
||||
* @param {Array} resultRows
|
||||
* @param {Element} comboInput
|
||||
* @param {Number} val
|
||||
* @returns {Element}
|
||||
*/
|
||||
const setFirstActiveRow = (next, resultRows, comboInput, val) => {
|
||||
// Set first option as active.
|
||||
next = resultRows[val];
|
||||
next.setAttribute('aria-selected', 'true');
|
||||
next.classList.add('active');
|
||||
comboInput.setAttribute('aria-activedescendant', next.id);
|
||||
next.querySelector('.result-cell').classList.add('focused-cell');
|
||||
return next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given we have a value to next set active, handle some of the basic handling.
|
||||
*
|
||||
* @param {Element} next
|
||||
* @param {Event} e
|
||||
* @param {Element} activeResultRow
|
||||
* @param {Element} comboInput
|
||||
*/
|
||||
const nextHandler = (next, e, activeResultRow, comboInput) => {
|
||||
if (next) {
|
||||
e.preventDefault();
|
||||
if (activeResultRow !== null) {
|
||||
activeResultRow.classList.remove('active');
|
||||
activeResultRow.querySelector('.result-cell').classList.remove('focused-cell');
|
||||
}
|
||||
next.classList.add('active');
|
||||
// Find whatever the first result cell is to add the class.
|
||||
next.querySelector('.result-cell').classList.add('focused-cell');
|
||||
comboInput.setAttribute('aria-activedescendant', next.id);
|
||||
}
|
||||
};
|
||||
@@ -43,6 +43,7 @@
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
role="searchbox"
|
||||
data-region="input"
|
||||
data-action="search"
|
||||
id="searchinput"
|
||||
|
||||
@@ -530,21 +530,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
.searchresultitemscontainer {
|
||||
max-height: 150px;
|
||||
overflow: auto;
|
||||
.search-widget {
|
||||
.dropdown-menu {
|
||||
padding: 0.8rem 1.2rem;
|
||||
|
||||
a:hover {
|
||||
background-color: $gray-100;
|
||||
}
|
||||
&.wide {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.focused-cell {
|
||||
outline-style: dotted;
|
||||
outline-color: green;
|
||||
&.narrow {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
span {
|
||||
&.email {
|
||||
color: $text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item:hover,
|
||||
.dropdown-item:active {
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.searchresultscontainer {
|
||||
height: 178px;
|
||||
font-size: 90%;
|
||||
|
||||
.searchresultitemscontainer {
|
||||
height: 178px;
|
||||
max-height: 178px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px;
|
||||
font-size: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@@ -19747,18 +19747,28 @@ p.arrow_button {
|
||||
.path-grade-report-singleview .reporttable .dropdown-toggle::after {
|
||||
display: none; }
|
||||
|
||||
.searchresultitemscontainer {
|
||||
max-height: 150px;
|
||||
overflow: auto; }
|
||||
.searchresultitemscontainer a:hover {
|
||||
background-color: #f8f9fa; }
|
||||
.searchresultitemscontainer .focused-cell {
|
||||
outline-style: dotted;
|
||||
outline-color: green; }
|
||||
|
||||
.unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px; }
|
||||
.search-widget .dropdown-menu {
|
||||
padding: 0.8rem 1.2rem; }
|
||||
.search-widget .dropdown-menu.wide {
|
||||
width: 350px; }
|
||||
.search-widget .dropdown-menu.narrow {
|
||||
width: 250px; }
|
||||
.search-widget .dropdown-menu .dropdown-item span.email {
|
||||
color: #6a737b; }
|
||||
.search-widget .dropdown-menu .dropdown-item:hover span,
|
||||
.search-widget .dropdown-menu .dropdown-item:active span {
|
||||
color: #fff; }
|
||||
.search-widget .dropdown-menu .searchresultscontainer {
|
||||
height: 178px;
|
||||
font-size: 90%; }
|
||||
.search-widget .dropdown-menu .searchresultscontainer .searchresultitemscontainer {
|
||||
height: 178px;
|
||||
max-height: 178px;
|
||||
overflow: auto; }
|
||||
.search-widget .dropdown-menu .unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px;
|
||||
font-size: 90%; }
|
||||
|
||||
.columns-autoflow-1to1to1 {
|
||||
column-count: 3; }
|
||||
|
||||
@@ -19747,18 +19747,28 @@ p.arrow_button {
|
||||
.path-grade-report-singleview .reporttable .dropdown-toggle::after {
|
||||
display: none; }
|
||||
|
||||
.searchresultitemscontainer {
|
||||
max-height: 150px;
|
||||
overflow: auto; }
|
||||
.searchresultitemscontainer a:hover {
|
||||
background-color: #f8f9fa; }
|
||||
.searchresultitemscontainer .focused-cell {
|
||||
outline-style: dotted;
|
||||
outline-color: green; }
|
||||
|
||||
.unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px; }
|
||||
.search-widget .dropdown-menu {
|
||||
padding: 0.8rem 1.2rem; }
|
||||
.search-widget .dropdown-menu.wide {
|
||||
width: 350px; }
|
||||
.search-widget .dropdown-menu.narrow {
|
||||
width: 250px; }
|
||||
.search-widget .dropdown-menu .dropdown-item span.email {
|
||||
color: #6a737b; }
|
||||
.search-widget .dropdown-menu .dropdown-item:hover span,
|
||||
.search-widget .dropdown-menu .dropdown-item:active span {
|
||||
color: #fff; }
|
||||
.search-widget .dropdown-menu .searchresultscontainer {
|
||||
height: 178px;
|
||||
font-size: 90%; }
|
||||
.search-widget .dropdown-menu .searchresultscontainer .searchresultitemscontainer {
|
||||
height: 178px;
|
||||
max-height: 178px;
|
||||
overflow: auto; }
|
||||
.search-widget .dropdown-menu .unsearchablecontentcontainer {
|
||||
border-top: 1px solid #dee2e6;
|
||||
padding-top: 10px;
|
||||
font-size: 90%; }
|
||||
|
||||
.columns-autoflow-1to1to1 {
|
||||
column-count: 3; }
|
||||
|
||||
Reference in New Issue
Block a user