Commit dc4e4f7d by Waheed Ahmed

Account settings navigation.

ECOM-2981
parent 83155840
...@@ -57,3 +57,9 @@ class AccountSettingsPage(FieldsMixin, PageObject): ...@@ -57,3 +57,9 @@ class AccountSettingsPage(FieldsMixin, PageObject):
Wait for loading indicator to become visible. Wait for loading indicator to become visible.
""" """
EmptyPromise(self._is_loading_in_progress, "Loading is in progress.").fulfill() EmptyPromise(self._is_loading_in_progress, "Loading is in progress.").fulfill()
def switch_account_settings_tabs(self, tab_id):
"""
Switch between the different account settings tabs.
"""
self.q(css='#{}'.format(tab_id)).click()
...@@ -228,13 +228,13 @@ class FieldsMixin(object): ...@@ -228,13 +228,13 @@ class FieldsMixin(object):
"Link field with link title \"{0}\" is visible.".format(expected_title) "Link field with link title \"{0}\" is visible.".format(expected_title)
).fulfill() ).fulfill()
def click_on_link_in_link_field(self, field_id): def click_on_link_in_link_field(self, field_id, field_type='a'):
""" """
Click the link in a link field. Click the link in a link field.
""" """
self.wait_for_field(field_id) self.wait_for_field(field_id)
query = self.q(css='.u-field-{} a'.format(field_id)) query = self.q(css='.u-field-{} {}'.format(field_id, field_type))
if query.present: if query.present:
query.first.click() query.first.click()
......
...@@ -158,7 +158,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -158,7 +158,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
""" """
expected_sections_structure = [ expected_sections_structure = [
{ {
'title': 'Basic Account Information (required)', 'title': 'Basic Account Information',
'fields': [ 'fields': [
'Username', 'Username',
'Full Name', 'Full Name',
...@@ -169,21 +169,13 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -169,21 +169,13 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
] ]
}, },
{ {
'title': 'Additional Information (optional)', 'title': 'Additional Information',
'fields': [ 'fields': [
'Education Completed', 'Education Completed',
'Gender', 'Gender',
'Year of Birth', 'Year of Birth',
'Preferred Language', 'Preferred Language',
] ]
},
{
'title': 'Connected Accounts',
'fields': [
'Dummy',
'Facebook',
'Google',
]
} }
] ]
...@@ -240,13 +232,13 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -240,13 +232,13 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
self.account_settings_page.wait_for_page() self.account_settings_page.wait_for_page()
self.assertEqual(self.account_settings_page.value_for_dropdown_field(field_id), new_value) self.assertEqual(self.account_settings_page.value_for_dropdown_field(field_id), new_value)
def _test_link_field(self, field_id, title, link_title, success_message): def _test_link_field(self, field_id, title, link_title, field_type, success_message):
""" """
Test behaviour a link field. Test behaviour a link field.
""" """
self.assertEqual(self.account_settings_page.title_for_field(field_id), title) self.assertEqual(self.account_settings_page.title_for_field(field_id), title)
self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title) self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title)
self.account_settings_page.click_on_link_in_link_field(field_id) self.account_settings_page.click_on_link_in_link_field(field_id, field_type=field_type)
self.account_settings_page.wait_for_message(field_id, success_message) self.account_settings_page.wait_for_message(field_id, success_message)
def test_username_field(self): def test_username_field(self):
...@@ -316,7 +308,8 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -316,7 +308,8 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
self._test_link_field( self._test_link_field(
u'password', u'password',
u'Password', u'Password',
u'Reset Password', u'Reset Your Password',
u'button',
success_message='Click the link in the message to reset your password.', success_message='Click the link in the message to reset your password.',
) )
...@@ -434,7 +427,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -434,7 +427,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
actual_events actual_events
) )
def test_connected_accounts(self): def test_linked_accounts(self):
""" """
Test that fields for third party auth providers exist. Test that fields for third party auth providers exist.
...@@ -442,9 +435,11 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -442,9 +435,11 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
because that would require accounts with the providers. because that would require accounts with the providers.
""" """
providers = ( providers = (
['auth-oa2-facebook', 'Facebook', 'Link'], ['auth-oa2-facebook', 'Facebook', 'Link Your Account'],
['auth-oa2-google-oauth2', 'Google', 'Link'], ['auth-oa2-google-oauth2', 'Google', 'Link Your Account'],
) )
# switch to "Linked Accounts" tab
self.account_settings_page.switch_account_settings_tabs('accounts-tab')
for field_id, title, link_title in providers: for field_id, title, link_title in providers:
self.assertEqual(self.account_settings_page.title_for_field(field_id), title) self.assertEqual(self.account_settings_page.title_for_field(field_id), title)
self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title) self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title)
......
...@@ -222,19 +222,29 @@ class LoginFromCombinedPageTest(UniqueCourseTest): ...@@ -222,19 +222,29 @@ class LoginFromCombinedPageTest(UniqueCourseTest):
def _link_dummy_account(self): def _link_dummy_account(self):
""" Go to Account Settings page and link the user's account to the Dummy provider """ """ Go to Account Settings page and link the user's account to the Dummy provider """
account_settings = AccountSettingsPage(self.browser).visit() account_settings = AccountSettingsPage(self.browser).visit()
# switch to "Linked Accounts" tab
account_settings.switch_account_settings_tabs('accounts-tab')
field_id = "auth-oa2-dummy" field_id = "auth-oa2-dummy"
account_settings.wait_for_field(field_id) account_settings.wait_for_field(field_id)
self.assertEqual("Link", account_settings.link_title_for_link_field(field_id)) self.assertEqual("Link Your Account", account_settings.link_title_for_link_field(field_id))
account_settings.click_on_link_in_link_field(field_id) account_settings.click_on_link_in_link_field(field_id)
account_settings.wait_for_link_title_for_link_field(field_id, "Unlink")
# make sure we are on "Linked Accounts" tab after the account settings
# page is reloaded
account_settings.switch_account_settings_tabs('accounts-tab')
account_settings.wait_for_link_title_for_link_field(field_id, "Unlink This Account")
def _unlink_dummy_account(self): def _unlink_dummy_account(self):
""" Verify that the 'Dummy' third party auth provider is linked, then unlink it """ """ Verify that the 'Dummy' third party auth provider is linked, then unlink it """
# This must be done after linking the account, or we'll get cross-test side effects # This must be done after linking the account, or we'll get cross-test side effects
account_settings = AccountSettingsPage(self.browser).visit() account_settings = AccountSettingsPage(self.browser).visit()
# switch to "Linked Accounts" tab
account_settings.switch_account_settings_tabs('accounts-tab')
field_id = "auth-oa2-dummy" field_id = "auth-oa2-dummy"
account_settings.wait_for_field(field_id) account_settings.wait_for_field(field_id)
self.assertEqual("Unlink", account_settings.link_title_for_link_field(field_id)) self.assertEqual("Unlink This Account", account_settings.link_title_for_link_field(field_id))
account_settings.click_on_link_in_link_field(field_id) account_settings.click_on_link_in_link_field(field_id)
account_settings.wait_for_message(field_id, "Successfully unlinked") account_settings.wait_for_message(field_id, "Successfully unlinked")
...@@ -372,9 +382,12 @@ class RegisterFromCombinedPageTest(UniqueCourseTest): ...@@ -372,9 +382,12 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
# Now unlink the account (To test the account settings view and also to prevent cross-test side effects) # Now unlink the account (To test the account settings view and also to prevent cross-test side effects)
account_settings = AccountSettingsPage(self.browser).visit() account_settings = AccountSettingsPage(self.browser).visit()
# switch to "Linked Accounts" tab
account_settings.switch_account_settings_tabs('accounts-tab')
field_id = "auth-oa2-dummy" field_id = "auth-oa2-dummy"
account_settings.wait_for_field(field_id) account_settings.wait_for_field(field_id)
self.assertEqual("Unlink", account_settings.link_title_for_link_field(field_id)) self.assertEqual("Unlink This Account", account_settings.link_title_for_link_field(field_id))
account_settings.click_on_link_in_link_field(field_id) account_settings.click_on_link_in_link_field(field_id)
account_settings.wait_for_message(field_id, "Successfully unlinked") account_settings.wait_for_message(field_id, "Successfully unlinked")
......
...@@ -141,7 +141,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -141,7 +141,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData()); AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
AjaxHelpers.respondWithJson(requests, {}); // Page viewed analytics event AjaxHelpers.respondWithJson(requests, {}); // Page viewed analytics event
var sectionsData = accountSettingsView.options.sectionsData; var sectionsData = accountSettingsView.options.tabSections.aboutTabSections;
expect(sectionsData[0].fields.length).toBe(6); expect(sectionsData[0].fields.length).toBe(6);
...@@ -180,14 +180,6 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -180,14 +180,6 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
defaultValue: null defaultValue: null
}, requests); }, requests);
}); });
var section2Fields = sectionsData[2].fields;
expect(section2Fields.length).toBe(2);
for (var i = 0; i < section2Fields.length; i++) {
var view = section2Fields[i].view;
AccountSettingsFieldViewSpecHelpers.verifyAuthField(view, view.options, requests);
}
}); });
}); });
}); });
...@@ -10,13 +10,13 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -10,13 +10,13 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
spyOn(view, 'redirect_to'); spyOn(view, 'redirect_to');
FieldViewsSpecHelpers.expectTitleAndMessageToContain(view, data.title, data.helpMessage); FieldViewsSpecHelpers.expectTitleAndMessageToContain(view, data.title, data.helpMessage);
expect(view.$(selector).text().trim()).toBe('Unlink'); expect(view.$(selector).text().trim()).toBe('Unlink This Account');
view.$(selector).click(); view.$(selector).click();
FieldViewsSpecHelpers.expectMessageContains(view, 'Unlinking'); FieldViewsSpecHelpers.expectMessageContains(view, 'Unlinking');
AjaxHelpers.expectRequest(requests, 'POST', data.disconnectUrl); AjaxHelpers.expectRequest(requests, 'POST', data.disconnectUrl);
AjaxHelpers.respondWithNoContent(requests); AjaxHelpers.respondWithNoContent(requests);
expect(view.$(selector).text().trim()).toBe('Link'); expect(view.$(selector).text().trim()).toBe('Link Your Account');
FieldViewsSpecHelpers.expectMessageContains(view, 'Successfully unlinked.'); FieldViewsSpecHelpers.expectMessageContains(view, 'Successfully unlinked.');
view.$(selector).click(); view.$(selector).click();
......
...@@ -32,7 +32,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -32,7 +32,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
}); });
var view = new AccountSettingsFieldViews.PasswordFieldView(fieldData).render(); var view = new AccountSettingsFieldViews.PasswordFieldView(fieldData).render();
view.$('.u-field-value > a').click(); view.$('.u-field-value > button').click();
AjaxHelpers.expectRequest(requests, 'POST', '/password_reset', "email=legolas%40woodland.middlearth"); AjaxHelpers.expectRequest(requests, 'POST', '/password_reset', "email=legolas%40woodland.middlearth");
AjaxHelpers.respondWithJson(requests, {"success": "true"}); AjaxHelpers.respondWithJson(requests, {"success": "true"});
FieldViewsSpecHelpers.expectMessageContains( FieldViewsSpecHelpers.expectMessageContains(
......
...@@ -15,7 +15,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -15,7 +15,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
var model = new UserAccountModel(); var model = new UserAccountModel();
model.set(Helpers.createAccountSettingsData()); model.set(Helpers.createAccountSettingsData());
var sectionsData = [ var aboutSectionsData = [
{ {
title: "Basic Account Information", title: "Basic Account Information",
fields: [ fields: [
...@@ -53,7 +53,9 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -53,7 +53,9 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
var accountSettingsView = new AccountSettingsView({ var accountSettingsView = new AccountSettingsView({
el: $('.wrapper-account-settings'), el: $('.wrapper-account-settings'),
model: model, model: model,
sectionsData : sectionsData tabSections: {
aboutTabSections: aboutSectionsData
}
}); });
return accountSettingsView; return accountSettingsView;
......
...@@ -75,8 +75,8 @@ define(['underscore'], function(_) { ...@@ -75,8 +75,8 @@ define(['underscore'], function(_) {
if ('fieldValue' in view) { if ('fieldValue' in view) {
expect(view.fieldValue()).toBe(view.modelValue()); expect(view.fieldValue()).toBe(view.modelValue());
} else if (view.fieldType === 'link') { } else if (view.fieldType === 'button') {
expect($(element).find('a').length).toBe(1); expect($(element).find('button').length).toBe(1);
} else { } else {
throw new Error('Unexpected field type: ' + view.fieldType); throw new Error('Unexpected field type: ' + view.fieldType);
} }
...@@ -87,7 +87,7 @@ define(['underscore'], function(_) { ...@@ -87,7 +87,7 @@ define(['underscore'], function(_) {
}; };
var expectSettingsSectionsAndFieldsToBeRendered = function (accountSettingsView, fieldsAreRendered) { var expectSettingsSectionsAndFieldsToBeRendered = function (accountSettingsView, fieldsAreRendered) {
var sectionsData = accountSettingsView.options.sectionsData; var sectionsData = accountSettingsView.options.tabSections.aboutTabSections;
var sectionElements = accountSettingsView.$('.section'); var sectionElements = accountSettingsView.$('.section');
expect(sectionElements.length).toBe(sectionsData.length); expect(sectionElements.length).toBe(sectionsData.length);
......
;(function (define, undefined) {
'use strict';
define([
'gettext',
'jquery',
'underscore',
'backbone',
'text!templates/student_account/account_settings_section.underscore'
], function (gettext, $, _, Backbone, sectionTemplate) {
var AccountSectionView = Backbone.View.extend({
initialize: function (options) {
this.options = options;
},
render: function () {
this.$el.html(_.template(sectionTemplate)({
sections: this.options.sections,
activeTabName: this.options.activeTabName
}));
}
});
return AccountSectionView;
});
}).call(this, define || RequireJS.define);
...@@ -2,30 +2,35 @@ ...@@ -2,30 +2,35 @@
'use strict'; 'use strict';
define([ define([
'gettext', 'jquery', 'underscore', 'backbone', 'logger', 'gettext', 'jquery', 'underscore', 'backbone', 'logger',
'js/views/fields',
'js/student_account/models/user_account_model', 'js/student_account/models/user_account_model',
'js/student_account/models/user_preferences_model', 'js/student_account/models/user_preferences_model',
'js/student_account/views/account_settings_fields', 'js/student_account/views/account_settings_fields',
'js/student_account/views/account_settings_view' 'js/student_account/views/account_settings_view'
], function (gettext, $, _, Backbone, Logger, FieldViews, UserAccountModel, UserPreferencesModel, ], function (gettext, $, _, Backbone, Logger, UserAccountModel, UserPreferencesModel,
AccountSettingsFieldViews, AccountSettingsView) { AccountSettingsFieldViews, AccountSettingsView) {
return function (fieldsData, authData, userAccountsApiUrl, userPreferencesApiUrl, accountUserId, platformName) { return function (fieldsData, authData, userAccountsApiUrl, userPreferencesApiUrl, accountUserId, platformName) {
var accountSettingsElement, userAccountModel, userPreferencesModel, aboutSectionsData,
accountsSectionData, accountSettingsView, showAccountSettingsPage, showLoadingError;
var accountSettingsElement = $('.wrapper-account-settings'); accountSettingsElement = $('.wrapper-account-settings');
var userAccountModel = new UserAccountModel(); userAccountModel = new UserAccountModel();
userAccountModel.url = userAccountsApiUrl; userAccountModel.url = userAccountsApiUrl;
var userPreferencesModel = new UserPreferencesModel(); userPreferencesModel = new UserPreferencesModel();
userPreferencesModel.url = userPreferencesApiUrl; userPreferencesModel.url = userPreferencesApiUrl;
var sectionsData = [ aboutSectionsData = [
{ {
title: gettext('Basic Account Information (required)'), title: gettext('Basic Account Information'),
subtitle: gettext(
'These settings include basic information about your account. You can also ' +
'specify additional information and see your linked social accounts on this page.'
),
fields: [ fields: [
{ {
view: new FieldViews.ReadonlyFieldView({ view: new AccountSettingsFieldViews.ReadonlyFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Username'), title: gettext('Username'),
valueAttribute: 'username', valueAttribute: 'username',
...@@ -35,7 +40,7 @@ ...@@ -35,7 +40,7 @@
}) })
}, },
{ {
view: new FieldViews.TextFieldView({ view: new AccountSettingsFieldViews.TextFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Full Name'), title: gettext('Full Name'),
valueAttribute: 'name', valueAttribute: 'name',
...@@ -60,12 +65,14 @@ ...@@ -60,12 +65,14 @@
view: new AccountSettingsFieldViews.PasswordFieldView({ view: new AccountSettingsFieldViews.PasswordFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Password'), title: gettext('Password'),
screenReaderTitle: gettext('Reset your Password'), screenReaderTitle: gettext('Reset Your Password'),
valueAttribute: 'password', valueAttribute: 'password',
emailAttribute: 'email', emailAttribute: 'email',
linkTitle: gettext('Reset Password'), linkTitle: gettext('Reset Your Password'),
linkHref: fieldsData.password.url, linkHref: fieldsData.password.url,
helpMessage: gettext('When you click "Reset Password", a message will be sent to your email address. Click the link in the message to reset your password.') helpMessage: gettext('When you click "Reset Your Password", edX will send a message ' +
'to the email address for your edX account. Click the link in the message to ' +
'reset your password.')
}) })
}, },
{ {
...@@ -83,7 +90,7 @@ ...@@ -83,7 +90,7 @@
}) })
}, },
{ {
view: new FieldViews.DropdownFieldView({ view: new AccountSettingsFieldViews.DropdownFieldView({
model: userAccountModel, model: userAccountModel,
required: true, required: true,
title: gettext('Country or Region'), title: gettext('Country or Region'),
...@@ -95,10 +102,10 @@ ...@@ -95,10 +102,10 @@
] ]
}, },
{ {
title: gettext('Additional Information (optional)'), title: gettext('Additional Information'),
fields: [ fields: [
{ {
view: new FieldViews.DropdownFieldView({ view: new AccountSettingsFieldViews.DropdownFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Education Completed'), title: gettext('Education Completed'),
valueAttribute: 'level_of_education', valueAttribute: 'level_of_education',
...@@ -107,7 +114,7 @@ ...@@ -107,7 +114,7 @@
}) })
}, },
{ {
view: new FieldViews.DropdownFieldView({ view: new AccountSettingsFieldViews.DropdownFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Gender'), title: gettext('Gender'),
valueAttribute: 'gender', valueAttribute: 'gender',
...@@ -116,7 +123,7 @@ ...@@ -116,7 +123,7 @@
}) })
}, },
{ {
view: new FieldViews.DropdownFieldView({ view: new AccountSettingsFieldViews.DropdownFieldView({
model: userAccountModel, model: userAccountModel,
title: gettext('Year of Birth'), title: gettext('Year of Birth'),
valueAttribute: 'year_of_birth', valueAttribute: 'year_of_birth',
...@@ -137,16 +144,17 @@ ...@@ -137,16 +144,17 @@
} }
]; ];
if (_.isArray(authData.providers)) { accountsSectionData = [
var accountsSectionData = { {
title: gettext('Connected Accounts'), title: gettext('Linked Accounts'),
subtitle: gettext(
'You can link your social media accounts to your edX account to make signing in to edx.org ' +
'and the edX mobile apps easier.'
),
fields: _.map(authData.providers, function(provider) { fields: _.map(authData.providers, function(provider) {
return { return {
'view': new AccountSettingsFieldViews.AuthFieldView({ 'view': new AccountSettingsFieldViews.AuthFieldView({
title: provider.name, title: provider.name,
screenReaderTitle: interpolate_text(
gettext("Connect your {accountName} account"), {accountName: provider['name']}
),
valueAttribute: 'auth-' + provider.id, valueAttribute: 'auth-' + provider.id,
helpMessage: '', helpMessage: '',
connected: provider.connected, connected: provider.connected,
...@@ -156,24 +164,23 @@ ...@@ -156,24 +164,23 @@
}) })
}; };
}) })
}; }
sectionsData.push(accountsSectionData); ];
}
var accountSettingsView = new AccountSettingsView({ accountSettingsView = new AccountSettingsView({
model: userAccountModel, model: userAccountModel,
accountUserId: accountUserId, accountUserId: accountUserId,
el: accountSettingsElement, el: accountSettingsElement,
sectionsData: sectionsData tabSections: {
aboutTabSections: aboutSectionsData,
accountsTabSections: accountsSectionData
},
userPreferencesModel: userPreferencesModel
}); });
accountSettingsView.render(); accountSettingsView.render();
var showLoadingError = function () { showAccountSettingsPage = function () {
accountSettingsView.showLoadingError();
};
var showAccountFields = function () {
// Record that the account settings page was viewed. // Record that the account settings page was viewed.
Logger.log('edx.user.settings.viewed', { Logger.log('edx.user.settings.viewed', {
page: "account", page: "account",
...@@ -185,11 +192,15 @@ ...@@ -185,11 +192,15 @@
accountSettingsView.renderFields(); accountSettingsView.renderFields();
}; };
showLoadingError = function () {
accountSettingsView.showLoadingError();
};
userAccountModel.fetch({ userAccountModel.fetch({
success: function () { success: function () {
// Fetch the user preferences model // Fetch the user preferences model
userPreferencesModel.fetch({ userPreferencesModel.fetch({
success: showAccountFields, success: showAccountSettingsPage,
error: showLoadingError error: showLoadingError
}); });
}, },
......
;(function (define, undefined) { ;(function (define, undefined) {
'use strict'; 'use strict';
define([ define([
'gettext', 'jquery', 'underscore', 'backbone', 'text!templates/student_account/account_settings.underscore' 'gettext',
], function (gettext, $, _, Backbone, accountSettingsTemplate) { 'jquery',
'underscore',
'backbone',
'js/student_account/views/account_section_view',
'text!templates/student_account/account_settings.underscore'
], function (gettext, $, _, Backbone, AccountSectionView, accountSettingsTemplate) {
var AccountSettingsView = Backbone.View.extend({ var AccountSettingsView = Backbone.View.extend({
navLink: '.account-nav-link',
activeTab: 'aboutTabSections',
accountSettingsTabs: [
{name: 'aboutTabSections', id: 'about-tab', label: gettext('Account Information'), class: 'active'},
{name: 'accountsTabSections', id: 'accounts-tab', label: gettext('Linked Accounts')}
],
events: {
'click .account-nav-link': 'changeTab'
},
initialize: function (options) { initialize: function (options) {
this.options = _.extend({}, options); this.options = options;
_.bindAll(this, 'render', 'renderFields', 'showLoadingError'); _.bindAll(this, 'render', 'changeTab', 'renderFields', 'showLoadingError');
}, },
render: function () { render: function () {
this.$el.html(_.template(accountSettingsTemplate)({ this.$el.html(_.template(accountSettingsTemplate)({
sections: this.options.sectionsData accountSettingsTabs: this.accountSettingsTabs
})); }));
this.renderSection(this.options.tabSections[this.activeTab]);
return this; return this;
}, },
renderFields: function () { changeTab: function(e) {
this.$('.ui-loading-indicator').addClass('is-hidden'); var $currentTab;
e.preventDefault();
$currentTab = $(e.target);
this.activeTab = $currentTab.data('name');
this.renderSection(this.options.tabSections[this.activeTab]);
this.renderFields();
$(this.navLink).removeClass('active');
$currentTab.addClass('active');
$(this.navLink).removeAttr('aria-describedby');
$currentTab.attr('aria-describedby', 'header-subtitle-'+this.activeTab);
},
renderSection: function (tabSections) {
var accountSectionView = new AccountSectionView({
activeTabName: this.activeTab,
sections: tabSections,
el: '.account-settings-sections'
});
accountSectionView.render();
},
renderFields: function () {
var view = this; var view = this;
_.each(this.$('.account-settings-section-body'), function (sectionEl, index) { view.$('.ui-loading-indicator').addClass('is-hidden');
_.each(view.options.sectionsData[index].fields, function (field) {
_.each(view.$('.account-settings-section-body'), function (sectionEl, index) {
_.each(view.options.tabSections[view.activeTab][index].fields, function (field) {
$(sectionEl).append(field.view.render().el); $(sectionEl).append(field.view.render().el);
}); });
}); });
......
...@@ -230,8 +230,15 @@ ...@@ -230,8 +230,15 @@
}, },
finishEditing: function() { finishEditing: function() {
var modelValue;
if (this.persistChanges === false || this.mode !== 'edit') {return;} if (this.persistChanges === false || this.mode !== 'edit') {return;}
if (this.fieldValue() !== this.modelValue()) {
modelValue = this.modelValue();
if (!(_.isUndefined(modelValue) || _.isNull(modelValue))) {
modelValue = modelValue.toString();
}
if (this.fieldValue() !== modelValue) {
this.saveValue(); this.saveValue();
} else { } else {
if (this.editable === 'always') { if (this.editable === 'always') {
......
...@@ -518,7 +518,6 @@ $dark-gray: rgb(51, 51, 51) !default; ...@@ -518,7 +518,6 @@ $dark-gray: rgb(51, 51, 51) !default;
$border-color: rgb(200, 200, 200) !default; $border-color: rgb(200, 200, 200) !default;
$sidebar-color: rgb(246, 246, 246) !default; $sidebar-color: rgb(246, 246, 246) !default;
$outer-border-color: $gray-l3; $outer-border-color: $gray-l3;
$light-gray: rgb(221,221,221) !default;
// used by descriptor css // used by descriptor css
$lightGrey: rgb(237,241,245) !default; $lightGrey: rgb(237,241,245) !default;
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
.wrapper-footer { .wrapper-footer {
@extend %ui-print-excluded; @extend %ui-print-excluded;
margin-top: ($baseline*2) + px;
box-shadow: 0 -1px 5px 0 $shadow-l1; box-shadow: 0 -1px 5px 0 $shadow-l1;
border-top: 1px solid tint(palette(grayscale, light), 50%); border-top: 1px solid tint(palette(grayscale, light), 50%);
padding: 25px ($baseline/2 + px) ($baseline*1.5 + px) ($baseline/2 + px); padding: 25px ($baseline/2 + px) ($baseline*1.5 + px) ($baseline/2 + px);
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
.wrapper-footer { .wrapper-footer {
@extend %ui-print-excluded; @extend %ui-print-excluded;
margin-top: ($baseline*2);
box-shadow: 0 -1px 5px 0 $shadow-l1; box-shadow: 0 -1px 5px 0 $shadow-l1;
border-top: 1px solid tint($m-gray, 50%); border-top: 1px solid tint($m-gray, 50%);
padding: 25px ($baseline/2) ($baseline*1.5) ($baseline/2); padding: 25px ($baseline/2) ($baseline*1.5) ($baseline/2);
......
...@@ -9,11 +9,13 @@ ...@@ -9,11 +9,13 @@
// +Container - Account Settings // +Container - Account Settings
.wrapper-account-settings { .wrapper-account-settings {
@extend .container; background: $white;
padding-top: ($baseline*2); width: 100%;
.account-settings-container { .account-settings-container {
padding: 0; max-width: grid-width(12);
padding: 10px;
margin: 0 auto;
} }
.ui-loading-indicator, .ui-loading-indicator,
...@@ -36,15 +38,59 @@ ...@@ -36,15 +38,59 @@
.wrapper-account-settings { .wrapper-account-settings {
.wrapper-header { .wrapper-header {
max-width: grid-width(12);
height: 139px;
border-bottom: 4px solid $m-gray-l4;
.header-title { .header-title {
@extend %t-title4; @extend %t-title4;
margin-bottom: ($baseline/2); margin-bottom: ($baseline/2);
padding-top: ($baseline*2);
} }
.header-subtitle { .header-subtitle {
color: $gray-l2; color: $gray-l2;
} }
.account-nav {
@include float(left);
margin: ($baseline/2) 0;
padding: 0;
list-style: none;
.account-nav-item {
@include float(left);
display: flex;
margin: 0;
text-transform: none;
justify-content: center;
.account-nav-link {
font-size: em(14);
color: $gray;
padding: 5px 25px 23px;
display: inline-block;
border-radius: 0;
}
button {
@extend %ui-clear-button;
@extend %btn-no-style;
@include appearance(none);
display:block;
padding: ($baseline/4);
&:hover,
&:focus {
text-decoration: none;
border-bottom: 4px solid $courseware-border-bottom-color !important;
}
&.active{
border-bottom: 4px solid $black-t3 !important;
}
}
}
}
} }
} }
...@@ -54,27 +100,189 @@ ...@@ -54,27 +100,189 @@
.section-header { .section-header {
@extend %t-title6; @extend %t-title6;
@extend %t-strong; @extend %t-strong;
padding-bottom: ($baseline/2); padding-top: ($baseline/2)*3;
border-bottom: 1px solid $gray-l4; color: $dark-gray1;
} }
.section { .section {
background-color: $white; background-color: $white;
padding: $baseline;
margin-top: $baseline; margin-top: $baseline;
border: 1px solid $gray-l4; border-bottom: 4px solid $m-gray-l4;
box-shadow: 0 0 1px 1px $shadow-l2;
border-radius: 5px;
}
a span { .account-settings-header-subtitle {
color: $link-color; font-size: em(18);
} line-height: normal;
color: $dark-gray;
padding-top: 20px;
padding-bottom: 10px;
}
.account-settings-section-body {
.u-field {
border-bottom: 2px solid $m-gray-l4;
.field {
width: 30%;
vertical-align: top;
display: inline-block;
position: relative;
select {
@include appearance(none);
padding: 14px 30px 14px 15px;
border: 1px solid $light-gray;
background-color: transparent;
border-radius: 2px;
position: relative;
z-index: 10;
&::-ms-expand{
display: none;
}
~ .icon-caret-down{
&:after{
content: "";
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 7px solid $blue;
position: absolute;
right: 10px;
bottom: 20px;
z-index: 0;
}
}
}
.field-label {
display: block;
width: auto;
margin-bottom: 0.625rem;
font-size: 1rem;
line-height: 1;
color: $dark-gray;
}
.field-input {
@include transition(all 0.125s ease-in-out 0s);
display: inline-block;
padding: 0.625rem;
border: 1px solid $light-gray;
border-radius: 2px;
background: $white;
font-size: $body-font-size;
color: $dark-gray;
width: 100%;
height: 48px;
box-shadow: none;
}
.u-field-link {
@extend %ui-clear-button;
// set styles
@extend %btn-pl-default-base;
@include font-size(18);
width: 100%;
border: 1px solid $blue;
color: $blue;
padding: 11px 14px;
line-height: normal;
}
#u-field-value-username {
padding-top: ($baseline/2);
}
}
.social-field-linked {
background: $m-gray-l4;
box-shadow: 0 1px 2px 1px $shadow-l2;
padding: 1.25rem;
box-sizing: border-box;
margin: 10px;
width: 100%;
.field-label {
@include font-size(24);
}
.u-field-social-help {
display: inline-block;
padding: 20px 0 6px;
}
.u-field-link {
@include font-size(14);
@include text-align(left);
border: none;
margin-top: $baseline;
font-weight: $font-semibold;
padding: 0;
&:focus, &:hover, &:active {
background-color: transparent;
color: $m-blue-d3;
border: none;
}
}
}
.social-field-unlinked {
background: $m-gray-l4;
box-shadow: 0 1px 2px 1px $shadow-l2;
padding: 1.25rem;
box-sizing: border-box;
text-align: center;
margin: 10px;
width: 100%;
.field-label {
@include font-size(24);
text-align: center;
}
.u-field-link {
@include font-size(14);
margin-top: $baseline;
font-weight: $font-semibold;
}
}
.u-field-message {
position: relative;
padding: 24px 0 0 ($baseline*5);
.u-field-message-notification {
position: absolute;
left: 0;
top: 0;
bottom: 0;
margin: auto;
padding: 38px 0 0 ($baseline*5);
}
}
&:last-child {
border-bottom: none;
margin-bottom: ($baseline*2);
}
}
.u-field-social {
border-bottom: none;
margin-right: 20px;
width: 30%;
display: inline-block;
vertical-align: top;
.u-field-social-help {
@include font-size(12);
color: $m-gray-d1;
}
}
}
a span { &:last-child {
&:hover, &:focus { border-bottom: none;
color: $pink;
text-decoration: none !important;
} }
} }
} }
<div class="u-field-value field">
<% if (editable !== 'never') { %>
<% if (title && titleVisible) { %>
<label class="u-field-title field-label" for="u-field-select-<%- id %>">
<%- title %>
</label>
<% } else { %>
<label class="sr" for="u-field-select-<%- id %>">
<%- screenReaderTitle %>
</label>
<% } %>
<% } %>
<% if (iconName) { %>
<span class="u-field-icon icon fa <%- iconName %> fa-fw" aria-hidden="true"></span>
<% } %>
<% if (editable === 'never') { %>
<span class="sr"><%- screenReaderTitle %></span>
<span class="u-field-value-readonly"></span>
<% } else { %>
<select name="select" id="u-field-select-<%- id %>" aria-describedby="u-field-help-message-<%- id %>">
<% if (showBlankOption) { %>
<option value=""></option>
<% } %>
<% _.each(selectOptions, function(selectOption) { %>
<option value="<%- selectOption[0] %>"><%- selectOption[1] %></option>
<% }); %>
</select>
<span class="icon-caret-down" aria-hidden="true"></span>
<button class="u-field-value-display">
<span class="sr"><%- screenReaderTitle %> &nbsp;</span>
<span class="u-field-value-readonly"></span>
<span class="sr"><%- gettext('Click to edit') %></span>
</button>
<% } %>
</div>
<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-help-message-<%- id %>"><%- message %></span>
</span>
<div class="u-field-value field">
<span class="u-field-title field-label"><%- title %></span>
<button class="u-field-link u-field-link-title-<%- id %> " id="u-field-link-<%- id %>" aria-describedby="u-field-message-help-<%- id %>"><%- linkTitle %></button>
</div>
<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 class="field">
<span class="u-field-title field-label"><%- title %></span>
<span class="u-field-value" id="u-field-value-<%- id %>" aria-describedby="u-field-message-help-<%- id %>"><b><%- value %></b></span>
</div>
<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 class="u-field-value field <%- linkClass %>">
<h4 class="u-field-title field-label"><%- title %></h4>
<span class="u-field-social-help"><%- subTitle %></span>
<a class="u-field-link" id="u-field-link-<%- id %>" href="<%- linkHref %>" aria-describedby="u-field-message-help-<%- id %>">
<span class="sr"><%- screenReaderTitle %></span>
<span class="u-field-link-title-<%- id %>" aria-hidden="true"><%- linkTitle %></span>
</a>
</div>
<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 class="u-field-value field">
<label class="u-field-title field-label" for="field-input-<%- id %>"><%- title %></label>
<input class="field-input input-text" type="text" id="field-input-<%- id %>" title="Input field for <%- id %>" aria-describedby="u-field-message-help-<%- id %>" name="input" value="<%- value %>" />
</div>
<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>
...@@ -2,25 +2,16 @@ ...@@ -2,25 +2,16 @@
<div class="account-settings-container"> <div class="account-settings-container">
<div class="wrapper-header"> <div class="wrapper-header">
<h2 class="header-title"><%- gettext("Account Settings") %></h2> <h2 class="header-title"><%- gettext("Account Settings") %></h2>
<small class="account-settings-header-subtitle"><%- gettext("These settings include basic information about your account. You can also specify additional information and see your linked social accounts on this page.") %></small> <ul class="left list-inline account-nav">
<% _.each(accountSettingsTabs, function(tab) { %>
<li class="account-nav-item">
<button id="<%- tab.id %>" data-name="<%- tab.name %>" <% if (tab.class) { %> aria-describedby="header-subtitle-<%- tab.name %>" <% } %> class="account-nav-link <%- tab.class %>" ><%- tab.label %></button>
</li>
<% }); %>
</ul>
</div> </div>
<div class="account-settings-sections"> <div class="account-settings-sections">
<% _.each(sections, function(section) { %>
<div class="section">
<h3 class="section-header"><%- gettext(section.title) %></h3>
<div class="account-settings-section-body">
<div class="ui-loading-indicator">
<span class="spin"><i class="icon fa fa-refresh" aria-hidden=true></i></span>
<span class="copy"><%- gettext("Loading") %></span>
</div>
<div class="ui-loading-error is-hidden">
<i class="fa fa-exclamation-triangle message-error" aria-hidden=true></i>
<span class="copy"><%- gettext("An error occurred. Please reload the page.") %></span>
</div>
</div>
</div>
<% }); %>
</div> </div>
</div> </div>
</main> </main>
<% _.each(sections, function(section) { %>
<div class="section">
<% if (section.subtitle) { %>
<p id="header-subtitle-<%- activeTabName %>" class="account-settings-header-subtitle"><%- gettext(section.subtitle) %></p>
<% } %>
<h3 class="section-header"><%- gettext(section.title) %></h3>
<div class="account-settings-section-body">
<div class="ui-loading-indicator">
<span class="spin"><span class="icon fa fa-refresh" aria-hidden=true></span></span>
<span class="copy"><%- gettext("Loading") %></span>
</div>
<div class="ui-loading-error is-hidden">
<span class="fa fa-exclamation-triangle message-error" aria-hidden=true></span>
<span class="copy"><%- gettext("An error occurred. Please reload the page.") %></span>
</div>
</div>
</div>
<% }); %>
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