From f6a778497e29e2828bdd73600209f8e7ea07bbde Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 4 Apr 2019 10:43:14 +0800 Subject: [PATCH 1/5] MDL-5311 qtype_multichoice: add clear my choice option --- .../multichoice/amd/build/clearchoice.min.js | 1 + .../type/multichoice/amd/src/clearchoice.js | 105 ++++++++++++++++++ .../multichoice/lang/en/qtype_multichoice.php | 1 + question/type/multichoice/question.php | 15 ++- question/type/multichoice/renderer.php | 64 +++++++++++ 5 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 question/type/multichoice/amd/build/clearchoice.min.js create mode 100644 question/type/multichoice/amd/src/clearchoice.js diff --git a/question/type/multichoice/amd/build/clearchoice.min.js b/question/type/multichoice/amd/build/clearchoice.min.js new file mode 100644 index 00000000000..dc8c9fbc04f --- /dev/null +++ b/question/type/multichoice/amd/build/clearchoice.min.js @@ -0,0 +1 @@ +define(["jquery","core/custom_interaction_events"],function(a,b){var c={CHOICE_ELEMENT:".answer input",CLEAR_CHOICE_ELEMENT:'div[class="qtype_multichoice_clearchoice"]'},d=function(a){a.find('input[type="radio"]').prop("checked",!0)},e=function(a,b){return a.find('div[id="'+b+'"]')},f=function(a){a.addClass("hidden")},g=function(a){a.removeClass("hidden")},h=function(a,h){var i=e(a,h);a.on(b.events.activate,c.CLEAR_CHOICE_ELEMENT,function(){d(i),f(i)}),a.on(b.events.activate,c.CHOICE_ELEMENT,function(){g(i)})},i=function(b,c){b=a("#"+b),h(b,c)};return{init:i}}); \ No newline at end of file diff --git a/question/type/multichoice/amd/src/clearchoice.js b/question/type/multichoice/amd/src/clearchoice.js new file mode 100644 index 00000000000..59a03e62ae6 --- /dev/null +++ b/question/type/multichoice/amd/src/clearchoice.js @@ -0,0 +1,105 @@ +// 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 . + +/** + * Manages 'Clear my choice' functionality actions. + * + * @module qtype_multichoice/clearchoice + * @copyright 2019 Simey Lameze + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 3.7 + */ +define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) { + + var SELECTORS = { + CHOICE_ELEMENT: '.answer input', + CLEAR_CHOICE_ELEMENT: 'div[class="qtype_multichoice_clearchoice"]' + }; + + /** + * Mark clear choice radio as checked. + * + * @param {Object} clearChoiceContainer The clear choice option container. + */ + var checkClearChoiceRadio = function(clearChoiceContainer) { + clearChoiceContainer.find('input[type="radio"]').prop('checked', true); + }; + + /** + * Get the clear choice div container. + * + * @param {Object} root The question root element. + * @param {string} fieldPrefix The question outer div prefix. + * @returns {Object} The clear choice div container. + */ + var getClearChoiceElement = function(root, fieldPrefix) { + return root.find('div[id="' + fieldPrefix + '"]'); + }; + + /** + * Hide clear choice option. + * + * @param {Object} clearChoiceContainer The clear choice option container. + */ + var hideClearChoiceOption = function(clearChoiceContainer) { + clearChoiceContainer.addClass('hidden'); + }; + + /** + * Shows clear choice option. + * + * @param {Object} clearChoiceContainer The clear choice option container. + */ + var showClearChoiceOption = function(clearChoiceContainer) { + clearChoiceContainer.removeClass('hidden'); + }; + + /** + * Register event listeners for the clear choice module. + * + * @param {Object} root The question outer div prefix. + * @param {string} fieldPrefix The "Clear choice" div prefix. + */ + var registerEventListeners = function(root, fieldPrefix) { + var clearChoiceContainer = getClearChoiceElement(root, fieldPrefix); + + root.on(CustomEvents.events.activate, SELECTORS.CLEAR_CHOICE_ELEMENT, function() { + // Mark the clear choice radio element as checked. + checkClearChoiceRadio(clearChoiceContainer); + // Now that the hidden radio has been checked, hide the clear choice option. + hideClearChoiceOption(clearChoiceContainer); + }); + + root.on(CustomEvents.events.activate, SELECTORS.CHOICE_ELEMENT, function() { + // If the event has been triggered by any other choice, show the clear choice option. + showClearChoiceOption(clearChoiceContainer); + }); + }; + + /** + * Initialise clear choice module. + + * @param {string} root The question outer div prefix. + * @param {string} fieldPrefix The "Clear choice" div prefix. + */ + var init = function(root, fieldPrefix) { + root = $('#' + root); + registerEventListeners(root, fieldPrefix); + }; + + return { + init: init + }; +}); diff --git a/question/type/multichoice/lang/en/qtype_multichoice.php b/question/type/multichoice/lang/en/qtype_multichoice.php index 9e4ebb819fa..2473d43384d 100644 --- a/question/type/multichoice/lang/en/qtype_multichoice.php +++ b/question/type/multichoice/lang/en/qtype_multichoice.php @@ -37,6 +37,7 @@ $string['answersingleno'] = 'Multiple answers allowed'; $string['answersingleyes'] = 'One answer only'; $string['choiceno'] = 'Choice {$a}'; $string['choices'] = 'Available choices'; +$string['clearchoice'] = 'Clear my choice'; $string['clozeaid'] = 'Enter missing word'; $string['correctansweris'] = 'The correct answer is: {$a}'; $string['correctanswersare'] = 'The correct answers are: {$a}'; diff --git a/question/type/multichoice/question.php b/question/type/multichoice/question.php index 09b949355c4..fcf63c5f9da 100644 --- a/question/type/multichoice/question.php +++ b/question/type/multichoice/question.php @@ -176,8 +176,7 @@ class qtype_multichoice_single_question extends qtype_multichoice_base { } public function summarise_response(array $response) { - if (!array_key_exists('answer', $response) || - !array_key_exists($response['answer'], $this->order)) { + if (!$this->is_complete_response($response)) { return null; } $ansid = $this->order[$response['answer']]; @@ -186,8 +185,7 @@ class qtype_multichoice_single_question extends qtype_multichoice_base { } public function classify_response(array $response) { - if (!array_key_exists('answer', $response) || - !array_key_exists($response['answer'], $this->order)) { + if (!$this->is_complete_response($response)) { return array($this->id => question_classified_response::no_response()); } $choiceid = $this->order[$response['answer']]; @@ -230,11 +228,18 @@ class qtype_multichoice_single_question extends qtype_multichoice_base { } public function is_same_response(array $prevresponse, array $newresponse) { + if (!$this->is_complete_response($prevresponse)) { + $prevresponse = []; + } + if (!$this->is_complete_response($newresponse)) { + $newresponse = []; + } return question_utils::arrays_same_at_key($prevresponse, $newresponse, 'answer'); } public function is_complete_response(array $response) { - return array_key_exists('answer', $response) && $response['answer'] !== ''; + return array_key_exists('answer', $response) && $response['answer'] !== '' + && (string) $response['answer'] !== '-1'; } public function is_gradable_response(array $response) { diff --git a/question/type/multichoice/renderer.php b/question/type/multichoice/renderer.php index 25a5b2e40bb..b48809af67b 100644 --- a/question/type/multichoice/renderer.php +++ b/question/type/multichoice/renderer.php @@ -35,6 +35,17 @@ defined('MOODLE_INTERNAL') || die(); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedback_renderer { + + /** + * Method to generating the bits of output after question choices. + * + * @param question_attempt $qa The question attempt object. + * @param question_display_options $options controls what should and should not be displayed. + * + * @return string HTML output. + */ + protected abstract function after_choices(question_attempt $qa, question_display_options $options); + protected abstract function get_input_type(); protected abstract function get_input_name(question_attempt $qa, $value); @@ -136,6 +147,8 @@ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedb } $result .= html_writer::end_tag('div'); // Answer. + $result .= $this->after_choices($qa, $options); + $result .= html_writer::end_tag('div'); // Ablock. if ($qa->get_state() == question_state::$invalid) { @@ -252,6 +265,53 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base } return $this->correct_choices($right); } + + public function after_choices(question_attempt $qa, question_display_options $options) { + // Only load the clear choice feature if it's not read only. + if ($options->readonly) { + return ''; + } + + $question = $qa->get_question(); + $response = $question->get_response($qa); + $hascheckedchoice = false; + foreach ($question->get_order($qa) as $value => $ansid) { + if ($question->is_choice_selected($response, $value)) { + $hascheckedchoice = true; + break; + } + } + + $clearchoiceid = $this->get_input_id($qa, -1); + $clearchoicefieldname = $qa->get_qt_field_name('clearchoice'); + $clearchoiceradioattrs = [ + 'type' => $this->get_input_type(), + 'name' => $qa->get_qt_field_name('answer'), + 'id' => $clearchoiceid, + 'value' => -1 + ]; + + $cssclass = 'qtype_multichoice_clearchoice'; + // When no choice selected during rendering, then hide the clear choice option. + if (!$hascheckedchoice && $response == -1) { + $cssclass .= ' hidden'; + $clearchoiceradioattrs['checked'] = 'checked'; + } + // Adds an hidden radio that will be checked to give the impression the choice has been cleared. + $clearchoiceradio = html_writer::empty_tag('input', $clearchoiceradioattrs); + $clearchoiceradio .= html_writer::tag('label', get_string('clearchoice', 'qtype_multichoice'), + ['for' => $clearchoiceid, 'role' => 'button', 'tabindex' => 0]); + + // Now wrap the radio and label inside a div. + $result = html_writer::tag('div', $clearchoiceradio, ['id' => $clearchoicefieldname, 'class' => $cssclass]); + + // Load required clearchoice AMD module. + $this->page->requires->js_call_amd('qtype_multichoice/clearchoice', 'init', + [$qa->get_outer_question_div_unique_id(), $clearchoicefieldname]); + + return $result; + } + } /** @@ -262,6 +322,10 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_multichoice_multi_renderer extends qtype_multichoice_renderer_base { + protected function after_choices(question_attempt $qa, question_display_options $options) { + return ''; + } + protected function get_input_type() { return 'checkbox'; } From 3c1717bb098c12ef6ae00211663b47f2b8aa3d9f Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 4 Apr 2019 10:44:34 +0800 Subject: [PATCH 2/5] MDL-5311 qtype_multichoice: add a bit of styling to the option --- question/type/multichoice/styles.css | 4 ++++ theme/boost/scss/moodle/question.scss | 11 +++++++++++ theme/boost/style/moodle.css | 10 ++++++++++ theme/classic/style/moodle.css | 10 ++++++++++ 4 files changed, 35 insertions(+) diff --git a/question/type/multichoice/styles.css b/question/type/multichoice/styles.css index 5707361ec0b..77b4968b897 100644 --- a/question/type/multichoice/styles.css +++ b/question/type/multichoice/styles.css @@ -26,6 +26,10 @@ width: 15px; } +.qtype_multichoice_clearchoice input { + display: none; +} + /* Editing form. */ body#page-question-type-multichoice div[id^=fitem_id_][id*=answer_] { background: #eee; diff --git a/theme/boost/scss/moodle/question.scss b/theme/boost/scss/moodle/question.scss index e3339389b54..e12fe7a96fe 100644 --- a/theme/boost/scss/moodle/question.scss +++ b/theme/boost/scss/moodle/question.scss @@ -310,6 +310,17 @@ body.path-question-type { .que.multichoice .answer div.r1 .icon.fa-remove { text-indent: 0; } +.qtype_multichoice_clearchoice { + padding-top: 10px; + label { + cursor: pointer; + text-decoration: underline; + padding-left: 30px; + } +} +.qtype_multichoice_clearchoice input { + display: none; +} .formulation input[type="text"], .formulation select { diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 0e6fadd3fce..332ea8c8cf6 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -14028,6 +14028,16 @@ body.path-question-type { .que.multichoice .answer div.r1 .icon.fa-remove { text-indent: 0; } +.qtype_multichoice_clearchoice { + padding-top: 10px; } + .qtype_multichoice_clearchoice label { + cursor: pointer; + text-decoration: underline; + padding-left: 30px; } + +.qtype_multichoice_clearchoice input { + display: none; } + .formulation input[type="text"], .formulation select { width: auto; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 64dbcba9904..8c5de0ea122 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -14279,6 +14279,16 @@ body.path-question-type { .que.multichoice .answer div.r1 .icon.fa-remove { text-indent: 0; } +.qtype_multichoice_clearchoice { + padding-top: 10px; } + .qtype_multichoice_clearchoice label { + cursor: pointer; + text-decoration: underline; + padding-left: 30px; } + +.qtype_multichoice_clearchoice input { + display: none; } + .formulation input[type="text"], .formulation select { width: auto; From 1ff39876006b96992f6fcd0e30edc1303c165aab Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 4 Apr 2019 10:45:18 +0800 Subject: [PATCH 3/5] MDL-5311 qtype_multichoice: add behat scenario to question preview --- .../type/multichoice/tests/behat/preview.feature | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/question/type/multichoice/tests/behat/preview.feature b/question/type/multichoice/tests/behat/preview.feature index ed50c9f05e5..19e3e797c18 100644 --- a/question/type/multichoice/tests/behat/preview.feature +++ b/question/type/multichoice/tests/behat/preview.feature @@ -70,3 +70,15 @@ Feature: Preview a Multiple choice question And I should see "Well done!" And I should see "The correct answer is: One" And I switch to the main window + + @javascript @_switch_window + Scenario: Preview a multiple choice question (single response) and clear a previous selected option. + When I click on "Preview" "link" in the "Multi-choice-002" "table_row" + And I switch to "questionpreview" window + And I set the field "How questions behave" to "Immediate feedback" + And I press "Start again with these options" + And I click on "One" "radio" + Then I should see "Clear my choice" + And I click on "Clear my choice" "text" + And I should not see "Clear my choice" + And I switch to the main window From 9064f17bbc15a7a8e7ab7b61e72db9d9a685c58a Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 4 Apr 2019 10:45:50 +0800 Subject: [PATCH 4/5] MDL-5311 qtype_multichoice: add php unit tests for the new option --- .../multichoice/tests/question_multi_test.php | 11 +++ .../tests/question_single_test.php | 28 +++++++- .../multichoice/tests/walkthrough_test.php | 69 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/question/type/multichoice/tests/question_multi_test.php b/question/type/multichoice/tests/question_multi_test.php index fa3775d123a..1e5f56bc452 100644 --- a/question/type/multichoice/tests/question_multi_test.php +++ b/question/type/multichoice/tests/question_multi_test.php @@ -133,6 +133,17 @@ class qtype_multichoice_multi_question_test extends advanced_testcase { $this->assertEquals('B; C', $summary); } + public function test_summarise_response_clearchoice() { + $mc = test_question_maker::make_a_multichoice_multi_question(); + $mc->shuffleanswers = false; + $mc->start_attempt(new question_attempt_step(), 1); + + $summary = $mc->summarise_response($mc->prepare_simulated_post_data(array('clearchoice' => -1)), + test_question_maker::get_a_qa($mc)); + + $this->assertNull($summary); + } + public function test_classify_response() { $mc = test_question_maker::make_a_multichoice_multi_question(); $mc->start_attempt(new question_attempt_step(), 1); diff --git a/question/type/multichoice/tests/question_single_test.php b/question/type/multichoice/tests/question_single_test.php index 7b6b6b19238..f5f10a48df6 100644 --- a/question/type/multichoice/tests/question_single_test.php +++ b/question/type/multichoice/tests/question_single_test.php @@ -47,6 +47,8 @@ class qtype_multichoice_single_question_test extends advanced_testcase { $this->assertFalse($question->is_complete_response(array())); $this->assertTrue($question->is_complete_response(array('answer' => '0'))); $this->assertTrue($question->is_complete_response(array('answer' => '2'))); + $this->assertFalse($question->is_complete_response(array('answer' => '-1'))); + $this->assertFalse($question->is_complete_response(array('answer' => -1))); } public function test_is_gradable_response() { @@ -55,6 +57,7 @@ class qtype_multichoice_single_question_test extends advanced_testcase { $this->assertFalse($question->is_gradable_response(array())); $this->assertTrue($question->is_gradable_response(array('answer' => '0'))); $this->assertTrue($question->is_gradable_response(array('answer' => '2'))); + $this->assertFalse($question->is_gradable_response(array('answer' => '-1'))); } public function test_is_same_response() { @@ -80,6 +83,22 @@ class qtype_multichoice_single_question_test extends advanced_testcase { $this->assertTrue($question->is_same_response( array('answer' => '2'), array('answer' => '2'))); + + $this->assertFalse($question->is_same_response( + array('answer' => '0'), + array('answer' => '-1'))); + + $this->assertFalse($question->is_same_response( + array('answer' => '-1'), + array('answer' => '0'))); + + $this->assertTrue($question->is_same_response( + array('answer' => '-1'), + array('answer' => '-1'))); + + $this->assertTrue($question->is_same_response( + array(), + array('answer' => '-1'))); } public function test_grading() { @@ -138,6 +157,9 @@ class qtype_multichoice_single_question_test extends advanced_testcase { test_question_maker::get_a_qa($mc)); $this->assertEquals('A', $summary); + + $this->assertNull($mc->summarise_response(array(), test_question_maker::get_a_qa($mc))); + $this->assertNull($mc->summarise_response(array('answer' => '-1'), test_question_maker::get_a_qa($mc))); } public function test_classify_response() { @@ -149,7 +171,11 @@ class qtype_multichoice_single_question_test extends advanced_testcase { $this->assertEquals(array( $mc->id => question_classified_response::no_response(), - ), $mc->classify_response(array())); + ), $mc->classify_response(array())); + + $this->assertEquals(array( + $mc->id => question_classified_response::no_response(), + ), $mc->classify_response(array('answer' => '-1'))); } public function test_make_html_inline() { diff --git a/question/type/multichoice/tests/walkthrough_test.php b/question/type/multichoice/tests/walkthrough_test.php index 9eb2be43b97..37bc62f630b 100644 --- a/question/type/multichoice/tests/walkthrough_test.php +++ b/question/type/multichoice/tests/walkthrough_test.php @@ -97,4 +97,73 @@ class qtype_multichoice_walkthrough_test extends qbehaviour_walkthrough_test_bas new question_pattern_expectation('/class="r0 correct"/'), new question_pattern_expectation('/class="r1"/')); } + + /** + * Test for clear choice option. + */ + public function test_deferredfeedback_feedback_multichoice_clearchoice() { + + // Create a multichoice, single question. + $mc = test_question_maker::make_a_multichoice_single_question(); + $mc->shuffleanswers = false; + + $clearchoice = -1; + $rightchoice = 0; + $wrongchoice = 2; + + $this->start_attempt_at_question($mc, 'deferredfeedback', 3); + + // Let's first submit the wrong choice (2). + $this->process_submission(array('answer' => $wrongchoice)); // Wrong choice (2). + + $this->check_current_mark(null); + // Clear choice radio should not be checked. + $this->check_current_output( + $this->get_contains_mc_radio_expectation($rightchoice, true, false), // Not checked. + $this->get_contains_mc_radio_expectation($rightchoice + 1, true, false), // Not checked. + $this->get_contains_mc_radio_expectation($rightchoice + 2, true, true), // Wrong choice (2) checked. + $this->get_contains_mc_radio_expectation($clearchoice, true, false), // Not checked. + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation() + ); + + // Now, let's clear our previous choice. + $this->process_submission(array('answer' => $clearchoice)); // Clear choice (-1). + $this->check_current_mark(null); + + // This time, the clear choice radio should be the only one checked. + $this->check_current_output( + $this->get_contains_mc_radio_expectation($rightchoice, true, false), // Not checked. + $this->get_contains_mc_radio_expectation($rightchoice + 1, true, false), // Not checked. + $this->get_contains_mc_radio_expectation($rightchoice + 2, true, false), // Not checked. + $this->get_contains_mc_radio_expectation($clearchoice, true, true), // Clear choice radio checked. + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation() + ); + + // Finally, let's submit the right choice. + $this->process_submission(array('answer' => $rightchoice)); // Right choice (0). + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_mc_radio_expectation($rightchoice, true, true), + $this->get_contains_mc_radio_expectation($rightchoice + 1, true, false), + $this->get_contains_mc_radio_expectation($rightchoice + 2, true, false), + $this->get_contains_mc_radio_expectation($clearchoice, true, false), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation() + ); + + // Finish the attempt. + $this->finish(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(3); + $this->check_current_output( + $this->get_contains_mc_radio_expectation($rightchoice, false, true), + $this->get_contains_correct_expectation(), + new question_pattern_expectation('/class="r0 correct"/'), + new question_pattern_expectation('/class="r1"/')); + } } From 2589dcea7b1e42bb52707b25ae7b8816115725c6 Mon Sep 17 00:00:00 2001 From: Simey Lameze Date: Thu, 4 Apr 2019 13:46:02 +0800 Subject: [PATCH 5/5] MDL-5311 qtype_multichoice: make clear choice option accessible Thanks to Damyon for the help with accessibility. --- question/type/multichoice/amd/build/clearchoice.min.js | 2 +- question/type/multichoice/amd/src/clearchoice.js | 4 ++-- question/type/multichoice/renderer.php | 7 ++++--- question/type/multichoice/styles.css | 4 ---- theme/boost/scss/moodle/question.scss | 3 --- theme/boost/style/moodle.css | 3 --- theme/classic/style/moodle.css | 3 --- 7 files changed, 7 insertions(+), 19 deletions(-) diff --git a/question/type/multichoice/amd/build/clearchoice.min.js b/question/type/multichoice/amd/build/clearchoice.min.js index dc8c9fbc04f..3a002441f49 100644 --- a/question/type/multichoice/amd/build/clearchoice.min.js +++ b/question/type/multichoice/amd/build/clearchoice.min.js @@ -1 +1 @@ -define(["jquery","core/custom_interaction_events"],function(a,b){var c={CHOICE_ELEMENT:".answer input",CLEAR_CHOICE_ELEMENT:'div[class="qtype_multichoice_clearchoice"]'},d=function(a){a.find('input[type="radio"]').prop("checked",!0)},e=function(a,b){return a.find('div[id="'+b+'"]')},f=function(a){a.addClass("hidden")},g=function(a){a.removeClass("hidden")},h=function(a,h){var i=e(a,h);a.on(b.events.activate,c.CLEAR_CHOICE_ELEMENT,function(){d(i),f(i)}),a.on(b.events.activate,c.CHOICE_ELEMENT,function(){g(i)})},i=function(b,c){b=a("#"+b),h(b,c)};return{init:i}}); \ No newline at end of file +define(["jquery","core/custom_interaction_events"],function(a,b){var c={CHOICE_ELEMENT:".answer input",CLEAR_CHOICE_ELEMENT:'div[class="qtype_multichoice_clearchoice"]'},d=function(a){a.find('input[type="radio"]').prop("checked",!0)},e=function(a,b){return a.find('div[id="'+b+'"]')},f=function(a){a.addClass("sr-only")},g=function(a){a.removeClass("sr-only")},h=function(a,h){var i=e(a,h);a.on(b.events.activate,c.CLEAR_CHOICE_ELEMENT,function(){d(i),f(i)}),a.on(b.events.activate,c.CHOICE_ELEMENT,function(){g(i)})},i=function(b,c){b=a("#"+b),h(b,c)};return{init:i}}); \ No newline at end of file diff --git a/question/type/multichoice/amd/src/clearchoice.js b/question/type/multichoice/amd/src/clearchoice.js index 59a03e62ae6..3c15efb7e34 100644 --- a/question/type/multichoice/amd/src/clearchoice.js +++ b/question/type/multichoice/amd/src/clearchoice.js @@ -54,7 +54,7 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) { * @param {Object} clearChoiceContainer The clear choice option container. */ var hideClearChoiceOption = function(clearChoiceContainer) { - clearChoiceContainer.addClass('hidden'); + clearChoiceContainer.addClass('sr-only'); }; /** @@ -63,7 +63,7 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) { * @param {Object} clearChoiceContainer The clear choice option container. */ var showClearChoiceOption = function(clearChoiceContainer) { - clearChoiceContainer.removeClass('hidden'); + clearChoiceContainer.removeClass('sr-only'); }; /** diff --git a/question/type/multichoice/renderer.php b/question/type/multichoice/renderer.php index b48809af67b..61585b41e55 100644 --- a/question/type/multichoice/renderer.php +++ b/question/type/multichoice/renderer.php @@ -288,19 +288,20 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base 'type' => $this->get_input_type(), 'name' => $qa->get_qt_field_name('answer'), 'id' => $clearchoiceid, - 'value' => -1 + 'value' => -1, + 'class' => 'sr-only' ]; $cssclass = 'qtype_multichoice_clearchoice'; // When no choice selected during rendering, then hide the clear choice option. if (!$hascheckedchoice && $response == -1) { - $cssclass .= ' hidden'; + $cssclass .= ' sr-only'; $clearchoiceradioattrs['checked'] = 'checked'; } // Adds an hidden radio that will be checked to give the impression the choice has been cleared. $clearchoiceradio = html_writer::empty_tag('input', $clearchoiceradioattrs); $clearchoiceradio .= html_writer::tag('label', get_string('clearchoice', 'qtype_multichoice'), - ['for' => $clearchoiceid, 'role' => 'button', 'tabindex' => 0]); + ['for' => $clearchoiceid]); // Now wrap the radio and label inside a div. $result = html_writer::tag('div', $clearchoiceradio, ['id' => $clearchoicefieldname, 'class' => $cssclass]); diff --git a/question/type/multichoice/styles.css b/question/type/multichoice/styles.css index 77b4968b897..5707361ec0b 100644 --- a/question/type/multichoice/styles.css +++ b/question/type/multichoice/styles.css @@ -26,10 +26,6 @@ width: 15px; } -.qtype_multichoice_clearchoice input { - display: none; -} - /* Editing form. */ body#page-question-type-multichoice div[id^=fitem_id_][id*=answer_] { background: #eee; diff --git a/theme/boost/scss/moodle/question.scss b/theme/boost/scss/moodle/question.scss index e12fe7a96fe..786c7f1a2a5 100644 --- a/theme/boost/scss/moodle/question.scss +++ b/theme/boost/scss/moodle/question.scss @@ -318,9 +318,6 @@ body.path-question-type { padding-left: 30px; } } -.qtype_multichoice_clearchoice input { - display: none; -} .formulation input[type="text"], .formulation select { diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 332ea8c8cf6..f24e0baabf0 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -14035,9 +14035,6 @@ body.path-question-type { text-decoration: underline; padding-left: 30px; } -.qtype_multichoice_clearchoice input { - display: none; } - .formulation input[type="text"], .formulation select { width: auto; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 8c5de0ea122..15e39d64ae8 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -14286,9 +14286,6 @@ body.path-question-type { text-decoration: underline; padding-left: 30px; } -.qtype_multichoice_clearchoice input { - display: none; } - .formulation input[type="text"], .formulation select { width: auto;