Fix various team accessibility issues.
Authors: - Peter Fogg - Daniel Friedman TNL-1953
This commit is contained in:
committed by
Daniel Friedman
parent
00eb18a1f9
commit
26bbbb967e
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -8,7 +8,8 @@ define(['backbone'], function (Backbone) {
|
||||
defaults: {
|
||||
'title': '',
|
||||
'description': '',
|
||||
'breadcrumbs': null
|
||||
'breadcrumbs': null,
|
||||
'nav_aria_label': ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
view.$('.nav-item[data-index=1]').click();
|
||||
expect(Backbone.history.navigate).toHaveBeenCalledWith(
|
||||
'test 2',
|
||||
{replace: true, trigger: true}
|
||||
{replace: true}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
.u-field-message-help,
|
||||
.u-field-message-notification {
|
||||
color: $gray-l1;
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user