diff --git a/question/type/multichoice/amd/build/clearchoice.min.js b/question/type/multichoice/amd/build/clearchoice.min.js index 16837effdd5..d118c819f45 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("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(a,b){d(i),f(i),b.originalEvent.preventDefault()}),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={ANSWER_RADIOS:".answer input",CLEARRESULTS_LINK:'a[data-action="clearresults"]'},d="d-none",e=function(e){var f=e.find(c.CLEARRESULTS_LINK);e.on(b.events.activate,c.CLEARRESULTS_LINK,function(b,f){e.find(c.ANSWER_RADIOS).each(function(){a(this).prop("checked",!1)}),a(b.target).addClass(d),f.originalEvent.preventDefault()}),e.on(b.events.activate,c.ANSWER_RADIOS,function(){f.removeClass(d)})},f=function(b){b=a("#"+b),e(b)};return{init:f}}); \ 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 5ca09ce44c2..e84f8d4d9fb 100644 --- a/question/type/multichoice/amd/src/clearchoice.js +++ b/question/type/multichoice/amd/src/clearchoice.js @@ -24,70 +24,31 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) { var SELECTORS = { - CHOICE_ELEMENT: '.answer input', - CLEAR_CHOICE_ELEMENT: 'div[class="qtype_multichoice_clearchoice"]' + ANSWER_RADIOS: '.answer input', + CLEARRESULTS_LINK: 'a[data-action="clearresults"]' }; - /** - * 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('sr-only'); - }; - - /** - * Shows clear choice option. - * - * @param {Object} clearChoiceContainer The clear choice option container. - */ - var showClearChoiceOption = function(clearChoiceContainer) { - clearChoiceContainer.removeClass('sr-only'); - }; + var CSSHIDDEN = 'd-none'; /** * 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); + var registerEventListeners = function(root) { - root.on(CustomEvents.events.activate, SELECTORS.CLEAR_CHOICE_ELEMENT, function(e, data) { + var clearChoiceButton = root.find(SELECTORS.CLEARRESULTS_LINK); - // 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); - - data.originalEvent.preventDefault(); + root.on(CustomEvents.events.activate, SELECTORS.CLEARRESULTS_LINK, function(e, data) { + root.find(SELECTORS.ANSWER_RADIOS).each(function() { + $(this).prop('checked', false); + }); + $(e.target).addClass(CSSHIDDEN); + data.originalEvent.preventDefault(); }); - 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); + root.on(CustomEvents.events.activate, SELECTORS.ANSWER_RADIOS, function() { + clearChoiceButton.removeClass(CSSHIDDEN); }); }; @@ -95,11 +56,10 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) { * 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) { + var init = function(root) { root = $('#' + root); - registerEventListeners(root, fieldPrefix); + registerEventListeners(root); }; return { diff --git a/question/type/multichoice/renderer.php b/question/type/multichoice/renderer.php index 1213c8b5dca..f43ef81b627 100644 --- a/question/type/multichoice/renderer.php +++ b/question/type/multichoice/renderer.php @@ -274,6 +274,7 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base $question = $qa->get_question(); $response = $question->get_response($qa); + $clearchoicefieldname = $qa->get_qt_field_name('clearchoice'); $hascheckedchoice = false; foreach ($question->get_order($qa) as $value => $ansid) { if ($question->is_choice_selected($response, $value)) { @@ -282,33 +283,26 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base } } - $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, - 'class' => 'sr-only' - ]; + $questiondivid = $qa->get_outer_question_div_unique_id(); - $cssclass = 'qtype_multichoice_clearchoice'; // When no choice selected during rendering, then hide the clear choice option. + $csshidden = ''; if (!$hascheckedchoice && $response == -1) { - $cssclass .= ' sr-only'; - $clearchoiceradioattrs['checked'] = 'checked'; + $csshidden = ' d-none'; } - // 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::link('', get_string('clearchoice', 'qtype_multichoice'), - ['for' => $clearchoiceid, 'role' => 'button']); - // Now wrap the radio and label inside a div. - $result = html_writer::tag('div', $clearchoiceradio, ['id' => $clearchoicefieldname, 'class' => $cssclass]); + $clearchoicelink = html_writer::link('', get_string('clearchoice', 'qtype_multichoice'), [ + 'class' => $csshidden, + 'role' => 'button', + 'data-action' => 'clearresults', + 'data-target' => '#' . $questiondivid + ]); + $result = html_writer::tag('div', $clearchoicelink, ['id' => $clearchoicefieldname, + 'class' => 'qtype_multichoice_clearchoice']); // Load required clearchoice AMD module. $this->page->requires->js_call_amd('qtype_multichoice/clearchoice', 'init', - [$qa->get_outer_question_div_unique_id(), $clearchoicefieldname]); + [$questiondivid]); return $result; } diff --git a/question/type/multichoice/tests/behat/clearanswers.feature b/question/type/multichoice/tests/behat/clearanswers.feature new file mode 100644 index 00000000000..2facc007521 --- /dev/null +++ b/question/type/multichoice/tests/behat/clearanswers.feature @@ -0,0 +1,42 @@ +@qtype @qtype_multichoice +Feature: Clear my answers + As a student + In order to reset Multiple choice ansers + I need to clear my choice + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | S1 | Student1 | student1@moodle.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + And the following "question categories" exist: + | contextlevel | reference | name | + | Course | C1 | Test questions | + And the following "questions" exist: + | questioncategory | qtype | name | template | questiontext | + | Test questions | multichoice | Multi-choice-001 | one_of_four | Question One | + And the following "activities" exist: + | activity | name | intro | course | idnumber | preferredbehaviour | canredoquestions | + | quiz | Quiz 1 | Quiz 1 description | C1 | quiz1 | immediatefeedback | 1 | + And quiz "Quiz 1" contains the following questions: + | question | page | + | Multi-choice-001 | 1 | + + @javascript + Scenario: Attempt a quiz and reset my chosen answer. + When I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Quiz 1" + And I press "Attempt quiz now" + And I should see "Question One" + And I click on "Four" "radio" in the "Question One" "question" + And I should see "Clear my choice" + And I click on "Clear my choice" "button" in the "Question One" "question" + Then I should not see "Clear my choice" + And I click on "Check" "button" in the "Question One" "question" + And I should see "Please select an answer" in the "Question One" "question" diff --git a/question/type/multichoice/tests/walkthrough_test.php b/question/type/multichoice/tests/walkthrough_test.php index 37bc62f630b..9eb2be43b97 100644 --- a/question/type/multichoice/tests/walkthrough_test.php +++ b/question/type/multichoice/tests/walkthrough_test.php @@ -97,73 +97,4 @@ 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"/')); - } }