Commit 0e180bd2 by Daniel Friedman

Add membership thumbnails to team cards.

TNL-3172
parent 7b77f816
...@@ -35,8 +35,8 @@ class UserMembershipSerializer(serializers.ModelSerializer): ...@@ -35,8 +35,8 @@ class UserMembershipSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Defines meta information for the ModelSerializer.""" """Defines meta information for the ModelSerializer."""
model = CourseTeamMembership model = CourseTeamMembership
fields = ("user", "date_joined") fields = ("user", "date_joined", "last_activity_at")
read_only_fields = ("date_joined",) read_only_fields = ("date_joined", "last_activity_at")
class CourseTeamSerializer(serializers.ModelSerializer): class CourseTeamSerializer(serializers.ModelSerializer):
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
this.server_api = _.extend( this.server_api = _.extend(
{ {
expand: 'team', expand: 'team,user',
username: this.username, username: this.username,
course_id: function () { return encodeURIComponent(self.course_id); } course_id: function () { return encodeURIComponent(self.course_id); }
}, },
......
...@@ -7,11 +7,12 @@ ...@@ -7,11 +7,12 @@
var TeamMembership = Backbone.Model.extend({ var TeamMembership = Backbone.Model.extend({
defaults: { defaults: {
date_joined: '', date_joined: '',
last_activity_at: '',
team: null, team: null,
user: null user: null
}, },
parse: function (response, options) { parse: function (response) {
response.team = new TeamModel(response.team); response.team = new TeamModel(response.team);
return response; return response;
} }
......
...@@ -65,7 +65,7 @@ define([ ...@@ -65,7 +65,7 @@ define([
requests, requests,
TeamSpecHelpers.testContext.teamMembershipsUrl, TeamSpecHelpers.testContext.teamMembershipsUrl,
{ {
expand : 'team', expand : 'team,user',
username : TeamSpecHelpers.testContext.userInfo.username, username : TeamSpecHelpers.testContext.userInfo.username,
course_id : TeamSpecHelpers.testContext.courseID, course_id : TeamSpecHelpers.testContext.courseID,
page : '1', page : '1',
......
...@@ -3,21 +3,24 @@ define(['jquery', ...@@ -3,21 +3,24 @@ define(['jquery',
'teams/js/views/team_card', 'teams/js/views/team_card',
'teams/js/models/team'], 'teams/js/models/team'],
function ($, _, TeamCardView, Team) { function ($, _, TeamCardView, Team) {
'use strict';
describe('TeamCardView', function () { describe('TeamCardView', function () {
var createTeamCardView, view; var createTeamCardView, view;
createTeamCardView = function () { createTeamCardView = function () {
var model = new Team({ var model = new Team({
id: 'test-team', id: 'test-team',
name: 'Test Team', name: 'Test Team',
is_active: true, is_active: true,
course_id: 'test/course/id', course_id: 'test/course/id',
topic_id: 'test-topic', topic_id: 'test-topic',
description: 'A team for testing', description: 'A team for testing',
last_activity_at: "2015-08-21T18:53:01.145Z", last_activity_at: "2015-08-21T18:53:01.145Z",
country: 'us', country: 'us',
language: 'en' language: 'en',
}), membership: []
teamCardClass = TeamCardView.extend({ }),
TeamCardClass = TeamCardView.extend({
maxTeamSize: '100', maxTeamSize: '100',
srInfo: { srInfo: {
id: 'test-sr-id', id: 'test-sr-id',
...@@ -26,7 +29,7 @@ define(['jquery', ...@@ -26,7 +29,7 @@ define(['jquery',
countries: {us: 'United States of America'}, countries: {us: 'United States of America'},
languages: {en: 'English'} languages: {en: 'English'}
}); });
return new teamCardClass({ return new TeamCardClass({
model: model model: model
}); });
}; };
...@@ -50,6 +53,101 @@ define(['jquery', ...@@ -50,6 +53,101 @@ define(['jquery',
it('navigates to the associated team page when its action button is clicked', function () { it('navigates to the associated team page when its action button is clicked', function () {
expect(view.$('.action').attr('href')).toEqual('#teams/test-topic/test-team'); expect(view.$('.action').attr('href')).toEqual('#teams/test-topic/test-team');
}); });
describe('Profile Image Thumbnails', function () {
/**
* Takes an array of objects representing team
* members, each having the keys 'username',
* 'image_url', and 'last_activity', and sets the
* teams membership accordingly and re-renders the
* view.
*/
var setMemberships, expectThumbnailsOrder;
setMemberships = function (memberships) {
view.model.set({
membership: _.map(memberships, function (m) {
return {
user: {username: m.username, profile_image: {image_url_small: m.image_url}},
last_activity_at: m.last_activity
};
})
});
view.render();
};
/**
* Takes an array of objects representing team
* members, each having the keys 'username' and
* 'image_url', and expects that the image thumbnails
* rendered on the team card match, in order, the
* members of the provided list.
*/
expectThumbnailsOrder = function (members) {
var thumbnails = view.$('.item-member-thumb img');
expect(thumbnails.length).toBe(members.length);
thumbnails.each(function (index, imgEl) {
expect(thumbnails.eq(index).attr('alt')).toBe(members[index].username);
expect(thumbnails.eq(index).attr('src')).toBe(members[index].image_url);
});
};
it('displays no thumbnails for an empty team', function () {
view.model.set({membership: []});
view.render();
expect(view.$('.item-member-thumb').length).toBe(0);
});
it('displays thumbnails for a nonempty team', function () {
var users = [
{
username: 'user_1', image_url: 'user_1_image',
last_activity: new Date("2010/1/1").toString()
}, {
username: 'user_2', image_url: 'user_2_image',
last_activity: new Date("2011/1/1").toString()
}
];
setMemberships(users);
expectThumbnailsOrder([
{username: 'user_2', image_url: 'user_2_image'},
{username: 'user_1', image_url: 'user_1_image'},
]);
});
it('displays thumbnails and an ellipsis for a team with greater than 5 members', function () {
var users = [
{
username: 'user_1', image_url: 'user_1_image',
last_activity: new Date("2001/1/1").toString()
}, {
username: 'user_2', image_url: 'user_2_image',
last_activity: new Date("2006/1/1").toString()
}, {
username: 'user_3', image_url: 'user_3_image',
last_activity: new Date("2003/1/1").toString()
}, {
username: 'user_4', image_url: 'user_4_image',
last_activity: new Date("2002/1/1").toString()
}, {
username: 'user_5', image_url: 'user_5_image',
last_activity: new Date("2005/1/1").toString()
}, {
username: 'user_6', image_url: 'user_6_image',
last_activity: new Date("2004/1/1").toString()
}
];
setMemberships(users);
expectThumbnailsOrder([
{username: 'user_2', image_url: 'user_2_image'},
{username: 'user_5', image_url: 'user_5_image'},
{username: 'user_6', image_url: 'user_6_image'},
{username: 'user_3', image_url: 'user_3_image'},
{username: 'user_4', image_url: 'user_4_image'},
]);
expect(view.$('.item-member-thumb').eq(-1)).toHaveText('and others…');
});
});
}); });
} }
); );
...@@ -7,34 +7,43 @@ ...@@ -7,34 +7,43 @@
'jquery.timeago', 'jquery.timeago',
'js/components/card/views/card', 'js/components/card/views/card',
'teams/js/views/team_utils', 'teams/js/views/team_utils',
'text!teams/templates/team-membership-details.underscore',
'text!teams/templates/team-country-language.underscore', 'text!teams/templates/team-country-language.underscore',
'text!teams/templates/team-activity.underscore' 'text!teams/templates/team-activity.underscore'
], function (Backbone, _, gettext, timeago, CardView, TeamUtils, teamCountryLanguageTemplate, teamActivityTemplate) { ], function (
Backbone,
_,
gettext,
timeago,
CardView,
TeamUtils,
teamMembershipDetailsTemplate,
teamCountryLanguageTemplate,
teamActivityTemplate
) {
var TeamMembershipView, TeamCountryLanguageView, TeamActivityView, TeamCardView; var TeamMembershipView, TeamCountryLanguageView, TeamActivityView, TeamCardView;
TeamMembershipView = Backbone.View.extend({ TeamMembershipView = Backbone.View.extend({
tagName: 'div', tagName: 'div',
className: 'team-members', className: 'team-members',
template: _.template( template: _.template(teamMembershipDetailsTemplate),
'<span class="member-count"><%= membership_message %></span>' +
'<ul class="list-member-thumbs"></ul>'
),
initialize: function (options) { initialize: function (options) {
this.maxTeamSize = options.maxTeamSize; this.maxTeamSize = options.maxTeamSize;
}, },
render: function () { render: function () {
var memberships = this.model.get('membership'), var allMemberships = _(this.model.get('membership'))
.sortBy(function (member) {return new Date(member.last_activity_at);}).reverse(),
displayableMemberships = allMemberships.slice(0, 5),
maxMemberCount = this.maxTeamSize; maxMemberCount = this.maxTeamSize;
this.$el.html(this.template({ this.$el.html(this.template({
membership_message: TeamUtils.teamCapacityText(memberships.length, maxMemberCount) membership_message: TeamUtils.teamCapacityText(allMemberships.length, maxMemberCount),
memberships: displayableMemberships,
has_additional_memberships: displayableMemberships.length < allMemberships.length,
// Translators: "and others" refers to fact that additional members of a team exist that are not displayed.
sr_message: gettext('and others')
})); }));
_.each(memberships, function (membership) {
this.$('list-member-thumbs').append(
'<li class="item-member-thumb"><img alt="' + membership.user.username + '" src=""></img></li>'
);
}, this);
return this; return this;
} }
}); });
......
<span class="member-count"><%= membership_message %></span>
<ul class="list-member-thumbs">
<% _.each(memberships, function (membership) { %>
<li class="item-member-thumb"><img alt="<%- membership.user.username %>" src="<%- membership.user.profile_image.image_url_small %>"></img></li>
<% }) %>
<% if (has_additional_memberships) { %>
<li class="item-member-thumb"><span class="sr"><%- sr_message %></span>&hellip;</li>
<% } %>
</ul>
...@@ -97,7 +97,7 @@ class TeamsDashboardView(View): ...@@ -97,7 +97,7 @@ class TeamsDashboardView(View):
team_memberships_page = Paginator(team_memberships, TEAM_MEMBERSHIPS_PER_PAGE).page(1) team_memberships_page = Paginator(team_memberships, TEAM_MEMBERSHIPS_PER_PAGE).page(1)
team_memberships_serializer = PaginatedMembershipSerializer( team_memberships_serializer = PaginatedMembershipSerializer(
instance=team_memberships_page, instance=team_memberships_page,
context={'expand': ('team',)}, context={'expand': ('team', 'user'), 'request': request},
) )
context = { context = {
......
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