Commit cb5c03f0 by AlasdairSwan

ECOM-626 Added required check for select dropdowns and validation

parent ad3c11e5
......@@ -125,7 +125,7 @@ class FormDescription(object):
def add_field(
self, name, label=u"", field_type=u"text", default=u"",
placeholder=u"", instructions=u"", required=True, restrictions=None,
options=None, error_messages=None
options=None, include_default_option=False, error_messages=None
):
"""Add a field to the form description.
......@@ -158,6 +158,9 @@ class FormDescription(object):
and `display_name` is the name to display to the user.
If the field type is "select", you *must* provide this kwarg.
include_default_option (boolean): If True, include a "default" empty option
at the beginning of the options list.
error_messages (dict): Custom validation error messages.
Currently, the only supported key is "required" indicating
that the messages should be displayed if the user does
......@@ -188,10 +191,20 @@ class FormDescription(object):
if field_type == "select":
if options is not None:
field_dict["options"] = [
field_dict["options"] = []
# Include an empty "default" option at the beginning of the list
if include_default_option:
field_dict["options"].append({
"value": "",
"name": "--",
"default": True
})
field_dict["options"].extend([
{"value": option_value, "name": option_name}
for option_value, option_name in options
]
])
else:
raise InvalidFieldError("You must provide options for a select field.")
......
......@@ -955,7 +955,7 @@ class RegistrationViewTest(ApiTestCase):
"required": False,
"label": "Highest Level of Education Completed",
"options": [
{"value": "", "name": "--"},
{"value": "", "name": "--", "default": True},
{"value": "p", "name": "Doctorate"},
{"value": "m", "name": "Master's or professional degree"},
{"value": "b", "name": "Bachelor's degree"},
......@@ -978,7 +978,7 @@ class RegistrationViewTest(ApiTestCase):
"required": False,
"label": "Gender",
"options": [
{"value": "", "name": "--"},
{"value": "", "name": "--", "default": True},
{"value": "m", "name": "Male"},
{"value": "f", "name": "Female"},
{"value": "o", "name": "Other"},
......@@ -989,7 +989,7 @@ class RegistrationViewTest(ApiTestCase):
def test_register_form_year_of_birth(self):
this_year = datetime.datetime.now(UTC).year
year_options = (
[{"value": "", "name": "--"}] + [
[{"value": "", "name": "--", "default": True}] + [
{"value": unicode(year), "name": unicode(year)}
for year in range(this_year, this_year - 120, -1)
]
......@@ -1042,7 +1042,7 @@ class RegistrationViewTest(ApiTestCase):
def test_registration_form_country(self):
country_options = (
[{"name": "--", "value": ""}] +
[{"name": "--", "value": "", "default": True}] +
[
{"value": country_code, "name": unicode(country_name)}
for country_code, country_name in SORTED_COUNTRIES
......
......@@ -379,7 +379,8 @@ class RegistrationView(APIView):
"level_of_education",
label=education_level_label,
field_type="select",
options=self._options_with_default(UserProfile.LEVEL_OF_EDUCATION_CHOICES),
options=UserProfile.LEVEL_OF_EDUCATION_CHOICES,
include_default_option=True,
required=required
)
......@@ -392,7 +393,8 @@ class RegistrationView(APIView):
"gender",
label=gender_label,
field_type="select",
options=self._options_with_default(UserProfile.GENDER_CHOICES),
options=UserProfile.GENDER_CHOICES,
include_default_option=True,
required=required
)
......@@ -406,7 +408,8 @@ class RegistrationView(APIView):
"year_of_birth",
label=yob_label,
field_type="select",
options=self._options_with_default(options),
options=options,
include_default_option=True,
required=required
)
......@@ -463,7 +466,8 @@ class RegistrationView(APIView):
"country",
label=country_label,
field_type="select",
options=self._options_with_default(options),
options=options,
include_default_option=True,
required=required
)
......@@ -534,12 +538,6 @@ class RegistrationView(APIView):
}
)
def _options_with_default(self, options):
"""Include a default option as the first option. """
return (
[("", "--")] + list(options)
)
def _apply_third_party_auth_overrides(self, request, form_desc):
"""Modify the registration form if the user has authenticated with a third-party provider.
......
......@@ -52,10 +52,10 @@ describe('edx.utils.validate', function () {
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('fails if a field is provided a value below its minimum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, SHORT_STRING);
// Verify optional field behavior
expectInvalid(MIN_ERROR_FRAGMENT);
......@@ -66,7 +66,7 @@ describe('edx.utils.validate', function () {
it('succeeds if a field with no minimum character limit is provided a value below its maximum character limit', function () {
createFixture('text', 'username', false, null, MAX_LENGTH, SHORT_STRING);
// Verify optional field behavior
expectValid();
......@@ -154,6 +154,31 @@ describe('edx.utils.validate', function () {
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('succeeds if a select is optional, or required and default is selected, but fails if a required select has the default option selected', function () {
var select = [
'<select id="dropdown" name="country">',
'<option value="" data-isdefault="true">Please select a country</option>',
'<option value="BE">Belgium</option>',
'<option value="DE">Germany</option>',
'</select>'
].join('');
setFixtures(select);
dropdown = $('#dropdown');
// Optional
expectValid();
// Required, default text selected
dropdown.attr('required', true);
expectInvalid(REQUIRED_ERROR_FRAGMENT);
// Required, country selected
dropdown.val('BE');
expectValid();
});
it('returns a custom error message if an invalid field has one attached', function () {
// Create a blank required field
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
......
......@@ -87,7 +87,18 @@ var edx = edx || {};
},
isBlank: function( $el ) {
return ( $el.attr('type') === 'checkbox' ) ? !$el.prop('checked') : !$el.val();
var type = $el.attr('type'),
isBlank;
if ( type === 'checkbox' ) {
isBlank = !$el.prop('checked');
} else if ( type === 'select' ) {
isBlank = ( $el.data('isdefault') === true );
} else {
isBlank = !$el.val();
}
return isBlank;
},
email: {
......
......@@ -208,6 +208,10 @@
select {
width: 100%;
&.error {
border-color: tint($red,50%);
}
}
/** FROM _accounts.scss - end **/
}
......
......@@ -10,9 +10,10 @@
<select id="<%= form %>-<%= name %>"
name="<%= name %>"
class="input-inline"
aria-describedby="<%= form %>-<%= name %>-desc">
aria-describedby="<%= form %>-<%= name %>-desc"
<% if ( required ) { %> required<% } %>>
<% _.each(options, function(el) { %>
<option value="<%= el.value%>"><%= el.name %></option>
<option value="<%= el.value%>"<% if ( el.default ) { %> data-isdefault="true"<% } %>><%= el.name %></option>
<% }); %>
</select>
<% } else if ( type === 'textarea' ) { %>
......
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