Fix various team accessibility issues.

Authors:
  - Peter Fogg
  - Daniel Friedman

TNL-1953
This commit is contained in:
Peter Fogg
2015-08-03 14:18:31 -04:00
committed by Daniel Friedman
parent 00eb18a1f9
commit 26bbbb967e
23 changed files with 129 additions and 42 deletions

View File

@@ -72,6 +72,12 @@ define([
expectFocus(teamsTabView.$('.warning'));
});
it('does not interfere with anchor links to #content', function () {
var teamsTabView = createTeamsTabView();
teamsTabView.router.navigate('#content', {trigger: true});
expect(teamsTabView.$('.warning')).toHaveClass('is-hidden');
});
it('displays and focuses an error message when trying to navigate to a nonexistent topic', function () {
var requests = AjaxHelpers.requests(this),
teamsTabView = createTeamsTabView();

View File

@@ -15,6 +15,7 @@
events: {
'click .action-primary': 'createTeam',
'submit form': 'createTeam',
'click .action-cancel': 'goBackToTopic'
},
@@ -48,13 +49,6 @@
helpMessage: gettext('A short description of the team to help other learners understand the goals or direction of the team (maximum 300 characters).')
});
this.optionalDescriptionField = new FieldViews.ReadonlyFieldView({
model: this.teamModel,
title: gettext('Optional Characteristics'),
valueAttribute: 'optional_description',
helpMessage: gettext('Help other learners decide whether to join your team by specifying some characteristics for your team. Choose carefully, because fewer people might be interested in joining your team if it seems too restrictive.')
});
this.teamLanguageField = new FieldViews.DropdownFieldView({
model: this.teamModel,
title: gettext('Language'),
@@ -82,7 +76,6 @@
this.$el.html(_.template(editTeamTemplate)({primaryButtonTitle: this.primaryButtonTitle}));
this.set(this.teamNameField, '.team-required-fields');
this.set(this.teamDescriptionField, '.team-required-fields');
this.set(this.optionalDescriptionField, '.team-optional-fields');
this.set(this.teamLanguageField, '.team-optional-fields');
this.set(this.teamCountryField, '.team-optional-fields');
return this;
@@ -97,7 +90,8 @@
}
},
createTeam: function () {
createTeam: function (event) {
event.preventDefault();
var view = this,
teamLanguage = this.teamLanguageField.fieldValue(),
teamCountry = this.teamCountryField.fieldValue();

View File

@@ -93,10 +93,8 @@
true
);
},
action: function (event) {
var url = 'teams/' + this.teamModel().get('topic_id') + '/' + this.teamModel().get('id');
event.preventDefault();
this.router.navigate(url, {trigger: true});
actionUrl: function () {
return '#teams/' + this.teamModel().get('topic_id') + '/' + this.teamModel().get('id');
}
});
return TeamCardView;

View File

@@ -9,6 +9,15 @@
var TeamsView = PaginatedView.extend({
type: 'teams',
events: {
'click button.action': '' // entry point for team creation
},
srInfo: {
id: "heading-browse-teams",
text: gettext('All teams')
},
initialize: function (options) {
this.topic = options.topic;
this.teamMemberships = options.teamMemberships;
@@ -18,7 +27,8 @@
topic: options.topic,
maxTeamSize: options.maxTeamSize,
countries: this.selectorOptionsArrayToHashWithBlank(options.teamParams.countries),
languages: this.selectorOptionsArrayToHashWithBlank(options.teamParams.languages)
languages: this.selectorOptionsArrayToHashWithBlank(options.teamParams.languages),
srInfo: this.srInfo
});
PaginatedView.prototype.initialize.call(this);
},

View File

@@ -22,6 +22,13 @@
TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection,
TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView,
teamsTemplate) {
var TeamsHeaderModel = HeaderModel.extend({
initialize: function (attributes) {
_.extend(this.defaults, {nav_aria_label: gettext('teams')});
HeaderModel.prototype.initialize.call(this);
}
});
var ViewWithHeader = Backbone.View.extend({
initialize: function (options) {
this.header = options.header;
@@ -57,6 +64,12 @@
router = this.router = new Backbone.Router();
_.each([
[':default', _.bind(this.routeNotFound, this)],
['content', _.bind(function () {
// The backbone router unfortunately usurps the
// default behavior of in-page-links. This hack
// prevents the screen reader in-page-link from
// being picked up by the backbone router.
}, this)],
['topics/:topic_id(/)', _.bind(this.browseTopic, this)],
['topics/:topic_id/create-team(/)', _.bind(this.newTeam, this)],
['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)],
@@ -102,7 +115,7 @@
this.mainView = this.tabbedView = new ViewWithHeader({
header: new HeaderView({
model: new HeaderModel({
model: new TeamsHeaderModel({
description: gettext("See all teams in your course, organized by topic. Join a team to collaborate with other learners who are interested in the same topic as you are."),
title: gettext("Teams")
})
@@ -113,7 +126,11 @@
url: 'my-teams',
view: this.myTeamsView
}, {
title: gettext('Browse'),
title: interpolate(
// Translators: sr_start and sr_end surround text meant only for screen readers. The whole string will be shown to users as "Browse teams" if they are using a screenreader, and "Browse" otherwise.
gettext("Browse %(sr_start)s teams %(sr_end)s"),
{"sr_start": '<span class="sr">', "sr_end": '</span>'}, true
),
url: 'browse',
view: this.topicsView
}],
@@ -165,7 +182,7 @@
this.getTeamsView(topicID).done(function (teamsView) {
self.mainView = new ViewWithHeader({
header: new HeaderView({
model: new HeaderModel({
model: new TeamsHeaderModel({
description: gettext("Create a new team if you can't find existing teams to join, or if you would like to learn with friends you know."),
title: gettext("Create a New Team"),
breadcrumbs: [
@@ -277,7 +294,7 @@
});
}
headerView = new HeaderView({
model: new HeaderModel({
model: new TeamsHeaderModel({
description: subject.get('description'),
title: subject.get('name'),
breadcrumbs: breadcrumbs

View File

@@ -30,9 +30,8 @@
CardView.prototype.initialize.apply(this, arguments);
},
action: function (event) {
event.preventDefault();
this.router.navigate('topics/' + this.model.get('id'), {trigger: true});
actionUrl: function () {
return '#topics/' + this.model.get('id');
},
configuration: 'square_card',

View File

@@ -7,8 +7,16 @@
var TopicsView = PaginatedView.extend({
type: 'topics',
srInfo: {
id: "heading-browse-topics",
text: gettext("All topics")
},
initialize: function (options) {
this.itemViewClass = TopicCardView.extend({router: options.router});
this.itemViewClass = TopicCardView.extend({
router: options.router,
srInfo: this.srInfo
});
PaginatedView.prototype.initialize.call(this);
}
});

View File

@@ -1,3 +1,4 @@
<form>
<div class="create-team wrapper-msg is-incontext urgency-low warning is-hidden" tabindex="-1">
<div class="msg">
<div class="msg-content">
@@ -20,6 +21,16 @@
</div>
<div class="team-optional-fields">
<fieldset>
<div class="u-field u-field-optional_description">
<legend aria-describedby="optional-characteristics-help">
<p class="u-field-title"><%- gettext('Optional Characteristics') %></p>
</legend>
<span id="optional-characteristics-help" class="u-field-message">
<p class="u-field-message-help"><%- gettext('Help other learners decide whether to join your team by specifying some characteristics for your team. Choose carefully, because fewer people might be interested in joining your team if it seems too restrictive.') %></p>
</span>
</div>
</fieldset>
</div>
</div>
@@ -45,3 +56,4 @@
%>
</button>
</div>
</form>

View File

@@ -23,6 +23,8 @@
'text!templates/components/card/card.underscore'],
function ($, _, Backbone, cardTemplate) {
var CardView = Backbone.View.extend({
tagName: 'li',
events: {
'click .action' : 'action'
},
@@ -82,7 +84,8 @@
action_class: this.callIfFunction(this.actionClass),
action_url: this.callIfFunction(this.actionUrl),
action_content: this.callIfFunction(this.actionContent),
configuration: this.callIfFunction(this.configuration)
configuration: this.callIfFunction(this.configuration),
srInfo: this.srInfo
}));
var detailsEl = this.$el.find('.card-meta');
_.each(this.callIfFunction(this.details), function (detail) {

View File

@@ -8,7 +8,8 @@ define(['backbone'], function (Backbone) {
defaults: {
'title': '',
'description': '',
'breadcrumbs': null
'breadcrumbs': null,
'nav_aria_label': ''
}
});

View File

@@ -48,7 +48,11 @@
}));
self.$('.page-content-nav').append(tabEl);
});
if(Backbone.history.getHash() === "") {
// Re-display the default (first) tab if the
// current route does not belong to one of the
// tabs. Otherwise continue displaying the tab
// corresponding to the current URL.
if (!(Backbone.history.getHash() in this.urlMap)) {
this.setActiveTab(0);
}
return this;
@@ -70,7 +74,7 @@
view.setElement(this.$('.page-content-main')).render();
this.$('.sr-is-focusable.sr-tab').focus();
if (this.router) {
this.router.navigate(tab.url, {replace: true, trigger: true});
this.router.navigate(tab.url, {replace: true});
}
},

View File

@@ -13,7 +13,7 @@
var testBreadcrumbs = function (breadcrumbs) {
model.set('breadcrumbs', breadcrumbs);
expect(view.$el.html()).toContain('<nav class="breadcrumbs">');
expect(view.$('nav.breadcrumbs').length).toBe(1);
_.each(view.$('.nav-item'), function (el, index) {
expect($(el).attr('href')).toEqual(breadcrumbs[index].url);
expect($(el).text()).toEqual(breadcrumbs[index].title);

View File

@@ -101,7 +101,7 @@
view.$('.nav-item[data-index=1]').click();
expect(Backbone.history.navigate).toHaveBeenCalledWith(
'test 2',
{replace: true, trigger: true}
{replace: true}
);
});

View File

@@ -118,7 +118,7 @@
.u-field-message-help,
.u-field-message-notification {
color: $gray-l1;
color: $gray;
}
}

View File

@@ -4,7 +4,12 @@
<% if (pennant) { %>
<small class="card-type"><%- pennant %></small>
<% } %>
<h3 class="card-title"><%- title %></h3>
<h3 class="card-title"
<% if (!_.isUndefined(srInfo)) { %>
aria-describedby="<%= srInfo.id %>"
<% } %>
><%- title %>
</h3>
<p class="card-description"><%- description %></p>
</div>
</div>
@@ -21,7 +26,12 @@
<% if (pennant) { %>
<small class="card-type"><%- pennant %></small>
<% } %>
<h3 class="card-title"><%- title %></h3>
<h3 class="card-title"
<% if (!_.isUndefined(srInfo)) { %>
aria-describedby="<%= srInfo.id %>"
<% } %>
><%- title %>
</h3>
<p class="card-description"><%- description %></p>
</div>
<div class="card-actions">

View File

@@ -1,7 +1,7 @@
<header class="page-header has-secondary">
<div class="page-header-main">
<% if (breadcrumbs !== null && breadcrumbs.length > 0) { %>
<nav class="breadcrumbs">
<nav class="breadcrumbs" aria-label="<%- nav_aria_label %>">
<% _.each(breadcrumbs, function (breadcrumb) { %>
<a class="nav-item" href="<%= breadcrumb.url %>"><%- breadcrumb.title %></a>
<span class="icon fa-angle-right" aria-hidden="true"></span>

View File

@@ -1 +1 @@
<a class="nav-item" href="" data-url="<%= url %>" data-index="<%= index %>" role="tab" aria-selected="false"><%- title %></a>
<a class="nav-item" href="" data-url="<%= url %>" data-index="<%= index %>" role="tab" aria-selected="false"><%= title %></a>