diff --git a/common/test/acceptance/pages/lms/login_and_register.py b/common/test/acceptance/pages/lms/login_and_register.py
index e4f400cb80..e14d7cdbb1 100644
--- a/common/test/acceptance/pages/lms/login_and_register.py
+++ b/common/test/acceptance/pages/lms/login_and_register.py
@@ -208,7 +208,7 @@ class CombinedLoginAndRegisterPage(PageObject):
"""
# Fill in the form
- self.wait_for_element_visibility('#register-email', 'Email field is shown')
+ self.wait_for_element_visibility('#register-honor_code', 'Honor code field is shown')
if email:
self.q(css="#register-email").fill(email)
if full_name:
@@ -223,6 +223,8 @@ class CombinedLoginAndRegisterPage(PageObject):
self.q(css="#register-favorite_movie").fill(favorite_movie)
if terms_of_service:
self.q(css="label[for='register-honor_code']").click()
+ self.q(css="#register-honor_code").click()
+ EmptyPromise(lambda: self.q(css='#register-honor_code:checked'), 'Honor code field is checked').fulfill()
# Submit it
self.q(css=".register-button").click()
diff --git a/lms/static/js/spec/student_account/register_spec.js b/lms/static/js/spec/student_account/register_spec.js
index b50e82c8f8..8d38c54520 100644
--- a/lms/static/js/spec/student_account/register_spec.js
+++ b/lms/static/js/spec/student_account/register_spec.js
@@ -279,6 +279,8 @@
// Create a fake click event
var clickEvent = $.Event('click');
+ $('#toggle_optional_fields').click();
+
// Simulate manual entry of registration form data
fillData();
@@ -481,6 +483,17 @@
expect(view.$submitButton).toHaveAttr('disabled');
});
+ it('hides optional fields by default', function() {
+ createRegisterView(this);
+ expect(view.$('.optional-fields')).toHaveClass('hidden');
+ });
+
+ it('displays optional fields when checkbox is selected', function() {
+ createRegisterView(this);
+ $('#toggle_optional_fields').click();
+ expect(view.$('.optional-fields')).not.toHaveClass('hidden');
+ });
+
it('displays a modal with the terms of service', function() {
var $modal,
$content;
diff --git a/lms/static/js/student_account/views/FormView.js b/lms/static/js/student_account/views/FormView.js
index 412ecf6b10..f01861fd28 100644
--- a/lms/static/js/student_account/views/FormView.js
+++ b/lms/static/js/student_account/views/FormView.js
@@ -84,7 +84,7 @@
data[i].errorMessages = this.escapeStrings(data[i].errorMessages);
}
- html.push(_.template(fieldTpl)($.extend(data[i], {
+ html.push(HtmlUtils.template(fieldTpl)($.extend(data[i], {
form: this.formType,
requiredStr: this.requiredStr,
optionalStr: this.optionalStr,
diff --git a/lms/static/js/student_account/views/RegisterView.js b/lms/static/js/student_account/views/RegisterView.js
index 57b5bca19a..bb64ba04fd 100644
--- a/lms/static/js/student_account/views/RegisterView.js
+++ b/lms/static/js/student_account/views/RegisterView.js
@@ -5,12 +5,14 @@
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/string-utils',
+ 'edx-ui-toolkit/js/utils/html-utils',
'js/student_account/views/FormView',
'text!templates/student_account/form_status.underscore'
],
function(
$, _, gettext,
StringUtils,
+ HtmlUtils,
FormView,
formStatusTpl
) {
@@ -65,6 +67,59 @@
this.listenTo(this.model, 'validation', this.renderLiveValidations);
},
+
+ renderFields: function(fields, className) {
+ var html = [],
+ i,
+ fieldTpl = this.fieldTpl;
+
+ html.push(HtmlUtils.joinHtml(
+ HtmlUtils.HTML('
')
+ ));
+ for (i = 0; i < fields.length; i++) {
+ html.push(HtmlUtils.template(fieldTpl)($.extend(fields[i], {
+ form: this.formType,
+ requiredStr: this.requiredStr,
+ optionalStr: this.optionalStr,
+ supplementalText: fields[i].supplementalText || '',
+ supplementalLink: fields[i].supplementalLink || ''
+ })));
+ }
+ html.push('
');
+ return html;
+ },
+
+ buildForm: function(data) {
+ var html = [],
+ i,
+ len = data.length,
+ requiredFields = [],
+ optionalFields = [];
+
+ this.fields = data;
+
+ for (i = 0; i < len; i++) {
+ if (data[i].errorMessages) {
+ // eslint-disable-next-line no-param-reassign
+ data[i].errorMessages = this.escapeStrings(data[i].errorMessages);
+ }
+
+ if (data[i].required) {
+ requiredFields.push(data[i]);
+ } else {
+ optionalFields.push(data[i]);
+ }
+ }
+
+ html = this.renderFields(requiredFields, 'required-fields');
+
+ html.push.apply(html, this.renderFields(optionalFields, 'optional-fields'));
+
+ this.render(html.join(''));
+ },
+
render: function(html) {
var fields = html || '',
formErrorsTitle = gettext('An error occurred.');
@@ -102,6 +157,101 @@
return this;
},
+ postRender: function() {
+ var inputs = [
+ this.$('#register-name'),
+ this.$('#register-email'),
+ this.$('#register-username'),
+ this.$('#register-password'),
+ this.$('#register-country')
+ ],
+ inputTipSelectors = ['tip error', 'tip tip-input'],
+ inputTipSelectorsHidden = ['tip error hidden', 'tip tip-input hidden'],
+ onInputFocus = function() {
+ // Apply on focus styles to input
+ $(this).prev('label').addClass('focus-in')
+ .removeClass('focus-out');
+
+ // Show each input tip
+ $(this).siblings().each(function() {
+ if (inputTipSelectorsHidden.includes($(this).attr('class'))) {
+ $(this).removeClass('hidden');
+ }
+ });
+ },
+ onInputFocusOut = function() {
+ // If input has no text apply focus out styles
+ if ($(this).val().length === 0) {
+ $(this).prev('label').addClass('focus-out')
+ .removeClass('focus-in');
+ }
+
+ // Hide each input tip
+ $(this).siblings().each(function() {
+ if (inputTipSelectors.includes($(this).attr('class'))) {
+ $(this).addClass('hidden');
+ }
+ });
+ },
+ handleInputBehavior = function(input) {
+ // Initially put label in input
+ if (input.val().length === 0) {
+ input.prev('label').addClass('focus-out')
+ .removeClass('focus-in');
+ }
+
+ // Initially hide each input tip
+ input.siblings().each(function() {
+ if (inputTipSelectors.includes($(this).attr('class'))) {
+ $(this).addClass('hidden');
+ }
+ });
+
+ input.focusin(onInputFocus);
+ input.focusout(onInputFocusOut);
+ },
+ handleAutocomplete = function() {
+ inputs.forEach(function(input) {
+ if (input.val().length === 0 && !input.is(':-webkit-autofill')) {
+ input.prev('label').addClass('focus-out')
+ .removeClass('focus-in');
+ } else {
+ input.prev('label').addClass('focus-in')
+ .removeClass('focus-out');
+ }
+ });
+ };
+
+ FormView.prototype.postRender.call(this);
+ $('.optional-fields').addClass('hidden');
+ $('#toggle_optional_fields').change(function() {
+ window.analytics.track('edx.bi.user.register.optional_fields_selected');
+ $('.optional-fields').toggleClass('hidden');
+ });
+
+ // We are swapping the order of these elements here because the honor code agreement
+ // is a required checkbox field and the optional fields toggle is a cosmetic
+ // improvement so that we don't have to show all the optional fields.
+ // xss-lint: disable=javascript-jquery-insert-into-target
+ $('.checkbox-optional_fields_toggle').insertBefore('.optional-fields');
+ // xss-lint: disable=javascript-jquery-insert-into-target
+ $('.checkbox-honor_code').insertAfter('.optional-fields');
+
+ // Clicking on links inside a label should open that link.
+ $('label a').click(function(ev) {
+ ev.stopPropagation();
+ ev.preventDefault();
+ window.open($(this).attr('href'), $(this).attr('target'));
+ });
+ $('#register-country option:first').html('');
+ inputs.forEach(function(input) {
+ if (input.length > 0) {
+ handleInputBehavior(input);
+ }
+ });
+ setTimeout(handleAutocomplete, 1000);
+ },
+
hideRequiredMessageExceptOnError: function($el) {
// We only handle blur if not in an error state.
if (!$el.hasClass('error')) {
diff --git a/lms/static/sass/views/_login-register.scss b/lms/static/sass/views/_login-register.scss
index 91b300c651..bebaba5e68 100644
--- a/lms/static/sass/views/_login-register.scss
+++ b/lms/static/sass/views/_login-register.scss
@@ -292,6 +292,7 @@
width: 100%;
margin: ($baseline/2) 0 0 0;
+
&.select-year_of_birth {
@include margin-left(15px);
}
@@ -313,6 +314,25 @@
font-family: $font-family-sans-serif;
font-style: normal;
font-weight: 500;
+
+ &.focus-in {
+ position: relative;
+ padding-top: 0;
+ padding-left: 0;
+ opacity: 1;
+ }
+
+ &.focus-out {
+ position: absolute;
+ padding-top: 5px;
+ padding-left: 9px;
+ opacity: 0.75;
+ z-index: 1;
+ }
+
+ a {
+ z-index: 1;
+ }
}
#login-remember {
diff --git a/lms/templates/student_account/form_field.underscore b/lms/templates/student_account/form_field.underscore
index 05b3692b3b..4cba9a5cd1 100644
--- a/lms/templates/student_account/form_field.underscore
+++ b/lms/templates/student_account/form_field.underscore
@@ -102,7 +102,7 @@
/>
<% if ( type === 'checkbox' ) { %>