From b3eb94230142e1e28de260e1dd719bd933f3d15a Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Tue, 4 Apr 2023 00:03:00 +0800 Subject: [PATCH] MDL-77856 qtype_multianswer: Use Bootstrap Popover for subq feedback The YUI Overlay widget encloses the subquestion feedback in a div which causes a div element to be enclosed in the subquestion span. This leads to an accessibility issue in terms of HTML parsing as inline elements (span) should not contain block elements (div) The YUI Overlay widget is also not accessible as it does not really hide the overlay contents via aria-hidden when the overlay is not being shown. It's better if we stop using this and use Bootstrap's popover component which is more accessible by default. This patch also removes module.js for the qtype_multianswer plugin as it only contains codes related to rendering the feedback contents in the YUI overlay widget which is no longer necessary. --- question/type/multianswer/module.js | 54 ---------------------- question/type/multianswer/renderer.php | 42 +++++++++++------ .../type/multianswer/tests/walkthrough_test.php | 2 +- 3 files changed, 30 insertions(+), 68 deletions(-) delete mode 100644 question/type/multianswer/module.js diff --git a/question/type/multianswer/module.js b/question/type/multianswer/module.js deleted file mode 100644 index 98d72b4ed83..00000000000 --- a/question/type/multianswer/module.js +++ /dev/null @@ -1,54 +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 . - -/** - * JavaScript required by the multianswer question type. - * - * @package qtype - * @subpackage multianswer - * @copyright 2011 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -M.qtype_multianswer = M.qtype_multianswer || {}; - - -M.qtype_multianswer.init = function (Y, questiondiv) { - Y.one(questiondiv).all('span.subquestion').each(function(subqspan) { - var feedbackspan = subqspan.one('.feedbackspan'); - if (!feedbackspan) { - return; - } - - var overlay = new Y.Overlay({ - srcNode: feedbackspan, - visible: false, - align: { - node: subqspan, - points: [Y.WidgetPositionAlign.TC, Y.WidgetPositionAlign.BC] - }, - constrain: subqspan.ancestor('div.que'), - zIndex: 1, - preventOverlap: true - }); - overlay.render(); - - Y.on('mouseover', function() { overlay.show(); }, subqspan); - Y.on('mouseout', function() { overlay.hide(); }, subqspan); - - feedbackspan.removeClass('accesshide'); - }); -}; diff --git a/question/type/multianswer/renderer.php b/question/type/multianswer/renderer.php index 2f3408ca5c0..d3a9c933cfd 100644 --- a/question/type/multianswer/renderer.php +++ b/question/type/multianswer/renderer.php @@ -88,13 +88,6 @@ class qtype_multianswer_renderer extends qtype_renderer { array('class' => 'validationerror')); } - $this->page->requires->js_init_call('M.qtype_multianswer.init', - array('#' . $qa->get_outer_question_div_unique_id()), false, array( - 'name' => 'qtype_multianswer', - 'fullpath' => '/question/type/multianswer/module.js', - 'requires' => array('base', 'node', 'event', 'overlay'), - )); - return $output; } @@ -202,8 +195,33 @@ abstract class qtype_multianswer_subq_renderer_base extends qtype_renderer { return ''; } - return html_writer::tag('span', implode('
', $feedback), - array('class' => 'feedbackspan accesshide')); + return html_writer::tag('span', implode('
', $feedback), [ + 'class' => 'feedbackspan', + ]); + } + + /** + * Render the feedback icon for a sub-question which is also the trigger for the feedback popover. + * + * @param string $icon The feedback icon + * @param string $feedbackcontents The feedback contents to be shown on the popover. + * @return string + */ + protected function get_feedback_image(string $icon, string $feedbackcontents): string { + if ($icon === '') { + return ''; + } + + return html_writer::tag('button', $icon, [ + 'type' => 'button', + 'class' => 'btn btn-link p-0', + 'data-toggle' => 'popover', + 'data-container' => 'body', + 'data-content' => $feedbackcontents, + 'data-placement' => 'right', + 'data-trigger' => 'focus', + 'data-html' => 'true', + ]); } /** @@ -315,8 +333,7 @@ class qtype_multianswer_textfield_renderer extends qtype_multianswer_subq_render $output .= html_writer::tag('label', $this->get_answer_label(), array('class' => 'subq accesshide', 'for' => $inputattributes['id'])); $output .= html_writer::empty_tag('input', $inputattributes); - $output .= $feedbackimg; - $output .= $feedbackpopup; + $output .= $this->get_feedback_image($feedbackimg, $feedbackpopup); $output .= html_writer::end_tag('span'); return $output; @@ -385,8 +402,7 @@ class qtype_multianswer_multichoice_inline_renderer $output .= html_writer::tag('label', $this->get_answer_label(), array('class' => 'subq accesshide', 'for' => $inputattributes['id'])); $output .= $select; - $output .= $feedbackimg; - $output .= $feedbackpopup; + $output .= $this->get_feedback_image($feedbackimg, $feedbackpopup); $output .= html_writer::end_tag('span'); return $output; diff --git a/question/type/multianswer/tests/walkthrough_test.php b/question/type/multianswer/tests/walkthrough_test.php index aaf525f7f18..389427a902e 100644 --- a/question/type/multianswer/tests/walkthrough_test.php +++ b/question/type/multianswer/tests/walkthrough_test.php @@ -41,7 +41,7 @@ class walkthrough_test extends \qbehaviour_walkthrough_test_base { protected function get_contains_subq_status(question_state $state) { return new \question_pattern_expectation('~' . - preg_quote($state->default_string(true), '~') . '
~'); + preg_quote($state->default_string(true), '~') . '~'); } public function test_deferred_feedback() { -- 2.11.4.GIT