create a new team
TNL-1899
This commit is contained in:
@@ -80,6 +80,15 @@ class FieldsMixin(object):
|
||||
query = self.q(css='.u-field-{} .u-field-message'.format(field_id))
|
||||
return query.text[0] if query.present else None
|
||||
|
||||
def message_for_textarea_field(self, field_id):
|
||||
"""
|
||||
Return the current message for textarea field.
|
||||
"""
|
||||
self.wait_for_field(field_id)
|
||||
|
||||
query = self.q(css='.u-field-{} .u-field-message-help'.format(field_id))
|
||||
return query.text[0] if query.present else None
|
||||
|
||||
def wait_for_message(self, field_id, message):
|
||||
"""
|
||||
Wait for a message to appear in a field.
|
||||
@@ -229,3 +238,10 @@ class FieldsMixin(object):
|
||||
query = self.q(css='.u-field-{} a'.format(field_id))
|
||||
if query.present:
|
||||
query.first.click()
|
||||
|
||||
def error_for_field(self, field_id):
|
||||
"""
|
||||
Returns bool based on the highlighted border for field.
|
||||
"""
|
||||
query = self.q(css='.u-field-{}.error'.format(field_id))
|
||||
return True if query.present else False
|
||||
|
||||
@@ -6,11 +6,14 @@ Teams pages.
|
||||
from .course_page import CoursePage
|
||||
from ..common.paging import PaginatedUIMixin
|
||||
|
||||
from .fields import FieldsMixin
|
||||
|
||||
|
||||
TOPIC_CARD_CSS = 'div.wrapper-card-core'
|
||||
BROWSE_BUTTON_CSS = 'a.nav-item[data-index="1"]'
|
||||
TEAMS_LINK_CSS = '.action-view'
|
||||
TEAMS_HEADER_CSS = '.teams-header'
|
||||
CREATE_TEAM_LINK_CSS = '.create-team'
|
||||
|
||||
|
||||
class TeamsPage(CoursePage):
|
||||
@@ -84,6 +87,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""Check if we're on the teams list page for a particular topic."""
|
||||
self.wait_for_element_presence('.team-actions', 'Wait for the bottom links to be present')
|
||||
has_correct_url = self.url.endswith(self.url_path)
|
||||
teams_list_view_present = self.q(css='.teams-main').present
|
||||
return has_correct_url and teams_list_view_present
|
||||
@@ -102,3 +106,76 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
|
||||
def team_cards(self):
|
||||
"""Get all the team cards on the page."""
|
||||
return self.q(css='.team-card')
|
||||
|
||||
def click_create_team_link(self):
|
||||
""" Click on create team link."""
|
||||
query = self.q(css=CREATE_TEAM_LINK_CSS)
|
||||
if query.present:
|
||||
query.first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def click_search_team_link(self):
|
||||
""" Click on create team link."""
|
||||
query = self.q(css='.search-team-descriptions')
|
||||
if query.present:
|
||||
query.first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def click_browse_all_teams_link(self):
|
||||
""" Click on browse team link."""
|
||||
query = self.q(css='.browse-teams')
|
||||
if query.present:
|
||||
query.first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
|
||||
class CreateTeamPage(CoursePage, FieldsMixin):
|
||||
"""
|
||||
Create team page.
|
||||
"""
|
||||
def __init__(self, browser, course_id, topic):
|
||||
"""
|
||||
Set up `self.url_path` on instantiation, since it dynamically
|
||||
reflects the current topic. Note that `topic` is a dict
|
||||
representation of a topic following the same convention as a
|
||||
course module's topic.
|
||||
"""
|
||||
super(CreateTeamPage, self).__init__(browser, course_id)
|
||||
self.topic = topic
|
||||
self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id'])
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""Check if we're on the create team page for a particular topic."""
|
||||
has_correct_url = self.url.endswith(self.url_path)
|
||||
teams_create_view_present = self.q(css='.team-edit-fields').present
|
||||
return has_correct_url and teams_create_view_present
|
||||
|
||||
@property
|
||||
def header_page_name(self):
|
||||
"""Get the page name displayed by the page header"""
|
||||
return self.q(css='.page-header .page-title')[0].text
|
||||
|
||||
@property
|
||||
def header_page_description(self):
|
||||
"""Get the page description displayed by the page header"""
|
||||
return self.q(css='.page-header .page-description')[0].text
|
||||
|
||||
@property
|
||||
def header_page_breadcrumbs(self):
|
||||
"""Get the page breadcrumb text displayed by the page header"""
|
||||
return self.q(css='.page-header .breadcrumbs')[0].text
|
||||
|
||||
@property
|
||||
def validation_message_text(self):
|
||||
"""Get the error message text"""
|
||||
return self.q(css='.create-team.wrapper-msg .copy')[0].text
|
||||
|
||||
def submit_form(self):
|
||||
"""Click on create team button"""
|
||||
self.q(css='.create-team .action-primary').first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def cancel_team(self):
|
||||
"""Click on cancel team button"""
|
||||
self.q(css='.create-team .action-cancel').first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
@@ -6,7 +6,7 @@ import json
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.lms.teams import TeamsPage, BrowseTopicsPage, BrowseTeamsPage
|
||||
from ...pages.lms.teams import TeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateTeamPage
|
||||
from ...fixtures import LMS_BASE_URL
|
||||
from ...fixtures.course import CourseFixture
|
||||
from ...pages.lms.tab_nav import TabNavPage
|
||||
@@ -270,6 +270,7 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
|
||||
self.topic = {u"name": u"Example Topic", u"id": "example_topic", u"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.topics_page = BrowseTopicsPage(self.browser, self.course_id)
|
||||
|
||||
def create_teams(self, num_teams):
|
||||
"""Create `num_teams` teams belonging to `self.topic`."""
|
||||
@@ -455,3 +456,196 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
|
||||
self.browse_teams_page.team_cards[0].find_element_by_css_selector('.member-count').text,
|
||||
'1 / 10 Members'
|
||||
)
|
||||
|
||||
def test_navigation_links(self):
|
||||
"""
|
||||
Scenario: User should be able to navigate to "browse all teams" and "search team description" links.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
containing one team
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see the link to "browse all team"
|
||||
And I should navigate to that link
|
||||
And I see the relevant page loaded
|
||||
And I should see the link to "search teams"
|
||||
And I should navigate to that link
|
||||
And I see the relevant page loaded
|
||||
"""
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
|
||||
self.browse_teams_page.click_browse_all_teams_link()
|
||||
self.assertTrue(self.topics_page.is_browser_on_page())
|
||||
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.browse_teams_page.click_search_team_link()
|
||||
# TODO Add search page expectation once that implemented.
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class CreateTeamTest(TeamsTabBase):
|
||||
"""
|
||||
Tests for creating a new Team within a Topic on the Teams page.
|
||||
"""
|
||||
|
||||
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'
|
||||
|
||||
def verify_page_header(self):
|
||||
"""
|
||||
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'])
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
self.create_team_page.value_for_textarea_field(
|
||||
field_id='description',
|
||||
value='The Avengers are a fictional team of superheroes.'
|
||||
)
|
||||
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')
|
||||
|
||||
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.assertEqual(
|
||||
self.create_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'),
|
||||
'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'),
|
||||
'The country that team members primarily identify with.'
|
||||
)
|
||||
self.assertEqual(
|
||||
self.create_team_page.message_for_field('language'),
|
||||
'The language that team members primarily use to communicate with each other.'
|
||||
)
|
||||
|
||||
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.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Create Team page for that topic
|
||||
Then I should see the Create Team header and form
|
||||
And When I click create team button without filling required fields
|
||||
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.assertEqual(
|
||||
self.create_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'))
|
||||
|
||||
def test_user_can_see_error_message_for_incorrect_data(self):
|
||||
"""
|
||||
Scenario: The user should be able to see error message in case of increasing length for required fields.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Create Team page for that topic
|
||||
Then I should see the Create Team header and form
|
||||
When I add text > than 255 characters for name field
|
||||
And I click Create button
|
||||
Then I should see the error message for exceeding length.
|
||||
"""
|
||||
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(
|
||||
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 '
|
||||
'audience, some at no charge. It also conducts research into learning based on how '
|
||||
'people use its platform. EdX was created for students and institutions that seek to'
|
||||
'transform themselves through cutting-edge technologies, innovative pedagogy, and '
|
||||
'rigorous courses. More than 70 schools, nonprofits, corporations, and international'
|
||||
'organizations offer or plan to offer courses on the edX website. As of 22 October 2014,'
|
||||
'edX has more than 4 million users taking more than 500 courses online.'
|
||||
)
|
||||
self.create_team_page.submit_form()
|
||||
|
||||
self.assertEqual(
|
||||
self.create_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'))
|
||||
|
||||
def test_user_can_create_new_team_successfully(self):
|
||||
"""
|
||||
Scenario: The user should be able to create new team.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Create Team page for that topic
|
||||
Then I should see the Create Team header and form
|
||||
When I fill all the fields present with appropriate data
|
||||
And I click Create button
|
||||
Then I should see teams list page with newly created team.
|
||||
"""
|
||||
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.fill_create_form()
|
||||
self.create_team_page.submit_form()
|
||||
|
||||
self.assertTrue(self.browse_teams_page.is_browser_on_page())
|
||||
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 1 out of 1 total')
|
||||
# Verify the newly created team content.
|
||||
team_card = self.browse_teams_page.team_cards.results[0]
|
||||
self.assertEqual(team_card.find_element_by_css_selector('.card-title').text, self.team_name)
|
||||
self.assertEqual(
|
||||
team_card.find_element_by_css_selector('.card-description').text,
|
||||
'The Avengers are a fictional team of superheroes.'
|
||||
)
|
||||
|
||||
def test_user_can_cancel_the_team_creation(self):
|
||||
"""
|
||||
Scenario: The user should be able to cancel the creation of new team.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Create Team page for that topic
|
||||
Then I should see the Create Team header and form
|
||||
When I click Cancel button
|
||||
Then I should see teams list page without any new team.
|
||||
"""
|
||||
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.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')
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
define(['backbone'], function (Backbone) {
|
||||
var Team = Backbone.Model.extend({
|
||||
defaults: {
|
||||
id: '',
|
||||
id: null,
|
||||
name: '',
|
||||
is_active: null,
|
||||
course_id: '',
|
||||
|
||||
170
lms/djangoapps/teams/static/teams/js/spec/edit_team_spec.js
Normal file
170
lms/djangoapps/teams/static/teams/js/spec/edit_team_spec.js
Normal file
@@ -0,0 +1,170 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'teams/js/views/edit_team'
|
||||
], function ($, _, Backbone, AjaxHelpers, TeamEditView) {
|
||||
'use strict';
|
||||
|
||||
describe('EditTeam', function () {
|
||||
var teamEditView,
|
||||
teamsUrl = '/api/team/v0/teams/',
|
||||
teamsData = {
|
||||
id: null,
|
||||
name: "TeamName",
|
||||
is_active: null,
|
||||
course_id: "a/b/c",
|
||||
topic_id: "awesomeness",
|
||||
date_created: "",
|
||||
description: "TeamDescription",
|
||||
country: "c",
|
||||
language: "a",
|
||||
membership: []
|
||||
},
|
||||
verifyValidation = function (requests, fieldsData) {
|
||||
_.each(fieldsData, function (fieldData) {
|
||||
teamEditView.$(fieldData[0]).val(fieldData[1]);
|
||||
});
|
||||
|
||||
teamEditView.$('.create-team.form-actions .action-primary').click();
|
||||
|
||||
var message = teamEditView.$('.wrapper-msg');
|
||||
expect(message.hasClass('is-hidden')).toBeFalsy();
|
||||
expect(message.find('.title').text().trim()).toBe("Your team could not be created!");
|
||||
expect(message.find('.copy').text().trim()).toBe(
|
||||
"Check the highlighted fields below and try again."
|
||||
);
|
||||
|
||||
_.each(fieldsData, function (fieldData) {
|
||||
if(fieldData[2] === 'error') {
|
||||
expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(1);
|
||||
} else if(fieldData[2] === 'success') {
|
||||
expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
expect(requests.length).toBe(0);
|
||||
},
|
||||
expectContent = function (selector, text) {
|
||||
expect(teamEditView.$(selector).text().trim()).toBe(text);
|
||||
},
|
||||
verifyDropdownData = function (selector, expectedItems) {
|
||||
var options = teamEditView.$(selector)[0].options;
|
||||
var renderedItems = $.map(options, function( elem ) {
|
||||
return [[elem.value, elem.text]];
|
||||
});
|
||||
for (var i = 0; i < expectedItems.length; i++) {
|
||||
expect(renderedItems).toContain(expectedItems[i]);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
setFixtures('<div class="teams-content"></div>');
|
||||
spyOn(Backbone.history, 'navigate');
|
||||
teamEditView = new TeamEditView({
|
||||
el: $('.teams-content'),
|
||||
teamParams: {
|
||||
teamsUrl: teamsUrl,
|
||||
courseId: "a/b/c",
|
||||
topicId: 'awesomeness',
|
||||
topicName: 'Awesomeness',
|
||||
languages: [['a', 'aaa'], ['b', 'bbb']],
|
||||
countries: [['c', 'ccc'], ['d', 'ddd']]
|
||||
}
|
||||
}).render();
|
||||
});
|
||||
|
||||
it('can render itself correctly', function () {
|
||||
var fieldClasses = [
|
||||
'.u-field-name',
|
||||
'.u-field-description',
|
||||
'.u-field-optional_description',
|
||||
'.u-field-language',
|
||||
'.u-field-country'
|
||||
];
|
||||
|
||||
_.each(fieldClasses, function (fieldClass) {
|
||||
expect(teamEditView.$el.find(fieldClass).length).toBe(1);
|
||||
});
|
||||
|
||||
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.$('.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.$('.create-team.form-actions .action-primary').click();
|
||||
AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData);
|
||||
AjaxHelpers.respondWithJson(requests, teamsData);
|
||||
|
||||
expect(teamEditView.$('.create-team.wrapper-msg .copy').text().trim().length).toBe(0);
|
||||
expect(Backbone.history.navigate.calls[0].args).toContain('topics/awesomeness');
|
||||
});
|
||||
|
||||
it('shows validation error message when field is empty', function () {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
verifyValidation(requests, [
|
||||
['.u-field-name input', 'Name', 'success'],
|
||||
['.u-field-textarea textarea', '', 'error']
|
||||
]);
|
||||
teamEditView.render();
|
||||
verifyValidation(requests, [
|
||||
['.u-field-name input', '', 'error'],
|
||||
['.u-field-textarea textarea', 'description', 'success']
|
||||
]);
|
||||
teamEditView.render();
|
||||
verifyValidation(requests, [
|
||||
['.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);
|
||||
var teamName = new Array(500 + 1).join( '$' );
|
||||
var teamDescription = new Array(500 + 1).join( '$' );
|
||||
|
||||
verifyValidation(requests, [
|
||||
['.u-field-name input', teamName, 'error'],
|
||||
['.u-field-textarea textarea', 'description', 'success']
|
||||
]);
|
||||
teamEditView.render();
|
||||
verifyValidation(requests, [
|
||||
['.u-field-name input', 'name', 'success'],
|
||||
['.u-field-textarea textarea', teamDescription, 'error']
|
||||
]);
|
||||
teamEditView.render();
|
||||
verifyValidation(requests, [
|
||||
['.u-field-name input', teamName, 'error'],
|
||||
['.u-field-textarea textarea', teamDescription, 'error']
|
||||
]);
|
||||
});
|
||||
|
||||
it("shows an error message for HTTP 500", function () {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
|
||||
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.");
|
||||
});
|
||||
|
||||
it("changes route on cancel click", function () {
|
||||
teamEditView.$('.create-team.form-actions .action-cancel').click();
|
||||
expect(Backbone.history.navigate.calls[0].args).toContain('topics/awesomeness');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
define([
|
||||
'jquery',
|
||||
'backbone',
|
||||
'teams/js/views/team_actions'
|
||||
], function ($, Backbone, TeamActionsView) {
|
||||
'use strict';
|
||||
|
||||
describe('TeamActions', function () {
|
||||
var teamActionsView;
|
||||
|
||||
beforeEach(function () {
|
||||
setFixtures('<div class="teams-content"></div>');
|
||||
spyOn(Backbone.history, 'navigate');
|
||||
teamActionsView = new TeamActionsView({
|
||||
el: $('.teams-content'),
|
||||
teamParams: {topicId: 'awesomeness'}
|
||||
}).render();
|
||||
});
|
||||
|
||||
it('can render itself correctly', function () {
|
||||
expect(teamActionsView.$('.title').text()).toBe('Are you having trouble finding a team to join?');
|
||||
expect(teamActionsView.$('.copy').text()).toBe(
|
||||
"Try browsing all teams or searching team descriptions. If you " +
|
||||
"still can't find a team to join, create a new team in this topic."
|
||||
);
|
||||
});
|
||||
|
||||
it('can navigate to correct routes', function () {
|
||||
teamActionsView.$('a.browse-teams').click();
|
||||
expect(Backbone.history.navigate.calls[0].args).toContain('browse');
|
||||
|
||||
teamActionsView.$('a.search-team-descriptions').click();
|
||||
// TODO! Should be updated once team description search feature is available
|
||||
expect(Backbone.history.navigate.calls[1].args).toContain('browse');
|
||||
|
||||
teamActionsView.$('a.create-team').click();
|
||||
expect(Backbone.history.navigate.calls[2].args).toContain('topics/awesomeness/create-team');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
define([
|
||||
'teams/js/collections/team', 'teams/js/views/teams'
|
||||
], function (TeamCollection, TeamsView) {
|
||||
'backbone', 'teams/js/collections/team', 'teams/js/views/teams'
|
||||
], function (Backbone, TeamCollection, TeamsView) {
|
||||
'use strict';
|
||||
describe('TeamsView', function () {
|
||||
var teamsView, teamCollection, initialTeams,
|
||||
@@ -32,7 +32,8 @@ define([
|
||||
);
|
||||
teamsView = new TeamsView({
|
||||
el: '.teams-container',
|
||||
collection: teamCollection
|
||||
collection: teamCollection,
|
||||
teamParams: {}
|
||||
}).render();
|
||||
});
|
||||
|
||||
|
||||
192
lms/djangoapps/teams/static/teams/js/views/edit_team.js
Normal file
192
lms/djangoapps/teams/static/teams/js/views/edit_team.js
Normal file
@@ -0,0 +1,192 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
|
||||
define(['backbone',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/views/fields',
|
||||
'teams/js/models/team',
|
||||
'text!teams/templates/edit-team.underscore'],
|
||||
function (Backbone, _, gettext, FieldViews, TeamModel, edit_team_template) {
|
||||
return Backbone.View.extend({
|
||||
|
||||
maxTeamNameLength: 255,
|
||||
maxTeamDescriptionLength: 300,
|
||||
|
||||
events: {
|
||||
"click .action-primary": "createTeam",
|
||||
"click .action-cancel": "goBackToTopic"
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.courseId = options.teamParams.courseId;
|
||||
this.teamsUrl = options.teamParams.teamsUrl;
|
||||
this.topicId = options.teamParams.topicId;
|
||||
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.teamNameField = new FieldViews.TextFieldView({
|
||||
model: this.teamModel,
|
||||
title: gettext("Team Name (Required) *"),
|
||||
valueAttribute: 'name',
|
||||
helpMessage: gettext("A name that identifies your team (maximum 255 characters).")
|
||||
});
|
||||
|
||||
this.teamDescriptionField = new FieldViews.TextareaFieldView({
|
||||
model: this.teamModel,
|
||||
title: gettext("Team Description (Required) *"),
|
||||
valueAttribute: 'description',
|
||||
editable: 'always',
|
||||
showMessages: false,
|
||||
helpMessage: gettext("A short description of the team to help other learners understand the goals or direction of the team (maximum 300 characters).")
|
||||
});
|
||||
|
||||
this.optionalDescriptionField = new FieldViews.ReadonlyFieldView({
|
||||
model: this.teamModel,
|
||||
title: gettext("Optional Characteristics"),
|
||||
valueAttribute: 'optional_description',
|
||||
helpMessage: gettext("Help other learners decide whether to join your team by specifying some characteristics for your team. Choose carefully, because fewer people might be interested in joining your team if it seems too restrictive.")
|
||||
});
|
||||
|
||||
this.teamLanguageField = new FieldViews.DropdownFieldView({
|
||||
model: this.teamModel,
|
||||
title: gettext("Language"),
|
||||
valueAttribute: 'language',
|
||||
required: false,
|
||||
showMessages: false,
|
||||
titleIconName: 'fa-comment-o',
|
||||
options: this.languages,
|
||||
helpMessage: gettext("The language that team members primarily use to communicate with each other.")
|
||||
});
|
||||
|
||||
this.teamCountryField = new FieldViews.DropdownFieldView({
|
||||
model: this.teamModel,
|
||||
title: gettext('Country'),
|
||||
valueAttribute: 'country',
|
||||
required: false,
|
||||
showMessages: false,
|
||||
titleIconName: 'fa-globe',
|
||||
options: this.countries,
|
||||
helpMessage: gettext("The country that team members primarily identify with.")
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$el.html(_.template(edit_team_template)({primaryButtonTitle: this.primaryButtonTitle}));
|
||||
this.set(this.teamNameField, '.team-required-fields');
|
||||
this.set(this.teamDescriptionField, '.team-required-fields');
|
||||
this.set(this.optionalDescriptionField, '.team-optional-fields');
|
||||
this.set(this.teamLanguageField, '.team-optional-fields');
|
||||
this.set(this.teamCountryField, '.team-optional-fields');
|
||||
return this;
|
||||
},
|
||||
|
||||
set: function(view, selector) {
|
||||
var viewEl = view.$el;
|
||||
if (this.$(selector).has(viewEl).length) {
|
||||
view.render().setElement(viewEl);
|
||||
} else {
|
||||
this.$(selector).append(view.render().$el);
|
||||
}
|
||||
},
|
||||
|
||||
createTeam: function () {
|
||||
var teamName = this.teamNameField.fieldValue();
|
||||
var teamDescription = this.teamDescriptionField.fieldValue();
|
||||
var teamLanguage = this.teamLanguageField.fieldValue();
|
||||
var teamCountry = this.teamCountryField.fieldValue();
|
||||
|
||||
var data = {
|
||||
course_id: this.courseId,
|
||||
topic_id: this.topicId,
|
||||
name: teamName,
|
||||
description: teamDescription,
|
||||
language: _.isNull(teamLanguage) ? '' : teamLanguage,
|
||||
country: _.isNull(teamCountry) ? '' : teamCountry
|
||||
};
|
||||
|
||||
var validationResult = this.validateTeamData(data);
|
||||
if (validationResult.status === false) {
|
||||
this.showMessage(validationResult.message, validationResult.srMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
var view = this;
|
||||
var options = {
|
||||
wait: true,
|
||||
success: function () {
|
||||
view.goBackToTopic();
|
||||
},
|
||||
error: function () {
|
||||
var message = gettext('An error occurred. Please try again.');
|
||||
view.showMessage(message, message);
|
||||
}
|
||||
};
|
||||
this.teamModel.save(data, options);
|
||||
},
|
||||
|
||||
validateTeamData: function (data) {
|
||||
var status = true,
|
||||
message = gettext("Check the highlighted fields below and try again.");
|
||||
var srMessages = [];
|
||||
|
||||
this.teamNameField.unhighlightField();
|
||||
this.teamDescriptionField.unhighlightField();
|
||||
|
||||
if (_.isEmpty(data.name.trim()) ) {
|
||||
status = false;
|
||||
this.teamNameField.highlightFieldOnError();
|
||||
srMessages.push(
|
||||
gettext("Enter team name.")
|
||||
);
|
||||
} else if (data.name.length > this.maxTeamNameLength) {
|
||||
status = false;
|
||||
this.teamNameField.highlightFieldOnError();
|
||||
srMessages.push(
|
||||
gettext("Team name cannot have more than 255 characters.")
|
||||
);
|
||||
}
|
||||
|
||||
if (_.isEmpty(data.description.trim()) ) {
|
||||
status = false;
|
||||
this.teamDescriptionField.highlightFieldOnError();
|
||||
srMessages.push(
|
||||
gettext("Enter team description.")
|
||||
);
|
||||
} else if (data.description.length > this.maxTeamDescriptionLength) {
|
||||
status = false;
|
||||
this.teamDescriptionField.highlightFieldOnError();
|
||||
srMessages.push(
|
||||
gettext("Team description cannot have more than 300 characters.")
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
status: status,
|
||||
message: message,
|
||||
srMessage: srMessages.join(" ")
|
||||
};
|
||||
},
|
||||
|
||||
showMessage: function (message, screenReaderMessage) {
|
||||
this.$('.wrapper-msg').removeClass('is-hidden');
|
||||
this.$('.msg-content .copy p').text(message);
|
||||
this.$('.wrapper-msg').focus();
|
||||
|
||||
if (screenReaderMessage) {
|
||||
this.$('.screen-reader-message').text(screenReaderMessage);
|
||||
}
|
||||
},
|
||||
|
||||
goBackToTopic: function () {
|
||||
Backbone.history.navigate("topics/" + this.topicId, {trigger: true});
|
||||
}
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
52
lms/djangoapps/teams/static/teams/js/views/team_actions.js
Normal file
52
lms/djangoapps/teams/static/teams/js/views/team_actions.js
Normal file
@@ -0,0 +1,52 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'gettext',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'text!teams/templates/team-actions.underscore'
|
||||
], function (gettext, _, Backbone, team_actions_template) {
|
||||
return Backbone.View.extend({
|
||||
events: {
|
||||
'click a.browse-teams': 'browseTeams',
|
||||
'click a.search-team-descriptions': 'searchTeamDescriptions',
|
||||
'click a.create-team': 'showCreateTeamForm'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.template = _.template(team_actions_template);
|
||||
this.teamParams = options.teamParams;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var message = interpolate_text(
|
||||
_.escape(gettext("Try {browse_span_start}browsing all teams{span_end} or {search_span_start}searching team descriptions{span_end}. If you still can't find a team to join, {create_span_start}create a new team in this topic{span_end}.")),
|
||||
{
|
||||
'browse_span_start': '<a class="browse-teams" href="">',
|
||||
'search_span_start': '<a class="search-team-descriptions" href="">',
|
||||
'create_span_start': '<a class="create-team" href="">',
|
||||
'span_end': '</a>'
|
||||
}
|
||||
);
|
||||
this.$el.html(this.template({message: message}));
|
||||
return this;
|
||||
},
|
||||
|
||||
browseTeams: function (event) {
|
||||
event.preventDefault();
|
||||
Backbone.history.navigate('browse', {trigger: true});
|
||||
},
|
||||
|
||||
searchTeamDescriptions: function (event) {
|
||||
event.preventDefault();
|
||||
// TODO! Will navigate to correct place once required functionality is available
|
||||
Backbone.history.navigate('browse', {trigger: true});
|
||||
},
|
||||
|
||||
showCreateTeamForm: function (event) {
|
||||
event.preventDefault();
|
||||
Backbone.history.navigate('topics/' + this.teamParams.topicId + '/create-team', {trigger: true});
|
||||
}
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,30 +1,31 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'backbone',
|
||||
'teams/js/views/team_card',
|
||||
'common/js/components/views/paginated_view'
|
||||
], function (TeamCardView, PaginatedView) {
|
||||
'common/js/components/views/paginated_view',
|
||||
'teams/js/views/team_actions'
|
||||
], function (Backbone, TeamCardView, PaginatedView, TeamActionsView) {
|
||||
var TeamsView = PaginatedView.extend({
|
||||
type: 'teams',
|
||||
|
||||
events: {
|
||||
'click button.action': '' // entry point for team creation
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.itemViewClass = TeamCardView.extend({
|
||||
router: options.router,
|
||||
maxTeamSize: options.maxTeamSize
|
||||
});
|
||||
PaginatedView.prototype.initialize.call(this);
|
||||
this.teamParams = options.teamParams;
|
||||
},
|
||||
|
||||
render: function () {
|
||||
PaginatedView.prototype.render.call(this);
|
||||
|
||||
this.$el.append(
|
||||
$('<button class="action action-primary">' + gettext('Create new team') + '</button>')
|
||||
);
|
||||
var teamActionsView = new TeamActionsView({
|
||||
teamParams: this.teamParams
|
||||
});
|
||||
this.$el.append(teamActionsView.$el);
|
||||
teamActionsView.render();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
'teams/js/collections/topic',
|
||||
'teams/js/views/teams',
|
||||
'teams/js/collections/team',
|
||||
'teams/js/views/edit_team',
|
||||
'text!teams/templates/teams_tab.underscore'],
|
||||
function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView,
|
||||
TopicsView, TopicModel, TopicCollection, TeamsView, TeamCollection, teamsTemplate) {
|
||||
TopicsView, TopicModel, TopicCollection, TeamsView, TeamCollection,
|
||||
TeamEditView, teamsTemplate) {
|
||||
var ViewWithHeader = Backbone.View.extend({
|
||||
initialize: function (options) {
|
||||
this.header = options.header;
|
||||
@@ -38,6 +40,8 @@
|
||||
this.topic_url = options.topic_url;
|
||||
this.teams_url = options.teams_url;
|
||||
this.maxTeamSize = options.maxTeamSize;
|
||||
this.languages = options.languages;
|
||||
this.countries = options.countries;
|
||||
// This slightly tedious approach is necessary
|
||||
// to use regular expressions within Backbone
|
||||
// routes, allowing us to capture which tab
|
||||
@@ -45,9 +49,10 @@
|
||||
router = this.router = new Backbone.Router();
|
||||
_.each([
|
||||
[':default', _.bind(this.routeNotFound, this)],
|
||||
['topics/:topic_id', _.bind(this.browseTopic, this)],
|
||||
[new RegExp('^(browse)$'), _.bind(this.goToTab, this)],
|
||||
[new RegExp('^(teams)$'), _.bind(this.goToTab, this)]
|
||||
['topics/:topic_id(/)', _.bind(this.browseTopic, this)],
|
||||
['topics/:topic_id/create-team(/)', _.bind(this.newTeam, this)],
|
||||
[new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)],
|
||||
[new RegExp('^(teams)\/?$'), _.bind(this.goToTab, this)]
|
||||
], function (route) {
|
||||
router.route.apply(router, route);
|
||||
});
|
||||
@@ -96,6 +101,35 @@
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the create new team form.
|
||||
*/
|
||||
newTeam: function (topicId) {
|
||||
var self = this;
|
||||
this.getTeamsView(topicId).done(function (teamsView) {
|
||||
self.mainView = new ViewWithHeader({
|
||||
header: new HeaderView({
|
||||
model: new HeaderModel({
|
||||
description: gettext("Create a new team if you can't find existing teams to join, or if you would like to learn with friends you know."),
|
||||
title: gettext("Create a New Team"),
|
||||
breadcrumbs: [
|
||||
{
|
||||
title: teamsView.main.teamParams.topicName,
|
||||
url: '#topics/' + teamsView.main.teamParams.topicId
|
||||
}
|
||||
]
|
||||
})
|
||||
}),
|
||||
main: new TeamEditView({
|
||||
tagName: 'create-new-team',
|
||||
teamParams: teamsView.main.teamParams,
|
||||
primaryButtonTitle: 'Create'
|
||||
})
|
||||
});
|
||||
self.render();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the list of teams for the given topic ID.
|
||||
*/
|
||||
@@ -172,6 +206,14 @@
|
||||
per_page: 10,
|
||||
parse: true
|
||||
}),
|
||||
teamParams: {
|
||||
courseId: this.course_id,
|
||||
teamsUrl: this.teams_url,
|
||||
topicId: topic.get('id'),
|
||||
topicName: topic.get('name'),
|
||||
languages: self.languages,
|
||||
countries: self.countries
|
||||
},
|
||||
maxTeamSize: this.maxTeamSize
|
||||
})
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<div class="create-team wrapper-msg is-incontext urgency-low warning is-hidden" tabindex="-1">
|
||||
<div class="msg">
|
||||
<div class="msg-content">
|
||||
<h3 class="title"><%- gettext("Your team could not be created!") %></h3>
|
||||
<span class="screen-reader-message sr"></span>
|
||||
<div class="copy">
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<div class="team-edit-fields">
|
||||
<div class="team-required-fields">
|
||||
</div>
|
||||
|
||||
<div class="team-optional-fields">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="create-team form-actions">
|
||||
<button class="action action-primary">
|
||||
<%=
|
||||
interpolate_text(
|
||||
_.escape(gettext("{primaryButtonTitle} {span_start}a new team{span_end}")),
|
||||
{
|
||||
'primaryButtonTitle': primaryButtonTitle, 'span_start': '<span class="sr">', 'span_end': '</span>'
|
||||
}
|
||||
)
|
||||
%>
|
||||
</button>
|
||||
<button class="action action-cancel">
|
||||
<%=
|
||||
interpolate_text(
|
||||
_.escape(gettext("Cancel {span_start}a new team{span_end}")),
|
||||
{
|
||||
'span_start': '<span class="sr">', 'span_end': '</span>'
|
||||
}
|
||||
)
|
||||
%>
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="team-actions">
|
||||
<h3 class="title"><%- gettext("Are you having trouble finding a team to join?") %></h3>
|
||||
<p class="copy"><%= message %></p>
|
||||
</div>
|
||||
@@ -28,7 +28,9 @@
|
||||
topics_url: '${ topics_url }',
|
||||
teams_url: '${ teams_url }',
|
||||
maxTeamSize: ${ course.teams_max_size },
|
||||
course_id: '${ unicode(course.id) }'
|
||||
course_id: '${ unicode(course.id) }',
|
||||
languages: ${ json.dumps(languages, cls=EscapedEdxJSONEncoder) },
|
||||
countries: ${ json.dumps(countries, cls=EscapedEdxJSONEncoder) }
|
||||
});
|
||||
</%static:require_module>
|
||||
</%block>
|
||||
|
||||
@@ -21,6 +21,7 @@ from rest_framework import permissions
|
||||
|
||||
from django.db.models import Count
|
||||
from django.contrib.auth.models import User
|
||||
from django_countries import countries
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
@@ -96,7 +97,9 @@ class TeamsDashboardView(View):
|
||||
'topics_detail', kwargs={'topic_id': 'topic_id', 'course_id': str(course_id)}, request=request
|
||||
),
|
||||
"topics_url": reverse('topics_list', request=request),
|
||||
"teams_url": reverse('teams_list', request=request)
|
||||
"teams_url": reverse('teams_list', request=request),
|
||||
"languages": settings.ALL_LANGUAGES,
|
||||
"countries": list(countries),
|
||||
}
|
||||
return render_to_response("teams/teams.html", context)
|
||||
|
||||
|
||||
@@ -533,6 +533,8 @@
|
||||
'lms/include/teams/js/spec/topics_spec.js',
|
||||
'lms/include/teams/js/spec/teams_spec.js',
|
||||
'lms/include/teams/js/spec/teams_tab_spec.js',
|
||||
'lms/include/teams/js/spec/team_actions_spec.js',
|
||||
'lms/include/teams/js/spec/edit_team_spec.js',
|
||||
'lms/include/js/spec/components/header/header_spec.js',
|
||||
'lms/include/js/spec/components/tabbed/tabbed_view_spec.js',
|
||||
'lms/include/js/spec/components/card/card_spec.js',
|
||||
|
||||
@@ -49,7 +49,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
var selector = '.u-field-value > select';
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.DropdownFieldView, {
|
||||
valueAttribute: 'language',
|
||||
options: FieldViewsSpecHelpers.SELECT_OPTIONS
|
||||
options: FieldViewsSpecHelpers.SELECT_OPTIONS,
|
||||
persistChanges: true
|
||||
});
|
||||
|
||||
var view = new AccountSettingsFieldViews.LanguagePreferenceFieldView(fieldData).render();
|
||||
@@ -92,7 +93,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
var selector = '.u-field-value > select';
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.DropdownFieldView, {
|
||||
valueAttribute: 'language_proficiencies',
|
||||
options: FieldViewsSpecHelpers.SELECT_OPTIONS
|
||||
options: FieldViewsSpecHelpers.SELECT_OPTIONS,
|
||||
persistChanges: true
|
||||
});
|
||||
fieldData.model.set({'language_proficiencies': [{'code': FieldViewsSpecHelpers.SELECT_OPTIONS[0][0]}]});
|
||||
|
||||
|
||||
@@ -101,7 +101,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
placeholderValue: "Tell other edX learners a little about yourself: where you live, " +
|
||||
"what your interests are, why you're taking courses on edX, or what you hope to learn.",
|
||||
valueAttribute: "bio",
|
||||
helpMessage: ''
|
||||
helpMessage: '',
|
||||
messagePosition: 'header'
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
@@ -126,6 +126,37 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
expectMessageContains(view, "Do not reset this!");
|
||||
};
|
||||
|
||||
var verifyPersistence = function (fieldClass, requests) {
|
||||
var fieldData = createFieldData(fieldClass, {
|
||||
title: 'Username',
|
||||
valueAttribute: 'username',
|
||||
helpMessage: 'The username that you use to sign in to edX.',
|
||||
validValue: 'My Name',
|
||||
persistChanges: false,
|
||||
messagePosition: 'header'
|
||||
});
|
||||
var view = new fieldClass(fieldData).render();
|
||||
var valueInputSelector;
|
||||
|
||||
switch (fieldClass) {
|
||||
case FieldViews.TextFieldView:
|
||||
valueInputSelector = '.u-field-value > input';
|
||||
break;
|
||||
case FieldViews.DropdownFieldView:
|
||||
valueInputSelector = '.u-field-value > select';
|
||||
_.extend(fieldData, {validValue: SELECT_OPTIONS[0][0]});
|
||||
break;
|
||||
case FieldViews.TextareaFieldView:
|
||||
valueInputSelector = '.u-field-value > textarea';
|
||||
break;
|
||||
}
|
||||
|
||||
view.$(valueInputSelector).val(fieldData.validValue).change();
|
||||
expect(view.fieldValue()).toBe(fieldData.validValue);
|
||||
expectMessageContains(view, view.helpMessage);
|
||||
expect(requests.length).toBe(0);
|
||||
};
|
||||
|
||||
var verifyEditableField = function (view, data, requests) {
|
||||
var request_data = {};
|
||||
var url = view.model.url;
|
||||
@@ -230,6 +261,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
verifySuccessMessageReset: verifySuccessMessageReset,
|
||||
verifyEditableField: verifyEditableField,
|
||||
verifyTextField: verifyTextField,
|
||||
verifyDropDownField: verifyDropDownField
|
||||
verifyDropDownField: verifyDropDownField,
|
||||
verifyPersistence: verifyPersistence
|
||||
};
|
||||
});
|
||||
|
||||
@@ -72,7 +72,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(fieldViewClass, {
|
||||
title: 'Preferred Language',
|
||||
valueAttribute: 'language',
|
||||
helpMessage: 'Your preferred language.'
|
||||
helpMessage: 'Your preferred language.',
|
||||
persistChanges: true
|
||||
});
|
||||
|
||||
var view = new fieldViewClass(fieldData);
|
||||
@@ -110,7 +111,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(FieldViews.TextFieldView, {
|
||||
title: 'Full Name',
|
||||
valueAttribute: 'name',
|
||||
helpMessage: 'How are you?'
|
||||
helpMessage: 'How are you?',
|
||||
persistChanges: true
|
||||
});
|
||||
var view = new FieldViews.TextFieldView(fieldData).render();
|
||||
|
||||
@@ -134,7 +136,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
title: 'Full Name',
|
||||
valueAttribute: 'name',
|
||||
helpMessage: 'edX full name',
|
||||
editable: 'never'
|
||||
editable: 'never',
|
||||
persistChanges: true
|
||||
|
||||
});
|
||||
var view = new FieldViews.DropdownFieldView(fieldData).render();
|
||||
@@ -154,7 +157,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(FieldViews.DropdownFieldView, {
|
||||
title: 'Full Name',
|
||||
valueAttribute: 'name',
|
||||
helpMessage: 'edX full name'
|
||||
helpMessage: 'edX full name',
|
||||
persistChanges: true
|
||||
});
|
||||
var view = new FieldViews.DropdownFieldView(fieldData).render();
|
||||
|
||||
@@ -178,7 +182,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
title: 'Full Name',
|
||||
valueAttribute: 'name',
|
||||
helpMessage: 'edX full name',
|
||||
editable: 'toggle'
|
||||
editable: 'toggle',
|
||||
persistChanges: true
|
||||
});
|
||||
var view = new FieldViews.DropdownFieldView(fieldData).render();
|
||||
|
||||
@@ -205,7 +210,8 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
valueAttribute: 'drop-down',
|
||||
helpMessage: 'edX drop down',
|
||||
editable: editable,
|
||||
required:true
|
||||
required:true,
|
||||
persistChanges: true
|
||||
});
|
||||
var view = new FieldViews.DropdownFieldView(fieldData).render();
|
||||
|
||||
@@ -230,7 +236,9 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
helpMessage: 'Wicked is good',
|
||||
placeholderValue: "Tell other edX learners a little about yourself: where you live, " +
|
||||
"what your interests are, why you’re taking courses on edX, or what you hope to learn.",
|
||||
editable: 'never'
|
||||
editable: 'never',
|
||||
persistChanges: true,
|
||||
messagePosition: 'header'
|
||||
});
|
||||
|
||||
// set bio to empty to see the placeholder.
|
||||
@@ -259,8 +267,9 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
helpMessage: 'Wicked is good',
|
||||
placeholderValue: "Tell other edX learners a little about yourself: where you live, " +
|
||||
"what your interests are, why you’re taking courses on edX, or what you hope to learn.",
|
||||
editable: 'toggle'
|
||||
|
||||
editable: 'toggle',
|
||||
persistChanges: true,
|
||||
messagePosition: 'header'
|
||||
});
|
||||
fieldData.model.set({'bio': ''});
|
||||
|
||||
@@ -300,5 +309,30 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
|
||||
FieldViewsSpecHelpers.expectTitleAndMessageToContain(view, fieldData.title, fieldData.helpMessage, false);
|
||||
expect(view.$('.u-field-value > a .u-field-link-title-' + view.options.valueAttribute).text().trim()).toBe(fieldData.linkTitle);
|
||||
});
|
||||
|
||||
it("correctly renders LinkFieldView", function() {
|
||||
var fieldData = FieldViewsSpecHelpers.createFieldData(FieldViews.LinkFieldView, {
|
||||
title: 'Title',
|
||||
linkTitle: 'Link title',
|
||||
helpMessage: 'Click the link.',
|
||||
valueAttribute: 'password-reset'
|
||||
});
|
||||
var view = new FieldViews.LinkFieldView(fieldData).render();
|
||||
|
||||
FieldViewsSpecHelpers.expectTitleAndMessageToContain(view, fieldData.title, fieldData.helpMessage, false);
|
||||
expect(view.$('.u-field-value > a .u-field-link-title-' + view.options.valueAttribute).text().trim()).toBe(fieldData.linkTitle);
|
||||
});
|
||||
|
||||
it("can't persist changes if persistChanges is off", function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
var fieldClasses = [
|
||||
FieldViews.TextFieldView,
|
||||
FieldViews.DropdownFieldView,
|
||||
FieldViews.TextareaFieldView
|
||||
];
|
||||
for (var i = 0; i < fieldClasses.length; i++) {
|
||||
FieldViewsSpecHelpers.verifyPersistence(fieldClasses[i], requests);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
model: userAccountModel,
|
||||
title: gettext('Full Name'),
|
||||
valueAttribute: 'name',
|
||||
helpMessage: gettext('The name that appears on your certificates. Other learners never see your full name.')
|
||||
helpMessage: gettext('The name that appears on your certificates. Other learners never see your full name.'),
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -49,7 +50,8 @@
|
||||
valueAttribute: 'email',
|
||||
helpMessage: interpolate_text(
|
||||
gettext('The email address you use to sign in. Communications from {platform_name} and your courses are sent to this address.'), {platform_name: platformName}
|
||||
)
|
||||
),
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -74,7 +76,8 @@
|
||||
helpMessage: interpolate_text(
|
||||
gettext('The language used throughout this site. This site is currently available in a limited number of languages.'), {platform_name: platformName}
|
||||
),
|
||||
options: fieldsData.language.options
|
||||
options: fieldsData.language.options,
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -83,7 +86,8 @@
|
||||
required: true,
|
||||
title: gettext('Country or Region'),
|
||||
valueAttribute: 'country',
|
||||
options: fieldsData['country']['options']
|
||||
options: fieldsData['country']['options'],
|
||||
persistChanges: true
|
||||
})
|
||||
}
|
||||
]
|
||||
@@ -96,7 +100,8 @@
|
||||
model: userAccountModel,
|
||||
title: gettext('Education Completed'),
|
||||
valueAttribute: 'level_of_education',
|
||||
options: fieldsData.level_of_education.options
|
||||
options: fieldsData.level_of_education.options,
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -104,7 +109,8 @@
|
||||
model: userAccountModel,
|
||||
title: gettext('Gender'),
|
||||
valueAttribute: 'gender',
|
||||
options: fieldsData.gender.options
|
||||
options: fieldsData.gender.options,
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -112,7 +118,8 @@
|
||||
model: userAccountModel,
|
||||
title: gettext('Year of Birth'),
|
||||
valueAttribute: 'year_of_birth',
|
||||
options: fieldsData['year_of_birth']['options']
|
||||
options: fieldsData['year_of_birth']['options'],
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -120,7 +127,8 @@
|
||||
model: userAccountModel,
|
||||
title: gettext('Preferred Language'),
|
||||
valueAttribute: 'language_proficiencies',
|
||||
options: fieldsData.preferred_language.options
|
||||
options: fieldsData.preferred_language.options,
|
||||
persistChanges: true
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
@@ -99,12 +99,13 @@
|
||||
},
|
||||
|
||||
saveValue: function () {
|
||||
var attributes = {},
|
||||
value = this.fieldValue() ? [{'code': this.fieldValue()}] : [];
|
||||
attributes[this.options.valueAttribute] = value;
|
||||
this.saveAttributes(attributes);
|
||||
if (this.persistChanges === true) {
|
||||
var attributes = {},
|
||||
value = this.fieldValue() ? [{'code': this.fieldValue()}] : [];
|
||||
attributes[this.options.valueAttribute] = value;
|
||||
this.saveAttributes(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
AccountSettingsFieldViews.AuthFieldView = FieldViews.LinkFieldView.extend({
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
['all_users', gettext('Full Profile')]
|
||||
],
|
||||
helpMessage: '',
|
||||
accountSettingsPageUrl: options.account_settings_page_url
|
||||
accountSettingsPageUrl: options.account_settings_page_url,
|
||||
persistChanges: true
|
||||
});
|
||||
|
||||
var profileImageFieldView = new LearnerProfileFieldsView.ProfileImageFieldView({
|
||||
@@ -87,7 +88,8 @@
|
||||
placeholderValue: gettext('Add Country'),
|
||||
valueAttribute: "country",
|
||||
options: options.country_options,
|
||||
helpMessage: ''
|
||||
helpMessage: '',
|
||||
persistChanges: true
|
||||
}),
|
||||
new AccountSettingsFieldViews.LanguageProficienciesFieldView({
|
||||
model: accountSettingsModel,
|
||||
@@ -100,7 +102,8 @@
|
||||
placeholderValue: gettext('Add language'),
|
||||
valueAttribute: "language_proficiencies",
|
||||
options: options.language_options,
|
||||
helpMessage: ''
|
||||
helpMessage: '',
|
||||
persistChanges: true
|
||||
})
|
||||
];
|
||||
|
||||
@@ -112,7 +115,9 @@
|
||||
title: gettext('About me'),
|
||||
placeholderValue: gettext("Tell other learners a little about yourself: where you live, what your interests are, why you're taking courses, or what you hope to learn."),
|
||||
valueAttribute: "bio",
|
||||
helpMessage: ''
|
||||
helpMessage: '',
|
||||
persistChanges: true,
|
||||
messagePosition: 'header'
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
;(function (define, undefined) {
|
||||
'use strict';
|
||||
define([
|
||||
'gettext', 'jquery', 'underscore', 'backbone', 'backbone-super', 'jquery.fileupload'
|
||||
], function (gettext, $, _, Backbone) {
|
||||
'gettext', 'jquery', 'underscore', 'backbone',
|
||||
'text!templates/fields/field_readonly.underscore',
|
||||
'text!templates/fields/field_dropdown.underscore',
|
||||
'text!templates/fields/field_link.underscore',
|
||||
'text!templates/fields/field_text.underscore',
|
||||
'text!templates/fields/field_textarea.underscore',
|
||||
'text!templates/fields/field_image.underscore',
|
||||
'backbone-super', 'jquery.fileupload'
|
||||
], function (gettext, $, _, Backbone,
|
||||
field_readonly_template,
|
||||
field_dropdown_template,
|
||||
field_link_template,
|
||||
field_text_template,
|
||||
field_textarea_template,
|
||||
field_image_template
|
||||
) {
|
||||
|
||||
var messageRevertDelay = 6000;
|
||||
var FieldViews = {};
|
||||
@@ -36,7 +50,7 @@
|
||||
|
||||
initialize: function () {
|
||||
|
||||
this.template = _.template($(this.templateSelector).text());
|
||||
this.template = _.template(this.fieldTemplate || '');
|
||||
|
||||
this.helpMessage = this.options.helpMessage || '';
|
||||
this.showMessages = _.isUndefined(this.options.showMessages) ? true : this.options.showMessages;
|
||||
@@ -142,6 +156,7 @@
|
||||
FieldViews.EditableFieldView = FieldViews.FieldView.extend({
|
||||
|
||||
initialize: function (options) {
|
||||
this.persistChanges = _.isUndefined(options.persistChanges) ? false : options.persistChanges;
|
||||
_.bindAll(this, 'saveAttributes', 'saveSucceeded', 'showDisplayMode', 'showEditMode',
|
||||
'startEditing', 'finishEditing'
|
||||
);
|
||||
@@ -158,20 +173,22 @@
|
||||
},
|
||||
|
||||
saveAttributes: function (attributes, options) {
|
||||
var view = this;
|
||||
var defaultOptions = {
|
||||
contentType: 'application/merge-patch+json',
|
||||
patch: true,
|
||||
wait: true,
|
||||
success: function () {
|
||||
view.saveSucceeded();
|
||||
},
|
||||
error: function (model, xhr) {
|
||||
view.showErrorMessage(xhr);
|
||||
}
|
||||
};
|
||||
this.showInProgressMessage();
|
||||
this.model.save(attributes, _.extend(defaultOptions, options));
|
||||
if (this.persistChanges === true) {
|
||||
var view = this;
|
||||
var defaultOptions = {
|
||||
contentType: 'application/merge-patch+json',
|
||||
patch: true,
|
||||
wait: true,
|
||||
success: function () {
|
||||
view.saveSucceeded();
|
||||
},
|
||||
error: function (model, xhr) {
|
||||
view.showErrorMessage(xhr);
|
||||
}
|
||||
};
|
||||
this.showInProgressMessage();
|
||||
this.model.save(attributes, _.extend(defaultOptions, options));
|
||||
}
|
||||
},
|
||||
|
||||
saveSucceeded: function () {
|
||||
@@ -210,6 +227,7 @@
|
||||
},
|
||||
|
||||
finishEditing: function() {
|
||||
if (this.persistChanges === false) {return;}
|
||||
if (this.fieldValue() !== this.modelValue()) {
|
||||
this.saveValue();
|
||||
} else {
|
||||
@@ -219,6 +237,14 @@
|
||||
this.showDisplayMode(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
highlightFieldOnError: function () {
|
||||
this.$el.addClass('error');
|
||||
},
|
||||
|
||||
unhighlightField: function () {
|
||||
this.$el.removeClass('error');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,7 +252,7 @@
|
||||
|
||||
fieldType: 'readonly',
|
||||
|
||||
templateSelector: '#field_readonly-tpl',
|
||||
fieldTemplate: field_readonly_template,
|
||||
|
||||
initialize: function (options) {
|
||||
this._super(options);
|
||||
@@ -259,7 +285,7 @@
|
||||
|
||||
fieldType: 'text',
|
||||
|
||||
templateSelector: '#field_text-tpl',
|
||||
fieldTemplate: field_text_template,
|
||||
|
||||
events: {
|
||||
'change input': 'saveValue'
|
||||
@@ -302,7 +328,7 @@
|
||||
|
||||
fieldType: 'dropdown',
|
||||
|
||||
templateSelector: '#field_dropdown-tpl',
|
||||
fieldTemplate: field_dropdown_template,
|
||||
|
||||
events: {
|
||||
'click': 'startEditing',
|
||||
@@ -421,7 +447,7 @@
|
||||
|
||||
fieldType: 'textarea',
|
||||
|
||||
templateSelector: '#field_textarea-tpl',
|
||||
fieldTemplate: field_textarea_template,
|
||||
|
||||
events: {
|
||||
'click .wrapper-u-field': 'startEditing',
|
||||
@@ -451,6 +477,7 @@
|
||||
mode: this.mode,
|
||||
value: value,
|
||||
message: this.helpMessage,
|
||||
messagePosition: this.options.messagePosition || 'footer',
|
||||
placeholderValue: this.options.placeholderValue
|
||||
}));
|
||||
this.delegateEvents();
|
||||
@@ -472,6 +499,7 @@
|
||||
},
|
||||
|
||||
adjustTextareaHeight: function() {
|
||||
if (this.persistChanges === false) {return;}
|
||||
var textarea = this.$('textarea');
|
||||
textarea.css('height', 'auto').css('height', textarea.prop('scrollHeight') + 10);
|
||||
},
|
||||
@@ -520,7 +548,7 @@
|
||||
|
||||
fieldType: 'link',
|
||||
|
||||
templateSelector: '#field_link-tpl',
|
||||
fieldTemplate: field_link_template,
|
||||
|
||||
events: {
|
||||
'click a': 'linkClicked'
|
||||
@@ -553,7 +581,7 @@
|
||||
|
||||
fieldType: 'image',
|
||||
|
||||
templateSelector: '#field_image-tpl',
|
||||
fieldTemplate: field_image_template,
|
||||
uploadButtonSelector: '.upload-button-input',
|
||||
|
||||
titleAdd: gettext("Upload an image"),
|
||||
|
||||
@@ -51,6 +51,20 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Below divider rules are moved here from _instructor_2.scss
|
||||
// UI: visual dividers
|
||||
.divider-lv0 {
|
||||
border-top: ($baseline/5) solid $gray-l4;
|
||||
}
|
||||
|
||||
.divider-lv1 {
|
||||
border-top: ($baseline/10) solid $gray-l4;
|
||||
}
|
||||
|
||||
.divider-lv2 {
|
||||
border-top: ($baseline/20) solid $gray-l4;
|
||||
}
|
||||
|
||||
// for verify_student/make_payment_step.underscore
|
||||
.payment-buttons {
|
||||
|
||||
|
||||
@@ -120,19 +120,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI: visual dividers
|
||||
.divider-lv0 {
|
||||
border-top: ($baseline/5) solid $gray-l4;
|
||||
}
|
||||
|
||||
.divider-lv1 {
|
||||
border-top: ($baseline/10) solid $gray-l4;
|
||||
}
|
||||
|
||||
.divider-lv2 {
|
||||
border-top: ($baseline/20) solid $gray-l4;
|
||||
}
|
||||
}
|
||||
|
||||
// instructor dashboard 2
|
||||
|
||||
@@ -472,4 +472,160 @@
|
||||
padding-left: 2%;
|
||||
}
|
||||
}
|
||||
|
||||
.team-actions {
|
||||
@extend %ui-well;
|
||||
margin: 20px 1.2%;
|
||||
color: $gray;
|
||||
text-align: center;
|
||||
|
||||
.title {
|
||||
@extend %t-title6;
|
||||
@extend %t-strong;
|
||||
margin-bottom: ($baseline/2);
|
||||
text-align: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.copy {
|
||||
text-align: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.teams-content {
|
||||
|
||||
.teams-main {
|
||||
|
||||
.team-edit-fields {
|
||||
@include clearfix();
|
||||
|
||||
.team-required-fields {
|
||||
@include float(left);
|
||||
width: 55%;
|
||||
border-right: 2px solid $gray-l4;;
|
||||
|
||||
.u-field.u-field-name {
|
||||
padding-bottom: $baseline;
|
||||
|
||||
.u-field-value {
|
||||
display: block;
|
||||
width: 90%;
|
||||
|
||||
input {
|
||||
border-radius: ($baseline/5);
|
||||
height: ($baseline*2);
|
||||
}
|
||||
}
|
||||
|
||||
.u-field-message {
|
||||
@include padding-left(0);
|
||||
padding-top: ($baseline/4);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.u-field.u-field-description {
|
||||
|
||||
.u-field-value {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
textarea {
|
||||
height: ($baseline*5);
|
||||
width: 90%;
|
||||
border-radius: ($baseline/5)
|
||||
}
|
||||
}
|
||||
|
||||
.u-field-message {
|
||||
display: block;
|
||||
@extend %t-copy-sub1;
|
||||
@include padding-left(0);
|
||||
margin-top: ($baseline/4);
|
||||
color: $gray-l1;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.u-field-title {
|
||||
padding-bottom: ($baseline/4);
|
||||
color: $base-font-color;
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.team-optional-fields {
|
||||
@include float(left);
|
||||
@include margin-left($baseline);
|
||||
width: 40%;
|
||||
|
||||
.u-field.u-field-optional_description {
|
||||
margin-bottom: ($baseline/2);
|
||||
|
||||
.u-field-title {
|
||||
color: $base-font-color;
|
||||
font-weight: $font-semibold;
|
||||
margin-bottom: ($baseline/5);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.u-field-value {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.u-field.u-field-language {
|
||||
margin-bottom: ($baseline/5);
|
||||
}
|
||||
|
||||
.u-field-value-display {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.u-field-value {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.u-field-title {
|
||||
display: block;
|
||||
color: $base-font-color;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.u-field-message {
|
||||
@include padding-left(0);
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.u-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.u-field.error {
|
||||
input, textarea {
|
||||
border-color: $error-red;
|
||||
}
|
||||
|
||||
.u-field-message-help, .u-field-description-message {
|
||||
color: $error-red !important;
|
||||
}
|
||||
}
|
||||
|
||||
.create-team.wrapper-msg {
|
||||
margin: 0 0 $baseline 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-instructions {
|
||||
margin: ($baseline/2) 0 $baseline 0;
|
||||
}
|
||||
|
||||
.create-team.form-actions {
|
||||
margin-top: $baseline;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
<div class="wrapper-u-field">
|
||||
<div class="u-field-header">
|
||||
<label class="u-field-title" for="u-field-textarea-<%- id %>" id="u-field-title-<%- id %>" aria-describedby="u-field-message-help-<%- id %>"></label>
|
||||
<span class="u-field-message" id="u-field-message-<%- id %>">
|
||||
<span class="u-field-message-notification" aria-live="polite"></span>
|
||||
<span class="u-field-message-help" id="u-field-message-help-<%- id %>"> <%- message %></span>
|
||||
</span>
|
||||
<% if (messagePosition === 'header') { %>
|
||||
<span class="u-field-message" id="u-field-message-<%- id %>">
|
||||
<span class="u-field-message-notification" aria-live="polite"></span>
|
||||
<span class="u-field-message-help" id="u-field-message-help-<%- id %>"> <%- message %></span>
|
||||
</span>
|
||||
<% }%>
|
||||
</div>
|
||||
|
||||
<div class="u-field-value" id="u-field-value-<%- id %>" aria-labelledby="u-field-title-<%- id %>"><%
|
||||
if (mode === 'edit') {
|
||||
%><textarea id="u-field-textarea-<%- id %>" rows="4" aria-describedby="u-field-placeholder-value-<%- id %>"><%- value %></textarea><%
|
||||
} else {
|
||||
var textareaDescribedBy = (message ? 'u-field-message-help-' : 'u-field-placeholder-value-') + id;
|
||||
if (mode === 'edit') {%>
|
||||
<textarea id="u-field-textarea-<%- id %>" rows="4" aria-describedby="<%- textareaDescribedBy %>"><%- value %></textarea>
|
||||
<% } else {
|
||||
%><a href="#"><span class="sr"><%- screenReaderTitle %></span><span class="u-field-value-readonly" aria-hidden="false" aria-describedby="u-field-placeholder-value-<%- id %>"><%- value %></span><span class="sr"><%- gettext('Click to edit') %></span></a><%
|
||||
}
|
||||
%><span class="sr" id="u-field-placeholder-value-<%- id %>"><%- placeholderValue %></span>
|
||||
</div>
|
||||
|
||||
<div class="u-field-footer">
|
||||
<% if (messagePosition === 'footer') { %>
|
||||
<span class="u-field-message" id="u-field-message-<%- id %>">
|
||||
<span class="u-field-message-notification" aria-live="polite"></span>
|
||||
<span class="u-field-message-help" id="u-field-message-help-<%- id %>"> <%- message %></span>
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user