fix ie 8 select clipping in quizzes, fixes #4681

the driver behind this change was to fix quizzes for IE 8 so that select
options didn't get clipped when the contents were relatively long

summary of changes:

 * replace all select controls in quizzes with ui.selectmenu's
 * upgrade ui.selectmenu
 * tweak active control background to use a gradient instead of an image
 * remove quiz select control bugfix hacks
 * clean up speedgrader selectmenu usuage, which incidentally fixes issue
   where the selectmenu wouldn't close on subsequent clicks
 * fix amd bug in app/views/quizzes/history.html.erb

this change will make html answers in dropdowns fairly trivial to
implement

test plan:
 * create a quiz with matching questions and multiple dropdown
   questions, some with extremely long answer options
 * take the quiz
 * ensure that the selectmenu dropdowns appear and function correctly
   (e.g. all text should be visible for each option when the dropdown is
   expanded)
 * ensure that the selectmenu dropdowns appear and function correctly
   when viewing the quiz submission after the fact (as the student
   and via the teacher, e.g. in speedgrader)
 * use the speedgrader and ensure there are no regressions in the
   student dropdown behavior

Change-Id: I9e50853d4bf1ff4e7e606de872577f374df93061
Reviewed-on: https://gerrit.instructure.com/8869
Reviewed-by: Jon Jensen <jon@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
This commit is contained in:
Jon Jensen 2012-02-21 13:42:06 -07:00
parent be58ab1e54
commit 9f9b7695eb
13 changed files with 811 additions and 479 deletions

View File

@ -0,0 +1,19 @@
define [
'jquery'
'vendor/ui.selectmenu'
], ($) ->
$ ->
# css tweaks to ensure that it's sufficiently wide so the text doesn't
# get clipped unnecessarily when we make it a selectmenu. we do this here
# rather than in the css so as to avoid a flash of ugly content
#
# TODO: support HTML answers in dropdowns. to do this, we'll just need to
# 1. get rid of the escapeHTML: true (default is false)
# 2. in the views, double-escape any non-html answers going into dropdown
# options (yes, really ... escapeHTML should more accurately be named
# dontUnescapeAlreadyEscapedHTML)
$('.question select').css
'-webkit-appearance': 'none'
'font-size': '100%'
'padding-right': '40px'
.selectmenu escapeHtml: true

View File

@ -1,7 +1,7 @@
@import environment.sass
fieldset.question, fieldset.display_question
padding: 0
div.question
border-radius: 3px
#sort_questions
:max-height 250px
@ -852,3 +852,14 @@ ul#quiz_versions
+border-radius(5px)
padding: 16px
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.75)
.ui-selectmenu-menu a, .ui-selectmenu
color: #222 !important
font-weight: normal
text-decoration: none !important
.ui-selectmenu
max-width: 100% !important
.question select
max-width: 100%

View File

@ -19,14 +19,14 @@
<div class="question_holder <%= "group" if in_group %>" id="<%= "question_template" unless question %>" style="<%= hidden unless question %>">
<div style="display: block; height: 1px; overflow: hidden;">&nbsp;</div>
<a name="question_<%= hash_get(question, :id, "blank") %>"></a>
<fieldset class="display_question question <%= question_type.question_type %> <%= "marked" if assessing && @stored_params && @stored_params["question_#{hash_get(question, :id)}_marked"] %> <%= "correct" if user_answer && hash_get(user_answer, :correct) == true %> <%= "incorrect" if user_answer && hash_get(user_answer, :correct) == false %>" id="question_<%= hash_get(question, :id, "new") %>">
<div class="display_question question <%= question_type.question_type %> <%= "marked" if assessing && @stored_params && @stored_params["question_#{hash_get(question, :id)}_marked"] %> <%= "correct" if user_answer && hash_get(user_answer, :correct) == true %> <%= "incorrect" if user_answer && hash_get(user_answer, :correct) == false %>" id="question_<%= hash_get(question, :id, "new") %>">
<div class="move"><%= image_tag "move.png", :class => "move_icon" %></div>
<% if assessing %>
<div class="flag_question">
<%= image_tag "flag_question.png", :title => t('titles.mark_to_revisit', "Mark this question to come back to later"), :class => "flag_icon" %>
</div>
<% end %>
<legend class="header" <%= hidden(true) if (assessing || assessment_results) && question_type && question_type.entry_type == "none" %>>
<div class="header" <%= hidden(true) if (assessing || assessment_results) && question_type && question_type.entry_type == "none" %>>
<span class="name question_name"><%= hash_get(question, :question_name) %></span>:
<span class="question_points_holder" style="<%= hidden if question_type && question_type.entry_type == "none" %>">
<%= t(:points_possible, "%{points_possible} pts", :points_possible => raw("<span class=\"points question_points\">#{hash_get(question, :points_possible, "0")}</span>")) %>
@ -36,7 +36,7 @@
<% else %>
<a class='ui-helper-hidden-accessible' href="#question_<%= hash_get(question, :id, "new") %>_question_text"><%= t(:skip_question_text, "Skip to question text.") %></a>
<% end %>
</legend>
</div>
<div class="links" style="<%= hidden if user_answer || assessing %>">
<a href="#" class="edit_question_link no-hover" title="<%= t 'links.edit_question', 'Edit this Question' %>"><%= image_tag "edit.png" %></a>
<a href="#" class="delete_question_link no-hover" title="<%= t 'links.delete_question', 'Delete this Question' %>"><%= image_tag "delete.png" %></a>
@ -339,5 +339,5 @@
</div>
<% end %>
<div class="clear"></div>
</fieldset>
</div>
</div>

View File

@ -85,7 +85,8 @@
<script>
require([
'i18nObj' /* I18n.t */,
'jquery' /* $ */
'jquery' /* $ */,
'compiled/behaviors/quiz_selectmenu'
], function(I18n, $) {
$(document).ready(function() {

View File

@ -128,8 +128,9 @@
require([
'jquery' /* $ */,
'jquery.instructure_misc_plugins' /* fragmentChange */,
'fragmentChangejquery.templateData' /* getTemplateData */,
'vendor/jquery.scrollTo' /* /\.scrollTo/ */
'jquery.templateData' /* getTemplateData */,
'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
'compiled/behaviors/quiz_selectmenu'
], function($) {
var data = $("#submission_details").getTemplateData({textValues: ['version_number', 'user_id']});

View File

@ -160,6 +160,7 @@ stylesheets:
quizzes:
- public/stylesheets/compiled/quizzes.css
- public/stylesheets/compiled/message_students.css
- public/stylesheets/static/ui.selectmenu.css
moderate_quiz:
- public/stylesheets/compiled/moderate_quiz.css
full_assignment:

View File

@ -57,7 +57,7 @@ require([
// Determines whether or to show the "show question details" link.
checkShowDetails: function() {
var hasQuestions = this.$questions.find('fieldset:not(.essay_question, .text_only_question)').length;
var hasQuestions = this.$questions.find('div.display_question:not(.essay_question, .text_only_question)').length;
this.$showDetailsWrap[hasQuestions ? 'show' : 'hide'](200);
},

View File

@ -938,18 +938,16 @@ require([
goToStudent: function(student_id){
var student = $.grep(jsonData.studentsWithSubmissions, function(o){
return o.id === student_id;
})[0];
return o.id === student_id;
})[0];
var indexOfStudentInOptions = $selectmenu.find('option').index($selectmenu.find('option[value="' + student.id + '"]'));
if (!(indexOfStudentInOptions >= 0)) {
throw "student not found";
}
$selectmenu.selectmenu("value", indexOfStudentInOptions);
//this is lame but I have to manually tell $selectmenu to fire its 'change' event if has changed.
if (!this.currentStudent || (this.currentStudent.id != student.id)) {
$selectmenu.change();
}
if (student) {
$selectmenu.selectmenu("value", student.id);
//this is lame but I have to manually tell $selectmenu to fire its 'change' event if has changed.
if (!this.currentStudent || (this.currentStudent.id != student.id)) {
$selectmenu.change();
}
}
},
currentIndex: function(){

View File

@ -26,7 +26,8 @@ require([
'jquery.instructure_misc_helpers' /* scrollSidebar */,
'jquery.rails_flash_notifications' /* flashMessage */,
'tinymce.editor_box' /* editorBox */,
'vendor/jquery.scrollTo' /* /\.scrollTo/ */
'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
'compiled/behaviors/quiz_selectmenu'
], function(I18n, $, timing) {
var lastAnswerSelected = null;
@ -188,9 +189,6 @@ require([
});
$(function() {
// prevent mousewheel from changing answers on dropdowns see #6143
$('select').bind('mousewheel', false);
$.scrollSidebar();
if($("#preview_mode_link").length == 0) {
@ -201,7 +199,7 @@ require([
}
};
$(document).delegate('a', 'click', function(event) {
if($(this).closest('.ui-dialog,.mceToolbar').length > 0) { return; }
if($(this).closest('.ui-dialog,.mceToolbar,.ui-selectmenu').length > 0) { return; }
if(!event.isDefaultPrevented()) {
var url = $(this).attr('href') || "";
var hashStripped = location.href;
@ -258,33 +256,6 @@ require([
}
});
/* the intent of this is to ensure that the class doesn't ever change while
the dropdown is open. in windows chrome, mouseleave events still fire
for ancestors when a dropdown is open, and any style changes to them
cause the dropdown to jump/reset. this effectively normalizes the
mouseenter/mouseleave behavior across platforms and browsers, but the
side effect is that the hover class is retained until the mouse has left
and the select has blurred. */
$questions.find('.question').bind({
mouseenter: function(event) {
var $container = $(this);
var $activeSelect = $container.find($(document.activeElement)).filter("select");
if ($activeSelect.length) $activeSelect.unbind('blur.unhoverQuestion');
if (!$container.hasClass('hover')) $container.addClass('hover');
},
mouseleave: function(event) {
var $container = $(this);
var $activeSelect = $container.find($(document.activeElement)).filter("select");
if ($activeSelect.length) {
$activeSelect.one('blur.unhoverQuestion', function() {
$(this).closest('.question').trigger('mouseleave');
});
} else {
$container.removeClass('hover');
}
}
});
$questions
.delegate(":checkbox,:radio,label", 'change mouseup', function(event) {
var $answer = $(this).parents(".answer");

File diff suppressed because it is too large Load Diff

View File

@ -60,7 +60,7 @@
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(/images/jqueryui/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #5F83B9; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #749AAF; background: #fdf5ce url(/images/jqueryui/button_bg.png) 50% 50% repeat-x; font-weight: bold; color: #1C4257; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #749AAF; background-color: #9ec8df; background-image: -webkit-gradient(linear, left top, left bottom, from(#b9e0f5), to(#83b0ca)); background-image: -webkit-linear-gradient(top, #b9e0f5, #83b0ca); background-image: -moz-linear-gradient(top, #b9e0f5, #83b0ca); background-image: -ms-linear-gradient(top, #b9e0f5, #83b0ca); background-image: -o-linear-gradient(top, #b9e0f5, #83b0ca); background-image: linear-gradient(top, #b9e0f5, #83b0ca); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#b9e0f5',EndColorStr='#83b0ca')"; font-weight: bold; color: #1C4257; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); }
.ui-state-hover a, .ui-state-hover a:hover { color: #1C4257; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(/images/jqueryui/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }

View File

@ -1,29 +1,27 @@
/* Selectmenu
----------------------------------*/
.ui-selectmenu { display: block; position:relative; height:2em; text-decoration: none; overflow:hidden;}
.ui-selectmenu { display: block; display: inline-block; position: relative; height: 2.2em; vertical-align: middle; text-decoration: none; overflow: hidden; zoom: 1; }
.ui-selectmenu-icon { position:absolute; right:6px; margin-top:-8px; top: 50%; }
.ui-selectmenu-menu { padding:0; margin:0; list-style:none; position:absolute; top: 0; visibility: hidden; display:none; overflow: auto; -moz-border-radius: 0; -webkit-border-radius: 0px; border-radius: 0;}
.ui-selectmenu-open { visibility: visible; display:block; }
.ui-selectmenu-menu { padding:0; margin:0; position:absolute; top: 0; display: none; z-index: 1005; background: #fff} /* z-index: 1005 to make selectmenu work with dialog */
.ui-selectmenu-menu ul { padding:0; margin:0; list-style:none; position: relative; overflow: auto; overflow-y: auto ; overflow-x: hidden; }
.ui-selectmenu-open { display: block; }
.ui-selectmenu-menu-popup { margin-top: -1px; }
.ui-selectmenu-menu-dropdown {
background-color: white;
border-color: #B5B8C8;
}
.ui-selectmenu-menu li { padding:0; margin:0; display: block; border-top: 1px dotted transparent; border-bottom: 1px dotted transparent; border-right-width: 0 !important; border-left-width: 0 !important; font-weight: normal !important; }
.ui-selectmenu-menu li a,.ui-selectmenu-status {line-height: 1em; display:block; padding:.3em 1em; outline:none; text-decoration:none; }
.ui-selectmenu-menu li a,.ui-selectmenu-status { line-height: 1.4em; display: block; padding: .405em 2.1em .405em 1em; outline:none; text-decoration:none; }
.ui-selectmenu-menu li.ui-state-disabled a, .ui-state-disabled { cursor: default; }
.ui-selectmenu-menu li.ui-selectmenu-hasIcon a,
.ui-selectmenu-hasIcon .ui-selectmenu-status { padding-left: 20px; position: relative; margin-left: 5px; }
.ui-selectmenu-menu li .ui-icon, .ui-selectmenu-status .ui-icon { position: absolute; top: 1em; margin-top: -8px; left: 0; }
.ui-selectmenu-status { line-height: 1.4em; }
.ui-selectmenu-open li.ui-selectmenu-item-focus a { background-color: transparent; }
.ui-selectmenu-open li.ui-selectmenu-item-selected { }
.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: 0; }
.ui-selectmenu-menu li .ui-selectmenu-item-header { }
.ui-selectmenu-menu li .ui-selectmenu-item-content { }
.ui-selectmenu-menu li .ui-selectmenu-item-footer {
margin-top: -2px;
}
/*for optgroups*/
.ui-selectmenu-menu li span,.ui-selectmenu-status span { display:block; margin-bottom: .2em; }
.ui-selectmenu-menu li .ui-selectmenu-item-header { font-weight: bold; }
.ui-selectmenu-menu li .ui-selectmenu-item-footer { opacity: .8; }
/* for optgroups */
.ui-selectmenu-menu .ui-selectmenu-group { font-size: 1em; }
.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding:.6em .5em 0; font-weight: bold; }
.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; }
.ui-selectmenu-menu .ui-selectmenu-group .ui-selectmenu-group-label { line-height: 1.4em; display:block; padding: .6em .5em 0; font-weight: bold; }
.ui-selectmenu-menu .ui-selectmenu-group ul { margin: 0; padding: 0; }
/* IE6 workaround (dotted transparent borders) */
* html .ui-selectmenu-menu li { border-color: pink; filter:chroma(color=pink); width:100%; }
* html .ui-selectmenu-menu li a { position: relative }
/* IE7 workaround (opacity disabled) */
*+html .ui-state-disabled, *+html .ui-state-disabled a { color: silver; }

View File

@ -266,32 +266,13 @@ describe "quizzes questions" do
end
end
# see blur.unhoverQuestion in take_quiz.js. avoids a windows chrome display glitch
it "should not unhover a question so long as one of its selects has focus" do
container = driver.find_element(:css, '.question')
driver.execute_script("$('.question').mouseenter()")
container.attribute(:class).should match(/hover/)
container.find_element(:css, 'select').click
driver.execute_script("$('.question').mouseleave()")
container.attribute(:class).should match(/hover/)
driver.execute_script("$('.question select').blur()")
container.attribute(:class).should_not match(/hover/)
end
it "should cancel mousewheel events on select elements" do
skip_if_ie('Out of memory')
driver.execute_script <<-EOF
window.mousewheelprevented = false;
jQuery('select').bind('mousewheel', function(event) {
mousewheelprevented = event.isDefaultPrevented();
}).trigger('mousewheel');
EOF
is_prevented = driver.execute_script('return window.mousewheelprevented')
is_prevented.should be_true
it "should selectmenu-ify select elements" do
select = driver.find_element(:css, '.question select')
keep_trying_until { !select.displayed? }
driver.find_element(:css, 'a.ui-selectmenu').click
driver.find_elements(:css, '.ui-selectmenu-open li')[1].click
select[:selectedIndex].should eql "1"
end
end
end