Commit 57f0999b by Anthony Mangano

improve screen reader support on logistration pages

ECOM-6107
parent 82647ee8
...@@ -356,10 +356,10 @@ class CombinedLoginAndRegisterPage(PageObject): ...@@ -356,10 +356,10 @@ class CombinedLoginAndRegisterPage(PageObject):
"""Wait for a status message to be visible following third_party registration, then return it.""" """Wait for a status message to be visible following third_party registration, then return it."""
def _check_func(): def _check_func():
"""Return third party auth status notice message.""" """Return third party auth status notice message."""
for selector in ['.already-authenticated-msg p', '.status p']: selector = '.js-auth-warning p'
msg_element = self.q(css=selector) msg_element = self.q(css=selector)
if msg_element.visible: if msg_element.visible:
return (True, msg_element.text[0]) return (True, msg_element.text[0])
return (False, None) return (False, None)
return Promise(_check_func, "Result of third party auth is visible").fulfill() return Promise(_check_func, "Result of third party auth is visible").fulfill()
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
tpl: formViewTpl, tpl: formViewTpl,
fieldTpl: formFieldTpl, fieldTpl: formFieldTpl,
formType: 'financial-assistance', formType: 'financial-assistance',
successTpl: successTpl,
defaultFormErrorsTitle: gettext('Unable to submit application'),
requiredStr: '', requiredStr: '',
submitButton: '.js-submit-form', submitButton: '.js-submit-form',
...@@ -81,7 +83,7 @@ ...@@ -81,7 +83,7 @@
}, },
renderSuccess: function() { renderSuccess: function() {
this.$el.html(_.template(successTpl)({ this.$el.html(_.template(this.successTpl)({
course: this.model.get('course'), course: this.model.get('course'),
dashboard_url: this.context.dashboard_url dashboard_url: this.context.dashboard_url
})); }));
...@@ -102,8 +104,7 @@ ...@@ -102,8 +104,7 @@
} }
this.errors = ['<li>' + msg + '</li>']; this.errors = ['<li>' + msg + '</li>'];
this.setErrors(); this.renderErrors(this.defaultFormErrorsTitle, this.errors);
this.element.hide(this.$resetSuccess);
this.toggleDisableButton(false); this.toggleDisableButton(false);
}, },
...@@ -112,9 +113,7 @@ ...@@ -112,9 +113,7 @@
}, },
validateCountry: function() { validateCountry: function() {
var $submissionContainer = $('.submission-error'), var $countryLabel = $('#user-country-title'),
$errorMessageContainer = $submissionContainer.find('.message-copy'),
$countryLabel = $('#user-country-title'),
txt = [ txt = [
'Please go to your {link_start}profile page{link_end} ', 'Please go to your {link_start}profile page{link_end} ',
'and provide your country of residence.' 'and provide your country of residence.'
...@@ -130,9 +129,8 @@ ...@@ -130,9 +129,8 @@
if (!this.model.get('country')) { if (!this.model.get('country')) {
$countryLabel.addClass('error'); $countryLabel.addClass('error');
$errorMessageContainer.append('<li>' + msg + '</li>'); this.renderErrors(this.defaultFormErrorsTitle, ['<li>' + msg + '</li>']);
this.toggleDisableButton(true); this.toggleDisableButton(true);
$submissionContainer.removeClass('hidden');
} }
}, },
......
...@@ -133,23 +133,23 @@ define([ ...@@ -133,23 +133,23 @@ define([
failedSubmission = function() { failedSubmission = function() {
expect(view.$('.js-success-message').length).toEqual(0); expect(view.$('.js-success-message').length).toEqual(0);
expect(view.$('.submission-error')).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
validSubmission(); validSubmission();
view.model.trigger('error', {status: 500}); view.model.trigger('error', {status: 500});
expect(view.$('.js-success-message').length).toEqual(0); expect(view.$('.js-success-message').length).toEqual(0);
expect(view.$('.submission-error')).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
}; };
invalidCountry = function() { invalidCountry = function() {
expect(view.$('.js-success-message').length).toEqual(0); expect(view.$('.js-success-message').length).toEqual(0);
expect(view.$('.submission-error')).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
expect(view.$('#user-country-title')).toHaveClass('error'); expect(view.$('#user-country-title')).toHaveClass('error');
expect(view.$('.js-submit-form').prop('disabled')).toBeTruthy(); expect(view.$('.js-submit-form').prop('disabled')).toBeTruthy();
}; };
validCountry = function() { validCountry = function() {
expect(view.$('.js-success-message').length).toEqual(0); expect(view.$('.js-success-message').length).toEqual(0);
expect(view.$('.submission-error')).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
expect(view.$('#user-country-title')).not.toHaveClass('error'); expect(view.$('#user-country-title')).not.toHaveClass('error');
expect(view.$('.js-submit-form').prop('disabled')).toBeFalsy(); expect(view.$('.js-submit-form').prop('disabled')).toBeFalsy();
}; };
...@@ -184,10 +184,10 @@ define([ ...@@ -184,10 +184,10 @@ define([
}); });
it('should not submit the form if the front end validation fails', function() { it('should not submit the form if the front end validation fails', function() {
expect(view.$('.submission-error')).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
view.$('.js-submit-form').click(); view.$('.js-submit-form').click();
expect(view.model.save).not.toHaveBeenCalled(); expect(view.model.save).not.toHaveBeenCalled();
expect(view.$('.submission-error')).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
}); });
it('should submit the form data and additional data if validation passes', function() { it('should submit the form data and additional data if validation passes', function() {
......
...@@ -191,10 +191,10 @@ ...@@ -191,10 +191,10 @@
}); });
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
requests, 'POST', requests, 'POST',
FORM_DESCRIPTION.submit_url, FORM_DESCRIPTION.submit_url,
$.param(expectedData) $.param(expectedData)
); );
}); });
it('displays third-party auth login buttons', function() { it('displays third-party auth login buttons', function() {
...@@ -212,6 +212,21 @@ ...@@ -212,6 +212,21 @@
expect($('.forgot-password')).toBeVisible(); expect($('.forgot-password')).toBeVisible();
}); });
it('displays password reset success message after password reset request', function() {
createLoginView(this);
// Verify that the success message is not visible
expect(view.$formFeedback.find('.' + view.passwordResetSuccessJsHook).length).toEqual(0);
/* After a successful password reset request, the resetModel will trigger a 'sync'
* event, which lets the LoginView know to render the password reset success message.
*/
view.resetModel.trigger('sync');
// Verify that the success message is visible
expect(view.$formFeedback.find('.' + view.passwordResetSuccessJsHook).length).toEqual(1);
});
it('validates login form fields', function() { it('validates login form fields', function() {
createLoginView(this); createLoginView(this);
...@@ -229,10 +244,11 @@ ...@@ -229,10 +244,11 @@
submitForm(false); submitForm(false);
// Verify that submission errors are visible // Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
// Expect auth complete NOT to have been triggered // Expect auth complete NOT to have been triggered
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
// Form button should be re-enabled when errors occur // Form button should be re-enabled when errors occur
expect(view.$submitButton).not.toHaveAttr('disabled'); expect(view.$submitButton).not.toHaveAttr('disabled');
}); });
...@@ -247,8 +263,10 @@ ...@@ -247,8 +263,10 @@
AjaxHelpers.respondWithError(requests); AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed and that auth complete is not triggered // Expect that an error is displayed and that auth complete is not triggered
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
// Form button should be re-enabled on server failure. // Form button should be re-enabled on server failure.
expect(view.$submitButton).not.toHaveAttr('disabled'); expect(view.$submitButton).not.toHaveAttr('disabled');
...@@ -262,17 +280,18 @@ ...@@ -262,17 +280,18 @@
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden and auth complete is triggered // Expect that the error is hidden and auth complete is triggered
expect(view.$errors).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
}); });
it('displays an error if there is no internet connection', function() { it('displays an error if there is no internet connection', function() {
var clock, var clock,
oldTimeout, oldTimeout,
timeout; timeout,
$error;
// We're defining "no internet connection" in this case as the // We're defining "no internet connection" in this case as the
// request timing out. We use a combination of the sinon fake // request timing out. We use a combination of the sinon fake
// timer and jQuery.ajaxSetup() to force a request timeout. // timer and jQuery.ajaxSetup() to force a request timeout.
clock = sinon.useFakeTimers(); clock = sinon.useFakeTimers();
oldTimeout = $.ajaxSetup().timeout; oldTimeout = $.ajaxSetup().timeout;
...@@ -288,11 +307,12 @@ ...@@ -288,11 +307,12 @@
clock.tick(timeout + 1); clock.tick(timeout + 1);
// Expect that an error is displayed and that auth complete is not triggered // Expect that an error is displayed and that auth complete is not triggered
expect(view.$errors).not.toHaveClass('hidden'); $error = view.$formFeedback.find('.' + view.formErrorsJsHook);
expect($error.length).toEqual(1);
expect($error.text()).toContain(
'An error has occurred. Check your Internet connection and try again.'
);
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
expect(view.$errors.text()).toContain(
'An error has occurred. Check your Internet connection and try again.'
);
// Finally, restore the old timeout and turn off the fake timer. // Finally, restore the old timeout and turn off the fake timer.
$.ajaxSetup({timeout: oldTimeout}); $.ajaxSetup({timeout: oldTimeout});
...@@ -300,6 +320,7 @@ ...@@ -300,6 +320,7 @@
}); });
it('displays an error if there is a server error', function() { it('displays an error if there is a server error', function() {
var $error;
createLoginView(this); createLoginView(this);
// Submit the form, with successful validation // Submit the form, with successful validation
...@@ -309,11 +330,12 @@ ...@@ -309,11 +330,12 @@
AjaxHelpers.respondWithError(requests, 500); AjaxHelpers.respondWithError(requests, 500);
// Expect that an error is displayed and that auth complete is not triggered // Expect that an error is displayed and that auth complete is not triggered
expect(view.$errors).not.toHaveClass('hidden'); $error = view.$formFeedback.find('.' + view.formErrorsJsHook);
expect($error.length).toEqual(1);
expect($error.text()).toContain(
'An error has occurred. Try refreshing the page, or check your Internet connection.'
);
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
expect(view.$errors.text()).toContain(
'An error has occurred. Try refreshing the page, or check your Internet connection.'
);
}); });
}); });
}); });
......
...@@ -74,26 +74,32 @@ ...@@ -74,26 +74,32 @@
}); });
it('allows the user to request a new password', function() { it('allows the user to request a new password', function() {
var syncSpy, passwordEmailSentSpy;
createPasswordResetView(this); createPasswordResetView(this);
// We expect these events to be triggered upon a successful password reset
syncSpy = jasmine.createSpy('syncEvent');
passwordEmailSentSpy = jasmine.createSpy('passwordEmailSentEvent');
view.listenTo(view.model, 'sync', syncSpy);
view.listenTo(view, 'password-email-sent', passwordEmailSentSpy);
// Submit the form, with successful validation // Submit the form, with successful validation
submitEmail(true); submitEmail(true);
// Verify that the client contacts the server with the expected data // Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
requests, 'POST', requests, 'POST',
FORM_DESCRIPTION.submit_url, FORM_DESCRIPTION.submit_url,
$.param({email: EMAIL}) $.param({email: EMAIL})
); );
// Respond with status code 200 // Respond with status code 200
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
// Verify that the success message is visible // Verify that the events were triggered
expect($('.js-reset-success')).not.toHaveClass('hidden'); expect(syncSpy).toHaveBeenCalled();
expect(passwordEmailSentSpy).toHaveBeenCalled();
// Verify that login form has loaded
expect($('#login-form')).not.toHaveClass('hidden');
// Verify that password reset view has been removed // Verify that password reset view has been removed
expect($(view.el).html().length).toEqual(0); expect($(view.el).html().length).toEqual(0);
...@@ -109,7 +115,7 @@ ...@@ -109,7 +115,7 @@
expect(view.validate).toHaveBeenCalledWith($('#password-reset-email')[0]); expect(view.validate).toHaveBeenCalledWith($('#password-reset-email')[0]);
// Verify that no submission errors are visible // Verify that no submission errors are visible
expect(view.$errors).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
}); });
it('displays password reset validation errors', function() { it('displays password reset validation errors', function() {
...@@ -119,7 +125,7 @@ ...@@ -119,7 +125,7 @@
submitEmail(false); submitEmail(false);
// Verify that submission errors are visible // Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
}); });
it('displays an error if the server returns an error while sending a password reset email', function() { it('displays an error if the server returns an error while sending a password reset email', function() {
...@@ -130,7 +136,7 @@ ...@@ -130,7 +136,7 @@
AjaxHelpers.respondWithError(requests); AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed // Expect that an error is displayed
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
// If we try again and succeed, the error should go away // If we try again and succeed, the error should go away
submitEmail(); submitEmail();
...@@ -139,7 +145,7 @@ ...@@ -139,7 +145,7 @@
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden // Expect that the error is hidden
expect(view.$errors).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
}); });
}); });
}); });
......
...@@ -243,16 +243,17 @@ ...@@ -243,16 +243,17 @@
// Verify that the client contacts the server with the expected data // Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
requests, 'POST', requests, 'POST',
FORM_DESCRIPTION.submit_url, FORM_DESCRIPTION.submit_url,
$.param(USER_DATA) $.param(USER_DATA)
); );
// Respond with status code 200 // Respond with status code 200
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
// Verify that auth complete is triggered // Verify that auth complete is triggered
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
// Form button should be disabled on success. // Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled'); expect(view.$submitButton).toHaveAttr('disabled');
}); });
...@@ -278,10 +279,10 @@ ...@@ -278,10 +279,10 @@
$.extend(expectedData, USER_DATA); $.extend(expectedData, USER_DATA);
AjaxHelpers.expectRequest( AjaxHelpers.expectRequest(
requests, 'POST', requests, 'POST',
FORM_DESCRIPTION.submit_url, FORM_DESCRIPTION.submit_url,
$.param(expectedData) $.param(expectedData)
); );
}); });
it('displays third-party auth registration buttons', function() { it('displays third-party auth registration buttons', function() {
...@@ -305,7 +306,8 @@ ...@@ -305,7 +306,8 @@
expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]); expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]);
// Verify that no submission errors are visible // Verify that no submission errors are visible
expect(view.$errors).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
// Form button should be disabled on success. // Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled'); expect(view.$submitButton).toHaveAttr('disabled');
}); });
...@@ -317,10 +319,11 @@ ...@@ -317,10 +319,11 @@
submitForm(false); submitForm(false);
// Verify that submission errors are visible // Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
// Expect that auth complete is NOT triggered // Expect that auth complete is NOT triggered
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
// Form button should be re-enabled on error. // Form button should be re-enabled on error.
expect(view.$submitButton).not.toHaveAttr('disabled'); expect(view.$submitButton).not.toHaveAttr('disabled');
}); });
...@@ -335,7 +338,7 @@ ...@@ -335,7 +338,7 @@
AjaxHelpers.respondWithError(requests); AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed and that auth complete is NOT triggered // Expect that an error is displayed and that auth complete is NOT triggered
expect(view.$errors).not.toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(1);
expect(authComplete).toBe(false); expect(authComplete).toBe(false);
// If we try again and succeed, the error should go away // If we try again and succeed, the error should go away
...@@ -345,8 +348,9 @@ ...@@ -345,8 +348,9 @@
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden and that auth complete is triggered // Expect that the error is hidden and that auth complete is triggered
expect(view.$errors).toHaveClass('hidden'); expect(view.$formFeedback.find('.' + view.formErrorsJsHook).length).toEqual(0);
expect(authComplete).toBe(true); expect(authComplete).toBe(true);
// Form button should be disabled on success. // Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled'); expect(view.$submitButton).toHaveAttr('disabled');
}); });
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
'jquery', 'jquery',
'underscore', 'underscore',
'backbone', 'backbone',
'common/js/utils/edx.utils.validate' 'common/js/utils/edx.utils.validate',
'edx-ui-toolkit/js/utils/html-utils',
'text!templates/student_account/form_errors.underscore'
], ],
function($, _, Backbone, EdxUtilsValidate) { function($, _, Backbone, EdxUtilsValidate, HtmlUtils, formErrorsTpl) {
return Backbone.View.extend({ return Backbone.View.extend({
tagName: 'form', tagName: 'form',
...@@ -16,6 +18,12 @@ ...@@ -16,6 +18,12 @@
fieldTpl: '#form_field-tpl', fieldTpl: '#form_field-tpl',
formErrorsTpl: formErrorsTpl,
formErrorsJsHook: 'js-form-errors',
defaultFormErrorsTitle: gettext('An error occurred.'),
events: {}, events: {},
errors: [], errors: [],
...@@ -66,7 +74,7 @@ ...@@ -66,7 +74,7 @@
postRender: function() { postRender: function() {
var $container = $(this.el); var $container = $(this.el);
this.$form = $container.find('form'); this.$form = $container.find('form');
this.$errors = $container.find('.submission-error'); this.$formFeedback = $container.find('.js-form-feedback');
this.$submitButton = $container.find(this.submitButton); this.$submitButton = $container.find(this.submitButton);
}, },
...@@ -126,21 +134,6 @@ ...@@ -126,21 +134,6 @@
return obj; return obj;
}, },
focusFirstError: function() {
var $error = this.$form.find('.error').first(),
$field = {},
$parent = {};
if ($error.is('label')) {
$parent = $error.parent('.form-field');
$error = $parent.find('input') || $parent.find('select');
} else {
$field = $error;
}
$error.focus();
},
forgotPassword: function(event) { forgotPassword: function(event) {
event.preventDefault(); event.preventDefault();
...@@ -185,32 +178,34 @@ ...@@ -185,32 +178,34 @@
saveError: function(error) { saveError: function(error) {
this.errors = ['<li>' + error.responseText + '</li>']; this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors(); this.renderErrors(this.defaultFormErrorsTitle, this.errors);
this.toggleDisableButton(false); this.toggleDisableButton(false);
}, },
setErrors: function() { /* Wrapper for renderFormFeedback provided for convenience since the majority of
var $msg = this.$errors.find('.message-copy'), * our calls to renderFormFeedback are for rendering error messages.
html = [], */
errors = this.errors, renderErrors: function(title, errorMessages) {
i, this.clearFormErrors();
len = errors.length;
for (i = 0; i < len; i++) {
html.push(errors[i]);
}
$msg.html(html.join('')); this.renderFormFeedback(this.formErrorsTpl, {
jsHook: this.formErrorsJsHook,
title: title,
messagesHtml: HtmlUtils.HTML(errorMessages.join(''))
});
},
this.element.show(this.$errors); renderFormFeedback: function(template, context) {
var tpl = HtmlUtils.template(template);
HtmlUtils.prepend(this.$formFeedback, tpl(context));
// Scroll to error messages // Scroll to feedback container
$('html,body').animate({ $('html,body').animate({
scrollTop: this.$errors.offset().top scrollTop: this.$formFeedback.offset().top
}, 'slow'); }, 'slow');
// Focus on first error field // Focus on the feedback container to ensure screen readers see the messages.
this.focusFirstError(); this.$formFeedback.focus();
}, },
/* Allows extended views to add non-form attributes /* Allows extended views to add non-form attributes
...@@ -233,9 +228,10 @@ ...@@ -233,9 +228,10 @@
data = this.setExtraData(data); data = this.setExtraData(data);
this.model.set(data); this.model.set(data);
this.model.save(); this.model.save();
this.toggleErrorMsg(false); this.clearFormErrors();
} else { } else {
this.toggleErrorMsg(true); this.renderErrors(this.defaultFormErrorsTitle, this.errors);
this.toggleDisableButton(false);
} }
this.postFormSubmission(); this.postFormSubmission();
...@@ -248,12 +244,15 @@ ...@@ -248,12 +244,15 @@
return true; return true;
}, },
toggleErrorMsg: function(show) { clearFormErrors: function() {
if (show) { var query = '.' + this.formErrorsJsHook;
this.setErrors(); this.clearFormFeedbackItems(query);
this.toggleDisableButton(false); },
} else {
this.element.hide(this.$errors); clearFormFeedbackItems: function(query) {
var $items = this.$formFeedback.find(query);
if ($items.length > 0) {
$items.remove();
} }
}, },
......
...@@ -26,15 +26,6 @@ ...@@ -26,15 +26,6 @@
this.listenTo(this.model, 'sync', this.saveSuccess); this.listenTo(this.model, 'sync', this.saveSuccess);
}, },
toggleErrorMsg: function(show) {
if (show) {
this.setErrors();
this.toggleDisableButton(false);
} else {
this.element.hide(this.$errors);
}
},
saveSuccess: function() { saveSuccess: function() {
this.trigger('password-email-sent'); this.trigger('password-email-sent');
......
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
define([ define([
'jquery', 'jquery',
'underscore', 'underscore',
'js/student_account/views/FormView' 'gettext',
'js/student_account/views/FormView',
'text!templates/student_account/form_status.underscore'
], ],
function($, _, FormView) { function($, _, gettext, FormView, formStatusTpl) {
return FormView.extend({ return FormView.extend({
el: '#register-form', el: '#register-form',
...@@ -18,13 +20,19 @@ ...@@ -18,13 +20,19 @@
formType: 'register', formType: 'register',
formStatusTpl: formStatusTpl,
authWarningJsHook: 'js-auth-warning',
defaultFormErrorsTitle: gettext('We couldn\'t create your account.'),
submitButton: '.js-register', submitButton: '.js-register',
preRender: function(data) { preRender: function(data) {
this.providers = data.thirdPartyAuth.providers || []; this.providers = data.thirdPartyAuth.providers || [];
this.hasSecondaryProviders = ( this.hasSecondaryProviders = (
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
); );
this.currentProvider = data.thirdPartyAuth.currentProvider || ''; this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.errorMessage = data.thirdPartyAuth.errorMessage || ''; this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName; this.platformName = data.platformName;
...@@ -34,7 +42,8 @@ ...@@ -34,7 +42,8 @@
}, },
render: function(html) { render: function(html) {
var fields = html || ''; var fields = html || '',
formErrorsTitle = gettext('An error occurred.');
$(this.el).html(_.template(this.tpl)({ $(this.el).html(_.template(this.tpl)({
/* We pass the context object to the template so that /* We pass the context object to the template so that
...@@ -43,7 +52,6 @@ ...@@ -43,7 +52,6 @@
context: { context: {
fields: fields, fields: fields,
currentProvider: this.currentProvider, currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers, providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders, hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName platformName: this.platformName
...@@ -52,6 +60,13 @@ ...@@ -52,6 +60,13 @@
this.postRender(); this.postRender();
// Must be called after postRender, since postRender sets up $formFeedback.
if (this.errorMessage) {
this.renderErrors(formErrorsTitle, [this.errorMessage]);
} else if (this.currentProvider) {
this.renderAuthWarning();
}
if (this.autoSubmit) { if (this.autoSubmit) {
$(this.el).hide(); $(this.el).hide();
$('#register-honor_code').prop('checked', true); $('#register-honor_code').prop('checked', true);
...@@ -76,18 +91,18 @@ ...@@ -76,18 +91,18 @@
saveError: function(error) { saveError: function(error) {
$(this.el).show(); // Show in case the form was hidden for auto-submission $(this.el).show(); // Show in case the form was hidden for auto-submission
this.errors = _.flatten( this.errors = _.flatten(
_.map( _.map(
// Something is passing this 'undefined'. Protect against this. // Something is passing this 'undefined'. Protect against this.
JSON.parse(error.responseText || '[]'), JSON.parse(error.responseText || '[]'),
function(error_list) { function(errorList) {
return _.map( return _.map(
error_list, errorList,
function(error) { return '<li>' + error.user_message + '</li>'; } function(errorItem) { return '<li>' + errorItem.user_message + '</li>'; }
); );
} }
) )
); );
this.setErrors(); this.renderErrors(this.defaultFormErrorsTitle, this.errors);
this.toggleDisableButton(false); this.toggleDisableButton(false);
}, },
...@@ -96,6 +111,22 @@ ...@@ -96,6 +111,22 @@
// The form did not get submitted due to validation errors. // The form did not get submitted due to validation errors.
$(this.el).show(); // Show in case the form was hidden for auto-submission $(this.el).show(); // Show in case the form was hidden for auto-submission
} }
},
renderAuthWarning: function() {
var msgPart1 = gettext('You\'ve successfully signed into %(currentProvider)s.'),
msgPart2 = gettext(
'We just need a little more information before you start learning with %(platformName)s.'
),
fullMsg = _.sprintf(
msgPart1 + ' ' + msgPart2,
{currentProvider: this.currentProvider, platformName: this.platformName}
);
this.renderFormFeedback(this.formStatusTpl, {
jsHook: this.authWarningJsHook,
message: fullMsg
});
} }
}); });
}); });
......
...@@ -7,10 +7,8 @@ ...@@ -7,10 +7,8 @@
</div> </div>
<form class="financial-assistance-form" method="POST"> <form class="financial-assistance-form" method="POST">
<div class="status submission-error hidden" aria-live="polite"> <div class="js-form-feedback" aria-live="assertive" tabindex="-1">
<h4 class="message-title"><%- gettext('Unable to submit application') %></h4> </div>
<ul class="message-copy"></ul>
</div>
<div class="user-info"> <div class="user-info">
<h2><%- gettext('About You') %></h2> <h2><%- gettext('About You') %></h2>
......
<div class="<%- jsHook %> status submission-error">
<h4 class="message-title"><%- title %></h4>
<ul class="message-copy">
<%= HtmlUtils.ensureHtml(messagesHtml) %>
</ul>
</div>
<div class="<%- jsHook %> status">
<p class="message-copy">
<%- message %>
</p>
</div>
<div class="<%- jsHook %> status submission-success">
<h4 class="message-title"><%- title %></h4>
<div class="message-copy">
<%= HtmlUtils.ensureHtml(messageHtml) %>
</div>
</div>
<div class="status already-authenticated-msg hidden"> <div class="js-form-feedback" aria-live="assertive" tabindex="-1">
<% if (context.currentProvider) { %>
<p class="message-copy">
<%- _.sprintf( gettext("You have successfully signed into %(currentProvider)s, but your %(currentProvider)s account does not have a linked %(platformName)s account. To link your accounts, sign in now using your %(platformName)s password."), context ) %>
</p>
<% } %>
</div> </div>
<div aria-live="polite">
<div class="js-reset-success status submission-success hidden">
<h4 class="message-title"><%- gettext("Check Your Email") %></h4>
<div class="message-copy">
</div>
</div>
<div class="status submission-error hidden">
<h4 class="message-title"><%- gettext("We couldn't sign you in.") %></h4>
<ul class="message-copy"></ul>
</div>
</div>
<% if (context.errorMessage) { %>
<div class="status submission-error">
<h4 class="message-title"><%- _.sprintf( gettext("An error occurred when signing you in to %(platformName)s."), context ) %></h4>
<ul class="message-copy"><%- context.errorMessage %></ul>
</div>
<% } %>
<form id="login" class="login-form" tabindex="-1" method="POST"> <form id="login" class="login-form" tabindex="-1" method="POST">
<div class="section-title lines"> <div class="section-title lines">
......
<div class="status submission-error hidden" aria-live="polite"> <div class="js-form-feedback" aria-live="assertive" tabindex="-1">
<h4 class="message-title"><%- gettext("An error occurred.") %></h4>
<ul class="message-copy"></ul>
</div> </div>
<form id="password-reset" class="password-reset-form" tabindex="-1" method="POST"> <form id="password-reset" class="password-reset-form" tabindex="-1" method="POST">
......
<div class="status submission-error hidden" aria-live="polite"> <div class="js-form-feedback" aria-live="assertive" tabindex="-1">
<h4 class="message-title"><%- gettext("We couldn't create your account.") %></h4>
<ul class="message-copy"></ul>
</div> </div>
<form id="register" class="register-form" autocomplete="off" tabindex="-1" method="POST"> <form id="register" class="register-form" autocomplete="off" tabindex="-1" method="POST">
<% if (!context.currentProvider) { %>
<% if (context.providers.length > 0 || context.hasSecondaryProviders) { %>
<div class="login-providers">
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("Create an account using") %></span>
</h2>
</div>
<%
_.each( context.providers, function( provider) {
if ( provider.registerUrl ) { %>
<button type="button" class="button button-primary button-<%- provider.id %> login-provider register-<%- provider.id %>" data-provider-url="<%- provider.registerUrl %>">
<div class="icon <% if ( provider.iconClass ) { %>fa <%- provider.iconClass %><% } %>" aria-hidden="true">
<% if ( provider.iconImage ) { %>
<img class="icon-image" src="<%- provider.iconImage %>" alt="<%- provider.name %> icon" />
<% } %>
</div>
<span aria-hidden="true"><%- provider.name %></span>
<span class="sr"><%- _.sprintf( gettext("Create account using %(providerName)s."), {providerName: provider.name} ) %></span>
</button>
<% }
}); %>
<% if (context.errorMessage) { %> <% if ( context.hasSecondaryProviders ) { %>
<div class="status submission-error"> <button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<h4 class="message-title"><%- gettext("An error occurred.") %></h4> <%- gettext("Use my institution/campus credentials") %>
<ul class="message-copy"><%- context.errorMessage %></ul> </button>
</div> <% } %>
<% } %> </div>
<% if (context.currentProvider) { %>
<div class="status" aria-hidden="false">
<p class="message-copy">
<%- _.sprintf( gettext("You've successfully signed into %(currentProvider)s."), context ) %>
<%- _.sprintf( gettext("We just need a little more information before you start learning with %(platformName)s."), context ) %>
</p>
</div>
<% } else if ( context.providers.length > 0 || context.hasSecondaryProviders ) { %>
<div class="login-providers">
<div class="section-title lines"> <div class="section-title lines">
<h2> <h2>
<span class="text"><%- gettext("Create an account using") %></span> <span class="text"><%- gettext("or create a new one here") %></span>
</h2> </h2>
</div> </div>
<% <% } else { %>
_.each( context.providers, function( provider) { <div class="section-title lines">
if ( provider.registerUrl ) { %> <h2>
<button type="button" class="button button-primary button-<%- provider.id %> login-provider register-<%- provider.id %>" data-provider-url="<%- provider.registerUrl %>"> <span class="text"><%- gettext("Create a new account") %></span>
<div class="icon <% if ( provider.iconClass ) { %>fa <%- provider.iconClass %><% } %>" aria-hidden="true"> </h2>
<% if ( provider.iconImage ) { %> </div>
<img class="icon-image" src="<%- provider.iconImage %>" alt="<%- provider.name %> icon" /> <% } %>
<% } %>
</div>
<span aria-hidden="true"><%- provider.name %></span>
<span class="sr"><%- _.sprintf( gettext("Create account using %(providerName)s."), {providerName: provider.name} ) %></span>
</button>
<% }
}); %>
<% if ( context.hasSecondaryProviders ) { %>
<button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<%- gettext("Use my institution/campus credentials") %>
</button>
<% } %>
</div>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("or create a new one here") %></span>
</h2>
</div>
<% } else { %>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("Create a new account") %></span>
</h2>
</div>
<% } %> <% } %>
<%= context.fields %> <%= context.fields %>
......
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