Commit 28fddda4 by Muzaffar yousaf

Merge pull request #9376 from edx/muzaffar/tnl1914-team-edit-page

Edit team page for Instructors only.
parents f9756eb4 f788caa6
......@@ -205,7 +205,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
self.wait_for_ajax()
class CreateTeamPage(CoursePage, FieldsMixin):
class CreateOrEditTeamPage(CoursePage, FieldsMixin):
"""
Create team page.
"""
......@@ -216,7 +216,7 @@ class CreateTeamPage(CoursePage, FieldsMixin):
representation of a topic following the same convention as a
course module's topic.
"""
super(CreateTeamPage, self).__init__(browser, course_id)
super(CreateOrEditTeamPage, self).__init__(browser, course_id)
self.topic = topic
self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id'])
......@@ -410,3 +410,12 @@ class TeamPage(CoursePage, PaginatedUIMixin):
"""Navigate to the 'All Topics' page."""
self.q(css='.breadcrumbs a').results[0].click()
self.wait_for_ajax()
@property
def edit_team_button_present(self):
""" Returns True if Edit Team button is present else False """
return self.q(css='.form-actions .action-edit-team').present
def click_edit_team_button(self):
""" Click on Edit Team button"""
self.q(css='.form-actions .action-edit-team').first.click()
......@@ -20,7 +20,7 @@ from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage
from ...pages.lms.learner_profile import LearnerProfilePage
from ...pages.lms.tab_nav import TabNavPage
from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateTeamPage, TeamPage
from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateOrEditTeamPage, TeamPage
TOPICS_PER_PAGE = 12
......@@ -335,7 +335,7 @@ class BrowseTopicsTest(TeamsTabBase):
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 = CreateOrEditTeamPage(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',
......@@ -629,78 +629,124 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
@attr('shard_5')
class CreateTeamTest(TeamsTabBase):
class TeamFormActions(TeamsTabBase):
"""
Tests for creating a new Team within a Topic on the Teams page.
Base class for create & edit team.
"""
TEAM_DESCRIPTION = 'The Avengers are a fictional team of superheroes.'
def setUp(self):
super(CreateTeamTest, self).setUp()
self.topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'}
self.set_team_configuration({'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]})
self.browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
self.browse_teams_page.visit()
self.create_team_page = CreateTeamPage(self.browser, self.course_id, self.topic)
self.team_name = 'Avengers'
topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'}
TEAMS_NAME = 'Avengers'
def verify_page_header(self):
def verify_page_header(self, title, description, breadcrumbs):
"""
Verify that the page header correctly reflects the
create team header, description and breadcrumb.
"""
self.assertEqual(self.create_team_page.header_page_name, 'Create a New Team')
self.assertEqual(
self.create_team_page.header_page_description,
'Create a new team if you can\'t find existing teams to join, '
'or if you would like to learn with friends you know.'
)
self.assertEqual(self.create_team_page.header_page_breadcrumbs, self.topic['name'])
self.assertEqual(self.create_or_edit_team_page.header_page_name, title)
self.assertEqual(self.create_or_edit_team_page.header_page_description, description)
self.assertEqual(self.create_or_edit_team_page.header_page_breadcrumbs, breadcrumbs)
def verify_and_navigate_to_create_team_page(self):
"""Navigates to the create team page and verifies."""
self.browse_teams_page.click_create_team_link()
self.verify_page_header()
self.verify_page_header(
title='Create a New Team',
description='Create a new team if you can\'t find existing teams to '
'join, or if you would like to learn with friends you know.',
breadcrumbs=self.topic['name']
)
def verify_and_navigate_to_edit_team_page(self):
"""Navigates to the edit team page and verifies."""
# pylint: disable=no-member
self.assertEqual(self.team_page.team_name, self.team['name'])
self.assertTrue(self.team_page.edit_team_button_present)
self.team_page.click_edit_team_button()
self.create_or_edit_team_page.wait_for_page()
def fill_create_form(self):
"""Fill the create team form fields with appropriate values."""
self.create_team_page.value_for_text_field(field_id='name', value=self.team_name, press_enter=False)
self.create_team_page.value_for_textarea_field(
# Edit page header.
self.verify_page_header(
title='Edit Team',
description='If you make significant changes, make sure you notify '
'members of the team before making these changes.',
breadcrumbs='All Topics {topic_name} {team_name}'.format(
topic_name=self.topic['name'],
team_name=self.team['name']
)
)
def verify_team_info(self, name, description, location, language):
"""Verify the team information on team page."""
# pylint: disable=no-member
self.assertEqual(self.team_page.team_name, name)
self.assertEqual(self.team_page.team_description, description)
self.assertEqual(self.team_page.team_location, location)
self.assertEqual(self.team_page.team_language, language)
def fill_create_or_edit_form(self):
"""Fill the create/edit team form fields with appropriate values."""
self.create_or_edit_team_page.value_for_text_field(field_id='name', value=self.TEAMS_NAME, press_enter=False)
self.create_or_edit_team_page.value_for_textarea_field(
field_id='description',
value='The Avengers are a fictional team of superheroes.'
value=self.TEAM_DESCRIPTION
)
self.create_team_page.value_for_dropdown_field(field_id='language', value='English')
self.create_team_page.value_for_dropdown_field(field_id='country', value='Pakistan')
self.create_or_edit_team_page.value_for_dropdown_field(field_id='language', value='English')
self.create_or_edit_team_page.value_for_dropdown_field(field_id='country', value='Pakistan')
def test_user_can_see_create_team_page(self):
def verify_all_fields_exist(self):
"""
Scenario: The user should be able to see the create team page via teams list page.
Given I am enrolled in a course with a team configuration and a topic
When I visit the Teams page for that topic
Then I should see the Create Team page link on bottom
And When I click create team link
Then I should see the create team page.
And I should see the create team header
And I should also see the help messages for fields.
Verify the fields for create/edit page.
"""
self.verify_and_navigate_to_create_team_page()
self.assertEqual(
self.create_team_page.message_for_field('name'),
self.create_or_edit_team_page.message_for_field('name'),
'A name that identifies your team (maximum 255 characters).'
)
self.assertEqual(
self.create_team_page.message_for_textarea_field('description'),
self.create_or_edit_team_page.message_for_textarea_field('description'),
'A short description of the team to help other learners understand '
'the goals or direction of the team (maximum 300 characters).'
)
self.assertEqual(
self.create_team_page.message_for_field('country'),
self.create_or_edit_team_page.message_for_field('country'),
'The country that team members primarily identify with.'
)
self.assertEqual(
self.create_team_page.message_for_field('language'),
self.create_or_edit_team_page.message_for_field('language'),
'The language that team members primarily use to communicate with each other.'
)
@ddt.ddt
class CreateTeamTest(TeamFormActions):
"""
Tests for creating a new Team within a Topic on the Teams page.
"""
def setUp(self):
super(CreateTeamTest, self).setUp()
self.set_team_configuration({'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]})
self.create_or_edit_team_page = CreateOrEditTeamPage(self.browser, self.course_id, self.topic)
self.browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
self.browse_teams_page.visit()
def test_user_can_see_create_team_page(self):
"""
Scenario: The user should be able to see the create team page via teams list page.
Given I am enrolled in a course with a team configuration and a topic
When I visit the Teams page for that topic
Then I should see the Create Team page link on bottom
And When I click create team link
Then I should see the create team page.
And I should see the create team header
And I should also see the help messages for fields.
"""
self.verify_and_navigate_to_create_team_page()
self.verify_all_fields_exist()
def test_user_can_see_error_message_for_missing_data(self):
"""
Scenario: The user should be able to see error message in case of missing required field.
......@@ -711,14 +757,14 @@ class CreateTeamTest(TeamsTabBase):
Then I should see the error message and highlighted fields.
"""
self.verify_and_navigate_to_create_team_page()
self.create_team_page.submit_form()
self.create_or_edit_team_page.submit_form()
self.assertEqual(
self.create_team_page.validation_message_text,
self.create_or_edit_team_page.validation_message_text,
'Check the highlighted fields below and try again.'
)
self.assertTrue(self.create_team_page.error_for_field(field_id='name'))
self.assertTrue(self.create_team_page.error_for_field(field_id='description'))
self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='name'))
self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='description'))
def test_user_can_see_error_message_for_incorrect_data(self):
"""
......@@ -733,7 +779,7 @@ class CreateTeamTest(TeamsTabBase):
self.verify_and_navigate_to_create_team_page()
# Fill the name field with >255 characters to see validation message.
self.create_team_page.value_for_text_field(
self.create_or_edit_team_page.value_for_text_field(
field_id='name',
value='EdX is a massive open online course (MOOC) provider and online learning platform. '
'It hosts online university-level courses in a wide range of disciplines to a worldwide '
......@@ -745,13 +791,13 @@ class CreateTeamTest(TeamsTabBase):
'edX has more than 4 million users taking more than 500 courses online.',
press_enter=False
)
self.create_team_page.submit_form()
self.create_or_edit_team_page.submit_form()
self.assertEqual(
self.create_team_page.validation_message_text,
self.create_or_edit_team_page.validation_message_text,
'Check the highlighted fields below and try again.'
)
self.assertTrue(self.create_team_page.error_for_field(field_id='name'))
self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='name'))
def test_user_can_create_new_team_successfully(self):
"""
......@@ -767,16 +813,19 @@ class CreateTeamTest(TeamsTabBase):
And the number of teams should be updated on the topic card
And if I switch to "My Team", the newly created team is displayed
"""
AutoAuthPage(self.browser, course_id=self.course_id).visit()
self.browse_teams_page.visit()
self.verify_and_navigate_to_create_team_page()
self.fill_create_form()
self.create_team_page.submit_form()
self.fill_create_or_edit_form()
self.create_or_edit_team_page.submit_form()
# Verify that the page is shown for the new team
team_page = TeamPage(self.browser, self.course_id)
team_page.wait_for_page()
self.assertEqual(team_page.team_name, self.team_name)
self.assertEqual(team_page.team_description, 'The Avengers are a fictional team of superheroes.')
self.assertEqual(team_page.team_name, self.TEAMS_NAME)
self.assertEqual(team_page.team_description, self.TEAM_DESCRIPTION)
self.assertEqual(team_page.team_user_membership_text, 'You are a member of this team.')
# Verify the new team was added to the topic list
......@@ -802,7 +851,7 @@ class CreateTeamTest(TeamsTabBase):
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total')
self.verify_and_navigate_to_create_team_page()
self.create_team_page.cancel_team()
self.create_or_edit_team_page.cancel_team()
self.assertTrue(self.browse_teams_page.is_browser_on_page())
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total')
......@@ -813,6 +862,157 @@ class CreateTeamTest(TeamsTabBase):
self.verify_my_team_count(0)
@ddt.ddt
class EditTeamTest(TeamFormActions):
"""
Tests for editing the team.
"""
def setUp(self):
super(EditTeamTest, self).setUp()
self.set_team_configuration(
{'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]},
global_staff=True
)
self.create_or_edit_team_page = CreateOrEditTeamPage(self.browser, self.course_id, self.topic)
self.team = self.create_teams(self.topic, num_teams=1)[0]
self.team_page = TeamPage(self.browser, self.course_id, team=self.team)
self.team_page.visit()
def test_staff_can_navigate_to_edit_team_page(self):
"""
Scenario: The user should be able to see and navigate to the edit team page.
Given I am staff user for a course with a team
When I visit the Team profile page
Then I should see the Edit Team button
And When I click edit team button
Then I should see the edit team page
And I should see the edit team header
And I should also see the help messages for fields
"""
self.verify_and_navigate_to_edit_team_page()
self.verify_all_fields_exist()
def test_staff_can_edit_team_successfully(self):
"""
Scenario: The staff should be able to edit team successfully.
Given I am staff user for a course with a team
When I visit the Team profile page
Then I should see the Edit Team button
And When I click edit team button
Then I should see the edit team page
When I edit all the fields with appropriate data
And I click Update button
Then I should see the page for my team with updated data
"""
self.verify_team_info(
name=self.team['name'],
description=self.team['description'],
location='Afghanistan',
language='Afar'
)
self.verify_and_navigate_to_edit_team_page()
self.fill_create_or_edit_form()
self.create_or_edit_team_page.submit_form()
self.team_page.wait_for_page()
self.verify_team_info(
name=self.TEAMS_NAME,
description=self.TEAM_DESCRIPTION,
location='Pakistan',
language='English'
)
def test_staff_can_cancel_the_team_edit(self):
"""
Scenario: The user should be able to cancel the editing of team.
Given I am staff user for a course with a team
When I visit the Team profile page
Then I should see the Edit Team button
And When I click edit team button
Then I should see the edit team page
Then I should see the Edit Team header
When I click Cancel button
Then I should see team page page without changes.
"""
self.verify_team_info(
name=self.team['name'],
description=self.team['description'],
location='Afghanistan',
language='Afar'
)
self.verify_and_navigate_to_edit_team_page()
self.fill_create_or_edit_form()
self.create_or_edit_team_page.cancel_team()
self.team_page.wait_for_page()
self.verify_team_info(
name=self.team['name'],
description=self.team['description'],
location='Afghanistan',
language='Afar'
)
def test_student_cannot_see_edit_button(self):
"""
Scenario: The student should not see the edit team button.
Given I am student for a course with a team
When I visit the Team profile page
Then I should not see the Edit Team button
"""
AutoAuthPage(self.browser, course_id=self.course_id).visit()
self.team_page.visit()
self.assertFalse(self.team_page.edit_team_button_present)
@ddt.data('Moderator', 'Community TA', 'Administrator')
def test_discussion_privileged_user_can_edit_team(self, role):
"""
Scenario: The user with specified role should see the edit team button.
Given I am user with privileged role for a course with a team
When I visit the Team profile page
Then I should see the Edit Team button
"""
kwargs = {
'course_id': self.course_id,
'staff': False
}
if role is not None:
kwargs['roles'] = role
AutoAuthPage(self.browser, **kwargs).visit()
self.team_page.visit()
self.teams_page.wait_for_page()
self.assertTrue(self.team_page.edit_team_button_present)
self.verify_team_info(
name=self.team['name'],
description=self.team['description'],
location='Afghanistan',
language='Afar'
)
self.verify_and_navigate_to_edit_team_page()
self.fill_create_or_edit_form()
self.create_or_edit_team_page.submit_form()
self.team_page.wait_for_page()
self.verify_team_info(
name=self.TEAMS_NAME,
description=self.TEAM_DESCRIPTION,
location='Pakistan',
language='English'
)
@attr('shard_5')
@ddt.ddt
class TeamPageTest(TeamsTabBase):
......
......@@ -4,13 +4,14 @@ define([
'backbone',
'common/js/spec_helpers/ajax_helpers',
'teams/js/views/edit_team',
'teams/js/models/team',
'teams/js/spec_helpers/team_spec_helpers'
], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamSpecHelpers) {
], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamModel, TeamSpecHelpers) {
'use strict';
describe('EditTeam', function () {
describe('CreateEditTeam', function() {
var teamsUrl = '/api/team/v0/teams/',
teamsData = {
createTeamData = {
id: null,
name: "TeamName",
is_active: null,
......@@ -18,11 +19,17 @@ define([
topic_id: "awesomeness",
date_created: "",
description: "TeamDescription",
country: "c",
language: "a",
country: "US",
language: "en",
membership: [],
last_activity_at: ''
},
editTeamData = {
name: "UpdatedAvengers",
description: "We do not discuss about avengers.",
country: "US",
language: "en"
},
verifyValidation = function (requests, teamEditView, fieldsData) {
_.each(fieldsData, function (fieldData) {
teamEditView.$(fieldData[0]).val(fieldData[1]);
......@@ -32,33 +39,57 @@ define([
var message = teamEditView.$('.wrapper-msg');
expect(message.hasClass('is-hidden')).toBeFalsy();
expect(message.find('.title').text().trim()).toBe("Your team could not be created!");
var actionMessage = (teamAction === 'create' ? 'Your team could not be created.' : 'Your team could not be updated.');
expect(message.find('.title').text().trim()).toBe(actionMessage);
expect(message.find('.copy').text().trim()).toBe(
"Check the highlighted fields below and try again."
);
_.each(fieldsData, function (fieldData) {
if(fieldData[2] === 'error') {
if (fieldData[2] === 'error') {
expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(1);
} else if(fieldData[2] === 'success') {
} else if (fieldData[2] === 'success') {
expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(0);
}
});
expect(requests.length).toBe(0);
};
},
editTeamID = 'av',
teamAction;
var createEditTeamView = function () {
var teamModel = {};
if (teamAction === 'edit') {
teamModel = new TeamModel(
{
id: editTeamID,
name: 'Avengers',
description: 'Team of dumbs',
language: 'en',
country: 'US',
membership: [],
url: '/api/team/v0/teams/' + editTeamID
},
{
parse: true
}
);
}
var createTeamEditView = function() {
return new TeamEditView({
teamEvents: TeamSpecHelpers.teamEvents,
el: $('.teams-content'),
action: teamAction,
model: teamModel,
teamParams: {
teamsUrl: teamsUrl,
courseID: "a/b/c",
topicID: 'awesomeness',
topicName: 'Awesomeness',
languages: [['a', 'aaa'], ['b', 'bbb']],
countries: [['c', 'ccc'], ['d', 'ddd']]
languages: [['aa', 'Afar'], ['fr', 'French'], ['en', 'English']],
countries: [['af', 'Afghanistan'], ['CA', 'Canada'], ['US', 'United States']],
teamsDetailUrl: teamModel.url
}
}).render();
};
......@@ -68,15 +99,15 @@ define([
spyOn(Backbone.history, 'navigate');
});
it('can render itself correctly', function () {
var assertFormRendersCorrectly = function() {
var fieldClasses = [
'.u-field-name',
'.u-field-description',
'.u-field-optional_description',
'.u-field-language',
'.u-field-country'
],
teamEditView = createTeamEditView();
'.u-field-name',
'.u-field-description',
'.u-field-optional_description',
'.u-field-language',
'.u-field-country'
],
teamEditView = createEditTeamView();
_.each(fieldClasses, function (fieldClass) {
expect(teamEditView.$el.find(fieldClass).length).toBe(1);
......@@ -84,32 +115,46 @@ define([
expect(teamEditView.$('.create-team.form-actions .action-primary').length).toBe(1);
expect(teamEditView.$('.create-team.form-actions .action-cancel').length).toBe(1);
});
it('can create a team', function () {
var requests = AjaxHelpers.requests(this),
teamEditView = createTeamEditView();
if (teamAction === 'edit') {
expect(teamEditView.$el.find('.u-field-name input').val()).toBe('Avengers');
expect(teamEditView.$el.find('.u-field-description textarea').val()).toBe('Team of dumbs');
expect(teamEditView.$el.find('.u-field-language select option:selected').text()).toBe('English');
expect(teamEditView.$el.find('.u-field-country select option:selected').text()).toBe('United States');
}
};
var requestMethod = function() {
return teamAction === 'create' ? 'POST' : 'PATCH';
};
var assertTeamCreateUpdateInfo = function(that, teamsData, teamsUrl, expectedUrl) {
var requests = AjaxHelpers.requests(that),
teamEditView = createEditTeamView();
teamEditView.$('.u-field-name input').val(teamsData.name);
teamEditView.$('.u-field-textarea textarea').val(teamsData.description);
teamEditView.$('.u-field-language select').val('a').attr("selected", "selected");
teamEditView.$('.u-field-country select').val('c').attr("selected", "selected");
teamEditView.$('.u-field-language select').val(teamsData.language).attr("selected", "selected");
teamEditView.$('.u-field-country select').val(teamsData.country).attr("selected", "selected");
teamEditView.$('.create-team.form-actions .action-primary').click();
AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData);
AjaxHelpers.respondWithJson(requests, _.extend(_.extend({}, teamsData), { id: '123'}));
AjaxHelpers.expectJsonRequest(requests, requestMethod(), teamsUrl, teamsData);
AjaxHelpers.respondWithJson(requests, _.extend(_.extend({}, teamsData), teamAction === 'create' ? {id: '123'} : {}));
expect(teamEditView.$('.create-team.wrapper-msg .copy').text().trim().length).toBe(0);
expect(Backbone.history.navigate.calls[0].args).toContain('teams/awesomeness/123');
});
expect(Backbone.history.navigate.calls[0].args).toContain(expectedUrl);
};
var assertValidationMessagesWhenFieldsEmpty = function(that) {
var requests = AjaxHelpers.requests(that),
teamEditView = createEditTeamView();
it('shows validation error message when field is empty', function () {
var requests = AjaxHelpers.requests(this),
teamEditView = createTeamEditView();
verifyValidation(requests, teamEditView, [
['.u-field-name input', 'Name', 'success'],
['.u-field-textarea textarea', '', 'error']
]);
teamEditView.render();
verifyValidation(requests, teamEditView, [
['.u-field-name input', '', 'error'],
......@@ -120,13 +165,13 @@ define([
['.u-field-name input', '', 'error'],
['.u-field-textarea textarea', '', 'error']
]);
});
};
it('shows validation error message when field value length exceeded the limit', function () {
var requests = AjaxHelpers.requests(this),
teamEditView = createTeamEditView(),
teamName = new Array(500 + 1).join( '$'),
teamDescription = new Array(500 + 1).join( '$' );
var assertValidationMessagesWhenInvalidData = function(that) {
var requests = AjaxHelpers.requests(that),
teamEditView = createEditTeamView(),
teamName = new Array(500 + 1).join('$'),
teamDescription = new Array(500 + 1).join('$');
verifyValidation(requests, teamEditView, [
['.u-field-name input', teamName, 'error'],
......@@ -142,48 +187,114 @@ define([
['.u-field-name input', teamName, 'error'],
['.u-field-textarea textarea', teamDescription, 'error']
]);
});
};
it("shows an error message for HTTP 500", function () {
var teamEditView = createTeamEditView(),
requests = AjaxHelpers.requests(this);
var assertShowMessageOnError = function(that, teamsData, teamsUrl, errorCode) {
var teamEditView = createEditTeamView(),
requests = AjaxHelpers.requests(that);
teamEditView.$('.u-field-name input').val(teamsData.name);
teamEditView.$('.u-field-textarea textarea').val(teamsData.description);
teamEditView.$('.create-team.form-actions .action-primary').click();
teamsData.country = '';
teamsData.language = '';
AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData);
AjaxHelpers.respondWithError(requests);
expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("An error occurred. Please try again.");
});
if (teamAction === 'create') {
teamsData.country = '';
teamsData.language = '';
}
AjaxHelpers.expectJsonRequest(requests, requestMethod(), teamsUrl, teamsData);
it("shows correct error message when server returns an error", function () {
var requests = AjaxHelpers.requests(this),
teamEditView = createTeamEditView();
if (errorCode < 500) {
AjaxHelpers.respondWithError(
requests,
errorCode,
{'user_message': 'User message', 'developer_message': 'Developer message'}
);
expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("User message");
} else {
AjaxHelpers.respondWithError(requests);
expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("An error occurred. Please try again.");
}
};
teamEditView.$('.u-field-name input').val(teamsData.name);
teamEditView.$('.u-field-textarea textarea').val(teamsData.description);
var assertRedirectsToCorrectUrlOnCancel = function(expectedUrl) {
var teamEditView = createEditTeamView();
teamEditView.$('.create-team.form-actions .action-cancel').click();
expect(Backbone.history.navigate.calls[0].args).toContain(expectedUrl);
};
teamEditView.$('.create-team.form-actions .action-primary').click();
teamsData.country = '';
teamsData.language = '';
AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData);
AjaxHelpers.respondWithError(
requests,
400,
{'user_message': 'User message', 'developer_message': 'Developer message'}
);
expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("User message");
describe('NewTeam', function () {
beforeEach(function() {
teamAction = 'create';
});
it('can render itself correctly', function () {
assertFormRendersCorrectly();
});
it('can create a team', function () {
assertTeamCreateUpdateInfo(this, createTeamData, teamsUrl, 'teams/awesomeness/123');
});
it('shows validation error message when field is empty', function () {
assertValidationMessagesWhenFieldsEmpty(this);
});
it('shows validation error message when field value length exceeded the limit', function () {
assertValidationMessagesWhenInvalidData(this);
});
it("shows an error message for HTTP 500", function () {
assertShowMessageOnError(this, createTeamData, teamsUrl, 500);
});
it("shows correct error message when server returns an error", function () {
assertShowMessageOnError(this, createTeamData, teamsUrl, 400);
});
it("changes route on cancel click", function () {
assertRedirectsToCorrectUrlOnCancel('topics/awesomeness');
});
});
it("changes route on cancel click", function () {
var teamEditView = createTeamEditView();
teamEditView.$('.create-team.form-actions .action-cancel').click();
expect(Backbone.history.navigate.calls[0].args).toContain('topics/awesomeness');
describe('EditTeam', function () {
beforeEach(function() {
teamAction = 'edit';
});
it('can render itself correctly', function () {
assertFormRendersCorrectly();
});
it('can edit a team', function () {
var copyTeamsData = _.clone(editTeamData);
copyTeamsData.country = 'CA';
copyTeamsData.language = 'fr';
assertTeamCreateUpdateInfo(this, copyTeamsData, teamsUrl + editTeamID + '?expand=user', 'teams/awesomeness/' + editTeamID);
});
it('shows validation error message when field is empty', function () {
assertValidationMessagesWhenFieldsEmpty(this);
});
it('shows validation error message when field value length exceeded the limit', function () {
assertValidationMessagesWhenInvalidData(this);
});
it("shows an error message for HTTP 500", function () {
assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 500);
});
it("shows correct error message when server returns an error", function () {
assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 400);
});
it("changes route on cancel click", function () {
assertRedirectsToCorrectUrlOnCancel('teams/awesomeness/' + editTeamID);
});
});
});
});
define([
'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team',
'teams/js/views/team_join', 'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, _, AjaxHelpers, TeamModel, TeamJoinView, TeamSpecHelpers) {
'teams/js/views/team_profile_header_actions', 'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) {
'use strict';
describe('TeamJoinView', function () {
describe('TeamProfileHeaderActionsView', function () {
var createTeamsUrl,
createTeamModelData,
createMembershipData,
createJoinView,
createHeaderActionsView,
verifyErrorMessage,
ACCOUNTS_API_URL = '/api/user/v1/accounts/',
TEAMS_URL = '/api/team/v0/teams/',
TEAMS_MEMBERSHIP_URL = '/api/team/v0/team_membership/';
beforeEach(function () {
setFixtures(
'<div class="teams-content"><div class="msg-content"><div class="copy"></div></div><div class="header-action-view"></div></div>'
);
});
verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) {
var view = createJoinView(1, 'ma', createTeamModelData('teamA', 'teamAlpha', []));
if (joinTeam) {
// if we want the error to return when user try to join team, respond with no membership
AjaxHelpers.respondWithJson(requests, {"count": 0});
view.$('.action.action-primary').click();
}
AjaxHelpers.respondWithTextError(requests, 400, errorMessage);
expect($('.msg-content .copy').text().trim()).toBe(expectedMessage);
};
createTeamsUrl = function (teamId) {
return TEAMS_URL + teamId + '?expand=user';
};
......@@ -38,28 +22,17 @@ define([
return {
id: teamId,
name: teamName,
membership: membership
membership: membership,
url: createTeamsUrl(teamId)
};
};
createMembershipData = function (username) {
return [
{
"user": {
"username": username,
"url": ACCOUNTS_API_URL + username
}
}
];
};
createJoinView = function(maxTeamSize, currentUsername, teamModelData, teamId) {
teamId = teamId || 'teamA';
createHeaderActionsView = function(maxTeamSize, currentUsername, teamModelData, showEditButton) {
var teamId = 'teamA';
var model = new TeamModel(teamModelData, { parse: true });
model.url = createTeamsUrl(teamId);
var teamJoinView = new TeamJoinView(
return new TeamProfileHeaderActionsView(
{
courseID: TeamSpecHelpers.testCourseID,
teamEvents: TeamSpecHelpers.teamEvents,
......@@ -67,152 +40,215 @@ define([
teamsUrl: createTeamsUrl(teamId),
maxTeamSize: maxTeamSize,
currentUsername: currentUsername,
teamMembershipsUrl: TEAMS_MEMBERSHIP_URL
teamMembershipsUrl: TEAMS_MEMBERSHIP_URL,
topicID: '',
showEditButton: showEditButton
}
);
return teamJoinView.render();
).render();
};
it('can render itself', function () {
var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'));
var view = createJoinView(1, 'ma', teamModelData);
createMembershipData = function (username) {
return [
{
"user": {
"username": username,
"url": ACCOUNTS_API_URL + username
}
}
];
};
expect(view.$('.join-team').length).toEqual(1);
});
describe('JoinButton', function () {
it('can join team successfully', function () {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var teamId = 'teamA';
var teamName = 'teamAlpha';
var teamModelData = createTeamModelData(teamId, teamName, []);
var view = createJoinView(1, currentUsername, teamModelData);
// a get request will be sent to get user membership info
// because current user is not member of current team
AjaxHelpers.expectRequest(
requests,
'GET',
TEAMS_MEMBERSHIP_URL + '?' + $.param({
'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID
})
);
// current user is not a member of any team so we should see the Join Team button
AjaxHelpers.respondWithJson(requests, {"count": 0});
expect(view.$('.action.action-primary').length).toEqual(1);
// a post request will be sent to add current user to current team
view.$('.action.action-primary').click();
AjaxHelpers.expectRequest(
requests,
'POST',
TEAMS_MEMBERSHIP_URL,
$.param({'username': currentUsername, 'team_id': teamId})
);
AjaxHelpers.respondWithJson(requests, {});
// on success, team model will be fetched and
// join team view and team profile will be re-rendered
AjaxHelpers.expectRequest(
requests,
'GET',
createTeamsUrl(teamId)
);
AjaxHelpers.respondWithJson(
requests, createTeamModelData(teamId, teamName, createMembershipData(currentUsername))
);
// current user is now member of the current team then there should be no button and no message
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').length).toEqual(0);
});
beforeEach(function () {
setFixtures(
'<div class="teams-content"><div class="msg-content"><div class="copy"></div></div><div class="header-action-view"></div></div>'
);
});
it('shows already member message', function () {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var view = createJoinView(1, currentUsername, createTeamModelData('teamA', 'teamAlpha', []));
// a get request will be sent to get user membership info
// because current user is not member of current team
AjaxHelpers.expectRequest(
requests,
'GET',
TEAMS_MEMBERSHIP_URL + '?' + $.param({
'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID
})
);
// current user is a member of another team so we should see the correct message
AjaxHelpers.respondWithJson(requests, {"count": 1});
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').text().trim()).toBe(view.alreadyMemberMessage);
});
verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) {
var view = createHeaderActionsView(1, 'ma', createTeamModelData('teamA', 'teamAlpha', []));
if (joinTeam) {
// if we want the error to return when user try to join team, respond with no membership
AjaxHelpers.respondWithJson(requests, {"count": 0});
view.$('.action.action-primary').click();
}
AjaxHelpers.respondWithTextError(requests, 400, errorMessage);
expect($('.msg-content .copy').text().trim()).toBe(expectedMessage);
};
it('shows team full message', function () {
var requests = AjaxHelpers.requests(this);
var view = createJoinView(
1,
'ma1',
createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'))
);
// team has no space and current user is a not member of
// current team so we should see the correct message
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').text().trim()).toBe(view.teamFullMessage);
// there should be no request made
expect(requests.length).toBe(0);
});
it('can render itself', function () {
var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'));
var view = createHeaderActionsView(1, 'ma', teamModelData);
expect(view.$('.join-team').length).toEqual(1);
});
it('can join team successfully', function () {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var teamId = 'teamA';
var teamName = 'teamAlpha';
var teamModelData = createTeamModelData(teamId, teamName, []);
var view = createHeaderActionsView(1, currentUsername, teamModelData);
// a get request will be sent to get user membership info
// because current user is not member of current team
AjaxHelpers.expectRequest(
requests,
'GET',
TEAMS_MEMBERSHIP_URL + '?' + $.param({
'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID
})
);
// current user is not a member of any team so we should see the Join Team button
AjaxHelpers.respondWithJson(requests, {"count": 0});
expect(view.$('.action.action-primary').length).toEqual(1);
it('shows correct error message if user fails to join team', function () {
var requests = AjaxHelpers.requests(this);
// verify user_message
verifyErrorMessage(
requests,
JSON.stringify({'user_message': "Can't be made member"}),
"Can't be made member",
true
);
// verify generic error message
verifyErrorMessage(
requests,
'',
'An error occurred. Try again.',
true
);
// verify error message when json parsing succeeded but error message format is incorrect
verifyErrorMessage(
requests,
JSON.stringify({'blah': "Can't be made member"}),
'An error occurred. Try again.',
true
);
// a post request will be sent to add current user to current team
view.$('.action.action-primary').click();
AjaxHelpers.expectRequest(
requests,
'POST',
TEAMS_MEMBERSHIP_URL,
$.param({'username': currentUsername, 'team_id': teamId})
);
AjaxHelpers.respondWithJson(requests, {});
// on success, team model will be fetched and
// join team view and team profile will be re-rendered
AjaxHelpers.expectRequest(
requests,
'GET',
createTeamsUrl(teamId)
);
AjaxHelpers.respondWithJson(
requests, createTeamModelData(teamId, teamName, createMembershipData(currentUsername))
);
// current user is now member of the current team then there should be no button and no message
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').length).toEqual(0);
});
it('shows already member message', function () {
var requests = AjaxHelpers.requests(this);
var currentUsername = 'ma1';
var view = createHeaderActionsView(1, currentUsername, createTeamModelData('teamA', 'teamAlpha', []));
// a get request will be sent to get user membership info
// because current user is not member of current team
AjaxHelpers.expectRequest(
requests,
'GET',
TEAMS_MEMBERSHIP_URL + '?' + $.param({
'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID
})
);
// current user is a member of another team so we should see the correct message
AjaxHelpers.respondWithJson(requests, {"count": 1});
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').text().trim()).toBe(view.alreadyMemberMessage);
});
it('shows team full message', function () {
var requests = AjaxHelpers.requests(this);
var view = createHeaderActionsView(
1,
'ma1',
createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'))
);
// team has no space and current user is a not member of
// current team so we should see the correct message
expect(view.$('.action.action-primary').length).toEqual(0);
expect(view.$('.join-team-message').text().trim()).toBe(view.teamFullMessage);
// there should be no request made
expect(requests.length).toBe(0);
});
it('shows correct error message if user fails to join team', function () {
var requests = AjaxHelpers.requests(this);
// verify user_message
verifyErrorMessage(
requests,
JSON.stringify({'user_message': "Can't be made member"}),
"Can't be made member",
true
);
// verify generic error message
verifyErrorMessage(
requests,
'',
'An error occurred. Try again.',
true
);
// verify error message when json parsing succeeded but error message format is incorrect
verifyErrorMessage(
requests,
JSON.stringify({'blah': "Can't be made member"}),
'An error occurred. Try again.',
true
);
});
it('shows correct error message if initializing the view fails', function () {
// Rendering the view sometimes require fetching user's memberships. This may fail.
var requests = AjaxHelpers.requests(this);
// verify user_message
verifyErrorMessage(
requests,
JSON.stringify({'user_message': "Can't return user memberships"}),
"Can't return user memberships",
false
);
// verify generic error message
verifyErrorMessage(
requests,
'',
'An error occurred. Try again.',
false
);
});
});
it('shows correct error message if initializing the view fails', function () {
// Rendering the view sometimes require fetching user's memberships. This may fail.
var requests = AjaxHelpers.requests(this);
// verify user_message
verifyErrorMessage(
requests,
JSON.stringify({'user_message': "Can't return user memberships"}),
"Can't return user memberships",
false
);
// verify generic error message
verifyErrorMessage(
requests,
'',
'An error occurred. Try again.',
false
);
describe('EditButton', function() {
var teamModelData,
view,
createAndAssertView;
createAndAssertView = function(showEditButton) {
teamModelData = createTeamModelData('aveA', 'avengers', createMembershipData('ma'));
view = createHeaderActionsView(1, 'ma', teamModelData, showEditButton);
expect(view.$('.action-edit-team').length).toEqual(showEditButton ? 1 : 0);
};
it('renders when option showEditButton is true', function () {
createAndAssertView(true);
});
it('does not render when option showEditButton is false', function () {
createAndAssertView(false);
});
it("can navigate to correct url", function () {
spyOn(Backbone.history, 'navigate');
createAndAssertView(true);
var editButton = view.$('.action-edit-team');
expect(editButton.length).toEqual(1);
$(editButton).click();
expect(Backbone.history.navigate.calls[0].args[0]).toContain('/edit-team');
});
});
});
});
......@@ -14,9 +14,9 @@
maxTeamDescriptionLength: 300,
events: {
'click .action-primary': 'createTeam',
'submit form': 'createTeam',
'click .action-cancel': 'goBackToTopic'
'click .action-primary': 'createOrUpdateTeam',
'submit form': 'createOrUpdateTeam',
'click .action-cancel': 'cancelAndGoBack'
},
initialize: function(options) {
......@@ -27,12 +27,20 @@
this.teamsUrl = options.teamParams.teamsUrl;
this.languages = options.teamParams.languages;
this.countries = options.teamParams.countries;
this.primaryButtonTitle = options.primaryButtonTitle || 'Submit';
_.bindAll(this, 'goBackToTopic', 'createTeam');
this.teamModel = new TeamModel({});
this.teamModel.url = this.teamsUrl;
this.teamsDetailUrl = options.teamParams.teamsDetailUrl;
this.action = options.action;
_.bindAll(this, 'cancelAndGoBack', 'createOrUpdateTeam');
if (this.action === 'create') {
this.teamModel = new TeamModel({});
this.teamModel.url = this.teamsUrl;
this.primaryButtonTitle = 'Create';
} else if(this.action === 'edit' ) {
this.teamModel = options.model;
this.teamModel.url = this.teamsDetailUrl.replace('team_id', options.model.get('id')) + '?expand=user';
this.primaryButtonTitle = 'Update';
}
this.teamNameField = new FieldViews.TextFieldView({
model: this.teamModel,
......@@ -74,7 +82,11 @@
},
render: function() {
this.$el.html(_.template(editTeamTemplate)({primaryButtonTitle: this.primaryButtonTitle}));
this.$el.html(_.template(editTeamTemplate) ({
primaryButtonTitle: this.primaryButtonTitle,
action: this.action,
totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length
}));
this.set(this.teamNameField, '.team-required-fields');
this.set(this.teamDescriptionField, '.team-required-fields');
this.set(this.teamLanguageField, '.team-optional-fields');
......@@ -91,20 +103,28 @@
}
},
createTeam: function (event) {
createOrUpdateTeam: function (event) {
event.preventDefault();
var view = this,
teamLanguage = this.teamLanguageField.fieldValue(),
teamCountry = this.teamCountryField.fieldValue();
var data = {
course_id: this.courseID,
topic_id: this.topicID,
name: this.teamNameField.fieldValue(),
description: this.teamDescriptionField.fieldValue(),
language: _.isNull(teamLanguage) ? '' : teamLanguage,
country: _.isNull(teamCountry) ? '' : teamCountry
};
teamCountry = this.teamCountryField.fieldValue(),
data = {
name: this.teamNameField.fieldValue(),
description: this.teamDescriptionField.fieldValue(),
language: _.isNull(teamLanguage) ? '' : teamLanguage,
country: _.isNull(teamCountry) ? '' : teamCountry
},
saveOptions = {
wait: true
};
if (this.action === 'create') {
data.course_id = this.courseID;
data.topic_id = this.topicID;
} else if (this.action === 'edit' ) {
saveOptions.patch = true;
saveOptions.contentType = 'application/merge-patch+json';
}
var validationResult = this.validateTeamData(data);
if (validationResult.status === false) {
......@@ -112,10 +132,10 @@
return;
}
this.teamModel.save(data, { wait: true })
this.teamModel.save(data, saveOptions)
.done(function(result) {
view.teamEvents.trigger('teams:update', {
action: 'create',
action: view.action,
team: result
});
Backbone.history.navigate(
......@@ -186,8 +206,15 @@
}
},
goBackToTopic: function () {
Backbone.history.navigate('topics/' + this.topicID, {trigger: true});
cancelAndGoBack: function (event) {
event.preventDefault();
var url;
if (this.action === 'create') {
url = 'topics/' + this.topicID;
} else if (this.action === 'edit' ) {
url = 'teams/' + this.topicID + '/' + this.teamModel.get('id');
}
Backbone.history.navigate(url, {trigger: true});
}
});
});
......
......@@ -5,8 +5,8 @@
'underscore',
'gettext',
'teams/js/views/team_utils',
'text!teams/templates/team-join.underscore'],
function (Backbone, _, gettext, TeamUtils, teamJoinTemplate) {
'text!teams/templates/team-profile-header-actions.underscore'],
function (Backbone, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
return Backbone.View.extend({
errorMessage: gettext("An error occurred. Try again."),
......@@ -14,31 +14,34 @@
teamFullMessage: gettext("This team is full."),
events: {
"click .action-primary": "joinTeam"
"click .action-primary": "joinTeam",
"click .action-edit-team": "editTeam"
},
initialize: function(options) {
this.teamEvents = options.teamEvents;
this.template = _.template(teamJoinTemplate);
this.template = _.template(teamProfileHeaderActionsTemplate);
this.courseID = options.courseID;
this.maxTeamSize = options.maxTeamSize;
this.currentUsername = options.currentUsername;
this.teamMembershipsUrl = options.teamMembershipsUrl;
_.bindAll(this, 'render', 'joinTeam', 'getUserTeamInfo');
this.showEditButton = options.showEditButton;
this.topicID = options.topicID;
_.bindAll(this, 'render', 'joinTeam','editTeam', 'getUserTeamInfo');
this.listenTo(this.model, "change", this.render);
},
render: function() {
var view = this,
message,
showButton,
showJoinButton,
teamHasSpace;
this.getUserTeamInfo(this.currentUsername, view.maxTeamSize).done(function (info) {
teamHasSpace = info.teamHasSpace;
// if user is the member of current team then we wouldn't show anything
if (!info.memberOfCurrentTeam) {
showButton = !info.alreadyMember && teamHasSpace;
showJoinButton = !info.alreadyMember && teamHasSpace;
if (info.alreadyMember) {
message = info.memberOfCurrentTeam ? '' : view.alreadyMemberMessage;
......@@ -47,7 +50,11 @@
}
}
view.$el.html(view.template({showButton: showButton, message: message}));
view.$el.html(view.template({
showJoinButton: showJoinButton,
message: message,
showEditButton: view.showEditButton
}));
});
return view;
},
......@@ -108,6 +115,10 @@
}
return deferred.promise();
},
editTeam: function (event) {
event.preventDefault();
Backbone.history.navigate('topics/' + this.topicID + '/' + this.model.get('id') +'/edit-team', {trigger: true});
}
});
});
......
......@@ -17,12 +17,12 @@
'teams/js/views/my_teams',
'teams/js/views/topic_teams',
'teams/js/views/edit_team',
'teams/js/views/team_join',
'teams/js/views/team_profile_header_actions',
'text!teams/templates/teams_tab.underscore'],
function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView,
TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection,
TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView,
TeamJoinView, teamsTemplate) {
TeamProfileHeaderActionsView, teamsTemplate) {
var TeamsHeaderModel = HeaderModel.extend({
initialize: function (attributes) {
_.extend(this.defaults, {nav_aria_label: gettext('teams')});
......@@ -52,6 +52,7 @@
this.topics = options.topics;
this.topicUrl = options.topicUrl;
this.teamsUrl = options.teamsUrl;
this.teamsDetailUrl = options.teamsDetailUrl;
this.teamMembershipsUrl = options.teamMembershipsUrl;
this.teamMembershipDetailUrl = options.teamMembershipDetailUrl;
this.maxTeamSize = options.maxTeamSize;
......@@ -74,6 +75,7 @@
}, this)],
['topics/:topic_id(/)', _.bind(this.browseTopic, this)],
['topics/:topic_id/create-team(/)', _.bind(this.newTeam, this)],
['topics/:topic_id/:team_id/edit-team(/)', _.bind(this.editTeam, this)],
['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)],
[new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)],
[new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)]
......@@ -208,10 +210,9 @@
})
}),
main: new TeamEditView({
action: 'create',
teamEvents: self.teamEvents,
tagName: 'create-new-team',
teamParams: teamsView.main.teamParams,
primaryButtonTitle: 'Create'
teamParams: teamsView.main.teamParams
})
});
self.render();
......@@ -219,6 +220,44 @@
},
/**
* Render the edit team form.
*/
editTeam: function (topicID, teamID) {
var self = this,
editViewWithHeader;
this.getTopic(topicID).done(function (topic) {
self.getTeam(teamID, false).done(function(team) {
var view = new TeamEditView({
action: 'edit',
teamEvents: self.teamEvents,
teamParams: {
courseID: self.courseID,
topicID: topic.get('id'),
teamsUrl: self.teamsUrl,
topicName: topic.get('name'),
languages: self.languages,
countries: self.countries,
teamsDetailUrl: self.teamsDetailUrl
},
model: team
});
editViewWithHeader = self.createViewWithHeader({
mainView: view,
subject: {
name: gettext("Edit Team"),
description: gettext("If you make significant changes, make sure you notify members of the team before making these changes.")
},
parentTeam: team,
parentTopic: topic
}
);
self.mainView = editViewWithHeader;
self.render();
});
});
},
/**
* Return a promise for the TeamsView for the given topic ID.
*/
getTeamsView: function (topicID) {
......@@ -227,7 +266,7 @@
var self = this,
router = this.router,
deferred = $.Deferred();
if (this.teamsCollection && this.teamsCollection.topic_id === topicID) {
if (this.teamsCollection && this.teamsCollection.topic_id === topicID && this.teamsView) {
deferred.resolve(this.teamsView);
} else {
this.getTopic(topicID)
......@@ -254,7 +293,8 @@
teamsUrl: self.teamsUrl,
topicName: topic.get('name'),
languages: self.languages,
countries: self.countries
countries: self.countries,
teamsDetailUrl: self.teamsDetailUrl
}
});
deferred.resolve(
......@@ -311,14 +351,17 @@
teamMembershipDetailUrl: self.teamMembershipDetailUrl,
setFocusToHeaderFunc: self.setFocusToHeader
});
var teamJoinView = new TeamJoinView({
var TeamProfileActionsView = new TeamProfileHeaderActionsView({
teamEvents: self.teamEvents,
courseID: courseID,
model: team,
courseID: courseID,
model: team,
teamsUrl: self.teamsUrl,
maxTeamSize: self.maxTeamSize,
currentUsername: self.userInfo.username,
teamMembershipsUrl: self.teamMembershipsUrl
teamMembershipsUrl: self.teamMembershipsUrl,
topicID: topicID,
showEditButton: self.userInfo.privileged || self.userInfo.staff
});
deferred.resolve(
self.createViewWithHeader(
......@@ -326,7 +369,7 @@
mainView: view,
subject: team,
parentTopic: topic,
headerActionsView: teamJoinView
headerActionsView: TeamProfileActionsView
}
)
);
......@@ -337,7 +380,8 @@
createViewWithHeader: function (options) {
var router = this.router,
breadcrumbs, headerView;
breadcrumbs, headerView,
viewDescription, viewTitle;
breadcrumbs = [{
title: gettext('All Topics'),
url: '#browse'
......@@ -348,10 +392,25 @@
url: '#topics/' + options.parentTopic.id
});
}
if (options.parentTeam) {
breadcrumbs.push({
title: options.parentTeam.get('name'),
url: '#teams/' + options.parentTopic.id + '/' + options.parentTeam.id
});
}
if (options.subject instanceof Backbone.Model) {
viewDescription = options.subject.get('description');
viewTitle = options.subject.get('name');
} else if (options.subject) {
viewDescription = options.subject.description;
viewTitle = options.subject.name;
}
headerView = new HeaderView({
model: new TeamsHeaderModel({
description: options.subject.get('description'),
title: options.subject.get('name'),
description: viewDescription,
title: viewTitle,
breadcrumbs: breadcrumbs
}),
headerActionsView: options.headerActionsView,
......
<form>
<div class="create-team wrapper-msg is-incontext urgency-low warning is-hidden" tabindex="-1">
<div class="create-team wrapper-msg is-incontext urgency-low error is-hidden" tabindex="-1">
<div class="msg">
<div class="msg-content">
<h3 class="title"><%- gettext("Your team could not be created!") %></h3>
<h3 class="title">
<% if (action === 'create') { %>
<%- gettext("Your team could not be created.") %>
<% } else if (action === 'edit') { %>
<%- gettext("Your team could not be updated.") %>
<% } %>
</h3>
<span class="screen-reader-message sr"></span>
<div class="copy">
<p></p>
......@@ -11,15 +17,16 @@
</div>
</div>
<div class="form-instructions create-team-instructions">
<p class="copy">
<%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p>
</div>
<% if (action === 'create') { %>
<div class="form-instructions create-team-instructions">
<p class="copy">
<%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p>
</div>
<% } %>
<div class="team-edit-fields">
<div class="team-required-fields">
</div>
<div class="team-optional-fields">
<fieldset>
<div class="u-field u-field-optional_description">
......@@ -38,7 +45,7 @@
<button class="action action-primary">
<%=
interpolate_text(
_.escape(gettext("{primaryButtonTitle} {span_start}a new team{span_end}")),
_.escape(gettext("{primaryButtonTitle} {span_start}a team{span_end}")),
{
'primaryButtonTitle': primaryButtonTitle, 'span_start': '<span class="sr">', 'span_end': '</span>'
}
......@@ -48,9 +55,10 @@
<button class="action action-cancel">
<%=
interpolate_text(
_.escape(gettext("Cancel {span_start}a new team{span_end}")),
_.escape(gettext("Cancel {span_start} {action} team {span_end}")),
{
'span_start': '<span class="sr">', 'span_end': '</span>'
'span_start': '<span class="sr">', 'span_end': '</span>',
'action': action === 'create' ? 'creating' : 'updating'
}
)
%>
......
<div class="join-team form-actions">
<% if (showButton) {%>
<% if (showJoinButton) {%>
<button class="action action-primary">
<%- gettext("Join Team") %>
</button>
<% } else if (message) { %>
<p class="join-team-message"><%- message %></p>
<% } %>
<% if (showEditButton) { %>
<button class="btn btn-secondary action-edit-team"><%- gettext("Edit Team") %></button>
<% } %>
</div>
......@@ -50,7 +50,7 @@
<% if (isMember) { %>
<div class="leave-team">
<button class="btn btn-link btn-base btn-secondary leave-team-link"><%- gettext("Leave Team") %></button>
<button class="btn btn-link leave-team-link"><%- gettext("Leave Team") %></button>
</div>
<div class="divider-lv1"></div>
......
......@@ -39,6 +39,7 @@
topicUrl: '${ topic_url }',
topicsUrl: '${ topics_url }',
teamsUrl: '${ teams_url }',
teamsDetailUrl: '${ teams_detail_url }',
teamMembershipsUrl: '${ team_memberships_url }',
teamMembershipDetailUrl: '${ team_membership_detail_url }',
maxTeamSize: ${ course.teams_max_size },
......
......@@ -117,6 +117,7 @@ class TeamsDashboardView(View):
),
"topics_url": reverse('topics_list', request=request),
"teams_url": reverse('teams_list', request=request),
"teams_detail_url": reverse('teams_detail', args=['team_id']),
"team_memberships_url": reverse('team_membership_list', request=request),
"team_membership_detail_url": reverse('team_membership_detail', args=['team_id', user.username]),
"languages": settings.ALL_LANGUAGES,
......
......@@ -802,7 +802,7 @@
'lms/include/teams/js/spec/views/topic_card_spec.js',
'lms/include/teams/js/spec/views/topic_teams_spec.js',
'lms/include/teams/js/spec/views/topics_spec.js',
'lms/include/teams/js/spec/views/team_join_spec.js'
'lms/include/teams/js/spec/views/team_profile_header_actions_spec.js'
]);
}).call(this, requirejs, define);
......@@ -404,7 +404,7 @@
}
}
.btn-link {
.btn-secondary {
@extend %btn-pl-secondary-base;
background-image: none;
......@@ -416,3 +416,24 @@
color: $link-color;
}
}
.btn-link {
@extend %shame-link-text;
// reset of inherited buttons
border-radius: 0;
border-color: $transparent;
padding: 1px;
background: $transparent;
background-image: none;
@extend %t-action3;
@extend %t-strong;
&:focus,
&:hover {
background-image: none !important;
background-color: $transparent !important;
box-shadow: none !important;
}
}
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