Commit a56e6bf7 by muzaffaryousaf

Team details page.

TNL-1906
parent c197e725
...@@ -252,3 +252,63 @@ class TeamPage(CoursePage, PaginatedUIMixin): ...@@ -252,3 +252,63 @@ class TeamPage(CoursePage, PaginatedUIMixin):
def team_description(self): def team_description(self):
"""Get the team's description as displayed in the page header""" """Get the team's description as displayed in the page header"""
return self.q(css=TEAMS_HEADER_CSS + ' .page-description')[0].text return self.q(css=TEAMS_HEADER_CSS + ' .page-description')[0].text
@property
def team_members_present(self):
"""Verifies that team members are present"""
return self.q(css='.page-content-secondary .team-members .team-member').present
@property
def team_capacity_text(self):
"""Returns team capacity text"""
return self.q(css='.page-content-secondary .team-capacity :last-child').text[0]
@property
def team_location(self):
""" Returns team location/country. """
return self.q(css='.page-content-secondary .team-country :last-child').text[0]
@property
def team_language(self):
""" Returns team location/country. """
return self.q(css='.page-content-secondary .team-language :last-child').text[0]
@property
def team_user_membership_text(self):
"""Returns the team membership text"""
query = self.q(css='.page-content-secondary > .team-user-membership-status')
return query.text[0] if query.present else ''
@property
def team_leave_link_present(self):
"""Verifies that team leave link is present"""
return self.q(css='.leave-team-link').present
@property
def team_invite_section_present(self):
"""Verifies that invite section is present"""
return self.q(css='.page-content-secondary .invite-team').present
@property
def team_members(self):
"""Returns the number of team members in this team"""
return len(self.q(css='.page-content-secondary .team-member'))
def click_first_profile_image(self):
"""Clicks on first team member's profile image"""
self.q(css='.page-content-secondary .members-info > .team-member').first.click()
@property
def first_member_username(self):
"""Returns the username of team member"""
return self.q(css='.page-content-secondary .tooltip-custom').text[0]
@property
def team_invite_help_text(self):
"""Returns the team invite help text"""
return self.q(css='.page-content-secondary .invite-text').text[0]
@property
def team_invite_url(self):
"""Returns the url of invite link box"""
return self.q(css='.page-content-secondary .invite-link-input').attrs('value')[0]
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
this.course_id = options.course_id; this.course_id = options.course_id;
this.server_api['topic_id'] = this.topic_id = options.topic_id; this.server_api['topic_id'] = this.topic_id = options.topic_id;
this.server_api['expand'] = 'user';
this.perPage = options.per_page; this.perPage = options.per_page;
this.server_api['course_id'] = function () { return encodeURIComponent(this.course_id); }; this.server_api['course_id'] = function () { return encodeURIComponent(this.course_id); };
this.server_api['order_by'] = function () { return 'name'; }; // TODO surface sort order in UI this.server_api['order_by'] = function () { return 'name'; }; // TODO surface sort order in UI
......
...@@ -5,26 +5,41 @@ define([ ...@@ -5,26 +5,41 @@ define([
], function (_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) { ], function (_, AjaxHelpers, TeamModel, TeamProfileView, TeamSpecHelpers, DiscussionSpecHelper) {
'use strict'; 'use strict';
describe('TeamProfileView', function () { describe('TeamProfileView', function () {
var discussionView, createTeamProfileView; var profileView, createTeamProfileView;
beforeEach(function () { beforeEach(function () {
DiscussionSpecHelper.setUnderscoreFixtures(); DiscussionSpecHelper.setUnderscoreFixtures();
}); });
createTeamProfileView = function(requests) { createTeamProfileView = function(requests, options) {
var model = new TeamModel( var model = new TeamModel(
{ {
id: "test-team", id: "test-team",
name: "Test Team", name: "Test Team",
discussion_topic_id: TeamSpecHelpers.testTeamDiscussionID discussion_topic_id: TeamSpecHelpers.testTeamDiscussionID,
country: options.country || '',
language: options.language || '',
membership: options.membership || []
}, },
{ parse: true } { parse: true }
); );
discussionView = new TeamProfileView({ profileView = new TeamProfileView({
courseID: TeamSpecHelpers.testCourseID, courseID: TeamSpecHelpers.testCourseID,
model: model model: model,
maxTeamSize: options.maxTeamSize || 3,
requestUsername: 'bilbo',
countries : [
['', ''],
['US', 'United States'],
['CA', 'Canada']
],
languages : [
['', ''],
['en', 'English'],
['fr', 'French']
]
}); });
discussionView.render(); profileView.render();
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
requests, requests,
'GET', 'GET',
...@@ -38,13 +53,147 @@ define([ ...@@ -38,13 +53,147 @@ define([
) )
); );
AjaxHelpers.respondWithJson(requests, TeamSpecHelpers.createMockDiscussionResponse()); AjaxHelpers.respondWithJson(requests, TeamSpecHelpers.createMockDiscussionResponse());
return discussionView; return profileView;
}; };
it('can render itself', function () { describe('DiscussionsView', function() {
var requests = AjaxHelpers.requests(this), it('can render itself', function () {
view = createTeamProfileView(requests); var requests = AjaxHelpers.requests(this),
expect(view.$('.discussion-thread').length).toEqual(3); view = createTeamProfileView(requests, {});
expect(view.$('.discussion-thread').length).toEqual(3);
});
});
describe('TeamDetailsView', function() {
var assertTeamDetails = function(view, members) {
expect(view.$('.team-detail-header').text()).toBe('Team Details');
expect(view.$('.team-country').text()).toContain('United States');
expect(view.$('.team-language').text()).toContain('English');
expect(view.$('.team-capacity').text()).toContain(members + ' / 3 Members');
expect(view.$('.team-member').length).toBe(members);
};
describe('Non-Member', function() {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests, {
country: 'US',
language: 'en'
});
assertTeamDetails(view, 0);
expect(view.$('.team-user-membership-status').length).toBe(0);
// Verify that invite and leave team sections are not present.
expect(view.$('.leave-team').length).toBe(0);
expect(view.$('.invite-team').length).toBe(0);
});
it('cannot see the country & language if empty', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests, {});
expect(view.$('.team-country').length).toBe(0);
expect(view.$('.team-language').length).toBe(0);
});
});
describe('Member', function() {
it('can render itself', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests, {
country: 'US',
language: 'en',
membership: [{
'user': {
'username': 'bilbo',
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
}
}
}]
});
assertTeamDetails(view, 1);
expect(view.$('.team-user-membership-status').text().trim()).toBe('You are a member of this team.');
// assert tooltip text.
expect(view.$('.member-profile p').text()).toBe('bilbo');
// assert user profile page url.
expect(view.$('.member-profile').attr('href')).toBe('/u/bilbo');
//Verify that invite and leave team sections are present
expect(view.$('.leave-team-link').text()).toContain('Leave Team');
expect(view.$('.invite-header').text()).toContain('Invite Others');
expect(view.$('.invite-text').text()).toContain('Send this link to friends so that they can join too.');
expect(view.$('.invite-link-input').length).toBe(1);
});
it('cannot see invite url box if team is full', function() {
var requests = AjaxHelpers.requests(this);
var view = createTeamProfileView(requests , {
country: 'US',
language: 'en',
membership: [{
'user': {
'username': 'bilbo',
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
}
}
},
{
'user': {
'username': 'bilbo1',
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
}
}
},
{
'user': {
'username': 'bilbo2',
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
}
}
}]
});
assertTeamDetails(view, 3);
expect(view.$('.invite-header').text()).toContain('Invite Others');
expect(view.$('.invite-text').text()).toContain('No invitations are available. This team is full.');
expect(view.$('.invite-link-input').length).toBe(0);
});
it('can see & select invite url if team has capacity', function() {
var requests = AjaxHelpers.requests(this);
spyOn(TeamProfileView.prototype, 'selectText');
var view = createTeamProfileView(requests, {
country: 'US',
language: 'en',
membership: [{
'user': {
'username': 'bilbo',
'profile_image': {
'has_image': true,
'image_url_medium': '/image-url'
}
}
}]
});
assertTeamDetails(view, 1);
expect(view.$('.invite-link-input').length).toBe(1);
view.$('.invite-link-input').click();
expect(view.selectText).toHaveBeenCalled();
});
});
}); });
}); });
}); });
...@@ -92,7 +92,7 @@ define([ ...@@ -92,7 +92,7 @@ define([
var requests = AjaxHelpers.requests(this), var requests = AjaxHelpers.requests(this),
teamsTabView = createTeamsTabView(); teamsTabView = createTeamsTabView();
teamsTabView.router.navigate('teams/test_topic/no_such_team', {trigger: true}); teamsTabView.router.navigate('teams/test_topic/no_such_team', {trigger: true});
AjaxHelpers.expectRequest(requests, 'GET', 'api/teams/no_such_team', null); AjaxHelpers.expectRequest(requests, 'GET', 'api/teams/no_such_team?expand=user', null);
AjaxHelpers.respondWithError(requests, 404); AjaxHelpers.respondWithError(requests, 404);
expectError(teamsTabView, 'The team "no_such_team" could not be found.'); expectError(teamsTabView, 'The team "no_such_team" could not be found.');
expectFocus(teamsTabView.$('.warning')); expectFocus(teamsTabView.$('.warning'));
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
'underscore', 'underscore',
'gettext', 'gettext',
'js/components/card/views/card', 'js/components/card/views/card',
'teams/js/views/team_utils',
'text!teams/templates/team-country-language.underscore' 'text!teams/templates/team-country-language.underscore'
], function (Backbone, _, gettext, CardView, teamCountryLanguageTemplate) { ], function (Backbone, _, gettext, CardView, TeamUtils, teamCountryLanguageTemplate) {
var TeamMembershipView, TeamCountryLanguageView, TeamCardView; var TeamMembershipView, TeamCountryLanguageView, TeamCardView;
TeamMembershipView = Backbone.View.extend({ TeamMembershipView = Backbone.View.extend({
...@@ -25,15 +26,7 @@ ...@@ -25,15 +26,7 @@
var memberships = this.model.get('membership'), var memberships = this.model.get('membership'),
maxMemberCount = this.maxTeamSize; maxMemberCount = this.maxTeamSize;
this.$el.html(this.template({ this.$el.html(this.template({
membership_message: interpolate( membership_message: TeamUtils.teamCapacityText(memberships.length, maxMemberCount)
// Translators: The following message displays the number of members on a team.
ngettext(
'%(member_count)s / %(max_member_count)s Member',
'%(member_count)s / %(max_member_count)s Members',
maxMemberCount
),
{member_count: memberships.length, max_member_count: maxMemberCount}, true
)
})); }));
_.each(memberships, function (membership) { _.each(memberships, function (membership) {
this.$('list-member-thumbs').append( this.$('list-member-thumbs').append(
......
...@@ -4,26 +4,66 @@ ...@@ -4,26 +4,66 @@
;(function (define) { ;(function (define) {
'use strict'; 'use strict';
define(['backbone', 'underscore', 'gettext', 'teams/js/views/team_discussion', define(['backbone', 'underscore', 'gettext', 'teams/js/views/team_discussion',
'text!teams/templates/team-profile.underscore'], 'teams/js/views/team_utils',
function (Backbone, _, gettext, TeamDiscussionView, teamTemplate) { 'text!teams/templates/team-profile.underscore',
'text!teams/templates/team-member.underscore'
],
function (Backbone, _, gettext, TeamDiscussionView, TeamUtils, teamTemplate, teamMemberTemplate) {
var TeamProfileView = Backbone.View.extend({ var TeamProfileView = Backbone.View.extend({
events: {
'click .invite-link-input': 'selectText'
},
initialize: function (options) { initialize: function (options) {
this.courseID = options.courseID; this.courseID = options.courseID;
this.discussionTopicID = this.model.get('discussion_topic_id'); this.discussionTopicID = this.model.get('discussion_topic_id');
this.maxTeamSize = options.maxTeamSize;
this.memberships = this.model.get('membership');
this.readOnly = options.readOnly; this.readOnly = options.readOnly;
this.requestUsername = options.requestUsername;
this.teamInviteUrl = options.teamInviteUrl;
this.countries = TeamUtils.selectorOptionsArrayToHashWithBlank(options.countries);
this.languages = TeamUtils.selectorOptionsArrayToHashWithBlank(options.languages);
}, },
render: function () { render: function () {
this.$el.html(_.template(teamTemplate, { this.$el.html(_.template(teamTemplate, {
courseID: this.courseID, courseID: this.courseID,
discussionTopicID: this.discussionTopicID, discussionTopicID: this.discussionTopicID,
readOnly: this.readOnly readOnly: this.readOnly,
country: this.countries[this.model.get('country')],
language: this.languages[this.model.get('language')],
membershipText: TeamUtils.teamCapacityText(this.memberships.length, this.maxTeamSize),
isMember: TeamUtils.isUserMemberOfTeam(this.memberships, this.requestUsername),
hasCapacity: this.memberships.length < this.maxTeamSize,
inviteLink: this.teamInviteUrl
})); }));
this.discussionView = new TeamDiscussionView({ this.discussionView = new TeamDiscussionView({
el: this.$('.discussion-module') el: this.$('.discussion-module')
}); });
this.discussionView.render(); this.discussionView.render();
this.renderTeamMembers();
return this; return this;
},
renderTeamMembers: function() {
var view = this;
_.each(this.memberships, function(membership) {
view.$('.members-info').append(_.template(teamMemberTemplate, {
imageUrl: membership.user.profile_image.image_url_medium,
username: membership.user.username,
memberProfileUrl: '/u/' + membership.user.username
}));
});
},
selectText: function(event) {
event.preventDefault();
$(event.currentTarget).select();
} }
}); });
......
/* Team utility methods*/
;(function (define) {
'use strict';
define([
], function () {
return {
/**
* Convert a 2d array to an object equivalent with an additional blank element
*
* @param options {Array.<Array.<string>>} Two dimensional options array
* @returns {Object} Hash version of the input array
* @example selectorOptionsArrayToHashWithBlank([["a", "alpha"],["b","beta"]])
* // returns {"a":"alpha", "b":"beta", "":""}
*/
selectorOptionsArrayToHashWithBlank: function (options) {
var map = _.object(options);
map[""] = "";
return map;
},
teamCapacityText: function (memberCount, maxMemberCount) {
return interpolate(
// Translators: The following message displays the number of members on a team.
ngettext(
'%(memberCount)s / %(maxMemberCount)s Member',
'%(memberCount)s / %(maxMemberCount)s Members',
maxMemberCount
),
{memberCount: memberCount, maxMemberCount: maxMemberCount}, true
)
},
isUserMemberOfTeam: function(memberships, requestUsername) {
return _.isObject(
_.find(memberships, function(membership)
{
return membership.user.username === requestUsername;
})
);
}
}
});
}).call(this, define || RequireJS.define);
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
'backbone', 'backbone',
'gettext', 'gettext',
'teams/js/views/team_card', 'teams/js/views/team_card',
'common/js/components/views/paginated_view' 'common/js/components/views/paginated_view',
], function (Backbone, gettext, TeamCardView, PaginatedView) { 'teams/js/views/team_utils'
], function (Backbone, gettext, TeamCardView, PaginatedView, TeamUtils) {
var TeamsView = PaginatedView.extend({ var TeamsView = PaginatedView.extend({
type: 'teams', type: 'teams',
...@@ -26,25 +27,11 @@ ...@@ -26,25 +27,11 @@
router: options.router, router: options.router,
topic: options.topic, topic: options.topic,
maxTeamSize: options.maxTeamSize, maxTeamSize: options.maxTeamSize,
countries: this.selectorOptionsArrayToHashWithBlank(options.teamParams.countries), srInfo: this.srInfo,
languages: this.selectorOptionsArrayToHashWithBlank(options.teamParams.languages), countries: TeamUtils.selectorOptionsArrayToHashWithBlank(options.teamParams.countries),
srInfo: this.srInfo languages: TeamUtils.selectorOptionsArrayToHashWithBlank(options.teamParams.languages)
}); });
PaginatedView.prototype.initialize.call(this); PaginatedView.prototype.initialize.call(this);
},
/**
* Convert a 2d array to an object equivalent with an additional blank element
*
* @param {Array.<Array.<string>>} Two dimensional options array
* @returns {Object} Hash version of the input array
* @example selectorOptionsArrayToHashWithBlank([["a", "alpha"],["b","beta"]])
* // returns {"a":"alpha", "b":"beta", "":""}
*/
selectorOptionsArrayToHashWithBlank: function (options) {
var map = _.object(options);
map[""] = "";
return map;
} }
}); });
return TeamsView; return TeamsView;
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
this.languages = options.languages; this.languages = options.languages;
this.countries = options.countries; this.countries = options.countries;
this.userInfo = options.userInfo; this.userInfo = options.userInfo;
this.teamsBaseUrl = options.teamsBaseUrl;
// This slightly tedious approach is necessary // This slightly tedious approach is necessary
// to use regular expressions within Backbone // to use regular expressions within Backbone
// routes, allowing us to capture which tab // routes, allowing us to capture which tab
...@@ -267,12 +267,17 @@ ...@@ -267,12 +267,17 @@
deferred = $.Deferred(), deferred = $.Deferred(),
courseID = this.courseID; courseID = this.courseID;
self.getTopic(topicID).done(function(topic) { self.getTopic(topicID).done(function(topic) {
self.getTeam(teamID).done(function(team) { self.getTeam(teamID, true).done(function(team) {
var readOnly = self.readOnlyDiscussion(team), var readOnly = self.readOnlyDiscussion(team),
view = new TeamProfileView({ view = new TeamProfileView({
courseID: courseID, courseID: courseID,
model: team, model: team,
readOnly: readOnly readOnly: readOnly,
maxTeamSize: self.maxTeamSize,
requestUsername: self.userInfo.username,
countries: self.countries,
languages: self.languages,
teamInviteUrl: self.teamsBaseUrl + '#teams/' + topicID + '/' + teamID + '?invite=true'
}); });
deferred.resolve(self.createViewWithHeader(view, team, topic)); deferred.resolve(self.createViewWithHeader(view, team, topic));
}); });
...@@ -350,18 +355,21 @@ ...@@ -350,18 +355,21 @@
* promise, since the team may need to be fetched from the * promise, since the team may need to be fetched from the
* server. * server.
* @param teamID the string identifier for the requested team * @param teamID the string identifier for the requested team
* @param expandUser bool to add the users info.
* @returns {promise} a jQuery deferred promise for the team. * @returns {promise} a jQuery deferred promise for the team.
*/ */
getTeam: function (teamID) { getTeam: function (teamID, expandUser) {
var team = this.teamsCollection ? this.teamsCollection.get(teamID) : null, var team = this.teamsCollection ? this.teamsCollection.get(teamID) : null,
self = this, self = this,
deferred = $.Deferred(); deferred = $.Deferred(),
teamUrl;
if (team) { if (team) {
deferred.resolve(team); deferred.resolve(team);
} else { } else {
teamUrl = this.teamsUrl + teamID + (expandUser ? '?expand=user': '');
team = new TeamModel({ team = new TeamModel({
id: teamID, id: teamID,
url: this.teamsUrl + teamID url: teamUrl
}); });
team.fetch() team.fetch()
.done(function() { .done(function() {
......
<span class="team-member">
<a class="member-profile" href="<%= memberProfileUrl %>">
<p class="tooltip-custom"><%= username %></p>
<img class="image-url" src="<%= imageUrl %>" alt="profile page" />
</a>
</span>
<div class="team-profile"> <div class="team-profile">
<div class="discussion-module" data-course-id="<%= courseID %>" data-discussion-id="<%= discussionTopicID %>" <div class="page-content-main">
data-read-only="<%= readOnly %>" <div class="discussion-module" data-course-id="<%= courseID %>" data-discussion-id="<%= discussionTopicID %>"
data-user-create-comment="<%= !readOnly %>" data-read-only="<%= readOnly %>"
data-user-create-subcomment="<%= !readOnly %>"> data-user-create-comment="<%= !readOnly %>"
<% if ( !readOnly) { %> data-user-create-subcomment="<%= !readOnly %>">
<a href="#" class="new-post-btn" role="button"><span class="icon fa fa-edit new-post-icon"></span><%= gettext("New Post") %></a> <% if ( !readOnly) { %>
<a href="#" class="new-post-btn" role="button"><span class="icon fa fa-edit new-post-icon"></span><%= gettext("New Post") %></a>
<% } %>
</div>
</div>
<div class="page-content-secondary">
<h4 class="team-detail-header"><%- gettext("Team Details") %></h4>
<% if (isMember) { %>
<div class="team-user-membership-status">
<p><%- gettext("You are a member of this team.") %></p>
</div>
<% } %>
<div class="team-members">
<span class="sr"><%- gettext("Team member profiles") %></span>
<div class="members-info"></div>
</div>
<div class="team-capacity">
<span class="sr"><%- gettext("Team capacity") %></span>
<span><%- membershipText %></span>
</div>
<% if (country) { %>
<div class="team-country">
<span class="sr"><%- gettext("The country that team members primarily identify with.") %></span>
<i class="icon fa fa-globe fa-fw" aria-hidden="true"></i>
<span>
<%- gettext(country) %>
</span>
</div>
<% } %>
<% if (language) { %>
<div class="team-language">
<span class="sr"><%- gettext("The language that team members primarily use to communicate with each other.") %></span>
<i class="icon fa fa-comment-o fa-fw" aria-hidden="true"></i>
<span>
<%- gettext(language) %>
</span>
</div>
<% } %>
<% if (isMember) { %>
<div class="leave-team">
<button class="btn btn-link btn-base btn-secondary leave-team-link"><%- gettext("Leave Team") %></button>
</div>
<div class="divider-lv1"></div>
<div class="invite-team">
<h4 class="invite-header"><%- gettext("Invite Others") %></h4>
<% if (hasCapacity) { %>
<input type="text" class="invite-link-input" value="<%= inviteLink %>" aria-describedby="invite-text" readonly >
<span class="invite-text" id="invite-text">
<%- gettext("Send this link to friends so that they can join too.") %>
</span>
<% } else { %>
<span class="invite-text">
<%- gettext("No invitations are available. This team is full.") %>
</span>
<% } %>
</div>
<% } %> <% } %>
</div> </div>
</div> </div>
...@@ -42,7 +42,8 @@ ...@@ -42,7 +42,8 @@
teamMembershipsUrl: '${ team_memberships_url }', teamMembershipsUrl: '${ team_memberships_url }',
maxTeamSize: ${ course.teams_max_size }, maxTeamSize: ${ course.teams_max_size },
languages: ${ json.dumps(languages, cls=EscapedEdxJSONEncoder) }, languages: ${ json.dumps(languages, cls=EscapedEdxJSONEncoder) },
countries: ${ json.dumps(countries, cls=EscapedEdxJSONEncoder) } countries: ${ json.dumps(countries, cls=EscapedEdxJSONEncoder) },
teamsBaseUrl: '${ teams_base_url }'
}); });
</%static:require_module> </%static:require_module>
</%block> </%block>
......
...@@ -111,6 +111,7 @@ class TeamsDashboardView(View): ...@@ -111,6 +111,7 @@ class TeamsDashboardView(View):
"languages": settings.ALL_LANGUAGES, "languages": settings.ALL_LANGUAGES,
"countries": list(countries), "countries": list(countries),
"disable_courseware_js": True, "disable_courseware_js": True,
"teams_base_url": reverse('teams_dashboard', request=request, kwargs={'course_id': course_id}),
} }
return render_to_response("teams/teams.html", context) return render_to_response("teams/teams.html", context)
......
...@@ -403,3 +403,16 @@ ...@@ -403,3 +403,16 @@
margin-bottom: none; margin-bottom: none;
} }
} }
.btn-link {
@extend %btn-pl-secondary-base;
background-image: none;
&:focus,
&:hover {
background-image: none !important;
background-color: transparent !important;
color: $link-color;
}
}
...@@ -10,3 +10,54 @@ ...@@ -10,3 +10,54 @@
text-align: center; text-align: center;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
// custom tool tip style.
@mixin tooltip-hover-style ($margin-top) {
p {
@extend %ui-depth2;
background: $dark-gray;
border-radius: ($baseline/5);
color: $white;
font-family: $sans-serif;
line-height: lh();
opacity: 0.0;
padding: 6px;
position: absolute;
text-shadow: 0 -1px 0 $black;
@include transition(all .1s $ease-in-out-quart 0s);
white-space: pre;
visibility: hidden;
pointer-events: none;
right: 0;
&:empty {
background: none;
&::after {
display: none;
}
}
&::after {
background: $dark-gray;
content: " ";
display: block;
height: ($baseline/2);
right: 18px;
position: absolute;
top: ($baseline + ($baseline/4));
@include transform(rotate(45deg));
width: ($baseline/2);
}
}
&:hover, &:focus {
p {
display: block;
margin-top: $margin-top;
opacity: 1.0;
visibility: visible;
}
}
}
...@@ -346,6 +346,64 @@ ...@@ -346,6 +346,64 @@
} }
} }
.team-profile {
.page-content-main {
display: inline-block;
width: flex-grid(8, 12);
vertical-align: top;
.forum-new-post-form,
.edit-post-form {
min-width: 700px;
}
}
.page-content-secondary {
display: inline-block;
width: flex-grid(4,12);
@include margin-left($baseline * 0.75);
margin-bottom: $baseline;
@extend %t-copy-sub1;
color: $gray-l1;
div, .team-detail-header {
margin-bottom: ($baseline/4);
}
.image-url {
border: 2px solid $outer-border-color;
border-radius: ($baseline/4);
margin-bottom: ($baseline/4);
}
.leave-team {
margin-bottom: $baseline;
margin-top: ($baseline/2);
}
.invite-header, .invite-text {
margin-bottom: ($baseline/2);
}
.invite-team {
margin-top: ($baseline);
margin-bottom: ($baseline/2);
}
.invite-link-input {
width: 100%;
}
.member-profile {
position: relative;
display: inline-block;
@include tooltip-hover-style(-($baseline*2));
}
}
}
.create-team { .create-team {
legend { legend {
......
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