diff --git a/common/test/acceptance/pages/lms/account_settings.py b/common/test/acceptance/pages/lms/account_settings.py index 72b09b7f81..793f714b45 100644 --- a/common/test/acceptance/pages/lms/account_settings.py +++ b/common/test/acceptance/pages/lms/account_settings.py @@ -33,7 +33,7 @@ class AccountSettingsPage(FieldsMixin, PageObject): """ structure = [] - sections = self.q(css='.section') + sections = self.q(css='#aboutTabSections-tabpanel .section') for section in sections: section_title_element = section.find_element_by_class_name('section-header') field_title_elements = section.find_elements_by_class_name('u-field-title') diff --git a/lms/static/js/spec/student_account/account_settings_factory_spec.js b/lms/static/js/spec/student_account/account_settings_factory_spec.js index fd417507c2..ff99a4af7e 100644 --- a/lms/static/js/spec/student_account/account_settings_factory_spec.js +++ b/lms/static/js/spec/student_account/account_settings_factory_spec.js @@ -73,18 +73,14 @@ define(['backbone', var accountSettingsView = createAccountSettingsPage(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); var request = requests[0]; expect(request.method).toBe('GET'); expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL); AjaxHelpers.respondWithError(requests, 500); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, false); Helpers.expectLoadingErrorIsVisible(accountSettingsView, true); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); }); @@ -93,18 +89,14 @@ define(['backbone', var accountSettingsView = createAccountSettingsPage(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); var request = requests[0]; expect(request.method).toBe('GET'); expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL); AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData()); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); request = requests[1]; expect(request.method).toBe('GET'); @@ -116,9 +108,7 @@ define(['backbone', expect(request.url).toBe(Helpers.USER_PREFERENCES_API_URL); AjaxHelpers.respondWithError(requests, 500); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, false); Helpers.expectLoadingErrorIsVisible(accountSettingsView, true); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); }); it('renders fields after the models are successfully fetched', function() { @@ -126,15 +116,14 @@ define(['backbone', var accountSettingsView = createAccountSettingsPage(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData()); AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE); AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData()); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, false); + accountSettingsView.render(); + Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); Helpers.expectSettingsSectionsAndFieldsToBeRendered(accountSettingsView); }); diff --git a/lms/static/js/spec/student_account/account_settings_view_spec.js b/lms/static/js/spec/student_account/account_settings_view_spec.js index 638fd119f0..f171762b53 100644 --- a/lms/static/js/spec/student_account/account_settings_view_spec.js +++ b/lms/static/js/spec/student_account/account_settings_view_spec.js @@ -71,26 +71,16 @@ define(['backbone', var accountSettingsView = createAccountSettingsView(); accountSettingsView.render(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); accountSettingsView.showLoadingError(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, false); Helpers.expectLoadingErrorIsVisible(accountSettingsView, true); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); }); it('renders all fields as expected', function() { var accountSettingsView = createAccountSettingsView(); accountSettingsView.render(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, true); - Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsButNotFieldsToBeRendered(accountSettingsView); - - accountSettingsView.renderFields(); - Helpers.expectLoadingIndicatorIsVisible(accountSettingsView, false); Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); Helpers.expectSettingsSectionsAndFieldsToBeRendered(accountSettingsView); }); diff --git a/lms/static/js/spec/student_account/helpers.js b/lms/static/js/spec/student_account/helpers.js index 0bfc695ef1..fbbce9d8d4 100644 --- a/lms/static/js/spec/student_account/helpers.js +++ b/lms/static/js/spec/student_account/helpers.js @@ -95,7 +95,7 @@ define(['underscore'], function(_) { var expectSettingsSectionsAndFieldsToBeRendered = function(accountSettingsView, fieldsAreRendered) { var sectionsData = accountSettingsView.options.tabSections.aboutTabSections; - var sectionElements = accountSettingsView.$('.section'); + var sectionElements = accountSettingsView.$('#aboutTabSections-tabpanel .section'); expect(sectionElements.length).toBe(sectionsData.length); _.each(sectionElements, function(sectionElement, sectionIndex) { diff --git a/lms/static/js/student_account/views/account_section_view.js b/lms/static/js/student_account/views/account_section_view.js index cd022eee9d..06339b750b 100644 --- a/lms/static/js/student_account/views/account_section_view.js +++ b/lms/static/js/student_account/views/account_section_view.js @@ -11,13 +11,28 @@ initialize: function(options) { this.options = options; + _.bindAll(this, 'render', 'renderFields'); }, render: function() { this.$el.html(_.template(sectionTemplate)({ sections: this.options.sections, - activeTabName: this.options.activeTabName + tabName: this.options.tabName, + tabLabel: this.options.tabLabel })); + + this.renderFields(); + }, + + renderFields: function() { + var view = this; + + _.each(view.$('.' + view.options.tabName + '-section-body'), function(sectionEl, index) { + _.each(view.options.sections[index].fields, function(field) { + $(sectionEl).append(field.view.render().el); + }); + }); + return this; } }); diff --git a/lms/static/js/student_account/views/account_settings_factory.js b/lms/static/js/student_account/views/account_settings_factory.js index fc92c267da..86fca0e190 100644 --- a/lms/static/js/student_account/views/account_settings_factory.js +++ b/lms/static/js/student_account/views/account_settings_factory.js @@ -265,9 +265,6 @@ visibility: null, user_id: accountUserId }); - - // Render the fields - accountSettingsView.renderFields(); }; showLoadingError = function() { diff --git a/lms/static/js/student_account/views/account_settings_fields.js b/lms/static/js/student_account/views/account_settings_fields.js index 1e350b22cc..3ce7ce8546 100644 --- a/lms/static/js/student_account/views/account_settings_fields.js +++ b/lms/static/js/student_account/views/account_settings_fields.js @@ -164,12 +164,20 @@ data: data, success: function() { view.showSuccessMessage(); + view.setMessageTimeout(); }, error: function(xhr) { view.showErrorMessage(xhr); + view.setMessageTimeout(); } }); }, + setMessageTimeout: function() { + var view = this; + setTimeout(function() { + view.showHelpMessage(); + }, 6000); + }, successMessage: function() { return HtmlUtils.joinHtml( this.indicators.success, diff --git a/lms/static/js/student_account/views/account_settings_view.js b/lms/static/js/student_account/views/account_settings_view.js index 3271154c67..f176047589 100644 --- a/lms/static/js/student_account/views/account_settings_view.js +++ b/lms/static/js/student_account/views/account_settings_view.js @@ -4,77 +4,109 @@ 'gettext', 'jquery', 'underscore', - 'backbone', + 'common/js/components/views/tabbed_view', 'edx-ui-toolkit/js/utils/html-utils', 'js/student_account/views/account_section_view', 'text!templates/student_account/account_settings.underscore' - ], function(gettext, $, _, Backbone, HtmlUtils, AccountSectionView, accountSettingsTemplate) { - var AccountSettingsView = Backbone.View.extend({ + ], function(gettext, $, _, TabbedView, HtmlUtils, AccountSectionView, accountSettingsTemplate) { + var AccountSettingsView = TabbedView.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')}, - {name: 'ordersTabSections', id: 'orders-tab', label: gettext('Order History')} + { + name: 'aboutTabSections', + id: 'about-tab', + label: gettext('Account Information'), + class: 'active', + tabindex: 0, + selected: true, + expanded: true + }, + { + name: 'accountsTabSections', + id: 'accounts-tab', + label: gettext('Linked Accounts'), + tabindex: -1, + selected: false, + expanded: false + }, + { + name: 'ordersTabSections', + id: 'orders-tab', + label: gettext('Order History'), + tabindex: -1, + selected: false, + expanded: false + } ], events: { - 'click .account-nav-link': 'changeTab' + 'click .account-nav-link': 'switchTab', + 'keydown .account-nav-link': 'keydownHandler' }, initialize: function(options) { this.options = options; - _.bindAll(this, 'render', 'changeTab', 'renderFields', 'showLoadingError'); + _.bindAll(this, 'render', 'switchTab', 'setActiveTab', 'showLoadingError'); }, render: function() { + var tabName, + view = this; HtmlUtils.setHtml(this.$el, HtmlUtils.template(accountSettingsTemplate)({ accountSettingsTabs: this.accountSettingsTabs })); - this.renderSection(this.options.tabSections[this.activeTab]); + _.each(view.accountSettingsTabs, function(tab) { + tabName = tab.name; + view.renderSection(view.options.tabSections[tabName], tabName, tab.label); + }); return this; }, - changeTab: function(e) { - var $currentTab; + switchTab: function(e) { + var $currentTab, + $accountNavLink = $('.account-nav-link'); - e.preventDefault(); - $currentTab = $(e.target); - this.activeTab = $currentTab.data('name'); - this.renderSection(this.options.tabSections[this.activeTab]); - this.renderFields(); + if (e) { + e.preventDefault(); + $currentTab = $(e.target); + this.activeTab = $currentTab.data('name'); - $(this.navLink).removeClass('active'); - $currentTab.addClass('active'); + _.each(this.$('.account-settings-tabpanels'), function(tabPanel) { + $(tabPanel).addClass('hidden'); + }); - $(this.navLink).removeAttr('aria-describedby'); - $currentTab.attr('aria-describedby', 'header-subtitle-' + this.activeTab); + $('#' + this.activeTab + '-tabpanel').removeClass('hidden'); + + $accountNavLink.attr('tabindex', -1); + $accountNavLink.attr('aria-selected', false); + $accountNavLink.attr('aria-expanded', false); + + $currentTab.attr('tabindex', 0); + $currentTab.attr('aria-selected', true); + $currentTab.attr('aria-expanded', true); + + $(this.navLink).removeClass('active'); + $currentTab.addClass('active'); + } }, - renderSection: function(tabSections) { + setActiveTab: function() { + this.switchTab(); + }, + + renderSection: function(tabSections, tabName, tabLabel) { var accountSectionView = new AccountSectionView({ - activeTabName: this.activeTab, + tabName: tabName, + tabLabel: tabLabel, sections: tabSections, - el: '.account-settings-sections' + el: '#' + tabName + '-tabpanel' }); accountSectionView.render(); }, - renderFields: function() { - var view = this; - view.$('.ui-loading-indicator').addClass('is-hidden'); - - _.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); - }); - }); - return this; - }, - showLoadingError: function() { - this.$('.ui-loading-indicator').addClass('is-hidden'); this.$('.ui-loading-error').removeClass('is-hidden'); } }); diff --git a/lms/static/sass/views/_account-settings.scss b/lms/static/sass/views/_account-settings.scss index de6cde161c..6a7fe19802 100644 --- a/lms/static/sass/views/_account-settings.scss +++ b/lms/static/sass/views/_account-settings.scss @@ -58,36 +58,32 @@ padding: 0; list-style: none; - .account-nav-item { + .account-nav-link { @include float(left); - display: flex; - margin: 0; - text-transform: none; - justify-content: center; + font-size: em(14); + color: $gray; + padding: 5px 25px 23px; + display: inline-block; + box-shadow: none; + border: none; + border-radius: 0; + background: transparent none; + } - .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; } - - 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; - } + &.active{ + border-bottom: 4px solid $black-t3 !important; } } } diff --git a/lms/templates/student_account/account_settings.underscore b/lms/templates/student_account/account_settings.underscore index d2a168630c..87e8d8ec00 100644 --- a/lms/templates/student_account/account_settings.underscore +++ b/lms/templates/student_account/account_settings.underscore @@ -2,16 +2,18 @@
<%- section.subtitle %>
+<%- section.subtitle %>
<% } %>