Adds move to option for modules

Makes the modules page more accessible for both screenreader and
keyboard only users.

fixes CNVS-9045

test plan:
  1. Using a screenreader go to the modules page (/courses/##/modules)
  2. The "keyboard instructions" area should inform you to use the
     move module options.
  3. Navigate to the admin links cog for a module
  4. You should see a "Move to..." option.
  5. Click it using whatever the screenreader uses for click
     (VO: Ctrl+Option+Space)
  6. A dialog should appear (This dialog should be accessible)
  7. You should be able to move a module to whatever location you
     want.
  8. Close the screenreader, repeat steps 3-7 using only the
     keyboard.
  9. When the drag-and-drop handle has focus a warning should appear.

Change-Id: Ie64b79ab6fa85d728fd97fba3842717b1a6ed99b
Reviewed-on: https://gerrit.instructure.com/39806
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
QA-Review: Clare Strong <clare@instructure.com>
Product-Review: Hilary Scharton <hilary@instructure.com>
This commit is contained in:
Clay Diffrient 2014-08-22 12:22:35 -06:00
parent 8bfb31f251
commit c930295ba6
3 changed files with 154 additions and 24 deletions

View File

@ -145,6 +145,34 @@
border: none !important;
}
input.move-module-label-spacer {
margin-right: 10px;
}
.move-module-or {
display: inline-block;
width: 60px;
margin-left: 20px;
}
.move-module-select-spacer {
margin-top: 10px;
}
.drag_and_drop_warning {
display: none;
color: #fff;
background-color: #DC3F3F;
border: 1px dashed #fff;
font-size: 12px;
padding: 1px;
z-index: 2;
position: absolute;
top: -20px;
left: -1px;
}
</style>
<% if course_home %>
@ -162,15 +190,9 @@
<% end %>
</div>
<div class="hidden-readable" tabindex="0" aria-label="keyboard instructions">
<%= t('modules_keyboard_hint',
'To change the order of the course modules and module items,
first turn the cursor off on your screen reader. Insert-z in JAWS.
Press tab to select the first module.
Press up and down to choose a module or module item.
Press space to select the module or module item to start dragging.
Then press up and down to select a destination.
Then press space a second time to drop selection after destination.') %>
<div class="hidden-readable screenreader-only" tabindex="0" aria-label="keyboard instructions">
<%= t('modules_keyboard_hint_updated',
'Warning: For improved accessibility, please use the Move To Dialog option found in the menu.') %>
</div>
<% keyboard_navigation([
@ -215,6 +237,26 @@ TEXT
<%= render :partial => 'context_modules/module_item_next', :object => nil, :locals => {:editable => editable} %>
<% if can_do(@context, @current_user, :manage_content) %>
<form id="move_context_module_form" style="display:none" class="form-dialog" title="Move Module">
<div class="form-dialog-content">
<h2>Place <span id="move_module_name"> </span></h2>
<div class="move-module-before-after-container">
<input type="radio" name="move_location" value="before" id="move_location_before" class="move-module-label-spacer" /><label class="move-module-label-width" for="move_location_before"><%= t('move_module.before', "Before") %></label>
<strong class="move-module-or">OR</strong>
<input type="radio" name="move_location" value="after" id="move_location_after" class="move-module-label-spacer" /><label class="move-module-label-width" for="move_location_after"><%= t('move_module.after', "After") %></label>
</div>
<div class="move-module-select-spacer">
<select name="move_context_module_select" id="move_context_module_select"></select>
</div>
</div>
<div class="form-controls">
<button type="button" id="move_module_cancel_btn" class="btn">Cancel</button>
<button type="submit" class="btn btn-primary">Move</button>
</div>
</form>
<%= form_for :context_module, :url => context_url(@context, :context_context_modules_url), :html => {:id => "add_context_module_form", :style => "display: none;"} do |f| %>
<table class="formtable">
<tr>
@ -377,8 +419,8 @@ TEXT
<tr>
<td class="left">
<div style="display: none;">
<%= before_label('sort_by', %{Sort By}) %>
<a href="#"><%= t('links.sort_by_name', %{Name}) %></a> |
<%= before_label('sort_by', %{Sort By}) %>
<a href="#"><%= t('links.sort_by_name', %{Name}) %></a> |
<a href="#"><%= t('links.sort_by_progress', %{Progress}) %></a>
</div>
<ul class="side_tabs student_list">

View File

@ -1,4 +1,4 @@
<%
<%
context_module = context_module_next
editable ||= can_do(@context, @current_user, :manage_content)
workflow_state = context_module && context_module.workflow_state
@ -24,6 +24,10 @@
title="<%= t('reorder_modules', %{Drag to reorder modules}) %>"
style="<%= hidden unless @modules.length > 1 && editable %>"
>
<span class="drag_and_drop_warning">
<%= t('modules_keyboard_drag_and_drop',
'Warning: For improved accessibility, please use the Move To Dialog option found in the menu.') %>
</span>
<a aria-label="<%= t('reorder_modules', 'Drag to reorder modules') %>" href="#" class="icon-drag-handle"></a>
</span>
<a
@ -81,13 +85,19 @@
<i class="icon-settings"></i><i class="icon-mini-arrow-down"></i>
</button>
<ul class="al-options">
<li>
<li role="presentation">
<a
href="<%= context_url(@context, :context_url) %>/modules/<%= context_module ? context_module.id : "{{ id }}" %>"
class="icon-edit edit_module_link"
title="<%= t('links.title.edit_module', %{Edit}) %>"><%= t('links.text.edit_module', %{Edit}) %></a>
</li>
<li>
<li role="presentation">
<a
href="#<%= context_module ? context_module.id : "{{ id }}" %>"
class="move_module_link icon-updown"
title="<%= t('links.title.move_module', %{Move this module}) %>"><%= t('links.text.move_module', %{Move To...}) %></a>
</li>
<li role="presentation">
<a
href="<%= context_url(@context, :context_url) %>/modules/<%= context_module ? context_module.id : "{{ id }}" %>"
class="delete_module_link icon-trash"

View File

@ -126,7 +126,7 @@ define([
.find(".progression_complete").showIf(data.progression_complete_count > 0).end()
.find(".progression_started").showIf(data.progression_started_count > 0);
});
$(".context_module .progression_complete").showIf($(".context_module .prerequisites_footer:visible,.context_module_item .criterion img.not_blank").length > 0);
if(show_links) {
$(".loading_module_progressions_link").remove();
@ -176,7 +176,7 @@ define([
var progression = data.context_module_progression;
if(progression.user_id == current_user_id) {
var $user_progression = $user_progression_list.find(".progression_" + progression.context_module_id)
if($user_progression.length === 0 && $user_progression_list.length > 0) {
$user_progression = $user_progression_list.find(".progression_blank").clone(true);
$user_progression.removeClass('progression_blank').addClass('progression_' + progression.context_module_id);
@ -248,6 +248,61 @@ define([
$this.attr('title', content_tag.title);
});
},
showMoveModule: function ($module) {
var $form = $('#move_context_module_form');
$form.data('current_module', $module);
// Set the module name
$('#move_module_name').text($module.children('.header').children('.collapse_module_link').children('.name').text());
// Get current module ordering
var currentOrdering = [];
var selectOptions = [];
$("#context_modules .context_module").each(function() {
var id = $(this).attr('id').substring('context_module_'.length);
var name = $(this).children('.header').children('.collapse_module_link').children('.name').text();
currentOrdering.push({
'id': id,
'name': name
});
selectOptions.push('<option value="' + id + '">' + name + '</option>');
});
var data = $module.getTemplateData({textValues: ['name', 'unlock_at', 'require_sequential_progress', 'publish_final_grade']});
$('#move_context_module_select').append(selectOptions.join(''));
//$form.fillFormData(data, {object_name: 'context_module'});
$form.dialog({
autoOpen: false,
modal: true,
width: 600,
height: 300,
close: function () {
modules.hideMoveModule(true);
}
}).dialog('open');
$module.removeClass('dont_remove');
// $form.find('.ui-dialog-titlebar-close').focus();
},
hideMoveModule: function (remove) {
$('#move_context_module_form:visible').dialog('close');
},
submitMoveModule: function () {
var beforeOrAfterVal = $('[name="move_location"]:checked').val();
var currentModule = $('#move_context_module_form').data('current_module');
var relativeToId = $('#move_context_module_select').val();
if (beforeOrAfterVal === 'before') {
$('#context_module_' + relativeToId).before(currentModule);
}
if (beforeOrAfterVal === 'after') {
$('#context_module_' + relativeToId).after(currentModule);
}
modules.hideMoveModule();
modules.updateModulePositions();
},
editModule: function($module) {
var $form = $("#add_context_module_form");
$form.data('current_module', $module);
@ -420,7 +475,7 @@ define([
}
});
});
for (var val in result.to_visit) {
if (result.to_visit.hasOwnProperty(val)) {
var ids = val.split("_");
@ -480,10 +535,10 @@ define([
}
};
})();
modules.initModuleManagement = function() {
// Create the context modules backbone view to manage the publish button.
// Create the context modules backbone view to manage the publish button.
var context_modules_view = new ContextModulesView({
el: $("#content"),
modules: modules
@ -731,6 +786,8 @@ define([
$(this).remove();
});
});
$(".delete_module_link").live('click', function(event) {
event.preventDefault();
$(this).parents(".context_module").confirmDelete({
@ -816,6 +873,27 @@ define([
});
});
$('.move_module_link').on('click keyclick', function (event) {
event.preventDefault();
modules.showMoveModule($(this).parents('.context_module'));
});
$('#move_context_module_form').on('submit', function (event) {
event.preventDefault();
modules.submitMoveModule();
});
$('#move_module_cancel_btn').on('click keyclick', function (event) {
modules.hideMoveModule();
});
$('.icon-drag-handle').on('focus', function (event) {
$(event.currentTarget).siblings('.drag_and_drop_warning').show();
});
$('.icon-drag-handle').on('blur', function (event) {
$(event.currentTarget).siblings('.drag_and_drop_warning').hide();
});
$(".edit_module_link").live('click', function(event) {
event.preventDefault();
modules.editModule($(this).parents(".context_module"));
@ -1202,11 +1280,11 @@ define([
if($("#context_modules").hasClass('editable')) {
setTimeout(modules.initModuleManagement, 1000);
}
modules.updateProgressions();
modules.refreshProgressions();
modules.updateAssignmentData();
$(".context_module").find(".expand_module_link,.collapse_module_link").bind('click', function(event, goSlow) {
event.preventDefault();
var expandCallback = null;
@ -1304,7 +1382,7 @@ define([
var $module = $(this);
var moduleData = $module.find(".header").getTemplateData({textValues: ['id', 'name']});
var $row = $("#student_progression_dialog .module_" + moduleData.id);
moduleData.progress = $studentWithProgressions.find(".progression_" + moduleData.id + ":first").getTemplateData({textValues: ['workflow_state']}).workflow_state;
moduleData.progress = moduleData.progress || "no information";
var type = "nothing";
@ -1387,12 +1465,12 @@ define([
var $module = $(this);
var moduleData = $module.find(".header").getTemplateData({textValues: ['id', 'name']});
var $template = $dialog.find(".module.blank:first").clone(true).removeClass('blank');
$template.addClass('module_' + moduleData.id);
$template.fillTemplateData({data: moduleData});
$dialog.find(".side_tabs_content tbody").append($template.show());
});
$("#student_progression_dialog").dialog({
width: 800,
open: function() {