From 94a88b461aec6e8b94a7b93c48392201d962def6 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Thu, 5 Feb 2026 14:14:47 +0800 Subject: [PATCH 1/5] MDL-81608 core: Label YUI dragdrop handles appropriately The YUI dragdrop handles rely on the button's title attribute for its accessible label. We should label it appropriately using the aria-label attribute. --- .../build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js | 1 + .../build/moodle-core-dragdrop/moodle-core-dragdrop-min.js | 4 ++-- .../yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js | 1 + public/lib/yui/src/dragdrop/js/dragdrop.js | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js b/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js index 0d0613b4271..9883e6137a9 100644 --- a/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js +++ b/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js @@ -183,6 +183,7 @@ Y.extend(DRAGDROP, Y.Base, { var dragelement = Y.Node.create('') .addClass(classname) .setAttribute('title', title) + .setAttribute('aria-label', title) .setAttribute('tabIndex', 0) .setAttribute('data-draggroups', this.groups) .setAttribute('role', 'button'); diff --git a/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js b/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js index 19dffe2c308..51c5acb1843 100644 --- a/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js +++ b/public/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-core-dragdrop",function(h,t){var d="moodle-core-dragdrop-draghandle",e=function(){e.superclass.constructor.apply(this,arguments)};h.extend(e,h.Base,{goingup:null,absgoingup:null,samenodeclass:null,parentnodeclass:null,samenodelabel:null,parentnodelabel:null,groups:[],lastdroptarget:null,detectkeyboarddirection:!1,listeners:null,initializer:function(){this.listeners=[],this.listeners.push(h.DD.DDM.on("drag:start",this.global_drag_start,this)),this.listeners.push(h.DD.DDM.on("drag:over",this.globalDragOver,this)),this.listeners.push(h.DD.DDM.on("drag:end",this.global_drag_end,this)),this.listeners.push(h.DD.DDM.on("drag:drag",this.global_drag_drag,this)),this.listeners.push(h.DD.DDM.on("drop:over",this.global_drop_over,this)),this.listeners.push(h.DD.DDM.on("drop:hit",this.global_drop_hit,this)),this.listeners.push(h.DD.DDM.on("drag:dropmiss",this.global_drag_dropmiss,this)),this.listeners.push(h.one(h.config.doc.body).delegate("key",this.global_keydown,"down:32, enter, esc","."+d,this)),this.listeners.push(h.one(h.config.doc.body).delegate("click",this.global_keydown,"."+d,this))},destructor:function(){new h.EventHandle(this.listeners).detach()},get_drag_handle:function(t,e,o){var r=h.Node.create("").addClass(e).setAttribute("title",t).setAttribute("tabIndex",0).setAttribute("data-draggroups",this.groups).setAttribute("role","button");return r.addClass(d),window.require(["core/templates"],function(t){t.renderPix("i/move_2d","core").then(function(t){t=h.Node.create(t);t.setStyle("cursor","move"),void 0!==o&&t.addClass(o),r.appendChild(t)})}),r},lock_drag_handle:function(t,e){t.removeHandle("."+e)},unlock_drag_handle:function(t,e){t.addHandle("."+e),t.get("activeHandle").focus()},ajax_failure:function(t){t={name:t.status+" "+t.statusText,message:t.responseText};return new M.core.exception(t)},in_group:function(e){var o=!1;return h.each(this.groups,function(t){e._groups[t]&&(o=!0)},this),o},global_drag_start:function(t){var e=t.target;this.in_group(e)&&(this.originalstyle=e.get("node").getAttribute("style"),e.get("node").setStyle("opacity",".25"),e.get("dragNode").setStyles({opacity:".75",borderColor:e.get("node").getStyle("borderColor"),backgroundColor:e.get("node").getStyle("backgroundColor")}),e.get("dragNode").empty(),this.drag_start(t))},globalDragOver:function(t){this.dragOver(t)},global_drag_end:function(t){var e=t.target;this.in_group(e)&&(e.get("node").setAttribute("style",this.originalstyle),this.drag_end(t))},global_drag_drag:function(t){var e=t.target,o=t.info;this.in_group(e)&&(o.start[1]o.xy[1]&&(this.absgoingup=!1),o.delta[1]<0?this.goingup=!0:0")).addClass("dragdrop-keyboard-drag ps-2"),h.all("."+this.samenodeclass+", ."+this.parentnodeclass).each(function(t){var e,o,r,a=!1,n=t,i=t.getAttribute("class").split(" ").join(", .");if(t.drop&&t.drop.inGroup(this.groups)&&t.drop.get("node")!==d&&(t.next(i)!==d||this.detectkeyboarddirection))a=!0;else for(e=t.getAttribute("data-draggroups").split(" "),o=0;o"),p=h.Node.create(""),s=this.find_element_text(n),u=this.samenodelabel&&t.hasClass(this.samenodeclass)?M.util.get_string(this.samenodelabel.identifier,this.samenodelabel.component,s):this.parentnodelabel&&t.hasClass(this.parentnodeclass)?M.util.get_string(this.parentnodelabel.identifier,this.parentnodelabel.component,s):M.util.get_string("tocontent","moodle",s),p.setContent(u),p.setAttribute("class","aalink d-inline-block mw-100 text-truncate"),p.setAttribute("data-drop-target",t.get("id")),p.setAttribute("tabindex","0"),p.setAttribute("role","button"),p.on("click",this.global_keyboard_drop,this),p.on("key",this.global_keyboard_drop,"down:enter,32",this),g.append(p),l.append(g))},this),M.core.dragdrop.dropui=new M.core.dialogue({headerContent:e,bodyContent:l,draggable:!0,visible:!0,center:!0,modal:!0}),M.core.dragdrop.dropui.after("visibleChange",function(t){t.prevVal&&!t.newVal&&this.global_cancel_keyboard_drag()},this),l.one("a")&&l.one("a").focus()},simulated_drag_drop_event:function(t,e){var o=function(t){this.node=t};o.prototype.get=function(t){return"node"===t||"dragNode"===t||"dropNode"===t?this.node:"activeHandle"===t?this.node.one(".editing_move"):null},o.prototype.inGroup=function(){return!0},o.prototype.addHandle=function(){},o.prototype.removeHandle=function(){},this.drop=new o(e),this.drag=new o(t),this.target=this.drop},global_keyboard_drop:function(t){var e, -o=M.core.dragdrop.keydragcontainer,r=h.one("#"+t.target.getAttribute("data-drop-target"));M.core.dragdrop.dropui.hide(),t.preventDefault(),this.detectkeyboarddirection&&o.getY()>r.getY()?(this.absgoingup=!0,this.goingup=!0):(this.absgoingup=!1,this.goingup=!1),t=new this.simulated_drag_drop_event(o,o),e=new this.simulated_drag_drop_event(o,r),this.drag_start(t),this.global_drop_over(e),r.hasClass(this.parentnodeclass)&&r.contains(o)&&r.prepend(o),this.global_drop_hit(e)},global_cancel_keyboard_drag:function(){M.core.dragdrop.keydragcontainer&&(M.core.dragdrop.keydraghandle.focus(),M.core.dragdrop.keydragcontainer=null),M.core.dragdrop.dropui&&M.core.dragdrop.dropui.destroy()},global_keydown:function(t){var e,o,r,a,n,i=t.target.ancestor("."+d,!0);if(null!==i){if(27===t.keyCode)return this.global_cancel_keyboard_drag(),void t.preventDefault();if(i.hasClass(d)&&(13===t.keyCode||32===t.keyCode||"click"===t.type)){for(o=i.getAttribute("data-draggroups").split(" "),n=!1,r=0;r").addClass(e).setAttribute("title",t).setAttribute("aria-label",t).setAttribute("tabIndex",0).setAttribute("data-draggroups",this.groups).setAttribute("role","button");return o.addClass(d),window.require(["core/templates"],function(t){t.renderPix("i/move_2d","core").then(function(t){t=h.Node.create(t);t.setStyle("cursor","move"),void 0!==r&&t.addClass(r),o.appendChild(t)})}),o},lock_drag_handle:function(t,e){t.removeHandle("."+e)},unlock_drag_handle:function(t,e){t.addHandle("."+e),t.get("activeHandle").focus()},ajax_failure:function(t){t={name:t.status+" "+t.statusText,message:t.responseText};return new M.core.exception(t)},in_group:function(e){var r=!1;return h.each(this.groups,function(t){e._groups[t]&&(r=!0)},this),r},global_drag_start:function(t){var e=t.target;this.in_group(e)&&(this.originalstyle=e.get("node").getAttribute("style"),e.get("node").setStyle("opacity",".25"),e.get("dragNode").setStyles({opacity:".75",borderColor:e.get("node").getStyle("borderColor"),backgroundColor:e.get("node").getStyle("backgroundColor")}),e.get("dragNode").empty(),this.drag_start(t))},globalDragOver:function(t){this.dragOver(t)},global_drag_end:function(t){var e=t.target;this.in_group(e)&&(e.get("node").setAttribute("style",this.originalstyle),this.drag_end(t))},global_drag_drag:function(t){var e=t.target,r=t.info;this.in_group(e)&&(r.start[1]r.xy[1]&&(this.absgoingup=!1),r.delta[1]<0?this.goingup=!0:0")).addClass("dragdrop-keyboard-drag ps-2"),h.all("."+this.samenodeclass+", ."+this.parentnodeclass).each(function(t){var e,r,o,a=!1,n=t,i=t.getAttribute("class").split(" ").join(", .");if(t.drop&&t.drop.inGroup(this.groups)&&t.drop.get("node")!==d&&(t.next(i)!==d||this.detectkeyboarddirection))a=!0;else for(e=t.getAttribute("data-draggroups").split(" "),r=0;r"),p=h.Node.create(""),s=this.find_element_text(n),u=this.samenodelabel&&t.hasClass(this.samenodeclass)?M.util.get_string(this.samenodelabel.identifier,this.samenodelabel.component,s):this.parentnodelabel&&t.hasClass(this.parentnodeclass)?M.util.get_string(this.parentnodelabel.identifier,this.parentnodelabel.component,s):M.util.get_string("tocontent","moodle",s),p.setContent(u),p.setAttribute("class","aalink d-inline-block mw-100 text-truncate"),p.setAttribute("data-drop-target",t.get("id")),p.setAttribute("tabindex","0"),p.setAttribute("role","button"),p.on("click",this.global_keyboard_drop,this),p.on("key",this.global_keyboard_drop,"down:enter,32",this),g.append(p),l.append(g))},this),M.core.dragdrop.dropui=new M.core.dialogue({headerContent:e,bodyContent:l,draggable:!0,visible:!0,center:!0,modal:!0}),M.core.dragdrop.dropui.after("visibleChange",function(t){t.prevVal&&!t.newVal&&this.global_cancel_keyboard_drag()},this),l.one("a")&&l.one("a").focus()},simulated_drag_drop_event:function(t,e){var r=function(t){this.node=t};r.prototype.get=function(t){return"node"===t||"dragNode"===t||"dropNode"===t?this.node:"activeHandle"===t?this.node.one(".editing_move"):null},r.prototype.inGroup=function(){return!0},r.prototype.addHandle=function(){},r.prototype.removeHandle=function(){},this.drop=new r(e),this.drag=new r(t),this.target=this.drop},global_keyboard_drop:function(t){ +var e,r=M.core.dragdrop.keydragcontainer,o=h.one("#"+t.target.getAttribute("data-drop-target"));M.core.dragdrop.dropui.hide(),t.preventDefault(),this.detectkeyboarddirection&&r.getY()>o.getY()?(this.absgoingup=!0,this.goingup=!0):(this.absgoingup=!1,this.goingup=!1),t=new this.simulated_drag_drop_event(r,r),e=new this.simulated_drag_drop_event(r,o),this.drag_start(t),this.global_drop_over(e),o.hasClass(this.parentnodeclass)&&o.contains(r)&&o.prepend(r),this.global_drop_hit(e)},global_cancel_keyboard_drag:function(){M.core.dragdrop.keydragcontainer&&(M.core.dragdrop.keydraghandle.focus(),M.core.dragdrop.keydragcontainer=null),M.core.dragdrop.dropui&&M.core.dragdrop.dropui.destroy()},global_keydown:function(t){var e,r,o,a,n,i=t.target.ancestor("."+d,!0);if(null!==i){if(27===t.keyCode)return this.global_cancel_keyboard_drag(),void t.preventDefault();if(i.hasClass(d)&&(13===t.keyCode||32===t.keyCode||"click"===t.type)){for(r=i.getAttribute("data-draggroups").split(" "),n=!1,o=0;o') .addClass(classname) .setAttribute('title', title) + .setAttribute('aria-label', title) .setAttribute('tabIndex', 0) .setAttribute('data-draggroups', this.groups) .setAttribute('role', 'button'); diff --git a/public/lib/yui/src/dragdrop/js/dragdrop.js b/public/lib/yui/src/dragdrop/js/dragdrop.js index b93cc627ec1..b03b000cfef 100644 --- a/public/lib/yui/src/dragdrop/js/dragdrop.js +++ b/public/lib/yui/src/dragdrop/js/dragdrop.js @@ -181,6 +181,7 @@ Y.extend(DRAGDROP, Y.Base, { var dragelement = Y.Node.create('') .addClass(classname) .setAttribute('title', title) + .setAttribute('aria-label', title) .setAttribute('tabIndex', 0) .setAttribute('data-draggroups', this.groups) .setAttribute('role', 'button'); From cbd7b7da6dcb39467a4b9f41f5482f857a4336c7 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Fri, 6 Feb 2026 17:39:12 +0800 Subject: [PATCH 2/5] MDL-81608 quiz: Adjust size and spacing for the quiz edit page buttons * In order to meet WCAG 2.2 criterion for minimum target size and make it easier for users who have mobility limitations to use the quiz editing page, we need to use the Bootstrap icon button styles and increase the size and the spacing for the following buttons: * Add/remove page break button * Lock/unlock question dependency button * This commit also makes the icons within the buttons as decorative and moves the accessible name to the button elements themselves via the aria-label attribute. * Adjust Behat tests for checking the dependency buttons. * Add accessibility Behat coverage for the quiz edit page. --- .../mod/quiz/classes/output/edit_renderer.php | 43 ++++++++++++++----- public/mod/quiz/styles.css | 13 +++--- .../behat/editing_require_previous.feature | 23 +++++----- .../moodle-mod_quiz-dragdrop-debug.js | 4 +- .../moodle-mod_quiz-dragdrop-min.js | 4 +- .../moodle-mod_quiz-dragdrop.js | 4 +- .../moodle-mod_quiz-util-slot-debug.js | 27 ++++++------ .../moodle-mod_quiz-util-slot-min.js | 2 +- .../moodle-mod_quiz-util-slot.js | 27 ++++++------ .../mod/quiz/yui/src/dragdrop/js/resource.js | 4 +- public/mod/quiz/yui/src/util/js/slot.js | 27 ++++++------ 11 files changed, 101 insertions(+), 77 deletions(-) diff --git a/public/mod/quiz/classes/output/edit_renderer.php b/public/mod/quiz/classes/output/edit_renderer.php index ce4277983f6..163e13605bf 100644 --- a/public/mod/quiz/classes/output/edit_renderer.php +++ b/public/mod/quiz/classes/output/edit_renderer.php @@ -998,9 +998,21 @@ class edit_renderer extends \plugin_renderer_base { if (!$structure->can_be_edited()) { $disabled = 'disabled'; } - return html_writer::span($this->action_link($url, $image, null, ['title' => $title, - 'class' => 'page_split_join cm-edit-action', 'disabled' => $disabled, 'data-action' => $action]), - 'page_split_join_wrapper'); + return html_writer::span( + $this->action_link( + $url, + $image, + null, + [ + 'title' => $title, + 'aria-label' => $title, + 'class' => 'page_split_join cm-edit-action btn btn-sm icon-no-margin', + 'disabled' => $disabled, + 'data-action' => $action, + ] + ), + 'page_split_join_wrapper' + ); } /** @@ -1018,13 +1030,11 @@ class edit_renderer extends \plugin_renderer_base { ]; if ($structure->is_question_dependent_on_previous_slot($slot)) { $title = get_string('questiondependencyremove', 'quiz', $a); - $image = $this->pix_icon('t/locked', get_string('questiondependsonprevious', 'quiz'), - 'moodle', ['title' => '']); + $image = $this->pix_icon('t/locked', ''); $action = 'removedependency'; } else { $title = get_string('questiondependencyadd', 'quiz', $a); - $image = $this->pix_icon('t/unlocked', get_string('questiondependencyfree', 'quiz'), - 'moodle', ['title' => '']); + $image = $this->pix_icon('t/unlocked', ''); $action = 'adddependency'; } @@ -1037,9 +1047,22 @@ class edit_renderer extends \plugin_renderer_base { if (!$structure->can_question_depend_on_previous_slot($slot)) { $extraclass = ' question_dependency_cannot_depend'; } - return html_writer::span($this->action_link('#', $image, null, ['title' => $title, - 'class' => 'cm-edit-action', 'disabled' => $disabled, 'data-action' => $action]), - 'question_dependency_wrapper' . $extraclass); + return html_writer::span( + $this->action_link( + '#', + $image, + null, + [ + 'title' => $title, + 'aria-label' => $title, + 'class' => 'cm-edit-action btn btn-link btn-sm icon-no-margin', + 'disabled' => $disabled, + 'data-action' => $action, + 'role' => 'button', + ] + ), + 'question_dependency_wrapper' . $extraclass + ); } /** diff --git a/public/mod/quiz/styles.css b/public/mod/quiz/styles.css index 33ab3fe8db2..9aad4c6e943 100644 --- a/public/mod/quiz/styles.css +++ b/public/mod/quiz/styles.css @@ -615,7 +615,7 @@ table.quizreviewsummary td.cell { #page-mod-quiz-edit ul.section { margin: 0; - padding: 0 20px; + padding: 0 38px; } #page-mod-quiz-edit ul.slots { @@ -688,8 +688,11 @@ table.quizreviewsummary td.cell { #page-mod-quiz-edit ul.slots li.section li.activity .page_split_join_wrapper { position: absolute; - left: -20px; - bottom: -11px; + left: -38px; + bottom: -18px; + .icon { + width: auto; + } } #page-mod-quiz-edit ul.slots li.section li.activity .instancemaxmark.decimalplaces_0 { @@ -742,8 +745,8 @@ table.quizreviewsummary td.cell { #page-mod-quiz-edit ul.slots li.section li.activity .question_dependency_wrapper { position: absolute; - top: -1em; - right: -20px; + top: -1.25em; + right: -38px; } #page-mod-quiz-edit ul.slots li.section li.activity .question_dependency_wrapper.question_dependency_cannot_depend { diff --git a/public/mod/quiz/tests/behat/editing_require_previous.feature b/public/mod/quiz/tests/behat/editing_require_previous.feature index a0c07568928..01b49712f31 100644 --- a/public/mod/quiz/tests/behat/editing_require_previous.feature +++ b/public/mod/quiz/tests/behat/editing_require_previous.feature @@ -51,7 +51,7 @@ Feature: Edit quizzes where some questions require the previous one to have been | TF1 | 1 | 0 | | TF2 | 1 | 1 | And I am on the "Quiz 1" "mod_quiz > Edit" page - Then "This question cannot be attempted until the previous question has been completed." "link" should be visible + Then "Question 2 cannot be attempted until the previous question 1 has been completed" "button" should be visible @javascript Scenario: A question can depend on a random question @@ -67,9 +67,9 @@ Feature: Edit quizzes where some questions require the previous one to have been | Random (Test questions) | 1 | 0 | | TF1 | 1 | 1 | And I am on the "Quiz 1" "mod_quiz > Edit" page - Then "This question cannot be attempted until the previous question has been completed." "link" should be visible + Then "Question 2 cannot be attempted until the previous question 1 has been completed" "button" should be visible - @javascript + @javascript @accessibility Scenario: The second question can be set to depend on the first Given the following "activities" exist: | activity | name | intro | course | idnumber | preferredbehaviour | @@ -85,9 +85,10 @@ Feature: Edit quizzes where some questions require the previous one to have been | TF2 | 1 | 0 | | TF3 | 1 | 0 | And I am on the "Quiz 1" "mod_quiz > Edit" page - When I follow "No restriction on when question 2 can be attempted • Click to change" - Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "link" should be visible - And "No restriction on when question 3 can be attempted • Click to change" "link" should be visible + When I click on "No restriction on when question 2 can be attempted • Click to change" "button" + Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "button" should be visible + And "No restriction on when question 3 can be attempted • Click to change" "button" should be visible + And the "region-main" "region" should meet accessibility standards with "best-practice" extra tests @javascript Scenario: A question that did depend on the previous can be un-linked @@ -105,9 +106,9 @@ Feature: Edit quizzes where some questions require the previous one to have been | TF2 | 1 | 1 | | TF3 | 1 | 1 | And I am on the "Quiz 1" "mod_quiz > Edit" page - When I follow "Question 3 cannot be attempted until the previous question 2 has been completed • Click to change" - Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "link" should be visible - And "No restriction on when question 3 can be attempted • Click to change" "link" should be visible + When I click on "Question 3 cannot be attempted until the previous question 2 has been completed • Click to change" "button" + Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "button" should be visible + And "No restriction on when question 3 can be attempted • Click to change" "button" should be visible @javascript Scenario: Question dependency cannot apply to deferred feedback quizzes so UI is hidden @@ -232,6 +233,6 @@ Feature: Edit quizzes where some questions require the previous one to have been | TF3 | 1 | 1 | And I am on the "Quiz 1" "mod_quiz > Edit" page When I move "TF1" to "After Question 3" in the quiz by clicking the move icon - Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "link" should be visible - And "No restriction on when question 3 can be attempted • Click to change" "link" should be visible + Then "Question 2 cannot be attempted until the previous question 1 has been completed • Click to change" "button" should be visible + And "No restriction on when question 3 can be attempted • Click to change" "button" should be visible And "be attempted" "link" in the "TF2" "list_item" should not be visible diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js index 77bb876564f..5c79980c446 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js @@ -384,8 +384,8 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { - var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), - CSS.EDITINGMOVE, CSS.ICONCLASS, true); + var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE; + var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), dragHandleClass, CSS.ICONCLASS); move.replace(resourcedraghandle); } }, this); diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js index 47197659f29..70d5b88def8 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-mod_quiz-dragdrop",function(g,e){var o,u=".actions",t="activity",p="mod-quiz-edit-content",r="editing_move",s="iconsmall",c="jumpmenu",n="left",h="movedown",_="moveup",m="page-content",d="right",f="slots",a="section-handle",v="slots",l="sectiondraggable",z="li.page",q="li.slot",i=function(){i.superclass.constructor.apply(this,arguments)};g.extend(i,M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[l],this.samenodeclass="section",this.parentnodeclass="slots",g.Node.one("."+c))return!1;var e;this.sectionlistselector="li.section",this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+l,target:!0,handles:["."+n],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+m,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,i,s=g.Moodle.core_course.util.section.getId(e);0'),t=g.Node.create('
    ');t.setStyle("margin",0),t.setContent(e.get("node").get("innerHTML")),o.appendChild(t),e.get("dragNode").setContent(o),e.get("dragNode").addClass(p)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" li.section",o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(d){var r,a,e,n,o,t=d.drag,c=t.get("node"),i=g.Moodle.core_course.util.section.getId(c),l=i,s=this.get_section_index(c),u=s;if(i!==s){for(o in ug.Moodle.core_course.util.section.getId(r.item(i))&&(n=r.item(i-1).get("id"),r.item(i-1).set("id",r.item(i).get("id")),r.item(i).set("id",n),M.mod_quiz.edit.swap_sections(g,i-1,i),s=!0)}while(u-=1,s);window.setTimeout(function(){a.hide()},250)},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"mod_quiz-dragdrop-section",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_section_dragdrop=function(e){new i(e)},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=t,this.parentnodeclass=f,this.samenodelabel={identifier:"dragtoafter",component:"quiz"},this.parentnodelabel={identifier:"dragtostart",component:"quiz"},this.setup_for_section(),e="li."+t,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+r],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+v}),e.dd.plug(g.Plugin.DDWinScroll),M.mod_quiz.quizbase.register_module(this),M.mod_quiz.dragres=this},setup_for_section:function(){g.Node.all(".mod-quiz-edit-content ul.slots ul.section").each(function(e){e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("li.activity")},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o,e=e.one("a."+r);e&&(o=this.get_drag_handle(M.util.get_string("move","moodle"),r,s,!0),e.replace(o))},this)},drag_start:function(e){e=e.target;e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all(".icon").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t=e.drag,i=t.get("node"),e=i.one(u),s=M.util.add_spinner(g,e),n={},d=this.get("config").pageparams;for(o in d)n[o]=d[o];n.sesskey=M.cfg.sesskey,n.courseid=this.get("courseid"),n.quizid=this.get("quizid"),n["class"]="resource",n.field="move",n.id=Number(g.Moodle.mod_quiz.util.slot.getId(i)),n.sectionId=g.Moodle.core_course.util.section.getId(i.ancestor("li.section",!0)),(e=i.previous(q))&&(n.previousid=Number(g.Moodle.mod_quiz.util.slot.getId(e))),(e=i.previous(z))&&(n.page=Number(g.Moodle.mod_quiz.util.page.getId(e))),e=M.cfg.wwwroot+this.get("ajaxurl"),g.io(e,{method:"POST",data:n,on:{start:function(){this.lock_drag_handle(t,r),s.show()},success:function(e,o){o=g.JSON.parse(o.responseText),o={element:i,visible:o.visible};M.mod_quiz.quizbase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(t,r),window.setTimeout(function(){s.hide()},250),M.mod_quiz.resource_toolbox.reorganise_edit_page()},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(t,a),s.hide(),window.location.reload(!0)}},context:this})},global_drop_over:function(e){var o,t,i;e.drop&&e.drop.inGroup(this.groups)&&(o=e.drag.get("node"),t=e.drop.get("node"),this.lastdroptarget=e.drop,t.hasClass(this.samenodeclass)?(i=this.goingup?"before":"after",t.insert(o,i)):!t.hasClass(this.parentnodeclass)&&!t.test('[data-droptarget="1"]')||t.contains(o)||(this.goingup?t.append(o):t.prepend(o)),this.drop_over(e))}},{NAME:"mod_quiz-dragdrop-resource",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}), -M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_resource_dragdrop=function(e){new o(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-mod_quiz-quizbase","moodle-mod_quiz-util-base","moodle-mod_quiz-util-page","moodle-mod_quiz-util-slot","moodle-course-util"]}); \ No newline at end of file +YUI.add("moodle-mod_quiz-dragdrop",function(g,e){var o,u=".actions",t="activity",p="mod-quiz-edit-content",r="editing_move",s="iconsmall",c="jumpmenu",n="left",h="movedown",_="moveup",m="page-content",d="right",f="slots",a="section-handle",v="slots",l="sectiondraggable",z="li.page",q="li.slot",i=function(){i.superclass.constructor.apply(this,arguments)};g.extend(i,M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[l],this.samenodeclass="section",this.parentnodeclass="slots",g.Node.one("."+c))return!1;var e;this.sectionlistselector="li.section",this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+l,target:!0,handles:["."+n],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+m,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,i,s=g.Moodle.core_course.util.section.getId(e);0'),t=g.Node.create('
      ');t.setStyle("margin",0),t.setContent(e.get("node").get("innerHTML")),o.appendChild(t),e.get("dragNode").setContent(o),e.get("dragNode").addClass(p)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" li.section",o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(d){var r,a,e,n,o,t=d.drag,c=t.get("node"),i=g.Moodle.core_course.util.section.getId(c),l=i,s=this.get_section_index(c),u=s;if(i!==s){for(o in ug.Moodle.core_course.util.section.getId(r.item(i))&&(n=r.item(i-1).get("id"),r.item(i-1).set("id",r.item(i).get("id")),r.item(i).set("id",n),M.mod_quiz.edit.swap_sections(g,i-1,i),s=!0)}while(u-=1,s);window.setTimeout(function(){a.hide()},250)},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"mod_quiz-dragdrop-section",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_section_dragdrop=function(e){new i(e)},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=t,this.parentnodeclass=f,this.samenodelabel={identifier:"dragtoafter",component:"quiz"},this.parentnodelabel={identifier:"dragtostart",component:"quiz"},this.setup_for_section(),e="li."+t,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+r],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+v}),e.dd.plug(g.Plugin.DDWinScroll),M.mod_quiz.quizbase.register_module(this),M.mod_quiz.dragres=this},setup_for_section:function(){g.Node.all(".mod-quiz-edit-content ul.slots ul.section").each(function(e){e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("li.activity")},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o,e=e.one("a."+r);e&&(o="btn px-1 icon-no-margin "+r,o=this.get_drag_handle(M.util.get_string("move","moodle"),o,s),e.replace(o))},this)},drag_start:function(e){e=e.target;e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all(".icon").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t=e.drag,i=t.get("node"),e=i.one(u),s=M.util.add_spinner(g,e),n={},d=this.get("config").pageparams;for(o in d)n[o]=d[o];n.sesskey=M.cfg.sesskey,n.courseid=this.get("courseid"),n.quizid=this.get("quizid"),n["class"]="resource",n.field="move",n.id=Number(g.Moodle.mod_quiz.util.slot.getId(i)),n.sectionId=g.Moodle.core_course.util.section.getId(i.ancestor("li.section",!0)),(e=i.previous(q))&&(n.previousid=Number(g.Moodle.mod_quiz.util.slot.getId(e))),(e=i.previous(z))&&(n.page=Number(g.Moodle.mod_quiz.util.page.getId(e))),e=M.cfg.wwwroot+this.get("ajaxurl"),g.io(e,{method:"POST",data:n,on:{start:function(){this.lock_drag_handle(t,r),s.show()},success:function(e,o){o=g.JSON.parse(o.responseText),o={element:i,visible:o.visible};M.mod_quiz.quizbase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(t,r),window.setTimeout(function(){s.hide()},250),M.mod_quiz.resource_toolbox.reorganise_edit_page()},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(t,a),s.hide(),window.location.reload(!0)}},context:this})},global_drop_over:function(e){var o,t,i;e.drop&&e.drop.inGroup(this.groups)&&(o=e.drag.get("node"),t=e.drop.get("node"),this.lastdroptarget=e.drop,t.hasClass(this.samenodeclass)?(i=this.goingup?"before":"after",t.insert(o,i)):!t.hasClass(this.parentnodeclass)&&!t.test('[data-droptarget="1"]')||t.contains(o)||(this.goingup?t.append(o):t.prepend(o)),this.drop_over(e))}},{NAME:"mod_quiz-dragdrop-resource",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{ +value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_resource_dragdrop=function(e){new o(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-mod_quiz-quizbase","moodle-mod_quiz-util-base","moodle-mod_quiz-util-page","moodle-mod_quiz-util-slot","moodle-course-util"]}); \ No newline at end of file diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js index d9f7a67875b..31cb2d0d747 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js @@ -378,8 +378,8 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { - var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), - CSS.EDITINGMOVE, CSS.ICONCLASS, true); + var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE; + var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), dragHandleClass, CSS.ICONCLASS); move.replace(resourcedraghandle); } }, this); diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js index 5653cef9c9e..0eecf257c1f 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js @@ -345,11 +345,10 @@ Y.Moodle.mod_quiz.util.slot = { // Update the link and image titles pagebreaklink.set('title', M.util.get_string(action, 'quiz')); + pagebreaklink.set('aria-label', M.util.get_string(action, 'quiz')); pagebreaklink.setData('action', action); // Update the image title. var icon = pagebreaklink.one(this.SELECTORS.ICON); - icon.set('title', M.util.get_string(action, 'quiz')); - icon.set('alt', M.util.get_string(action, 'quiz')); // Update the image src. icon.set('src', M.util.image_url(iconname)); @@ -417,27 +416,27 @@ Y.Moodle.mod_quiz.util.slot = { requiresprevious = link.getData('action') === 'removedependency'; } + var iconname = ''; if (requiresprevious) { link.set('title', M.util.get_string('questiondependencyremove', 'quiz', a)); + link.set('aria-label', M.util.get_string('questiondependencyremove', 'quiz', a)); link.setData('action', 'removedependency'); - window.require(['core/templates'], function(Templates) { - Templates.renderPix('t/locked', 'core', M.util.get_string('questiondependsonprevious', 'quiz')).then( - function(html) { - icon.replace(html); - } - ); - }); + iconname = 't/locked'; } else { link.set('title', M.util.get_string('questiondependencyadd', 'quiz', a)); + link.set('aria-label', M.util.get_string('questiondependencyadd', 'quiz', a)); link.setData('action', 'adddependency'); - window.require(['core/templates'], function(Templates) { - Templates.renderPix('t/unlocked', 'core', M.util.get_string('questiondependencyfree', 'quiz')).then( + iconname = 't/unlocked'; + } + window.require(['core/templates', 'core/notification'], function(Templates, Notification) { + Templates.renderPix(iconname, 'core', '') + .then( function(html) { icon.replace(html); + return; } - ); - }); - } + ).catch(Notification.exception); + }); } }; diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js index b062aafe066..9630a0909ab 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js @@ -1 +1 @@ -YUI.add("moodle-mod_quiz-util-slot",function(u,e){u.namespace("Moodle.mod_quiz.util.slot"),u.Moodle.mod_quiz.util.slot={CSS:{SLOT:"slot",QUESTIONTYPEDESCRIPTION:"qtype_description",CANNOT_DEPEND:"question_dependency_cannot_depend"},CONSTANTS:{SLOTIDPREFIX:"slot-",QUESTION:M.util.get_string("question","moodle")},SELECTORS:{SLOT:"li.slot",INSTANCENAME:".instancename",NUMBER:"span.slotnumber",PAGECONTENT:"div#page-content",PAGEBREAK:"span.page_split_join_wrapper",ICON:".icon",QUESTIONTYPEDESCRIPTION:".qtype_description",SECTIONUL:"ul.section",DEPENDENCY_WRAPPER:".question_dependency_wrapper",DEPENDENCY_LINK:".question_dependency_wrapper .cm-edit-action",DEPENDENCY_ICON:".question_dependency_wrapper .icon"},getSlotFromComponent:function(e){return u.one(e).ancestor(this.SELECTORS.SLOT,!0)},getId:function(e){e=e.get("id").replace(this.CONSTANTS.SLOTIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},getName:function(e){e=e.one(this.SELECTORS.INSTANCENAME);return e?e.get("firstChild").get("data"):null},getNumber:function(e){if(!e)return!1;e=e.one(this.SELECTORS.NUMBER).get("text").replace(this.CONSTANTS.QUESTION,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setNumber:function(e,t){e.one(this.SELECTORS.NUMBER).setHTML(''+this.CONSTANTS.QUESTION+" "+t)},getSlots:function(){return u.all(this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL+" "+this.SELECTORS.SLOT)},getNumberedSlots:function(){var e=this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL;return e+=" "+this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")",u.all(e)},getPrevious:function(e){return e.previous(this.SELECTORS.SLOT)},getPreviousNumbered:function(e){var t,i,n=e.previous(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")");if(n)return n;for(t=e.ancestor("li.section").previous("li.section");t;){if(0<(i=t.all(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")")).size())return i.item(i.size()-1);t=t.previous("li.section")}return!1},reorderSlots:function(){this.getSlots().each(function(e){var t,i;u.Moodle.mod_quiz.util.page.getPageFromSlot(e)||(t=e.next(u.Moodle.mod_quiz.util.page.SELECTORS.PAGE),e.swap(t)),t=this.getPreviousNumbered(e),i=0,e.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)||(t&&(i=this.getNumber(t)),this.setNumber(e,i+1))},this)},updateOneSlotSections:function(){u.all(".mod-quiz-edit-content ul.slots li.section").each(function(e){1'+this.CONSTANTS.QUESTION+" "+t)},getSlots:function(){return u.all(this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL+" "+this.SELECTORS.SLOT)},getNumberedSlots:function(){var e=this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL;return e+=" "+this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")",u.all(e)},getPrevious:function(e){return e.previous(this.SELECTORS.SLOT)},getPreviousNumbered:function(e){var t,i,n=e.previous(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")");if(n)return n;for(t=e.ancestor("li.section").previous("li.section");t;){if(0<(i=t.all(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")")).size())return i.item(i.size()-1);t=t.previous("li.section")}return!1},reorderSlots:function(){this.getSlots().each(function(e){var t,i;u.Moodle.mod_quiz.util.page.getPageFromSlot(e)||(t=e.next(u.Moodle.mod_quiz.util.page.SELECTORS.PAGE),e.swap(t)),t=this.getPreviousNumbered(e),i=0,e.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)||(t&&(i=this.getNumber(t)),this.setNumber(e,i+1))},this)},updateOneSlotSections:function(){u.all(".mod-quiz-edit-content ul.slots li.section").each(function(e){1 Date: Fri, 6 Feb 2026 19:20:29 +0800 Subject: [PATCH 3/5] MDL-81608 core: Add alt text to the YUI spinner image The accessibility Behat test added to the quiz editing page uncovered that the loading spinner icon does not have an appropriate alt text. --- public/lib/javascript-static.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/lib/javascript-static.js b/public/lib/javascript-static.js index c581376d533..e7147195900 100644 --- a/public/lib/javascript-static.js +++ b/public/lib/javascript-static.js @@ -826,6 +826,7 @@ M.util.add_spinner = function(Y, node) { var spinner = Y.Node.create('') .setAttribute('src', M.util.image_url(WAITICON.pix, WAITICON.component)) + .setAttribute('alt', M.util.get_string('loading', 'core')) .addClass('spinner icon') .hide(); From f06752f9c58974ee0fe627b18580fc8ed6d372fd Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Wed, 11 Feb 2026 18:15:31 +0800 Subject: [PATCH 4/5] MDL-81608 quiz: Set more meaningful button labels * Add/remove page break buttons * Move question buttons --- .../mod/quiz/classes/output/edit_renderer.php | 26 ++++++++++++------- public/mod/quiz/lang/en/quiz.php | 3 +++ .../mod/quiz/tests/behat/behat_mod_quiz.php | 2 +- .../moodle-mod_quiz-dragdrop-debug.js | 3 ++- .../moodle-mod_quiz-dragdrop-min.js | 2 +- .../moodle-mod_quiz-dragdrop.js | 3 ++- .../moodle-mod_quiz-util-slot-debug.js | 8 ++++-- .../moodle-mod_quiz-util-slot-min.js | 2 +- .../moodle-mod_quiz-util-slot.js | 8 ++++-- .../mod/quiz/yui/src/dragdrop/js/resource.js | 3 ++- public/mod/quiz/yui/src/util/js/slot.js | 8 ++++-- 11 files changed, 47 insertions(+), 21 deletions(-) diff --git a/public/mod/quiz/classes/output/edit_renderer.php b/public/mod/quiz/classes/output/edit_renderer.php index 163e13605bf..2a3385dacad 100644 --- a/public/mod/quiz/classes/output/edit_renderer.php +++ b/public/mod/quiz/classes/output/edit_renderer.php @@ -889,9 +889,15 @@ class edit_renderer extends \plugin_renderer_base { * @return string The markup for the move action. */ public function question_move_icon(structure $structure, $slot) { - return html_writer::link(new \moodle_url('#'), - $this->pix_icon('i/dragdrop', get_string('move'), 'moodle', ['class' => 'iconsmall', 'title' => '']), - ['class' => 'editing_move', 'data-action' => 'move'] + $slotnumber = $structure->get_displayed_number_for_slot($slot); + return html_writer::link( + new \moodle_url('#'), + $this->pix_icon('i/dragdrop', '', 'moodle', ['class' => 'iconsmall']), + [ + 'class' => 'editing_move', + 'data-action' => 'move', + 'aria-label' => get_string('movequestionnumber', 'quiz', $slotnumber), + ] ); } @@ -983,13 +989,14 @@ class edit_renderer extends \plugin_renderer_base { $url = new \moodle_url('repaginate.php', ['quizid' => $structure->get_quizid(), 'slot' => $slot, 'repag' => $insertpagebreak ? 2 : 1, 'sesskey' => sesskey()]); + $slotname = $structure->get_displayed_number_for_slot($slot); if ($insertpagebreak) { - $title = get_string('addpagebreak', 'quiz'); - $image = $this->image_icon('e/insert_page_break', $title); + $title = get_string('addpagebreakafter', 'quiz', $slotname); + $image = $this->image_icon('e/insert_page_break', ''); $action = 'addpagebreak'; } else { - $title = get_string('removepagebreak', 'quiz'); - $image = $this->image_icon('e/remove_page_break', $title); + $title = get_string('removepagebreakafter', 'quiz', $slotname); + $image = $this->image_icon('e/remove_page_break', ''); $action = 'removepagebreak'; } @@ -1009,6 +1016,7 @@ class edit_renderer extends \plugin_renderer_base { 'class' => 'page_split_join cm-edit-action btn btn-sm icon-no-margin', 'disabled' => $disabled, 'data-action' => $action, + 'role' => 'button', ] ), 'page_split_join_wrapper' @@ -1326,7 +1334,7 @@ class edit_renderer extends \plugin_renderer_base { ], 'moodle'); $this->page->requires->strings_for_js([ - 'addpagebreak', + 'addpagebreakafter', 'cannotremoveallsectionslots', 'cannotremoveslots', 'confirmremovesectionheading', @@ -1337,7 +1345,7 @@ class edit_renderer extends \plugin_renderer_base { 'sectionheadingedit', 'sectionheadingremove', 'sectionnoname', - 'removepagebreak', + 'removepagebreakafter', 'questiondependencyadd', 'questiondependencyfree', 'questiondependencyremove', diff --git a/public/mod/quiz/lang/en/quiz.php b/public/mod/quiz/lang/en/quiz.php index 6292e0be533..2235291e18b 100644 --- a/public/mod/quiz/lang/en/quiz.php +++ b/public/mod/quiz/lang/en/quiz.php @@ -44,6 +44,7 @@ $string['addnewpagesafterselected'] = 'Add new pages after selected questions'; $string['addnewquestionsqbank'] = 'Add questions to the category {$a->catname}: {$a->link}'; $string['addnewuseroverride'] = 'Add user override'; $string['addpagebreak'] = 'Add page break'; +$string['addpagebreakafter'] = 'Add page break after question {$a}'; $string['addpagehere'] = 'Add page here'; $string['addquestion'] = 'Add question'; $string['addquestionfrombankatend'] = 'Add from the question bank at the end'; @@ -583,6 +584,7 @@ Quizzes may be used * For self-assessment'; $string['modulename_link'] = 'mod/quiz/view'; $string['modulenameplural'] = 'Quizzes'; +$string['movequestionnumber'] = 'Move question: {$a}'; $string['moveselectedonpage'] = 'Move selected questions to page: {$a}'; $string['multichoice'] = 'Multiple choice'; $string['multipleanswers'] = 'Choose at least one answer.'; @@ -901,6 +903,7 @@ $string['removeallquizattempts'] = 'All quiz attempts'; $string['removealluseroverrides'] = 'All user overrides'; $string['removeemptypage'] = 'Remove empty page'; $string['removepagebreak'] = 'Remove page break'; +$string['removepagebreakafter'] = 'Remove page break after question {$a}'; $string['removeselected'] = 'Remove selected'; $string['rename'] = 'Rename'; $string['renderingserverconnectfailed'] = 'The server {$a} failed to process an RQP request. Check that the URL is correct.'; diff --git a/public/mod/quiz/tests/behat/behat_mod_quiz.php b/public/mod/quiz/tests/behat/behat_mod_quiz.php index 2aa850178bd..d89898583a2 100644 --- a/public/mod/quiz/tests/behat/behat_mod_quiz.php +++ b/public/mod/quiz/tests/behat/behat_mod_quiz.php @@ -565,7 +565,7 @@ class behat_mod_quiz extends behat_question_base { */ protected function get_xpath_page_break_icon_after_question($addorremoves, $questionname) { return "//li[contains(@class, 'slot') and contains(., '" . $this->escape($questionname) . - "')]//a[contains(@class, 'page_split_join') and @title = '" . $addorremoves . " page break']"; + "')]//a[contains(@class, 'page_split_join') and contains(@aria-label, '$addorremoves page break')]"; } /** diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js index 5c79980c446..43ef07ec361 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js @@ -384,8 +384,9 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { + var slotName = move.getAttribute('aria-label'); var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE; - var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), dragHandleClass, CSS.ICONCLASS); + var resourcedraghandle = this.get_drag_handle(slotName, dragHandleClass, CSS.ICONCLASS); move.replace(resourcedraghandle); } }, this); diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js index 70d5b88def8..31fb1ebf6bc 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-mod_quiz-dragdrop",function(g,e){var o,u=".actions",t="activity",p="mod-quiz-edit-content",r="editing_move",s="iconsmall",c="jumpmenu",n="left",h="movedown",_="moveup",m="page-content",d="right",f="slots",a="section-handle",v="slots",l="sectiondraggable",z="li.page",q="li.slot",i=function(){i.superclass.constructor.apply(this,arguments)};g.extend(i,M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[l],this.samenodeclass="section",this.parentnodeclass="slots",g.Node.one("."+c))return!1;var e;this.sectionlistselector="li.section",this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+l,target:!0,handles:["."+n],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+m,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,i,s=g.Moodle.core_course.util.section.getId(e);0'),t=g.Node.create('
        ');t.setStyle("margin",0),t.setContent(e.get("node").get("innerHTML")),o.appendChild(t),e.get("dragNode").setContent(o),e.get("dragNode").addClass(p)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" li.section",o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(d){var r,a,e,n,o,t=d.drag,c=t.get("node"),i=g.Moodle.core_course.util.section.getId(c),l=i,s=this.get_section_index(c),u=s;if(i!==s){for(o in ug.Moodle.core_course.util.section.getId(r.item(i))&&(n=r.item(i-1).get("id"),r.item(i-1).set("id",r.item(i).get("id")),r.item(i).set("id",n),M.mod_quiz.edit.swap_sections(g,i-1,i),s=!0)}while(u-=1,s);window.setTimeout(function(){a.hide()},250)},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"mod_quiz-dragdrop-section",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_section_dragdrop=function(e){new i(e)},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=t,this.parentnodeclass=f,this.samenodelabel={identifier:"dragtoafter",component:"quiz"},this.parentnodelabel={identifier:"dragtostart",component:"quiz"},this.setup_for_section(),e="li."+t,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+r],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+v}),e.dd.plug(g.Plugin.DDWinScroll),M.mod_quiz.quizbase.register_module(this),M.mod_quiz.dragres=this},setup_for_section:function(){g.Node.all(".mod-quiz-edit-content ul.slots ul.section").each(function(e){e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("li.activity")},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o,e=e.one("a."+r);e&&(o="btn px-1 icon-no-margin "+r,o=this.get_drag_handle(M.util.get_string("move","moodle"),o,s),e.replace(o))},this)},drag_start:function(e){e=e.target;e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all(".icon").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t=e.drag,i=t.get("node"),e=i.one(u),s=M.util.add_spinner(g,e),n={},d=this.get("config").pageparams;for(o in d)n[o]=d[o];n.sesskey=M.cfg.sesskey,n.courseid=this.get("courseid"),n.quizid=this.get("quizid"),n["class"]="resource",n.field="move",n.id=Number(g.Moodle.mod_quiz.util.slot.getId(i)),n.sectionId=g.Moodle.core_course.util.section.getId(i.ancestor("li.section",!0)),(e=i.previous(q))&&(n.previousid=Number(g.Moodle.mod_quiz.util.slot.getId(e))),(e=i.previous(z))&&(n.page=Number(g.Moodle.mod_quiz.util.page.getId(e))),e=M.cfg.wwwroot+this.get("ajaxurl"),g.io(e,{method:"POST",data:n,on:{start:function(){this.lock_drag_handle(t,r),s.show()},success:function(e,o){o=g.JSON.parse(o.responseText),o={element:i,visible:o.visible};M.mod_quiz.quizbase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(t,r),window.setTimeout(function(){s.hide()},250),M.mod_quiz.resource_toolbox.reorganise_edit_page()},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(t,a),s.hide(),window.location.reload(!0)}},context:this})},global_drop_over:function(e){var o,t,i;e.drop&&e.drop.inGroup(this.groups)&&(o=e.drag.get("node"),t=e.drop.get("node"),this.lastdroptarget=e.drop,t.hasClass(this.samenodeclass)?(i=this.goingup?"before":"after",t.insert(o,i)):!t.hasClass(this.parentnodeclass)&&!t.test('[data-droptarget="1"]')||t.contains(o)||(this.goingup?t.append(o):t.prepend(o)),this.drop_over(e))}},{NAME:"mod_quiz-dragdrop-resource",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{ +YUI.add("moodle-mod_quiz-dragdrop",function(g,e){var o,u=".actions",t="activity",p="mod-quiz-edit-content",d="editing_move",s="iconsmall",c="jumpmenu",n="left",h="movedown",_="moveup",m="page-content",r="right",f="slots",a="section-handle",v="slots",l="sectiondraggable",z="li.page",q="li.slot",i=function(){i.superclass.constructor.apply(this,arguments)};g.extend(i,M.core.dragdrop,{sectionlistselector:null,initializer:function(){if(this.groups=[l],this.samenodeclass="section",this.parentnodeclass="slots",g.Node.one("."+c))return!1;var e;this.sectionlistselector="li.section",this.sectionlistselector&&(this.sectionlistselector="."+p+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector),(e=new g.DD.Delegate({container:"."+p,nodes:"."+l,target:!0,handles:["."+n],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+m,stickY:!0}),e.dd.plug(g.Plugin.DDWinScroll))},setup_for_section:function(e){g.Node.all(e).each(function(e){var o,t,i,s=g.Moodle.core_course.util.section.getId(e);0'),t=g.Node.create('
          ');t.setStyle("margin",0),t.setContent(e.get("node").get("innerHTML")),o.appendChild(t),e.get("dragNode").setContent(o),e.get("dragNode").addClass(p)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(e){var o="."+p+" li.section",o=g.all(o);return o.indexOf(e)-o.indexOf(g.one("#section-0"))},drop_hit:function(r){var d,a,e,n,o,t=r.drag,c=t.get("node"),i=g.Moodle.core_course.util.section.getId(c),l=i,s=this.get_section_index(c),u=s;if(i!==s){for(o in ug.Moodle.core_course.util.section.getId(d.item(i))&&(n=d.item(i-1).get("id"),d.item(i-1).set("id",d.item(i).get("id")),d.item(i).set("id",n),M.mod_quiz.edit.swap_sections(g,i-1,i),s=!0)}while(u-=1,s);window.setTimeout(function(){a.hide()},250)},failure:function(e,o){this.ajax_failure(o),a.hide()}},context:this})}}},{NAME:"mod_quiz-dragdrop-section",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_section_dragdrop=function(e){new i(e)},g.extend(o=function(){o.superclass.constructor.apply(this,arguments)},M.core.dragdrop,{initializer:function(){var e;this.groups=["resource"],this.samenodeclass=t,this.parentnodeclass=f,this.samenodelabel={identifier:"dragtoafter",component:"quiz"},this.parentnodelabel={identifier:"dragtostart",component:"quiz"},this.setup_for_section(),e="li."+t,(e=new g.DD.Delegate({container:"."+p,nodes:e,target:!0,handles:["."+d],dragConfig:{groups:this.groups}})).dd.plug(g.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),e.dd.plug(g.Plugin.DDConstrained,{constrain:"#"+v}),e.dd.plug(g.Plugin.DDWinScroll),M.mod_quiz.quizbase.register_module(this),M.mod_quiz.dragres=this},setup_for_section:function(){g.Node.all(".mod-quiz-edit-content ul.slots ul.section").each(function(e){e.setAttribute("data-draggroups",this.groups.join(" ")),new g.DD.Drop({node:e,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("li.activity")},this)},setup_for_resource:function(e){g.Node.all(e).each(function(e){var o,t,e=e.one("a."+d);e&&(t=e.getAttribute("aria-label"),o="btn px-1 icon-no-margin "+d,t=this.get_drag_handle(t,o,s),e.replace(t))},this)},drag_start:function(e){e=e.target;e.get("dragNode").setContent(e.get("node").get("innerHTML")),e.get("dragNode").all(".icon").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(e){var o,t=e.drag,i=t.get("node"),e=i.one(u),s=M.util.add_spinner(g,e),n={},r=this.get("config").pageparams;for(o in r)n[o]=r[o];n.sesskey=M.cfg.sesskey,n.courseid=this.get("courseid"),n.quizid=this.get("quizid"),n["class"]="resource",n.field="move",n.id=Number(g.Moodle.mod_quiz.util.slot.getId(i)),n.sectionId=g.Moodle.core_course.util.section.getId(i.ancestor("li.section",!0)),(e=i.previous(q))&&(n.previousid=Number(g.Moodle.mod_quiz.util.slot.getId(e))),(e=i.previous(z))&&(n.page=Number(g.Moodle.mod_quiz.util.page.getId(e))),e=M.cfg.wwwroot+this.get("ajaxurl"),g.io(e,{method:"POST",data:n,on:{start:function(){this.lock_drag_handle(t,d),s.show()},success:function(e,o){o=g.JSON.parse(o.responseText),o={element:i,visible:o.visible};M.mod_quiz.quizbase.invoke_function("set_visibility_resource_ui",o),this.unlock_drag_handle(t,d),window.setTimeout(function(){s.hide()},250),M.mod_quiz.resource_toolbox.reorganise_edit_page()},failure:function(e,o){this.ajax_failure(o),this.unlock_drag_handle(t,a),s.hide(),window.location.reload(!0)}},context:this})},global_drop_over:function(e){var o,t,i;e.drop&&e.drop.inGroup(this.groups)&&(o=e.drag.get("node"),t=e.drop.get("node"),this.lastdroptarget=e.drop,t.hasClass(this.samenodeclass)?(i=this.goingup?"before":"after",t.insert(o,i)):!t.hasClass(this.parentnodeclass)&&!t.test('[data-droptarget="1"]')||t.contains(o)||(this.goingup?t.append(o):t.prepend(o)),this.drop_over(e))}},{NAME:"mod_quiz-dragdrop-resource",ATTRS:{courseid:{value:null},quizid:{value:null},ajaxurl:{ value:0},config:{value:0}}}),M.mod_quiz=M.mod_quiz||{},M.mod_quiz.init_resource_dragdrop=function(e){new o(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-mod_quiz-quizbase","moodle-mod_quiz-util-base","moodle-mod_quiz-util-page","moodle-mod_quiz-util-slot","moodle-course-util"]}); \ No newline at end of file diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js index 31cb2d0d747..69ff2f0ce30 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js @@ -378,8 +378,9 @@ Y.extend(DRAGRESOURCE, M.core.dragdrop, { // Replace move icons var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); if (move) { + var slotName = move.getAttribute('aria-label'); var dragHandleClass = 'btn px-1 icon-no-margin ' + CSS.EDITINGMOVE; - var resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), dragHandleClass, CSS.ICONCLASS); + var resourcedraghandle = this.get_drag_handle(slotName, dragHandleClass, CSS.ICONCLASS); move.replace(resourcedraghandle); } }, this); diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js index 0eecf257c1f..2b9ff6dd296 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-debug.js @@ -335,17 +335,21 @@ Y.Moodle.mod_quiz.util.slot = { // Get the correct title. var action = ''; var iconname = ''; + var actionStr = ''; + var slotNumber = this.getNumber(slot); if (Y.Moodle.mod_quiz.util.page.isPage(nextitem)) { action = 'removepagebreak'; + actionStr = 'removepagebreakafter'; iconname = 'e/remove_page_break'; } else { action = 'addpagebreak'; + actionStr = 'addpagebreakafter'; iconname = 'e/insert_page_break'; } // Update the link and image titles - pagebreaklink.set('title', M.util.get_string(action, 'quiz')); - pagebreaklink.set('aria-label', M.util.get_string(action, 'quiz')); + pagebreaklink.set('title', M.util.get_string(actionStr, 'quiz', slotNumber)); + pagebreaklink.set('aria-label', M.util.get_string(actionStr, 'quiz', slotNumber)); pagebreaklink.setData('action', action); // Update the image title. var icon = pagebreaklink.one(this.SELECTORS.ICON); diff --git a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js index 9630a0909ab..11c44c0fdbc 100644 --- a/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js +++ b/public/mod/quiz/yui/build/moodle-mod_quiz-util-slot/moodle-mod_quiz-util-slot-min.js @@ -1 +1 @@ -YUI.add("moodle-mod_quiz-util-slot",function(u,e){u.namespace("Moodle.mod_quiz.util.slot"),u.Moodle.mod_quiz.util.slot={CSS:{SLOT:"slot",QUESTIONTYPEDESCRIPTION:"qtype_description",CANNOT_DEPEND:"question_dependency_cannot_depend"},CONSTANTS:{SLOTIDPREFIX:"slot-",QUESTION:M.util.get_string("question","moodle")},SELECTORS:{SLOT:"li.slot",INSTANCENAME:".instancename",NUMBER:"span.slotnumber",PAGECONTENT:"div#page-content",PAGEBREAK:"span.page_split_join_wrapper",ICON:".icon",QUESTIONTYPEDESCRIPTION:".qtype_description",SECTIONUL:"ul.section",DEPENDENCY_WRAPPER:".question_dependency_wrapper",DEPENDENCY_LINK:".question_dependency_wrapper .cm-edit-action",DEPENDENCY_ICON:".question_dependency_wrapper .icon"},getSlotFromComponent:function(e){return u.one(e).ancestor(this.SELECTORS.SLOT,!0)},getId:function(e){e=e.get("id").replace(this.CONSTANTS.SLOTIDPREFIX,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},getName:function(e){e=e.one(this.SELECTORS.INSTANCENAME);return e?e.get("firstChild").get("data"):null},getNumber:function(e){if(!e)return!1;e=e.one(this.SELECTORS.NUMBER).get("text").replace(this.CONSTANTS.QUESTION,"");return!("number"!=typeof(e=parseInt(e,10))||!isFinite(e))&&e},setNumber:function(e,t){e.one(this.SELECTORS.NUMBER).setHTML(''+this.CONSTANTS.QUESTION+" "+t)},getSlots:function(){return u.all(this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL+" "+this.SELECTORS.SLOT)},getNumberedSlots:function(){var e=this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL;return e+=" "+this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")",u.all(e)},getPrevious:function(e){return e.previous(this.SELECTORS.SLOT)},getPreviousNumbered:function(e){var t,i,n=e.previous(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")");if(n)return n;for(t=e.ancestor("li.section").previous("li.section");t;){if(0<(i=t.all(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")")).size())return i.item(i.size()-1);t=t.previous("li.section")}return!1},reorderSlots:function(){this.getSlots().each(function(e){var t,i;u.Moodle.mod_quiz.util.page.getPageFromSlot(e)||(t=e.next(u.Moodle.mod_quiz.util.page.SELECTORS.PAGE),e.swap(t)),t=this.getPreviousNumbered(e),i=0,e.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)||(t&&(i=this.getNumber(t)),this.setNumber(e,i+1))},this)},updateOneSlotSections:function(){u.all(".mod-quiz-edit-content ul.slots li.section").each(function(e){1'+this.CONSTANTS.QUESTION+" "+t)},getSlots:function(){return l.all(this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL+" "+this.SELECTORS.SLOT)},getNumberedSlots:function(){var e=this.SELECTORS.PAGECONTENT+" "+this.SELECTORS.SECTIONUL;return e+=" "+this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")",l.all(e)},getPrevious:function(e){return e.previous(this.SELECTORS.SLOT)},getPreviousNumbered:function(e){var t,i,n=e.previous(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")");if(n)return n;for(t=e.ancestor("li.section").previous("li.section");t;){if(0<(i=t.all(this.SELECTORS.SLOT+":not("+this.SELECTORS.QUESTIONTYPEDESCRIPTION+")")).size())return i.item(i.size()-1);t=t.previous("li.section")}return!1},reorderSlots:function(){this.getSlots().each(function(e){var t,i;l.Moodle.mod_quiz.util.page.getPageFromSlot(e)||(t=e.next(l.Moodle.mod_quiz.util.page.SELECTORS.PAGE),e.swap(t)),t=this.getPreviousNumbered(e),i=0,e.hasClass(this.CSS.QUESTIONTYPEDESCRIPTION)||(t&&(i=this.getNumber(t)),this.setNumber(e,i+1))},this)},updateOneSlotSections:function(){l.all(".mod-quiz-edit-content ul.slots li.section").each(function(e){1 Date: Mon, 23 Feb 2026 18:22:21 +0800 Subject: [PATCH 5/5] MDL-81608 quiz: Add `Add` menu spacing to prevent overlap --- public/mod/quiz/classes/output/edit_renderer.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/public/mod/quiz/classes/output/edit_renderer.php b/public/mod/quiz/classes/output/edit_renderer.php index 2a3385dacad..3114eab3611 100644 --- a/public/mod/quiz/classes/output/edit_renderer.php +++ b/public/mod/quiz/classes/output/edit_renderer.php @@ -101,8 +101,11 @@ class edit_renderer extends \plugin_renderer_base { if ($structure->is_last_section($section)) { $output .= \html_writer::start_div('last-add-menu'); - $output .= html_writer::tag('span', $this->add_menu_actions($structure, 0, - $pageurl, $contexts, $pagevars), ['class' => 'add-menu-outer']); + $output .= html_writer::tag( + 'span', + $this->add_menu_actions($structure, 0, $pageurl, $contexts, $pagevars), + ['class' => 'add-menu-outer pe-3'] + ); $output .= \html_writer::end_div(); } @@ -588,9 +591,11 @@ class edit_renderer extends \plugin_renderer_base { if ($structure->is_first_slot_on_page($slot)) { // Add the add-menu at the page level. - $addmenu = html_writer::tag('span', $this->add_menu_actions($structure, - $pagenumber, $pageurl, $contexts, $pagevars), - ['class' => 'add-menu-outer']); + $addmenu = html_writer::tag( + 'span', + $this->add_menu_actions($structure, $pagenumber, $pageurl, $contexts, $pagevars), + ['class' => 'add-menu-outer pe-2'] + ); $addquestionform = $this->add_question_form($structure, $pagenumber, $pageurl, $pagevars);