From bc8bf406c8920a57c4eedc2a998b97f5a0f5bf37 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 7 Mar 2022 11:31:56 +1100 Subject: [PATCH] MDL-71305 core_question: Use toggle button for flag question element --- question/engine/lib.php | 12 ++---- question/engine/renderer.php | 27 ++++++------- question/flags.js | 73 ++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 46 deletions(-) diff --git a/question/engine/lib.php b/question/engine/lib.php index 62990f180bd..ace64193025 100644 --- a/question/engine/lib.php +++ b/question/engine/lib.php @@ -747,26 +747,22 @@ abstract class question_flags { 'requires' => array('base', 'dom', 'event-delegate', 'io-base'), ); $actionurl = $CFG->wwwroot . '/question/toggleflag.php'; - $flagtext = array( - 0 => get_string('clickflag', 'question'), - 1 => get_string('clickunflag', 'question') - ); $flagattributes = array( 0 => array( 'src' => $OUTPUT->image_url('i/unflagged') . '', 'title' => get_string('clicktoflag', 'question'), - 'alt' => get_string('notflagged', 'question'), - // 'text' => get_string('clickflag', 'question'), + 'alt' => get_string('flagged', 'question'), // Label on toggle should not change. + 'text' => get_string('clickflag', 'question'), ), 1 => array( 'src' => $OUTPUT->image_url('i/flagged') . '', 'title' => get_string('clicktounflag', 'question'), 'alt' => get_string('flagged', 'question'), - // 'text' => get_string('clickunflag', 'question'), + 'text' => get_string('clickunflag', 'question'), ), ); $PAGE->requires->js_init_call('M.core_question_flags.init', - array($actionurl, $flagattributes, $flagtext), false, $module); + array($actionurl, $flagattributes), false, $module); $done = true; } } diff --git a/question/engine/renderer.php b/question/engine/renderer.php index be328f8447a..4ce0e7c193c 100644 --- a/question/engine/renderer.php +++ b/question/engine/renderer.php @@ -256,8 +256,6 @@ class core_question_renderer extends plugin_renderer_base { * @param int $flagsoption the option that says whether flags should be displayed. */ protected function question_flag(question_attempt $qa, $flagsoption) { - global $CFG; - $divattributes = array('class' => 'questionflag'); switch ($flagsoption) { @@ -283,17 +281,14 @@ class core_question_renderer extends plugin_renderer_base { $flagcontent = html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $id, 'value' => 0)) . - html_writer::empty_tag('input', $checkboxattributes) . html_writer::empty_tag('input', array('type' => 'hidden', 'value' => $postdata, 'class' => 'questionflagpostdata')) . + html_writer::empty_tag('input', $checkboxattributes) . html_writer::tag('label', $this->get_flag_html($qa->is_flagged(), $id . 'img'), array('id' => $id . 'label', 'for' => $id . 'checkbox')) . "\n"; $divattributes = array( 'class' => 'questionflag editable', - 'aria-atomic' => 'true', - 'aria-relevant' => 'text', - 'aria-live' => 'assertive', ); break; @@ -315,18 +310,16 @@ class core_question_renderer extends plugin_renderer_base { protected function get_flag_html($flagged, $id = '') { if ($flagged) { $icon = 'i/flagged'; - $alt = get_string('flagged', 'question'); $label = get_string('clickunflag', 'question'); } else { $icon = 'i/unflagged'; - $alt = get_string('notflagged', 'question'); $label = get_string('clickflag', 'question'); } - $attributes = array( + $attributes = [ 'src' => $this->image_url($icon), - 'alt' => $alt, + 'alt' => '', 'class' => 'questionflagimage', - ); + ]; if ($id) { $attributes['id'] = $id; } @@ -336,10 +329,14 @@ class core_question_renderer extends plugin_renderer_base { return $img; } - protected function edit_question_link(question_attempt $qa, - question_display_options $options) { - global $CFG; - + /** + * Generate the display of the edit question link. + * + * @param question_attempt $qa The question attempt to display. + * @param question_display_options $options controls what should and should not be displayed. + * @return string + */ + protected function edit_question_link(question_attempt $qa, question_display_options $options) { if (empty($options->editquestionparams)) { return ''; } diff --git a/question/flags.js b/question/flags.js index aa8dac16cc5..3720d2409b1 100644 --- a/question/flags.js +++ b/question/flags.js @@ -28,13 +28,11 @@ M.core_question_flags = { flagattributes: null, actionurl: null, - flagtext: null, listeners: [], - init: function(Y, actionurl, flagattributes, flagtext) { + init: function(Y, actionurl, flagattributes) { M.core_question_flags.flagattributes = flagattributes; M.core_question_flags.actionurl = actionurl; - M.core_question_flags.flagtext = flagtext; Y.all('div.questionflag').each(function(flagdiv, i) { var checkbox = flagdiv.one('input[type=checkbox]'); @@ -47,38 +45,67 @@ M.core_question_flags = { input.set('name', checkbox.get('name')); input.set('value', checkbox.get('checked') ? 1 : 0); - // Create an image input to replace the img tag. - var image = Y.Node.create(''); - var flagtext = Y.Node.create('.'); - M.core_question_flags.update_flag(input, image, flagtext); + var ariaPressed = checkbox.get('checked') ? 'true' : 'false'; + var toggle = Y.Node.create('' + + '.' + + ''); + M.core_question_flags.update_flag(input, toggle); checkbox.remove(); flagdiv.one('label').remove(); flagdiv.append(input); - flagdiv.append(image); - flagdiv.append(flagtext); + flagdiv.append(toggle); }); Y.delegate('click', function(e) { - var input = this.one('input.questionflagvalue'); - input.set('value', 1 - input.get('value')); - M.core_question_flags.update_flag(input, this.one('input.questionflagimage'), - this.one('span.questionflagtext')); - var postdata = this.one('input.questionflagpostdata').get('value') + - input.get('value'); - e.halt(); - Y.io(M.core_question_flags.actionurl , {method: 'POST', 'data': postdata}); - M.core_question_flags.fire_listeners(postdata); + M.core_question_flags.process(this); }, document.body, 'div.questionflag'); + Y.delegate('key', function(e) { + e.halt(); + if (e.keyCode == 13) { + M.core_question_flags.process(this); + } + }, document.body, 'down:enter, space', 'div.questionflag'); + Y.delegate('key', function(e) { + e.halt(); + M.core_question_flags.process(this); + }, document.body, 'up:space', 'div.questionflag'); }, - update_flag: function(input, image, flagtext) { + update_flag: function(input, toggle) { var value = input.get('value'); - image.setAttrs(M.core_question_flags.flagattributes[value]); - flagtext.replaceChild(flagtext.create(M.core_question_flags.flagtext[value]), - flagtext.get('firstChild')); - flagtext.set('title', M.core_question_flags.flagattributes[value].title); + toggle.setContent( + '' + + M.core_question_flags.flagattributes[value].text + ); + toggle.set('aria-pressed', parseInt(value) ? 'true' : 'false'); + toggle.set('aria-label', M.core_question_flags.flagattributes[value].alt); + if (M.core_question_flags.flagattributes[value].title != M.core_question_flags.flagattributes[value].text) { + toggle.set('title', M.core_question_flags.flagattributes[value].title); + } else { + toggle.removeAttribute('title'); + } + }, + + /** + * Process the change of flag status. + * + * @param {Y.Node} target The root element + */ + process: function(target) { + var input = target.one('input.questionflagvalue'); + input.set('value', 1 - input.get('value')); + M.core_question_flags.update_flag(input, target.one('[aria-pressed]')); + var postdata = target.one('input.questionflagpostdata').get('value') + + input.get('value'); + + Y.io(M.core_question_flags.actionurl, {method: 'POST', 'data': postdata}); + M.core_question_flags.fire_listeners(postdata); }, add_listener: function(listener) {