Merge pull request #9450 from edx/peter-fogg/sort-topic-page
Add sorting controls on topics page.
This commit is contained in:
@@ -82,7 +82,7 @@
|
||||
setPage: function (page) {
|
||||
var oldPage = this.currentPage,
|
||||
self = this;
|
||||
this.goTo(page - (this.isZeroIndexed ? 1 : 0), {reset: true}).then(
|
||||
return this.goTo(page - (this.isZeroIndexed ? 1 : 0), {reset: true}).then(
|
||||
function () {
|
||||
self.trigger('page_changed');
|
||||
},
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
var PagingHeader = Backbone.View.extend({
|
||||
initialize: function (options) {
|
||||
this.srInfo = options.srInfo;
|
||||
this.collections = options.collection;
|
||||
this.showSortControls = options.showSortControls;
|
||||
this.collection.bind('add', _.bind(this.render, this));
|
||||
this.collection.bind('remove', _.bind(this.render, this));
|
||||
this.collection.bind('reset', _.bind(this.render, this));
|
||||
},
|
||||
|
||||
events: {
|
||||
'change #paging-header-select': 'sortCollection'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var message,
|
||||
start = _.isUndefined(this.collection.start) ? 0 : this.collection.start,
|
||||
@@ -31,9 +35,18 @@
|
||||
}
|
||||
this.$el.html(_.template(headerTemplate, {
|
||||
message: message,
|
||||
srInfo: this.srInfo
|
||||
srInfo: this.srInfo,
|
||||
sortableFields: this.collection.sortableFields,
|
||||
sortOrder: this.sortOrder,
|
||||
showSortControls: this.showSortControls
|
||||
}));
|
||||
return this;
|
||||
},
|
||||
|
||||
sortCollection: function () {
|
||||
var selected = this.$('#paging-header-select option:selected');
|
||||
this.sortOrder = selected.attr('value');
|
||||
this.collection.setSortField(this.sortOrder);
|
||||
}
|
||||
});
|
||||
return PagingHeader;
|
||||
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
var pagingHeader,
|
||||
newCollection = function (size, perPage) {
|
||||
var pageSize = 5,
|
||||
results = _.map(_.range(size), function () { return {}; });
|
||||
results = _.map(_.range(size), function (i) { return {foo: i}; });
|
||||
var collection = new PagingCollection(
|
||||
{
|
||||
count: results.length,
|
||||
@@ -22,6 +22,14 @@ define([
|
||||
collection.start = 0;
|
||||
collection.totalCount = results.length;
|
||||
return collection;
|
||||
},
|
||||
sortableHeader = function (sortable) {
|
||||
var collection = newCollection(5, 4);
|
||||
collection.registerSortableField('foo', 'Display Name');
|
||||
return new PagingHeader({
|
||||
collection: collection,
|
||||
showSortControls: _.isUndefined(sortable) ? true : sortable
|
||||
});
|
||||
};
|
||||
|
||||
it('correctly displays which items are being viewed', function () {
|
||||
@@ -47,5 +55,16 @@ define([
|
||||
expect(pagingHeader.$el.find('.search-count').text())
|
||||
.toContain('Showing 1 out of 1 total');
|
||||
});
|
||||
|
||||
it('optionally shows sorting controls', function () {
|
||||
pagingHeader = sortableHeader().render();
|
||||
expect(pagingHeader.$el.find('.listing-sort').text())
|
||||
.toMatch(/Sorted by\s+Display Name/);
|
||||
});
|
||||
|
||||
it('does not show sorting controls if the `showSortControls` option is not passed', function () {
|
||||
pagingHeader = sortableHeader(false).render();
|
||||
expect(pagingHeader.$el.text()).not.toContain('Sorted by');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
<% if (!_.isUndefined(srInfo)) { %>
|
||||
<h2 class="sr" id="<%= srInfo.id %>"><%- srInfo.text %></h2>
|
||||
<% } %>
|
||||
<div class="search-tools">
|
||||
<span class="search-count">
|
||||
<div class="search-tools listing-tools">
|
||||
<span class="search-count listing-count">
|
||||
<%= message %>
|
||||
</span>
|
||||
<% if (showSortControls) { %>
|
||||
|
|
||||
<span class="field listing-sort">
|
||||
<label class="field-label" for="paging-header-select"><%- gettext("Sorted by") %></label>
|
||||
<select id="paging-header-select" name="paging-header-select" class="field-input input-select listing-sort-select">
|
||||
<% _.each(sortableFields, function (option, key) { %>
|
||||
<option value="<%= key %>" <% if (key === sortOrder) { %> selected="true" <% } %>>
|
||||
<%- option.displayName %>
|
||||
</option>
|
||||
<% }) %>
|
||||
</select>
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ from .fields import FieldsMixin
|
||||
|
||||
|
||||
TOPIC_CARD_CSS = 'div.wrapper-card-core'
|
||||
CARD_TITLE_CSS = 'h3.card-title'
|
||||
MY_TEAMS_BUTTON_CSS = 'a.nav-item[data-index="0"]'
|
||||
BROWSE_BUTTON_CSS = 'a.nav-item[data-index="1"]'
|
||||
TEAMS_LINK_CSS = '.action-view'
|
||||
@@ -121,6 +122,11 @@ class BrowseTopicsPage(CoursePage, PaginatedUIMixin):
|
||||
"""Return a list of the topic cards present on the page."""
|
||||
return self.q(css=TOPIC_CARD_CSS).results
|
||||
|
||||
@property
|
||||
def topic_names(self):
|
||||
"""Return a list of the topic names present on the page."""
|
||||
return self.q(css=CARD_TITLE_CSS).map(lambda e: e.text).results
|
||||
|
||||
def browse_teams_for_topic(self, topic_name):
|
||||
"""
|
||||
Show the teams list for `topic_name`.
|
||||
@@ -130,6 +136,13 @@ class BrowseTopicsPage(CoursePage, PaginatedUIMixin):
|
||||
)[0].click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def sort_topics_by(self, sort_order):
|
||||
"""Sort the list of topics by the given `sort_order`."""
|
||||
self.q(
|
||||
css='#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
|
||||
).click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
|
||||
class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
|
||||
"""
|
||||
@@ -388,3 +401,8 @@ class TeamPage(CoursePage, PaginatedUIMixin):
|
||||
def new_post_button_present(self):
|
||||
""" Returns True if New Post button is present else False """
|
||||
return self.q(css='.discussion-module .new-post-btn').present
|
||||
|
||||
def click_all_topics_breadcrumb(self):
|
||||
"""Navigate to the 'All Topics' page."""
|
||||
self.q(css='.breadcrumbs a').results[0].click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Acceptance tests for the teams feature.
|
||||
"""
|
||||
import json
|
||||
import random
|
||||
|
||||
import ddt
|
||||
from flaky import flaky
|
||||
@@ -22,6 +23,9 @@ from ...pages.lms.tab_nav import TabNavPage
|
||||
from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateTeamPage, TeamPage
|
||||
|
||||
|
||||
TOPICS_PER_PAGE = 12
|
||||
|
||||
|
||||
class TeamsTabBase(UniqueCourseTest):
|
||||
"""Base class for Teams Tab tests"""
|
||||
def setUp(self):
|
||||
@@ -274,6 +278,7 @@ class MyTeamsTest(TeamsTabBase):
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
@ddt.ddt
|
||||
class BrowseTopicsTest(TeamsTabBase):
|
||||
"""
|
||||
Tests for the Browse tab of the Teams page.
|
||||
@@ -283,6 +288,66 @@ class BrowseTopicsTest(TeamsTabBase):
|
||||
super(BrowseTopicsTest, self).setUp()
|
||||
self.topics_page = BrowseTopicsPage(self.browser, self.course_id)
|
||||
|
||||
@ddt.data(('name', False), ('team_count', True))
|
||||
@ddt.unpack
|
||||
def test_sort_topics(self, sort_order, reverse):
|
||||
"""
|
||||
Scenario: the user should be able to sort the list of topics by name or team count
|
||||
Given I am enrolled in a course with team configuration and topics
|
||||
When I visit the Teams page
|
||||
And I browse topics
|
||||
Then I should see a list of topics for the course
|
||||
When I choose a sort order
|
||||
Then I should see the paginated list of topics in that order
|
||||
"""
|
||||
topics = self.create_topics(TOPICS_PER_PAGE + 1)
|
||||
self.set_team_configuration({u"max_team_size": 100, u"topics": topics})
|
||||
for i, topic in enumerate(random.sample(topics, len(topics))):
|
||||
self.create_teams(topic, i)
|
||||
topic['team_count'] = i
|
||||
self.topics_page.visit()
|
||||
self.topics_page.sort_topics_by(sort_order)
|
||||
topic_names = self.topics_page.topic_names
|
||||
self.assertEqual(len(topic_names), TOPICS_PER_PAGE)
|
||||
self.assertEqual(
|
||||
topic_names,
|
||||
[t['name'] for t in sorted(topics, key=lambda t: t[sort_order], reverse=reverse)][:TOPICS_PER_PAGE]
|
||||
)
|
||||
|
||||
def test_sort_topics_update(self):
|
||||
"""
|
||||
Scenario: the list of topics should remain sorted after updates
|
||||
Given I am enrolled in a course with team configuration and topics
|
||||
When I visit the Teams page
|
||||
And I browse topics and choose a sort order
|
||||
Then I should see the paginated list of topics in that order
|
||||
When I create a team in one of those topics
|
||||
And I return to the topics list
|
||||
Then I should see the topics in the correct sorted order
|
||||
"""
|
||||
topics = self.create_topics(3)
|
||||
self.set_team_configuration({u"max_team_size": 100, u"topics": topics})
|
||||
self.topics_page.visit()
|
||||
self.topics_page.sort_topics_by('team_count')
|
||||
topic_name = self.topics_page.topic_names[-1]
|
||||
topic = [t for t in topics if t['name'] == topic_name][0]
|
||||
self.topics_page.browse_teams_for_topic(topic_name)
|
||||
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic)
|
||||
self.assertTrue(browse_teams_page.is_browser_on_page())
|
||||
browse_teams_page.click_create_team_link()
|
||||
create_team_page = CreateTeamPage(self.browser, self.course_id, topic)
|
||||
create_team_page.value_for_text_field(field_id='name', value='Team Name', press_enter=False)
|
||||
create_team_page.value_for_textarea_field(
|
||||
field_id='description',
|
||||
value='Team description.'
|
||||
)
|
||||
create_team_page.submit_form()
|
||||
team_page = TeamPage(self.browser, self.course_id)
|
||||
self.assertTrue(team_page.is_browser_on_page)
|
||||
team_page.click_all_topics_breadcrumb()
|
||||
self.assertTrue(self.topics_page.is_browser_on_page())
|
||||
self.assertEqual(topic_name, self.topics_page.topic_names[0])
|
||||
|
||||
def test_list_topics(self):
|
||||
"""
|
||||
Scenario: a list of topics should be visible in the "Browse" tab
|
||||
@@ -294,7 +359,7 @@ class BrowseTopicsTest(TeamsTabBase):
|
||||
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(2)})
|
||||
self.topics_page.visit()
|
||||
self.assertEqual(len(self.topics_page.topic_cards), 2)
|
||||
self.assertEqual(self.topics_page.get_pagination_header_text(), 'Showing 1-2 out of 2 total')
|
||||
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-2 out of 2 total'))
|
||||
self.assertFalse(self.topics_page.pagination_controls_visible())
|
||||
self.assertFalse(self.topics_page.is_previous_page_button_enabled())
|
||||
self.assertFalse(self.topics_page.is_next_page_button_enabled())
|
||||
@@ -309,8 +374,8 @@ class BrowseTopicsTest(TeamsTabBase):
|
||||
"""
|
||||
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(20)})
|
||||
self.topics_page.visit()
|
||||
self.assertEqual(len(self.topics_page.topic_cards), 12)
|
||||
self.assertEqual(self.topics_page.get_pagination_header_text(), 'Showing 1-12 out of 20 total')
|
||||
self.assertEqual(len(self.topics_page.topic_cards), TOPICS_PER_PAGE)
|
||||
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-12 out of 20 total'))
|
||||
self.assertTrue(self.topics_page.pagination_controls_visible())
|
||||
self.assertFalse(self.topics_page.is_previous_page_button_enabled())
|
||||
self.assertTrue(self.topics_page.is_next_page_button_enabled())
|
||||
@@ -360,10 +425,10 @@ class BrowseTopicsTest(TeamsTabBase):
|
||||
self.topics_page.visit()
|
||||
self.topics_page.press_next_page_button()
|
||||
self.assertEqual(len(self.topics_page.topic_cards), 1)
|
||||
self.assertEqual(self.topics_page.get_pagination_header_text(), 'Showing 13-13 out of 13 total')
|
||||
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 13-13 out of 13 total'))
|
||||
self.topics_page.press_previous_page_button()
|
||||
self.assertEqual(len(self.topics_page.topic_cards), 12)
|
||||
self.assertEqual(self.topics_page.get_pagination_header_text(), 'Showing 1-12 out of 13 total')
|
||||
self.assertEqual(len(self.topics_page.topic_cards), TOPICS_PER_PAGE)
|
||||
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-12 out of 13 total'))
|
||||
|
||||
def test_topic_description_truncation(self):
|
||||
"""
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
var self = this,
|
||||
deferred = $.Deferred();
|
||||
if (force || this.isStale) {
|
||||
this.fetch()
|
||||
this.setPage(1)
|
||||
.done(function() {
|
||||
self.isStale = false;
|
||||
deferred.resolve();
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
define([
|
||||
'gettext',
|
||||
'teams/js/views/topic_card',
|
||||
'common/js/components/views/paging_header',
|
||||
'common/js/components/views/paginated_view'
|
||||
], function (gettext, TopicCardView, PaginatedView) {
|
||||
], function (gettext, TopicCardView, PagingHeader, PaginatedView) {
|
||||
var TopicsView = PaginatedView.extend({
|
||||
type: 'topics',
|
||||
|
||||
@@ -21,11 +22,18 @@
|
||||
PaginatedView.prototype.initialize.call(this);
|
||||
},
|
||||
|
||||
createHeaderView: function () {
|
||||
return new PagingHeader({
|
||||
collection: this.options.collection,
|
||||
srInfo: this.srInfo,
|
||||
showSortControls: true
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
this.collection.refresh()
|
||||
.done(function() {
|
||||
self.collection.isStale = false;
|
||||
PaginatedView.prototype.render.call(self);
|
||||
});
|
||||
return this;
|
||||
|
||||
Reference in New Issue
Block a user