From: Steve Hull Date: Thu, 13 Mar 2014 17:54:50 +0000 (-0700) Subject: Support custom validators with abide X-Git-Tag: v5.2.1~12^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F4669%2Fhead;p=thirdparty%2Ffoundation%2Ffoundation-sites.git Support custom validators with abide --- diff --git a/doc/includes/abide/examples_abide_javascript_options.html b/doc/includes/abide/examples_abide_javascript_options.html index ebae95bb2..66e4c2eac 100644 --- a/doc/includes/abide/examples_abide_javascript_options.html +++ b/doc/includes/abide/examples_abide_javascript_options.html @@ -36,8 +36,14 @@ $(document).foundation({ // #FFF or #FFFFFF color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/ + }, + validators: { + diceRoll: function(el, required, parent) { + var possibilities = [true, false]; + return possibilities[Math.round(Math.random())]; + } } } }); ``` -{{/markdown}} \ No newline at end of file +{{/markdown}} diff --git a/doc/pages/components/abide.html b/doc/pages/components/abide.html index 4855f156c..9fb8ec018 100644 --- a/doc/pages/components/abide.html +++ b/doc/pages/components/abide.html @@ -95,6 +95,35 @@ To handle the `submit` event yourself, use `data-abide="ajax"` instead of `data- *** +## Custom Validators + +`equalTo` is actually an example of a built-in validator. To get an idea of how validators work, check out the implementation of `equalTo`: + +{{#markdown}} +```javascript +equalTo: function(el, required, parent) { + var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value, + to = el.value, + valid = (from === to); + + return valid; +} +``` +{{/markdown}} + +Your function will be passed three arguments: `el`, `required`, and `parent`. These are the elements being validated, whether or not it was marked as required, and the parent of the element being validated, respectively. +Your function should return `true` if the element is valid, or `false` if it is invalid. You can define your custom validators and pass them into abide options when initializing foundation. See the Configuration section for more details on defining custom validators. + +Suppose you've defined a custom validator `diceRoll` which randomly marks your element as valid or invalid. You would use it like so: + +{{#markdown}} +```html + +``` +{{/markdown}} + +*** + ## Using the Javascript
diff --git a/js/foundation/foundation.abide.js b/js/foundation/foundation.abide.js index 8cb4b7872..021268a8a 100644 --- a/js/foundation/foundation.abide.js +++ b/js/foundation/foundation.abide.js @@ -39,6 +39,15 @@ // #FFF or #FFFFFF color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/ + }, + validators : { + equalTo: function(el, required, parent) { + var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value, + to = el.value, + valid = (from === to); + + return valid; + } } }, @@ -135,7 +144,7 @@ } else if (pattern.length > 0) { return [el, new RegExp(pattern), required]; } - + if (this.settings.patterns.hasOwnProperty(type)) { return [el, this.settings.patterns[type], required]; } @@ -154,13 +163,16 @@ required = el_patterns[i][2], value = el.value, direct_parent = this.S(el).parent(), - is_equal = el.getAttribute(this.add_namespace('data-equalto')), + validator = el.getAttribute(this.add_namespace('data-abide-validator')), is_radio = el.type === "radio", is_checkbox = el.type === "checkbox", label = this.S('label[for="' + el.getAttribute('id') + '"]'), valid_length = (required) ? (el.value.length > 0) : true; - var parent; + var parent, valid; + + // support old way to do equalTo validations + if(el.getAttribute(this.add_namespace('data-equalto'))) { validator = "equalTo" } if (!direct_parent.is('label')) { parent = direct_parent; @@ -172,8 +184,17 @@ validations.push(this.valid_radio(el, required)); } else if (is_checkbox && required) { validations.push(this.valid_checkbox(el, required)); - } else if (is_equal && required) { - validations.push(this.valid_equal(el, required, parent)); + } else if (validator) { + valid = this.settings.validators[validator].apply(this, [el, required, parent]) + validations.push(valid); + + if (valid) { + this.S(el).removeAttr(this.invalid_attr); + parent.removeClass('error'); + } else { + this.S(el).attr(this.invalid_attr, ''); + parent.addClass('error'); + } } else { if (el_patterns[i][1].test(value) && valid_length || @@ -231,22 +252,6 @@ } } - return valid; - }, - - valid_equal: function(el, required, parent) { - var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value, - to = el.value, - valid = (from === to); - - if (valid) { - this.S(el).removeAttr(this.invalid_attr); - parent.removeClass('error'); - } else { - this.S(el).attr(this.invalid_attr, ''); - parent.addClass('error'); - } - return valid; } }; diff --git a/spec/abide/abide.js b/spec/abide/abide.js index 23a219e39..5d8f14d74 100644 --- a/spec/abide/abide.js +++ b/spec/abide/abide.js @@ -90,4 +90,84 @@ describe('abide:', function() { expect('invalid').not.toHaveBeenTriggeredOn('input[name="user_email"]'); }); }); + + describe('advanced validation', function() { + beforeEach(function() { + document.body.innerHTML = __html__['spec/abide/advanced.html']; + }); + + it('should support builtin equalTo validator', function() { + $(document).foundation({ + abide: { + validators: { + range: function(){ return true; } + } + } + }); + + + expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid'); + expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid'); + + $('input[name="user_password"]').val("foobarbaz"); + // now they're not equal + $('form').submit(); + + var invalid_fields = $('form').find('[data-invalid]'); + expect(invalid_fields.length).toBe(1); + expect($('input[name="user_password_confirmation"]')).toHaveAttr('data-invalid'); + + $('input[name="user_password_confirmation"]').val("foobarbaz"); + // now they're equal + spyOnEvent('form', 'invalid'); + spyOnEvent('form', 'valid'); + + $('form').submit(); + + expect('valid').toHaveBeenTriggeredOn('form'); + expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid'); + expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid'); + }); + + it('should support custom validators', function() { + $(document).foundation({ + abide: { + validators: { + range: function(el, required, parent) { + var start = parseInt(this.S("[name='user_start_num']").val()), + end = parseInt(el.value); + + return start < end; + } + } + } + }); + + expect($('input[name="user_start_num"]')).not.toHaveData('invalid'); + expect($('input[name="user_end_num"]')).not.toHaveData('invalid'); + + // invalid + $('input[name="user_start_num"]').val("10"); + $('input[name="user_end_num"]').val("2"); + + $('form').submit(); + + var invalid_fields = $('form').find('[data-invalid]'); + expect(invalid_fields.length).toBe(1); + + expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid'); + expect($('input[name="user_end_num"]')).toHaveAttr('data-invalid'); + + // valid now + $('input[name="user_end_num"]').val("12"); + spyOnEvent('form', 'invalid'); + spyOnEvent('form', 'valid'); + + $('form').submit(); + + expect('valid').toHaveBeenTriggeredOn('form'); + expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid'); + expect($('input[name="user_end_num"]')).not.toHaveAttr('data-invalid'); + }); + }); }); diff --git a/spec/abide/advanced.html b/spec/abide/advanced.html new file mode 100644 index 000000000..718f6e3d5 --- /dev/null +++ b/spec/abide/advanced.html @@ -0,0 +1,22 @@ +
+
+ + + Starting number must be before the end date +
+
+ + + Ending number must be before the end date +
+
+ + +
+
+ + + Password and password confirmation must match. +
+ +