MDL-68864 qtype_multichoice: shift focus to first option on keyboard tab

- This prevents losing focus when using keyboard navigation.
This commit is contained in:
Bas Brands 2020-06-09 15:39:34 +02:00 committed by Jun Pataleta
parent b59de78bd1
commit 233eafd8eb
5 changed files with 77 additions and 7 deletions

View File

@ -1,2 +1,2 @@
define ("qtype_multichoice/clearchoice",["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)})};return{init:function init(b,c){b=a("#"+b);h(b,c)}}});
define ("qtype_multichoice/clearchoice",["jquery","core/custom_interaction_events"],function(a,b){var c={CHOICE_ELEMENT:".answer input",LINK:"a",RADIO:"input[type=\"radio\"]"},d=function(a){a.find(c.RADIO).prop("disabled",!1).prop("checked",!0)},e=function(a,b){return a.find("div[id=\""+b+"\"]")},f=function(a){a.addClass("sr-only");a.find(c.LINK).attr("tabindex",-1)},g=function(a){a.removeClass("sr-only");a.find(c.LINK).attr("tabindex",0);a.find(c.RADIO).prop("disabled",!0)},h=function(a,h){var i=e(a,h);i.on(b.events.activate,c.LINK,function(a,b){d(i);f(i);b.originalEvent.preventDefault()});a.on(b.events.activate,c.CHOICE_ELEMENT,function(){g(i)});i.find(c.RADIO).focus(function(){var b=a.find(c.CHOICE_ELEMENT).first();b.focus()})};return{init:function init(b,c){b=a("#"+b);h(b,c)}}});
//# sourceMappingURL=clearchoice.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -25,16 +25,17 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
var SELECTORS = {
CHOICE_ELEMENT: '.answer input',
CLEAR_CHOICE_ELEMENT: 'div[class="qtype_multichoice_clearchoice"]'
LINK: 'a',
RADIO: 'input[type="radio"]'
};
/**
* Mark clear choice radio as checked.
* Mark clear choice radio as enabled and checked.
*
* @param {Object} clearChoiceContainer The clear choice option container.
*/
var checkClearChoiceRadio = function(clearChoiceContainer) {
clearChoiceContainer.find('input[type="radio"]').prop('checked', true);
clearChoiceContainer.find(SELECTORS.RADIO).prop('disabled', false).prop('checked', true);
};
/**
@ -55,6 +56,7 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
*/
var hideClearChoiceOption = function(clearChoiceContainer) {
clearChoiceContainer.addClass('sr-only');
clearChoiceContainer.find(SELECTORS.LINK).attr('tabindex', -1);
};
/**
@ -64,6 +66,8 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
*/
var showClearChoiceOption = function(clearChoiceContainer) {
clearChoiceContainer.removeClass('sr-only');
clearChoiceContainer.find(SELECTORS.LINK).attr('tabindex', 0);
clearChoiceContainer.find(SELECTORS.RADIO).prop('disabled', true);
};
/**
@ -75,7 +79,7 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
var registerEventListeners = function(root, fieldPrefix) {
var clearChoiceContainer = getClearChoiceElement(root, fieldPrefix);
root.on(CustomEvents.events.activate, SELECTORS.CLEAR_CHOICE_ELEMENT, function(e, data) {
clearChoiceContainer.on(CustomEvents.events.activate, SELECTORS.LINK, function(e, data) {
// Mark the clear choice radio element as checked.
checkClearChoiceRadio(clearChoiceContainer);
@ -89,6 +93,13 @@ define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
// If the event has been triggered by any other choice, show the clear choice option.
showClearChoiceOption(clearChoiceContainer);
});
// If the clear choice radio receives focus from using the tab key, return the focus
// to the first answer option.
clearChoiceContainer.find(SELECTORS.RADIO).focus(function() {
var firstChoice = root.find(SELECTORS.CHOICE_ELEMENT).first();
firstChoice.focus();
});
};
/**

View File

@ -298,14 +298,16 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base
$cssclass = 'qtype_multichoice_clearchoice';
// When no choice selected during rendering, then hide the clear choice option.
$linktabindex = 0;
if (!$hascheckedchoice && $response == -1) {
$cssclass .= ' sr-only';
$clearchoiceradioattrs['checked'] = 'checked';
$linktabindex = -1;
}
// 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']);
['for' => $clearchoiceid, 'role' => 'button', 'tabindex' => $linktabindex]);
// Now wrap the radio and label inside a div.
$result = html_writer::tag('div', $clearchoiceradio, ['id' => $clearchoicefieldname, 'class' => $cssclass]);

View File

@ -0,0 +1,57 @@
@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"
@javascript
Scenario: Attempt a quiz and revisit a cleared 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 follow "Finish attempt ..."
And I click on "Return to attempt" "button"
And I click on "Clear my choice" "button" in the "Question One" "question"
And I follow "Finish attempt ..."
And I click on "Return to attempt" "button"
Then I should not see "Clear my choice"