Commit 50240c3e by Mark Sadecki

Merge pull request #9114 from mitocw/fix/aq/ccx_schedule

Fixed accessibility issues in Schedule Tab in CCX
parents 460b3311 4c8e45c7
......@@ -86,9 +86,9 @@ var edx = edx || {};
}
else {
self.vertical_select.html('').prop('disabled', true);
}
}
});
this.vertical_select.on('change', function() {
var vertical_location = self.vertical_select.val();
if (vertical_location !== 'all') {
......@@ -115,9 +115,9 @@ var edx = edx || {};
due = self.get_datetime('due');
units.map(self.show);
var unit = units[units.length - 1];
self.schedule_apply([unit], self.show);
if (unit !== undefined && start) { unit.start = start; }
if (unit !== undefined && due) { unit.due = due; }
self.schedule_apply([unit], self.show);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
......@@ -139,22 +139,27 @@ var edx = edx || {};
return !node.hidden;});
this.$el.html(schedule_template({chapters: this.showing}));
$('table.ccx-schedule .sequential,.vertical').hide();
$('table.ccx-schedule .toggle-collapse').on('click', this.toggle_collapse);
$('table.ccx-schedule .unit .toggle-collapse').on('click', this.toggle_collapse);
//
// Hidden hover fields for empty date fields
$('table.ccx-schedule .date a').each(function() {
if (! $(this).text()) {
$(this).text('Set date').addClass('empty');
$('table.ccx-schedule .date button').each(function() {
if ($(this).text().trim() === gettext("Click to change")) {
$(this).html('Set date <span class="sr"> ' +
gettext("Click to change") + '</span>');
}
});
// Handle date edit clicks
$('table.ccx-schedule .date a').attr('href', '#enter-date-modal')
$('table.ccx-schedule .date button').attr('href', '#enter-date-modal')
.leanModal({closeButton: '.close-modal'});
$('table.ccx-schedule .due-date a').on('click', this.enterNewDate('due'));
$('table.ccx-schedule .start-date a').on('click', this.enterNewDate('start'));
$('table.ccx-schedule .due-date button').on('click', this.enterNewDate('due'));
$('table.ccx-schedule .start-date button').on('click', this.enterNewDate('start'));
// click handler for expand all
$('#ccx_expand_all_btn').on('click', self.expandAll);
// click handler for collapse all
$('#ccx_collapse_all_btn').on('click', self.collapseAll);
// Click handler for remove all
$('table.ccx-schedule a#remove-all').on('click', function(event) {
$('table.ccx-schedule button#remove-all').on('click', function(event) {
event.preventDefault();
self.schedule_apply(self.schedule, self.hide);
self.dirty = true;
......@@ -162,14 +167,14 @@ var edx = edx || {};
self.render();
});
// Remove unit handler
$('table.ccx-schedule a.remove-unit').on('click', function() {
$('table.ccx-schedule button.remove-unit').on('click', function() {
var row = $(this).closest('tr'),
path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]);
self.schedule_apply([unit], self.hide);
self.schedule_collection.set(self.schedule);
self.dirty = true;
self.render();
self.render();
});
......@@ -191,7 +196,13 @@ var edx = edx || {};
}
// Show or hide save button
if (this.dirty) {$('#dirty-schedule').show();}
if (this.dirty) {
$('#dirty-schedule').show();
$('html, body').animate(
{ scrollTop: $('#dirty-schedule').offset().top },
'slow', function() {$('#dirty-schedule').focus();}
);
}
else {$('#dirty-schedule').hide();}
$('#ajax-error').hide();
......@@ -202,8 +213,8 @@ var edx = edx || {};
save: function() {
self.schedule_collection.set(self.schedule);
var button = $('#dirty-schedule #save-changes');
button.prop('disabled', true).text(gettext("Saving")+'...');
button.prop('disabled', true).text(gettext("Saving"));
$.ajax({
url: save_url,
type: 'POST',
......@@ -213,14 +224,14 @@ var edx = edx || {};
self.dirty = false;
self.render();
button.prop('disabled', false).text(gettext("Save changes"));
// Update textarea with grading policy JSON, since grading policy
// may have changed.
// may have changed.
$('#grading-policy').text(data.grading_policy);
},
error: function(jqXHR) {
console.log(jqXHR.responseText);
$('#ajax-error').show();
$('#ajax-error').show().focus();
$('#dirty-schedule').hide();
$('form#add-unit select,input,button').prop('disabled', true);
button.prop('disabled', false).text(gettext("Save changes"));
......@@ -290,21 +301,49 @@ var edx = edx || {};
var children = self.get_children(row);
if (row.is('.expanded')) {
$(this).removeClass('fa-caret-down').addClass('fa-caret-right');
$(this).attr('aria-expanded', 'false');
$(this).find(".fa-caret-down").removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
children.hide();
children.hide();
}
else {
$(this).removeClass('fa-caret-right').addClass('fa-caret-down');
$(this).attr('aria-expanded', 'true');
$(this).find(".fa-caret-right").removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
children.filter('.collapsed').each(function() {
children = children.not(self.get_children(this));
});
children.show();
children.show();
}
},
expandAll : function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (!row.is('.expanded')) {
var children = self.get_children(row);
row.find(".ccx_sr_alert").attr("aria-expanded", "true");
row.find(".fa-caret-right").removeClass('fa-caret-right').addClass('fa-caret-down');
row.removeClass('collapsed').addClass('expanded');
children.filter('.collapsed').each(function() {
children = children.not(self.get_children(this));
});
children.show();
}
});
},
collapseAll: function() {
$('table.ccx-schedule > tbody > tr').each(function() {
var row = $(this);
if (row.is('.expanded')) {
$(row).find('.ccx_sr_alert').attr('aria-expanded', 'false');
$(row).find('.fa-caret-down').removeClass('fa-caret-down').addClass('fa-caret-right');
row.removeClass('expanded').addClass('collapsed');
$('table.ccx-schedule .sequential,.vertical').hide();
}
});
},
enterNewDate: function(what) {
return function() {
var row = $(this).closest('tr');
......@@ -312,10 +351,27 @@ var edx = edx || {};
.data('what', what)
.data('location', row.data('location'));
modal.find('h2').text(
what === 'due' ? gettext("Enter Due Date") :
gettext("Enter Start Date"));
modal.find('label').text(row.find('td:first').text());
what === 'due' ? gettext("Enter Due Date and Time") :
gettext("Enter Start Date and Time"));
modal.focus();
$(document).on('focusin', function(event) {
try {
if (!_.isUndefined(event.target.closest('.modal').id) &&
event.target.closest('.modal').id !== 'enter-date-modal' &&
event.target.id !== 'enter-date-modal') {
event.preventDefault();
modal.find('.close-modal').focus();
}
} catch (err) {
event.preventDefault();
modal.find('.close-modal').focus();
}
});
modal.find('.close-modal').click(function () {
$(document).off('focusin');
});
var path = row.data('location').split(' '),
unit = self.find_unit(self.schedule, path[0], path[1], path[2]),
parts = unit[what] ? unit[what].split(' ') : ['', ''],
......
......@@ -164,7 +164,7 @@ define(['common/js/spec_helpers/ajax_helpers', 'js/ccx/schedule'],
view.save();
expect(requests.length).toEqual(1)
AjaxHelpers.expectJsonRequest(requests, 'POST', 'save_ccx', view.schedule);
expect($('#dirty-schedule #save-changes').text()).toEqual("Saving...");
expect($('#dirty-schedule #save-changes').text()).toEqual("Saving");
AjaxHelpers.respondWithJson(requests, {
data: view.schedule
});
......
......@@ -55,3 +55,22 @@ form.ccx-form {
margin: 5px 0 5px 0;
}
}
button.ccx-button-link {
background: none;
border: none;
padding: 0;
color: #069;
cursor: pointer;
&:after {
content: "\00a0 ";
}
&:active {
background: none;
border: none;
padding: 0;
}
&:hover {
color: brown;
}
}
......@@ -38,7 +38,8 @@ from django.core.urlresolvers import reverse
<section>
<form action="${create_ccx_url}" method="POST">
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}"/>
<input name="name" placeholder="Name your CCX"/><br/>
<label class="sr" for="ccx_name">${_('Name your CCX')}</label>
<input name="name" id="ccx_name" placeholder="${_('Name your CCX')}"/><br/>
<button id="create-ccx">Coach a new Custom Course for EdX</button>
</form>
</section>
......
......@@ -29,22 +29,24 @@
<div id="new-ccx-schedule"></div>
</div>
<section id="enter-date-modal" class="modal" aria-hidden="true">
<div class="inner-wrapper" role="dialog">
<section id="enter-date-modal" class="modal"
tabindex="-1" role="dialog" aria-labelledby="ccx_schedule_set_date_heading">
<div class="inner-wrapper">
<button class="close-modal">
<i class="icon fa fa-remove"></i>
<span class="sr">
${_("Close")}
</span>
<i class="icon fa fa-remove" aria-hidden="true"></i>
<span class="sr">${_("Close")}</span>
</button>
<header>
<h2></h2>
<h2 id="ccx_schedule_set_date_heading"></h2>
</header>
<form role="form">
<form>
<div class="field datepair">
<label></label>
<input placeholder="Date" class="date" type="text" name="date"/ size="11">
<input placeholder="Time" class="time" type="text" name="time"/ size="6">
## Translators: This explains to people using a screen reader how to interpret the format of YYYY-MM-DD
<label class="sr" for="ccx_dialog_date">${_('Date format four digit year dash two digit month dash two digit day')}</label>
<input placeholder="Date" class="date" type="text" name="date" id="ccx_dialog_date" size="11" />
## Translators: This explains to people using a screen reader how to interpret the format of HH:MM
<label class="sr" for="ccx_dialog_time">${_('Time format two digit hours colon two digit minutes')}</label>
<input placeholder="Time" class="time" type="text" name="time" id="ccx_dialog_time" size="6" />
</div>
<div class="field">
<button type="submit" class="btn btn-primary">${_('Set date')}</button>
......@@ -54,44 +56,62 @@
</section>
<div class="ccx-schedule-sidebar">
<div class="ccx-sidebar-panel" id="dirty-schedule">
<h2>${_('Save changes')}</h2>
<form role="form">
<p>${_("You have unsaved changes.")}</p>
<div class="ccx-sidebar-panel" id="dirty-schedule" tabindex="-1" role="region"
aria-labelledby="ccx_schedule_save_changes_heading">
<h2 id="ccx_schedule_save_changes_heading">${_('Save changes')}</h2>
<form>
<p id="message_save">${_("You have unsaved changes.")}</p>
<div class="field">
<br/>
<button id="save-changes">${_("Save changes")}</button>
<button id="save-changes" aria-describedby="message_save">${_("Save changes")}</button>
</div>
</form>
</div>
<div class="ccx-sidebar-panel" id="ajax-error">
<div class="ccx-sidebar-panel" id="ajax-error" tabindex="-1" role="region" aria-labelledby="ccx_schedule_error_message">
<h2>${_('Error')}</h2>
<p>${_("There was an error saving changes.")}</p>
<p id="ccx_schedule_error_message">${_("There was an error saving changes.")}</p>
</div>
<div class="ccx-sidebar-panel">
<h2>${_('Schedule a Unit')}</h2>
<div class="ccx-sidebar-panel" aria-labelledby="ccx_schedule_unit"
id="ccx_schedule_unit_panel" role="region">
<h2 id="ccx_schedule_unit">${_('Schedule a Unit')}</h2>
<form role="form" id="add-unit" name="add-unit" class="ccx-form">
<div class="field">
<b>${_('Section')}</b><br/>
<select name="chapter"></select>
<label for="ccx_chapter"><b>${_('Section')}</b></label>
<select name="chapter" id="ccx_chapter" ></select>
</div>
<div class="field">
<b>${_('Subsection')}</b><br/>
<select name="sequential"></select>
<label for="ccx_sequential"><b>${_('Subsection')}</b></label>
<select name="sequential" id="ccx_sequential"></select>
</div>
<div class="field">
<b>${_('Unit')}</b><br/>
<select name="vertical"></select>
<label for="ccx_vertical"><b>${_('Unit')}</b></label>
<select name="vertical" id="ccx_vertical"></select>
</div>
<div class="field datepair">
<b>${_('Start Date')}</b><br/>
<input placeholder="Date" type="date" class="date" name="start_date"/>
<input placeholder="time" type="time" class="time" name="start_time"/>
<label for="ccx_start_date">
<b>${_('Start Date')}</b>
<span class="sr">
## Translators: This explains to people using a screen reader how to interpret the format of YYYY-MM-DD
&nbsp;${_('format four digit year dash two digit month dash two digit day')}
</span>
</label>
<input placeholder="yyyy-mm-dd" type="text" class="date" name="start_date" id="ccx_start_date" />
## Translators: This explains to people using a screen reader how to interpret the format of HH:MM
<label for="ccx_start_time" class="sr">${_('Start time format two digit hours colon two digit minutes')}</label>
<input placeholder="time" type="text" class="time" name="start_time" id="ccx_start_time"/>
</div>
<div class="field datepair">
<b>${_('Due Date')}</b> ${_('(Optional)')}<br/>
<input placeholder="Date" type="date" class="date" name="due_date"/>
<input placeholder="time" type="time" class="time" name="due_time"/>
<label for="ccx_due_date">
<b>${_('Due Date')}</b> ${_('(Optional)')}
<span class="sr">
## Translators: This explains to people using a screen reader how to interpret the format of YYYY-MM-DD
&nbsp;${_('format four digit year dash two digit month dash two digit day')}
</span>
</label>
<input placeholder="yyyy-mm-dd" type="text" class="date" name="due_date" id="ccx_due_date"/>
## Translators: This explains to people using a screen reader how to interpret the format of HH:MM
<label for="ccx_due_time" class="sr">${_('Due Time format two digit hours colon two digit minutes')}</label>
<input placeholder="time" type="text" class="time" name="due_time" id="ccx_due_time"/>
</div>
<div class="field">
<br/>
......@@ -102,7 +122,7 @@
<button id="add-all">${_('Add All Units')}</button>
</div>
</form>
<div id="all-units-added">
<div id="all-units-added" tabindex="-1" role="region">
${_("All units have been added.")}
</div>
</div>
......@@ -118,11 +138,20 @@ $(function() {
//ccx_schedule.render();
$('.datepair .time').timepicker({
'showDuration': true,
'timeFormat': 'G:i'
'timeFormat': 'G:i',
'autoclose': true
});
$('.datepair .date').datepicker({
'dateFormat': 'yy-mm-dd',
'autoclose': true
});
$('.datepair .date').change(function() {
var date = $(this).datepicker( "getDate" );
if (date) {
$(this).val(date.getFullYear() + "-" +
('0' + (date.getMonth() + 1)).slice(-2) + "-" +
('0' + date.getDate()).slice(-2));
}
});
});
</script>
<div align="right">
<button id="ccx_expand_all_btn" class="ccx-button-link">
<i class="fa fa-expand" aria-hidden="true"></i> <%- gettext('Expand All') %>
</button>
<button id="ccx_collapse_all_btn" class="ccx-button-link">
<i class="fa fa-compress" aria-hidden="true"></i> <%- gettext('Collapse All') %>
</button>
</div>
<br/>
<table class="ccx-schedule">
<thead>
<tr>
<th><%- gettext('Unit') %></th>
<th><%- gettext('Start Date') %></th>
<th><%- gettext('Due Date') %></th>
<th><a href="#" id="remove-all">
<td><button id="remove-all" class="ccx-button-link">
<i class="fa fa-remove"></i> <%- gettext('remove all') %>
</a></th>
</button></td>
</tr>
</thead>
<tbody>
<% _.each(chapters, function(chapter) { %>
<tr class="chapter collapsed" data-location="<%= chapter.location %>" data-depth="1">
<td class="unit">
<a href="#"><i class="fa fa-caret-right toggle-collapse"></i></a>
<%= chapter.display_name %>
<button class="toggle-collapse ccx-button-link" aria-expanded="false">
<i class="fa fa-caret-right"></i>
<span class="sr"><%- gettext('toggle chapter') %>&nbsp;<%= chapter.display_name %></span>
</button>
<span class="sr"><%- gettext('Section') %>&nbsp;</span><%= chapter.display_name %>
</td>
<td class="date start-date"><a><%= chapter.start %></a></td>
<td class="date due-date"><a><%= chapter.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="fa fa-remove"></i> <%- gettext('remove') %>
</a></td>
<td class="date start-date">
<button class="ccx-button-link">
<%= chapter.start %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td class="date due-date">
<button class="ccx-button-link">
<%= chapter.due %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td><button class="remove-unit ccx-button-link" aria-label="Remove chapter <%= chapter.display_name %>">
<i class="fa fa-remove" aria-hidden="true"></i> <%- gettext('remove') %>
</button></td>
</tr>
<% _.each(chapter.children, function(child) { %>
<tr class="sequential collapsed" data-depth="2"
data-location="<%= chapter.location %> <%= child.location %>">
<td class="unit">
<a href="#"><i class="fa fa-caret-right toggle-collapse"></i></a>
<%= child.display_name %>
<button class="toggle-collapse ccx-button-link" aria-expanded="false">
<i class="fa fa-caret-right"></i>
<span class="sr"><%- gettext('toggle subsection') %>&nbsp;<%= child.display_name %></span>
</button>
<span class="sr"><%- gettext('Subsection') %>&nbsp;</span><%= child.display_name %>
</td>
<td class="date start-date">
<button class="ccx-button-link">
<%= child.start %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td class="date due-date">
<button class="ccx-button-link">
<%= child.due %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td class="date start-date"><a><%= child.start %></a></td>
<td class="date due-date"><a><%= child.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="fa fa-remove"></i> <%- gettext('remove') %>
</a></td>
<td><button class="remove-unit ccx-button-link" aria-label="Remove subsection <%= child.display_name %>">
<i class="fa fa-remove" aria-hidden="true"></i> <%- gettext('remove') %>
</button></td>
</tr>
<% _.each(child.children, function(subchild) { %>
<tr class="vertical" data-dapth="3"
data-location="<%= chapter.location %> <%= child.location %> <%= subchild.location %>">
<td class="unit">&nbsp;<%= subchild.display_name %></td>
<td class="date start-date"><a><%= subchild.start %></a></td>
<td class="date due-date"><a><%= subchild.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="fa fa-remove"></i> <%- gettext('remove') %>
</a></td>
<td class="unit">&nbsp;
<span class="sr"><%- gettext('Unit') %>&nbsp;</span>
<%= subchild.display_name %>
</td>
<td class="date start-date">
<button class="ccx-button-link">
<%= subchild.start %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td class="date due-date">
<button class="ccx-button-link">
<%= subchild.due %>
<span class="sr"><%- gettext('Click to change') %></span>
</button>
</td>
<td><button class="remove-unit ccx-button-link" aria-label="Remove unit <%= subchild.display_name %>">
<i class="fa fa-remove" aria-hidden="true"></i> <%- gettext('remove') %>
</button></td>
<% }); %>
<% }); %>
<% }); %>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment