Add teams-for-topic list view to Teams page.
TNL-1898 Also adds a generic paginated list view and Teams list view. The common PaginatedView takes a collection and a view class for its items and creates a header and footer with correct pagination. The topics list view is refactored to use this generic view. Authors: - Peter Fogg <pfogg@edx.org> - Daniel Friedman <dfriedman58@gmail.com>
This commit is contained in:
@@ -81,7 +81,8 @@
|
||||
description: description,
|
||||
action_class: this.callIfFunction(this.actionClass),
|
||||
action_url: this.callIfFunction(this.actionUrl),
|
||||
action_content: this.callIfFunction(this.actionContent)
|
||||
action_content: this.callIfFunction(this.actionContent),
|
||||
configuration: this.callIfFunction(this.configuration)
|
||||
}));
|
||||
var detailsEl = this.$el.find('.card-meta');
|
||||
_.each(this.callIfFunction(this.details), function (detail) {
|
||||
|
||||
@@ -19,35 +19,59 @@
|
||||
* following properties:
|
||||
* view (Backbone.View): the view to render for this tab.
|
||||
* title (string): The title to display for this tab.
|
||||
* url (string): The URL fragment which will navigate to this tab.
|
||||
* url (string): The URL fragment which will
|
||||
* navigate to this tab when a router is
|
||||
* provided.
|
||||
* If a router is passed in (via options.router),
|
||||
* use that router to keep track of history between
|
||||
* tabs. Backbone.history.start() must be called
|
||||
* by the router's instatiator after this view is
|
||||
* initialized.
|
||||
*/
|
||||
initialize: function (options) {
|
||||
this.router = new Backbone.Router();
|
||||
this.$el.html(this.template({}));
|
||||
var self = this;
|
||||
this.router = options.router || null;
|
||||
this.tabs = options.tabs;
|
||||
this.urlMap = _.reduce(this.tabs, function (map, value) {
|
||||
map[value.url] = value;
|
||||
return map;
|
||||
}, {});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var self = this;
|
||||
this.$el.html(this.template({}));
|
||||
_.each(this.tabs, function(tabInfo, index) {
|
||||
var tabEl = $(_.template(tabTemplate, {
|
||||
index: index,
|
||||
title: tabInfo.title
|
||||
title: tabInfo.title,
|
||||
url: tabInfo.url
|
||||
}));
|
||||
self.$('.page-content-nav').append(tabEl);
|
||||
|
||||
self.router.route(tabInfo.url, function () {
|
||||
self.setActiveTab(index);
|
||||
});
|
||||
});
|
||||
this.setActiveTab(0);
|
||||
if(Backbone.history.getHash() === "") {
|
||||
this.setActiveTab(0);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
setActiveTab: function (index) {
|
||||
var tab = this.tabs[index],
|
||||
view = tab.view;
|
||||
var tab, tabEl, view;
|
||||
if (typeof index === 'string') {
|
||||
tab = this.urlMap[index];
|
||||
tabEl = this.$('a[data-url='+index+']');
|
||||
}
|
||||
else {
|
||||
tab = this.tabs[index];
|
||||
tabEl = this.$('a[data-index='+index+']');
|
||||
}
|
||||
view = tab.view;
|
||||
this.$('a.is-active').removeClass('is-active').attr('aria-selected', 'false');
|
||||
this.$('a[data-index='+index+']').addClass('is-active').attr('aria-selected', 'true');
|
||||
tabEl.addClass('is-active').attr('aria-selected', 'true');
|
||||
view.setElement(this.$('.page-content-main')).render();
|
||||
this.$('.sr-is-focusable.sr-tab').focus();
|
||||
this.router.navigate(tab.url, {replace: true});
|
||||
if (this.router) {
|
||||
this.router.navigate(tab.url, {replace: true, trigger: true});
|
||||
}
|
||||
},
|
||||
|
||||
switchTab: function (event) {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
it('can render itself as a list card', function () {
|
||||
var view = new CardView({ configuration: 'list_card' });
|
||||
expect(view.$el).toHaveClass('list-card');
|
||||
expect(view.$el.find('.wrapper-card-meta .action').length).toBe(1);
|
||||
expect(view.$el.find('.wrapper-card-core .action').length).toBe(1);
|
||||
});
|
||||
|
||||
it('renders a pennant only if the pennant value is truthy', function () {
|
||||
|
||||
@@ -20,23 +20,15 @@
|
||||
|
||||
describe('TabbedView component', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.history, 'navigate').andCallThrough();
|
||||
Backbone.history.start();
|
||||
view = new TabbedView({
|
||||
tabs: [{
|
||||
url: 'test 1',
|
||||
title: 'Test 1',
|
||||
view: new TestSubview({text: 'this is test text'})
|
||||
}, {
|
||||
url: 'test 2',
|
||||
title: 'Test 2',
|
||||
view: new TestSubview({text: 'other text'})
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Backbone.history.stop();
|
||||
}).render();
|
||||
});
|
||||
|
||||
it('can render itself', function () {
|
||||
@@ -59,12 +51,6 @@
|
||||
expect(view.$el.text()).toContain('other text');
|
||||
});
|
||||
|
||||
it('changes tabs on navigation', function () {
|
||||
expect(view.$('.nav-item.is-active').data('index')).toEqual(0);
|
||||
Backbone.history.navigate('test 2', {trigger: true});
|
||||
expect(view.$('.nav-item.is-active').data('index')).toEqual(1);
|
||||
});
|
||||
|
||||
it('marks the active tab as selected using aria attributes', function () {
|
||||
expect(view.$('.nav-item[data-index=0]')).toHaveAttr('aria-selected', 'true');
|
||||
expect(view.$('.nav-item[data-index=1]')).toHaveAttr('aria-selected', 'false');
|
||||
@@ -73,17 +59,59 @@
|
||||
expect(view.$('.nav-item[data-index=1]')).toHaveAttr('aria-selected', 'true');
|
||||
});
|
||||
|
||||
it('updates the page URL on tab switches without adding to browser history', function () {
|
||||
view.$('.nav-item[data-index=1]').click();
|
||||
expect(Backbone.history.navigate).toHaveBeenCalledWith('test 2', {replace: true});
|
||||
});
|
||||
|
||||
it('sets focus for screen readers', function () {
|
||||
spyOn($.fn, 'focus');
|
||||
view.$('.nav-item[data-index=1]').click();
|
||||
expect(view.$('.sr-is-focusable.sr-tab').focus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('history', function() {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.history, 'navigate').andCallThrough();
|
||||
view = new TabbedView({
|
||||
tabs: [{
|
||||
url: 'test 1',
|
||||
title: 'Test 1',
|
||||
view: new TestSubview({text: 'this is test text'})
|
||||
}, {
|
||||
url: 'test 2',
|
||||
title: 'Test 2',
|
||||
view: new TestSubview({text: 'other text'})
|
||||
}],
|
||||
router: new Backbone.Router({
|
||||
routes: {
|
||||
'test 1': function () {
|
||||
view.setActiveTab(0);
|
||||
},
|
||||
'test 2': function () {
|
||||
view.setActiveTab(1);
|
||||
}
|
||||
}
|
||||
})
|
||||
}).render();
|
||||
Backbone.history.start();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.router.navigate('');
|
||||
Backbone.history.stop();
|
||||
});
|
||||
|
||||
it('updates the page URL on tab switches without adding to browser history', function () {
|
||||
view.$('.nav-item[data-index=1]').click();
|
||||
expect(Backbone.history.navigate).toHaveBeenCalledWith(
|
||||
'test 2',
|
||||
{replace: true, trigger: true}
|
||||
);
|
||||
});
|
||||
|
||||
it('changes tabs on URL navigation', function () {
|
||||
expect(view.$('.nav-item.is-active').data('index')).toEqual(0);
|
||||
Backbone.history.navigate('test 2', {trigger: true});
|
||||
expect(view.$('.nav-item.is-active').data('index')).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -531,6 +531,8 @@
|
||||
'lms/include/teams/js/spec/topic_card_spec.js',
|
||||
'lms/include/teams/js/spec/topic_collection_spec.js',
|
||||
'lms/include/teams/js/spec/topics_spec.js',
|
||||
'lms/include/teams/js/spec/teams_spec.js',
|
||||
'lms/include/teams/js/spec/teams_tab_spec.js',
|
||||
'lms/include/js/spec/components/header/header_spec.js',
|
||||
'lms/include/js/spec/components/tabbed/tabbed_view_spec.js',
|
||||
'lms/include/js/spec/components/card/card_spec.js',
|
||||
|
||||
Reference in New Issue
Block a user