MDL-74610 quiz: multiple grades - rename grade item

This commit is contained in:
Tim Hunt 2023-08-11 12:49:51 +01:00
parent 6794f1293f
commit be49319a87
6 changed files with 171 additions and 16 deletions

View File

@ -1,10 +1,10 @@
define("mod_quiz/edit_multiple_grades",["exports","core/ajax","core/loadingicon","core/notification","core/pending","core/str"],(function(_exports,_ajax,_loadingicon,_notification,_pending,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
define("mod_quiz/edit_multiple_grades",["exports","core/ajax","core/config","core/loadingicon","core/notification","core/pending","core/str"],(function(_exports,_ajax,_config,_loadingicon,_notification,_pending,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* JavaScript for managing multiple grade items for a quiz.
*
* @module mod_quiz/edit_multiple_grades
* @copyright 2023 THe Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const handleGradeItemDelete=e=>{e.preventDefault();const pending=new _pending.default("delete-quiz-grade-item"),tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainerRemoveOnCompletion)(tableCell,pending);const tableRow=tableCell.closest("tr");((quizId,gradeItemId)=>(0,_ajax.call)([{methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId}]}}])[0])(tableRow.closest("table").dataset.quizId,tableRow.dataset.quizGradeItemId).then((()=>pending.resolve())).then((()=>{window.location.reload()})).catch(_notification.default.exception)},handleGradeItemClick=e=>{const link=e.target.closest("a");link&&link.dataset.actionDelete&&handleGradeItemDelete(e)},handleAddGradeItemClick=e=>{e.preventDefault();const pending=new _pending.default("delete-quiz-grade-item");(0,_loadingicon.addIconToContainerRemoveOnCompletion)(e.target.parentNode,pending);const quizId=e.target.dataset.quizId;(0,_str.get_string)("gradeitemdefaultname","quiz").then((name=>((quizId,name)=>(0,_ajax.call)([{methodname:"mod_quiz_create_grade_items",args:{quizid:quizId,quizgradeitems:[{name:name}]}}])[0])(quizId,name))).then((()=>pending.resolve())).then((()=>{window.location.reload()})).catch(_notification.default.exception)};_exports.init=()=>{(()=>{const gradeItemTable=document.getElementById("mod_quiz-grade-item-list");gradeItemTable&&gradeItemTable.addEventListener("click",handleGradeItemClick),document.getElementById("mod_quiz-add_grade_item").addEventListener("click",handleAddGradeItemClick)})()}}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_config=_interopRequireDefault(_config),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const handleGradeItemDelete=e=>{e.preventDefault();const pending=new _pending.default("delete-quiz-grade-item"),tableCell=e.target.closest("td");(0,_loadingicon.addIconToContainerRemoveOnCompletion)(tableCell,pending);const tableRow=tableCell.closest("tr");((quizId,gradeItemId)=>(0,_ajax.call)([{methodname:"mod_quiz_delete_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId}]}}])[0])(tableRow.closest("table").dataset.quizId,tableRow.dataset.quizGradeItemId).then((()=>pending.resolve())).then((()=>window.location.reload())).catch(_notification.default.exception)},stopEditingGadeItem=editableSpan=>{editableSpan.innerHTML=editableSpan.dataset.oldContent,delete editableSpan.dataset.oldContent,editableSpan.classList.remove("inplaceeditingon"),editableSpan.querySelector("[data-action-edit]").focus()},handleGradeItemKeyDown=e=>{if(13!==e.keyCode)return;const editableSpan=e.target.closest("span.inplaceeditable.inplaceeditingon");if(!editableSpan)return;e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-save"),newName=editableSpan.querySelector("input").value,tableCell=e.target.closest("th");(0,_loadingicon.addIconToContainerRemoveOnCompletion)(tableCell,pending);const tableRow=tableCell.closest("tr");((quizId,gradeItemId,newName)=>(0,_ajax.call)([{methodname:"mod_quiz_update_grade_items",args:{quizid:quizId,quizgradeitems:[{id:gradeItemId,name:newName}]}}])[0])(tableRow.closest("table").dataset.quizId,tableRow.dataset.quizGradeItemId,newName).then((()=>pending.resolve())).then((()=>window.location.reload())).catch(_notification.default.exception)},handleGradeItemKeyUp=e=>{if(27!==e.keyCode)return;const editableSpan=e.target.closest("span.inplaceeditable.inplaceeditingon");editableSpan&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleGradeItemFocusOut=e=>{if(_config.default.behatsiterunning)return;const editableSpan=e.target.closest("span.inplaceeditable.inplaceeditingon");editableSpan&&(e.preventDefault(),stopEditingGadeItem(editableSpan))},handleGradeItemClick=e=>{const link=e.target.closest("a");link&&(link.dataset.actionDelete&&handleGradeItemDelete(e),link.dataset.actionEdit&&(e=>{e.preventDefault();const pending=new _pending.default("edit-quiz-grade-item-start"),editableSpan=e.target.closest("span.inplaceeditable");document.querySelectorAll("span.inplaceeditable.inplaceeditingon").forEach(stopEditingGadeItem),editableSpan.dataset.oldContent=editableSpan.innerHTML,(0,_str.get_string)("edittitleinstructions").then((instructions=>{const uniqueId="gi-edit-input-"+editableSpan.closest("tr").dataset.quizGradeItemId;editableSpan.innerHTML='<span class="editinstructions">'+instructions+'</span><label class="sr-only" for="'+uniqueId+'">'+editableSpan.dataset.editLabel+'</label><input type="text" id="'+uniqueId+'" value="'+editableSpan.dataset.rawName+'" class="ignoredirty form-control">';const inputElement=editableSpan.querySelector("input");return inputElement.focus(),inputElement.select(),editableSpan.classList.add("inplaceeditingon"),pending.resolve(),null})).catch(_notification.default.exception)})(e))},handleAddGradeItemClick=e=>{e.preventDefault();const pending=new _pending.default("create-quiz-grade-item");(0,_loadingicon.addIconToContainerRemoveOnCompletion)(e.target.parentNode,pending);const quizId=e.target.dataset.quizId;(0,_str.get_string)("gradeitemdefaultname","quiz").then((name=>((quizId,name)=>(0,_ajax.call)([{methodname:"mod_quiz_create_grade_items",args:{quizid:quizId,quizgradeitems:[{name:name}]}}])[0])(quizId,name))).then((()=>pending.resolve())).then((()=>window.location.reload())).catch(_notification.default.exception)};_exports.init=()=>{(()=>{const gradeItemTable=document.getElementById("mod_quiz-grade-item-list");gradeItemTable&&(gradeItemTable.addEventListener("click",handleGradeItemClick),gradeItemTable.addEventListener("keydown",handleGradeItemKeyDown),gradeItemTable.addEventListener("keyup",handleGradeItemKeyUp),gradeItemTable.addEventListener("focusout",handleGradeItemFocusOut)),document.getElementById("mod_quiz-add_grade_item").addEventListener("click",handleAddGradeItemClick)})()}}));
//# sourceMappingURL=edit_multiple_grades.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -22,19 +22,20 @@
*/
import {call as fetchMany} from 'core/ajax';
import MoodleConfig from 'core/config';
import {addIconToContainerRemoveOnCompletion} from 'core/loadingicon';
import Notification from 'core/notification';
import Pending from 'core/pending';
import {get_string as getString} from 'core/str';
/**
* Call the Ajax service to add a quiz grade item.
* Call the Ajax service to create a quiz grade item.
*
* @param {Number} quizId
* @param {String} name
* @return {Promise}
*/
const addGradeItem = (quizId, name) => fetchMany([{
const createGradeItem = (quizId, name) => fetchMany([{
methodname: 'mod_quiz_create_grade_items',
args: {
quizid: quizId,
@ -42,6 +43,22 @@ const addGradeItem = (quizId, name) => fetchMany([{
}
}])[0];
/**
* Call the Ajax service to update a quiz grade item.
*
* @param {Number} quizId
* @param {Number} gradeItemId
* @param {String} newName
* @return {Promise}
*/
const updateGradeItem = (quizId, gradeItemId, newName) => fetchMany([{
methodname: 'mod_quiz_update_grade_items',
args: {
quizid: quizId,
quizgradeitems: [{id: gradeItemId, name: newName}],
}
}])[0];
/**
* Call the Ajax service to delete a quiz grade item.
*
@ -75,12 +92,124 @@ const handleGradeItemDelete = (e) => {
deleteGradeItem(quizId, gradeItemId)
.then(() => pending.resolve())
.then(() => {
window.location.reload();
.then(() => window.location.reload())
.catch(Notification.exception);
};
/**
*
* @param {HTMLElement} editableSpan the editable to turn off.
*/
const stopEditingGadeItem = (editableSpan) => {
editableSpan.innerHTML = editableSpan.dataset.oldContent;
delete editableSpan.dataset.oldContent;
editableSpan.classList.remove('inplaceeditingon');
editableSpan.querySelector('[data-action-edit]').focus();
};
/**
* Handle click events on the start rename icon.
*
* @param {Event} e click event.
*/
const handleGradeItemEditStart = (e) => {
e.preventDefault();
const pending = new Pending('edit-quiz-grade-item-start');
const editableSpan = e.target.closest('span.inplaceeditable');
document.querySelectorAll('span.inplaceeditable.inplaceeditingon').forEach(stopEditingGadeItem);
editableSpan.dataset.oldContent = editableSpan.innerHTML;
getString('edittitleinstructions')
.then((instructions) => {
const uniqueId = 'gi-edit-input-' + editableSpan.closest('tr').dataset.quizGradeItemId;
editableSpan.innerHTML = '<span class="editinstructions">' + instructions + '</span>' +
'<label class="sr-only" for="' + uniqueId + '">' + editableSpan.dataset.editLabel + '</label>' +
'<input type="text" id="' + uniqueId + '" value="' + editableSpan.dataset.rawName +
'" class="ignoredirty form-control">';
const inputElement = editableSpan.querySelector('input');
inputElement.focus();
inputElement.select();
editableSpan.classList.add('inplaceeditingon');
pending.resolve();
return null;
})
.catch(Notification.exception);
};
/**
* Handle key down in the editable.
*
* @param {Event} e key event.
*/
const handleGradeItemKeyDown = (e) => {
if (e.keyCode !== 13) {
return;
}
const editableSpan = e.target.closest('span.inplaceeditable.inplaceeditingon');
if (!editableSpan) {
return;
}
e.preventDefault();
const pending = new Pending('edit-quiz-grade-item-save');
const newName = editableSpan.querySelector('input').value;
const tableCell = e.target.closest('th');
addIconToContainerRemoveOnCompletion(tableCell, pending);
const tableRow = tableCell.closest('tr');
const quizId = tableRow.closest('table').dataset.quizId;
const gradeItemId = tableRow.dataset.quizGradeItemId;
updateGradeItem(quizId, gradeItemId, newName)
.then(() => pending.resolve())
.then(() => window.location.reload())
.catch(Notification.exception);
};
/**
* Handle key up in the editable.
*
* @param {Event} e key event.
*/
const handleGradeItemKeyUp = (e) => {
if (e.keyCode !== 27) {
return;
}
const editableSpan = e.target.closest('span.inplaceeditable.inplaceeditingon');
if (!editableSpan) {
return;
}
e.preventDefault();
stopEditingGadeItem(editableSpan);
};
/**
* Handle focus out of the editable.
*
* @param {Event} e event.
*/
const handleGradeItemFocusOut = (e) => {
if (MoodleConfig.behatsiterunning) {
// Behat triggers focusout too often so ignore.
return;
}
const editableSpan = e.target.closest('span.inplaceeditable.inplaceeditingon');
if (!editableSpan) {
return;
}
e.preventDefault();
stopEditingGadeItem(editableSpan);
};
/**
* Handle clicks in the table the shows the grade items.
*
@ -96,6 +225,10 @@ const handleGradeItemClick = (e) => {
if (link.dataset.actionDelete) {
handleGradeItemDelete(e);
}
if (link.dataset.actionEdit) {
handleGradeItemEditStart(e);
}
};
/**
@ -105,17 +238,15 @@ const handleGradeItemClick = (e) => {
*/
const handleAddGradeItemClick = (e) => {
e.preventDefault();
const pending = new Pending('delete-quiz-grade-item');
const pending = new Pending('create-quiz-grade-item');
addIconToContainerRemoveOnCompletion(e.target.parentNode, pending);
const quizId = e.target.dataset.quizId;
getString('gradeitemdefaultname', 'quiz')
.then((name) => addGradeItem(quizId, name))
.then((name) => createGradeItem(quizId, name))
.then(() => pending.resolve())
.then(() => {
window.location.reload();
})
.then(() => window.location.reload())
.catch(Notification.exception);
};
@ -126,6 +257,9 @@ const registerEventListeners = () => {
const gradeItemTable = document.getElementById('mod_quiz-grade-item-list');
if (gradeItemTable) {
gradeItemTable.addEventListener('click', handleGradeItemClick);
gradeItemTable.addEventListener('keydown', handleGradeItemKeyDown);
gradeItemTable.addEventListener('keyup', handleGradeItemKeyUp);
gradeItemTable.addEventListener('focusout', handleGradeItemFocusOut);
}
document.getElementById('mod_quiz-add_grade_item').addEventListener('click', handleAddGradeItemClick);

View File

@ -442,6 +442,7 @@ $string['gradeessays'] = 'Grade essays';
$string['gradehighest'] = 'Highest grade';
$string['gradeitemdefaultname'] = 'New grade item';
$string['gradeitemdelete'] = 'Delete grade item {$a}';
$string['gradeitemedit'] = 'Edit name of grade item {$a}';
$string['gradeitemnewname'] = 'New name for grade item {$a}';
$string['gradeitems'] = 'Grade items';
$string['gradeitemsetup'] = 'Quiz grading setup (advanced view)';

View File

@ -69,9 +69,9 @@
<tr data-quiz-grade-item-id="{{id}}">
<th scope="row">
<span class="inplaceeditable inplaceeditable-text"
data-raw-name="{{name}}" data-editlabel="{{#str}} gradeitemnewname, quiz, {{{displayname}}} {{/str}}">
<a href="#" class="quickeditlink aalink">
{{{displayname}}}
data-raw-name="{{name}}" data-edit-label="{{#str}} gradeitemnewname, quiz, {{{displayname}}} {{/str}}">
<a href="#" class="quickeditlink" data-action-edit="1" title="{{#str}} gradeitemedit, quiz, {{{displayname}}} {{/str}}">
<span class="displayvalue">{{{displayname}}}</span>
<span class="quickediticon">
{{#pix}}t/editstring, core{{/pix}}
</span>

View File

@ -44,7 +44,7 @@ Feature: Setup multiple grades for a quiz
And "Delete" "icon" should exist in the "Unused grade item" "table_row"
@javascript
Scenario: A grade item can be created
Scenario: A grade item can be created and renamed
Given quiz "Quiz 1" contains the following questions:
| question | page |
| Question A | 1 |
@ -52,6 +52,26 @@ Feature: Setup multiple grades for a quiz
And I should see "This quiz does not yet have any grade items defined"
And I press "Add grade item"
Then "New grade item" "table_row" should exist
And I click on "Edit" "link" in the "New grade item" "table_row"
And I set the field "New name for grade item" to "Intelligence"
And I press enter
And I should not see "New grade item"
And "Intelligence" "table_row" should exist
@javascript
Scenario: Editing the name of a grade item can be cancelled
Given the following "mod_quiz > grade items" exist:
| quiz | name |
| Quiz 1 | Intuition |
And quiz "Quiz 1" contains the following questions:
| question | page |
| Question A | 1 |
When I am on the "Quiz 1" "mod_quiz > multiple grades setup" page logged in as teacher
And I click on "Edit" "link" in the "Intuition" "table_row"
And I set the field "New name for grade item" to "Intelligence"
And I press the escape key
And I should not see "Intelligence"
And "Intuition" "table_row" should exist
@javascript
Scenario: Unused grade items can be deleted