diff --git a/common/static/js/spec_helpers/edx.utils.validate.js b/common/static/js/spec_helpers/edx.utils.validate.js
index cf41b58568..67cab08d37 100644
--- a/common/static/js/spec_helpers/edx.utils.validate.js
+++ b/common/static/js/spec_helpers/edx.utils.validate.js
@@ -13,9 +13,8 @@ var edx = edx || {};
email: '
A properly formatted e-mail is required',
min: '<%= field %> must be a minimum of <%= count %> characters long',
max: '<%= field %> must be a maximum of <%= count %> characters long',
- password: 'A valid password is required',
required: '<%= field %> field is required',
- terms: 'To enroll you must agree to the Terms of Service and Honor Code'
+ custom: '<%= content %>'
},
field: function( el ) {
@@ -78,7 +77,7 @@ var edx = edx || {};
},
isBlank: function( $el ) {
- return ( $el.attr('type') === 'checkbox' ) ? $el.prop('checked') : !$el.val();
+ return ( $el.attr('type') === 'checkbox' ) ? !$el.prop('checked') : !$el.val();
},
email: {
@@ -105,22 +104,33 @@ var edx = edx || {};
var txt = [],
tpl,
name,
- obj;
+ obj,
+ customMsg;
_.each( tests, function( value, key ) {
if ( !value ) {
name = $el.attr('name');
+ customMsg = $el.data('errormsg-' + key) || false;
- tpl = _fn.validate.msg[key];
+ // If the field has a custom error msg attached use it
+ if ( customMsg ) {
+ tpl = _fn.validate.msg.custom;
- obj = {
- field: _fn.validate.str.capitalizeFirstLetter( name )
- };
+ obj = {
+ content: customMsg
+ };
+ } else {
+ tpl = _fn.validate.msg[key];
- if ( key === 'min' ) {
- obj.count = $el.attr('minlength');
- } else if ( key === 'max' ) {
- obj.count = $el.attr('maxlength');
+ obj = {
+ field: _fn.validate.str.capitalizeFirstLetter( name )
+ };
+
+ if ( key === 'min' ) {
+ obj.count = $el.attr('minlength');
+ } else if ( key === 'max' ) {
+ obj.count = $el.attr('maxlength');
+ }
}
txt.push( _.template( tpl, obj ) );
diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js
index f5ecdb181a..acdb4b9565 100644
--- a/lms/static/js/spec/main.js
+++ b/lms/static/js/spec/main.js
@@ -263,13 +263,20 @@
// Student account registration/login
// Loaded explicitly until these are converted to RequireJS
+ 'js/student_account/views/FormView': {
+ exports: 'js/student_account/views/FormView',
+ deps: ['jquery', 'underscore', 'backbone', 'gettext']
+ },
'js/student_account/models/LoginModel': {
exports: 'js/student_account/models/LoginModel',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
},
'js/student_account/views/LoginView': {
exports: 'js/student_account/views/LoginView',
- deps: ['js/student_account/models/LoginModel']
+ deps: [
+ 'js/student_account/models/LoginModel',
+ 'js/student_account/views/FormView'
+ ]
},
'js/student_account/models/PasswordResetModel': {
exports: 'js/student_account/models/PasswordResetModel',
@@ -277,7 +284,10 @@
},
'js/student_account/views/PasswordResetView': {
exports: 'js/student_account/views/PasswordResetView',
- deps: ['js/student_account/models/PasswordResetModel']
+ deps: [
+ 'js/student_account/models/PasswordResetModel',
+ 'js/student_account/views/FormView'
+ ]
},
'js/student_account/models/RegisterModel': {
exports: 'js/student_account/models/RegisterModel',
@@ -285,7 +295,10 @@
},
'js/student_account/views/RegisterView': {
exports: 'js/student_account/views/RegisterView',
- deps: ['js/student_account/models/RegisterModel']
+ deps: [
+ 'js/student_account/models/RegisterModel',
+ 'js/student_account/views/FormView'
+ ]
},
'js/student_account/views/AccessView': {
exports: 'js/student_account/views/AccessView',
diff --git a/lms/static/js/spec/student_account/password_reset_spec.js b/lms/static/js/spec/student_account/password_reset_spec.js
index 9b2662a58d..ce8dbe69b5 100644
--- a/lms/static/js/spec/student_account/password_reset_spec.js
+++ b/lms/static/js/spec/student_account/password_reset_spec.js
@@ -4,11 +4,21 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/Password
'use strict';
var view = null,
- ajaxSuccess = true;
+ ajaxSuccess = true,
+ model = new edx.student.account.PasswordResetModel(),
+ data = [{
+ label: 'E-mail',
+ instructions: 'This is the e-mail address you used to register with edX',
+ name: 'email',
+ required: true,
+ type: 'email',
+ restrictions: [],
+ defaultValue: ''
+ }];
var submitEmail = function(validationSuccess) {
// Simulate manual entry of an email address
- $('#reset-password-email').val('foo@bar.baz');
+ $('#password-reset-email').val('foo@bar.baz');
// Create a fake click event
var clickEvent = $.Event('click');
@@ -16,7 +26,10 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/Password
// Used to avoid spying on view.validate twice
if (typeof validationSuccess !== 'undefined') {
// Force validation to return as expected
- spyOn(view, 'validate').andReturn(validationSuccess);
+ spyOn(view, 'validate').andReturn({
+ isValid: validationSuccess,
+ message: "We're all good."
+ });
}
// Submit the email address
@@ -43,12 +56,15 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/Password
if (ajaxSuccess) {
defer.resolve();
} else {
- defer.reject();
+ defer.rejectWith(this, ["The server could not be contacted."]);
}
}).promise();
});
- view = new edx.student.account.PasswordResetView();
+ view = new edx.student.account.PasswordResetView({
+ fields: data,
+ model: model
+ });
});
it("allows the user to request a new password", function() {
@@ -72,13 +88,13 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/Password
// If we get an error status on the AJAX request, display an error
ajaxSuccess = false;
submitEmail(true);
- expect(view.$resetFail).not.toHaveClass('hidden');
+ expect(view.$'#submission-error').not.toHaveClass('hidden');
// If we try again and succeed, the error should go away
ajaxSuccess = true;
// No argument means we won't spy on view.validate again
submitEmail();
- expect(view.$resetFail).toHaveClass('hidden');
+ expect(view.$'#submission-error').toHaveClass('hidden');
});
});
}
diff --git a/lms/static/js/student_account/models/PasswordResetModel.js b/lms/static/js/student_account/models/PasswordResetModel.js
index 00958667b2..bfc1997fa9 100644
--- a/lms/static/js/student_account/models/PasswordResetModel.js
+++ b/lms/static/js/student_account/models/PasswordResetModel.js
@@ -29,8 +29,8 @@ var edx = edx || {};
.done(function() {
model.trigger('success');
})
- .fail( function() {
- model.trigger('error');
+ .fail( function( error ) {
+ model.trigger( 'error', error );
});
}
});
diff --git a/lms/static/js/student_account/views/AccessView.js b/lms/static/js/student_account/views/AccessView.js
index dc2a45953f..f29450b7d0 100644
--- a/lms/static/js/student_account/views/AccessView.js
+++ b/lms/static/js/student_account/views/AccessView.js
@@ -126,8 +126,8 @@ var edx = edx || {};
},
resetPassword: function() {
- this.$header.addClass('hidden');
- $(this.el).find('.form-type').addClass('hidden');
+ this.element.hide( this.$header );
+ this.element.hide( $(this.el).find('.form-type') );
this.loadForm('reset');
},
@@ -139,14 +139,29 @@ var edx = edx || {};
this.loadForm( type );
}
- $(this.el).find('.form-wrapper').addClass('hidden');
- $form.removeClass('hidden');
+ this.element.hide( $(this.el).find('.form-wrapper') );
+ this.element.show( $form );
},
form: {
isLoaded: function( $form ) {
return $form.html().length > 0;
}
+ },
+
+ /* Helper method ot toggle display
+ * including accessibility considerations
+ */
+ element: {
+ hide: function( $el ) {
+ $el.addClass('hidden')
+ .attr('aria-hidden', true);
+ },
+
+ show: function( $el ) {
+ $el.removeClass('hidden')
+ .attr('aria-hidden', false);
+ }
}
});
diff --git a/lms/static/js/student_account/views/FormView.js b/lms/static/js/student_account/views/FormView.js
index 18460a9626..1d00f59a70 100644
--- a/lms/static/js/student_account/views/FormView.js
+++ b/lms/static/js/student_account/views/FormView.js
@@ -29,16 +29,25 @@ var edx = edx || {};
requiredStr: '*',
initialize: function( data ) {
+ this.preRender( data );
this.tpl = $(this.tpl).html();
this.fieldTpl = $(this.fieldTpl).html();
this.buildForm( data.fields );
this.model = data.model;
- this.listenTo( this.model, 'error', this.modelError );
+ this.listenTo( this.model, 'error', this.saveError );
+ },
+
+ /* Allows extended views to add custom
+ * init steps without needing to repeat
+ * default init steps
+ */
+ preRender: function( data ) {
+ /* custom code goes here */
+ return data;
},
- // Renders the form.
render: function( html ) {
var fields = html || '';
@@ -76,6 +85,31 @@ var edx = edx || {};
this.render( html.join('') );
},
+ /* Helper method ot toggle display
+ * including accessibility considerations
+ */
+ element: {
+ hide: function( $el ) {
+ if ( $el ) {
+ $el.addClass('hidden')
+ .attr('aria-hidden', true);
+ }
+ },
+
+ show: function( $el ) {
+ if ( $el ) {
+ $el.removeClass('hidden')
+ .attr('aria-hidden', false);
+ }
+ }
+ },
+
+ forgotPassword: function( event ) {
+ event.preventDefault();
+
+ this.trigger('password-help');
+ },
+
getFormData: function() {
var obj = {},
@@ -116,10 +150,25 @@ var edx = edx || {};
return obj;
},
- forgotPassword: function( event ) {
- event.preventDefault();
+ saveError: function( error ) {
+ this.errors = ['' + error.responseText + ''];
+ this.setErrors();
+ },
- this.trigger('password-help');
+ setErrors: function() {
+ var $msg = this.$errors.find('.message-copy'),
+ html = [],
+ errors = this.errors,
+ i,
+ len = errors.length;
+
+ for ( i=0; i' + error.responseText + ''];
+ this.setErrors();
/* If we've gotten a 403 error, it means that we've successfully
* authenticated with a third-party provider, but we haven't
* linked the account to an EdX account. In this case,
* we need to prompt the user to enter a little more information
* to complete the registration process.
- */
- if (error.status === 403 && error.responseText === "third-party-auth" && this.currentProvider) {
- this.$alreadyAuthenticatedMsg.removeClass("hidden");
+ */
+ if ( error.status === 403 &&
+ error.responseText === 'third-party-auth' &&
+ this.currentProvider ) {
+ this.element.show( this.$authError );
+ this.element.hide( this.$errors );
+ } else {
+ this.element.hide( this.$authError );
+ this.element.show( this.$errors );
}
- else {
- this.$alreadyAuthenticatedMsg.addClass("hidden");
- // TODO -- display the error
- }
-
}
});
-})(jQuery, _, Backbone, gettext);
\ No newline at end of file
+})(jQuery, _, gettext);
diff --git a/lms/static/js/student_account/views/PasswordResetView.js b/lms/static/js/student_account/views/PasswordResetView.js
index 48b4480998..3474e7e3c5 100644
--- a/lms/static/js/student_account/views/PasswordResetView.js
+++ b/lms/static/js/student_account/views/PasswordResetView.js
@@ -1,6 +1,6 @@
var edx = edx || {};
-(function($, _, Backbone, gettext) {
+(function($, _, gettext) {
'use strict';
edx.student = edx.student || {};
@@ -24,34 +24,25 @@ var edx = edx || {};
this.$form = $container.find('form');
- this.$resetFail = $container.find('.js-reset-fail');
this.$errors = $container.find('.submission-error');
this.listenTo( this.model, 'success', this.resetComplete );
- this.listenTo( this.model, 'error', this.resetError );
+ this.listenTo( this.model, 'error', this.saveError );
},
toggleErrorMsg: function( show ) {
if ( show ) {
this.setErrors();
} else {
- this.$errors
- .addClass('hidden')
- .attr('aria-hidden', true);
+ this.element.hide( this.$errors );
}
},
resetComplete: function() {
var $el = $(this.el);
- $el.find('#password-reset-form').addClass('hidden');
- $el.find('.js-reset-success').removeClass('hidden');
-
- this.$resetFail.addClass('hidden');
- },
-
- resetError: function() {
- this.$resetFail.removeClass('hidden');
+ this.element.hide( $el.find('#password-reset-form') );
+ this.element.show( $el.find('.js-reset-success') );
},
submitForm: function( event ) {
@@ -73,4 +64,4 @@ var edx = edx || {};
}
});
-})(jQuery, _, Backbone, gettext);
+})(jQuery, _, gettext);
diff --git a/lms/static/js/student_account/views/RegisterView.js b/lms/static/js/student_account/views/RegisterView.js
index 697c19cf7c..c455741b19 100644
--- a/lms/static/js/student_account/views/RegisterView.js
+++ b/lms/static/js/student_account/views/RegisterView.js
@@ -1,6 +1,6 @@
var edx = edx || {};
-(function($, _, Backbone, gettext) {
+(function($, _, gettext) {
'use strict';
edx.student = edx.student || {};
@@ -18,13 +18,7 @@ var edx = edx || {};
formType: 'register',
- initialize: function( data ) {
- this.tpl = $(this.tpl).html();
- this.fieldTpl = $(this.fieldTpl).html();
-
- this.buildForm( data.fields );
- this.model = data.model;
-
+ preRender: function( data ) {
this.providers = data.thirdPartyAuth.providers || [];
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
},
@@ -45,6 +39,7 @@ var edx = edx || {};
thirdPartyAuth: function( event ) {
var providerUrl = $(event.target).data('provider-url') || '';
+
if (providerUrl) {
window.location.href = providerUrl;
} else {
@@ -54,4 +49,4 @@ var edx = edx || {};
}
});
-})(jQuery, _, Backbone, gettext);
\ No newline at end of file
+})(jQuery, _, gettext);
diff --git a/lms/static/sass/views/_login-register.scss b/lms/static/sass/views/_login-register.scss
index cda41059fa..d6f8bbe0c9 100644
--- a/lms/static/sass/views/_login-register.scss
+++ b/lms/static/sass/views/_login-register.scss
@@ -119,6 +119,8 @@
}
.form-field {
+ @include clearfix;
+ clear: both;
width: 100%;
margin: 0 0 $baseline 0;
@@ -218,6 +220,45 @@
text-transform: none;
}
+ .login-provider {
+ @extend %btn-secondary-blue-outline;
+ width: 100%;
+ margin-top: 20px;
+
+ .icon {
+ color: inherit;
+ margin-right: $baseline/2;
+ }
+
+ &.button-Google:hover, &.button-Google:focus {
+ background-color: #dd4b39;
+ border: 1px solid #A5382B;
+ }
+
+ &.button-Google:hover {
+ box-shadow: 0 2px 1px 0 #8D3024;
+ }
+
+ &.button-Facebook:hover, &.button-Facebook:focus {
+ background-color: #3b5998;
+ border: 1px solid #263A62;
+ }
+
+ &.button-Facebook:hover {
+ box-shadow: 0 2px 1px 0 #30487C;
+ }
+
+ &.button-LinkedIn:hover , &.button-LinkedIn:focus {
+ background-color: #0077b5;
+ border: 1px solid #06527D;
+ }
+
+ &.button-LinkedIn:hover {
+ box-shadow: 0 2px 1px 0 #005D8E;
+ }
+
+ }
+
/** Error Container - from _account.scss **/
.status {
@include box-sizing(border-box);
@@ -277,6 +318,12 @@
@include media( $tablet ) {
$grid-columns: 8;
+ %inline-form-field-tablet {
+ clear: none;
+ display: inline-block;
+ float: left;
+ }
+
.headline,
.tagline,
.form-type {
@@ -287,11 +334,30 @@
.form-toggle {
margin-right: 5px;
}
+
+ .form-field {
+ &.select-gender {
+ @extend %inline-form-field-tablet;
+ width: calc( 50% - 10px );
+ margin-right: 20px;
+ }
+
+ &.select-year_of_birth {
+ @extend %inline-form-field-tablet;
+ width: calc( 50% - 10px );
+ }
+ }
}
@include media( $desktop ) {
$grid-columns: 12;
+ %inline-form-field-desktop {
+ clear: none;
+ display: inline-block;
+ float: left;
+ }
+
.headline,
.tagline,
.form-type {
@@ -302,5 +368,38 @@
.form-toggle {
margin-right: 10px;
}
+
+ .form-field {
+ &.select-level_of_education {
+ @extend %inline-form-field-desktop;
+ width: 290px;
+ margin-right: 20px;
+ }
+
+ &.select-gender {
+ @extend %inline-form-field-desktop;
+ width: 60px;
+ margin-right: 20px;
+ }
+
+ &.select-year_of_birth {
+ @extend %inline-form-field-desktop;
+ width: 100px;
+ }
+ }
+
+ // TODO: Update so actually using the grid
+ .login-provider {
+ @include span-columns(6);
+ /*width: calc( 50% - 12px );
+
+ &:nth-child(odd) {
+ margin-left: 10px;
+ }
+
+ &:nth-child(even) {
+ margin-right: 10px;
+ }*/
+ }
}
}
diff --git a/lms/templates/student_account/access.underscore b/lms/templates/student_account/access.underscore
index 6106cbc857..a920aaed56 100644
--- a/lms/templates/student_account/access.underscore
+++ b/lms/templates/student_account/access.underscore
@@ -8,7 +8,7 @@
checked<% } %> >
-
+
diff --git a/lms/templates/student_account/form_field.underscore b/lms/templates/student_account/form_field.underscore
index e6db67513d..b994e527b2 100644
--- a/lms/templates/student_account/form_field.underscore
+++ b/lms/templates/student_account/form_field.underscore
@@ -1,4 +1,4 @@
-
+
<% if ( type !== 'checkbox' ) { %>
diff --git a/lms/templates/student_account/register.underscore b/lms/templates/student_account/register.underscore
index 198f4c8acf..e98d0f5147 100644
--- a/lms/templates/student_account/register.underscore
+++ b/lms/templates/student_account/register.underscore
@@ -1,8 +1,8 @@
<% if (currentProvider) { %>
-
- You've successfully signed in with <%- currentProvider %>.
- We just need a little more information before you start learning with edX.
-
+
+
You've successfully signed in with <%- currentProvider %>.
+
You've successfully signed in with <%- currentProvider %>. We just need a little more information before you start learning with edX.
+
<% } else {
_.each( providers, function( provider) { %>