]> git.ipfire.org Git - thirdparty/foundation/foundation-sites.git/commitdiff
feat(abide): add validator-specific error messages 11698/head
authorsoylent <soylent>
Sun, 24 Feb 2019 01:08:40 +0000 (18:08 -0700)
committersoylent <soylent>
Tue, 8 Oct 2019 03:54:29 +0000 (21:54 -0600)
Now you can add different error messages for each validator using the
`[data-form-error-on]` attribute.

<input required type="email">
<span class="form-error" data-form-error-on="required">Required</span>
<span class="form-error" data-form-error-on="pattern">Invalid</span>

BREAKING CHANGE: `Foundation.Abide#validateText()` no longer checks if
the input is required. Use the `Foundation.Abide#requiredCheck()` method
for that.

Closes #10799

docs/pages/abide.md
js/foundation.abide.js
test/visual/abide/error-messages.html [new file with mode: 0644]

index e317896015de02e5c17dfd25e1b453e5db5cac4d..2033f3041dfdcc40a41570ded7dd9a5d5f79e883 100644 (file)
@@ -168,6 +168,28 @@ When the Form Errors cannot be placed next to its field, like in an Input Group,
 </form>
 ```
 
+You can specify validator-specific error messages using `[data-form-error-on]`
+attribute, for example:
+
+- `data-form-error-on="required"`
+- `data-form-error-on="pattern"`
+- `data-form-error-on="equalTo"`
+- `data-form-error-on="your_custom_validator"`
+
+```html_example
+<form data-abide novalidate>
+  <label>Email Required
+    <input type="text" required pattern="email">
+    <span class="form-error" data-form-error-on="required">
+      Yo, you had better fill this out, it's required.
+    </span>
+    <span class="form-error" data-form-error-on="pattern">
+      Invalid Email
+    </span>
+  </label>
+  <button class="button" type="submit" value="Submit">Submit</button>
+</form>
+```
 
 ## Initial State
 
index 7fbd9df8d94cb20df82de3ccc04a7addb6877b9d..2fadf6ab45bc81f87d9102bd124e46327188215a 100644 (file)
@@ -173,9 +173,10 @@ class Abide extends Plugin {
    * This allows for multiple form errors per input, though if none are found, no form errors will be shown.
    *
    * @param {Object} $el - jQuery object to use as reference to find the form error selector.
+   * @param {String[]} [failedValidators] - List of failed validators.
    * @returns {Object} jQuery object with the selector.
    */
-  findFormError($el) {
+  findFormError($el, failedValidators) {
     var id = $el.length ? $el[0].id : '';
     var $error = $el.siblings(this.options.formErrorSelector);
 
@@ -187,6 +188,15 @@ class Abide extends Plugin {
       $error = $error.add(this.$element.find(`[data-form-error-for="${id}"]`));
     }
 
+    if (!!failedValidators) {
+      $error = $error.not('[data-form-error-on]')
+
+      failedValidators.forEach((v) => {
+        $error = $error.add($el.siblings(`[data-form-error-on="${v}"]`));
+        $error = $error.add(this.$element.find(`[data-form-error-for="${id}"][data-form-error-on="${v}"]`));
+      });
+    }
+
     return $error;
   }
 
@@ -256,10 +266,11 @@ class Abide extends Plugin {
   /**
    * Adds the CSS error class as specified by the Abide settings to the label, input, and the form
    * @param {Object} $el - jQuery object to add the class to
+   * @param {String[]} [failedValidators] - List of failed validators.
    */
-  addErrorClasses($el) {
+  addErrorClasses($el, failedValidators) {
     var $label = this.findLabel($el);
-    var $formError = this.findFormError($el);
+    var $formError = this.findFormError($el, failedValidators);
 
     if ($label.length) {
       $label.addClass(this.options.labelErrorClass);
@@ -422,10 +433,9 @@ class Abide extends Plugin {
    */
   validateInput($el) {
     var clearRequire = this.requiredCheck($el),
-        validated = false,
-        customValidator = true,
         validator = $el.attr('data-validator'),
-        equalTo = true;
+        failedValidators = [],
+        manageErrorClasses = true;
 
     // skip validation if disabled
     if (this._validationIsDisabled()) {
@@ -439,34 +449,39 @@ class Abide extends Plugin {
 
     switch ($el[0].type) {
       case 'radio':
-        validated = this.validateRadio($el.attr('name'));
+        this.validateRadio($el.attr('name')) || failedValidators.push('required');
         break;
 
       case 'checkbox':
-        validated = this.validateCheckbox($el.attr('name'));
-        clearRequire = true;
+        this.validateCheckbox($el.attr('name')) || failedValidators.push('required');
+        // validateCheckbox() adds/removes error classes
+        manageErrorClasses = false;
         break;
 
       case 'select':
       case 'select-one':
       case 'select-multiple':
-        validated = clearRequire;
+        clearRequire || failedValidators.push('required');
         break;
 
       default:
-        validated = this.validateText($el);
+        clearRequire || failedValidators.push('required');
+        this.validateText($el) || failedValidators.push('pattern');
     }
 
     if (validator) {
-      customValidator = this.matchValidation($el, validator, $el.attr('required'));
+      const required = $el.attr('required') ? true : false;
+
+      validator.split(' ').forEach((v) => {
+        this.options.validators[v]($el, required, $el.parent()) || failedValidators.push(v);
+      });
     }
 
     if ($el.attr('data-equalto')) {
-      equalTo = this.options.validators.equalTo($el);
+      this.options.validators.equalTo($el) || failedValidators.push('equalTo');
     }
 
-
-    var goodToGo = [clearRequire, validated, customValidator, equalTo].indexOf(false) === -1;
+    var goodToGo = failedValidators.length === 0;
     var message = (goodToGo ? 'valid' : 'invalid') + '.zf.abide';
 
     if (goodToGo) {
@@ -482,7 +497,13 @@ class Abide extends Plugin {
       }
     }
 
-    this[goodToGo ? 'removeErrorClasses' : 'addErrorClasses']($el);
+    if (manageErrorClasses) {
+      this.removeErrorClasses($el);
+
+      if (!goodToGo) {
+        this.addErrorClasses($el, failedValidators);
+      }
+    }
 
     /**
      * Fires when the input is done checking for validation. Event trigger is either `valid.zf.abide` or `invalid.zf.abide`
@@ -559,7 +580,7 @@ class Abide extends Plugin {
     // A pattern can be passed to this function, or it will be infered from the input's "pattern" attribute, or it's "type" attribute
     pattern = (pattern || $el.attr('data-pattern') || $el.attr('pattern') || $el.attr('type'));
     var inputText = $el.val();
-    var valid = false;
+    var valid = true;
 
     if (inputText.length) {
       // If the pattern attribute on the element is in Abide's list of patterns, then test that regexp
@@ -570,13 +591,6 @@ class Abide extends Plugin {
       else if (pattern !== $el.attr('type')) {
         valid = new RegExp(pattern).test(inputText);
       }
-      else {
-        valid = true;
-      }
-    }
-    // An empty field is valid if it's not required
-    else if (!$el.prop('required')) {
-      valid = true;
     }
 
     return valid;
@@ -658,7 +672,7 @@ class Abide extends Plugin {
     // Refresh error class for all input
     $group.each((i, e) => {
       if (!valid) {
-        this.addErrorClasses($(e));
+        this.addErrorClasses($(e), ['required']);
       } else {
         this.removeErrorClasses($(e));
       }
diff --git a/test/visual/abide/error-messages.html b/test/visual/abide/error-messages.html
new file mode 100644 (file)
index 0000000..d35e1fe
--- /dev/null
@@ -0,0 +1,120 @@
+<!doctype html>
+<!--[if IE 9]><html class="lt-ie10" lang="en" > <![endif]-->
+<html class="no-js" lang="en" dir="ltr">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+    <title>Foundation for Sites Testing</title>
+    <link href="../assets/css/foundation.css" rel="stylesheet" />
+  </head>
+  <body>
+    <div class="grid-container">
+      <div class="grid-x grid-padding-x">
+        <div class="cell">
+          <h1>Abide: Error Messages</h1>
+
+          <p>This form has two different error messages.</p>
+
+          <form data-abide novalidate>
+            <label>
+              <input required type="email" placeholder="Email Required">
+              <span class="form-error" data-form-error-on="required">Required</span>
+              <span class="form-error" data-form-error-on="pattern">Invalid</span>
+            </label>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+
+          <hr>
+
+          <p>This form has two different error messages and uses the [data-form-error-for] attribute to specify the target input.</p>
+
+          <form data-abide novalidate>
+            <label for="url">Url
+              <input required id="url" type="url" placeholder="Url Required">
+            </label>
+            <span class="form-error" data-form-error-for="url" data-form-error-on="required">Required</span>
+            <span class="form-error" data-form-error-for="url" data-form-error-on="pattern">Invalid</span>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+
+          <hr>
+
+          <p>This form has one generic and one validator-specific error message.</p>
+
+          <form data-abide novalidate>
+            <label>
+              <input required type="email" placeholder="Email Required">
+              <span class="form-error">Error</span>
+              <span class="form-error" data-form-error-on="pattern">Invalid</span>
+            </label>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+
+          <hr>
+
+          <p>This form uses the equalTo validator.</p>
+
+          <form data-abide novalidate>
+            <label>
+              <input required id="email" type="email" placeholder="Email Required">
+              <span class="form-error">Error</span>
+            </label>
+            </label>
+              <input type="email" placeholder="Email Confirmation" data-equalto="email">
+              <span class="form-error" data-form-error-on="pattern">Invalid</span>
+              <span class="form-error" data-form-error-on="equalTo">Mismatch</span>
+            </label>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+
+          <hr>
+
+          <p>This form has two input fields.</p>
+
+          <form data-abide novalidate>
+            <label>Email
+              <input required type="email" placeholder="Email Required">
+              <span class="form-error" data-form-error-on="required">Required</span>
+              <span class="form-error" data-form-error-on="pattern">Invalid</span>
+            </label>
+            <label>Url
+              <input required type="url" placeholder="Url Required">
+              <span class="form-error" data-form-error-on="required">Required</span>
+              <span class="form-error" data-form-error-on="pattern">Invalid</span>
+            </label>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+
+          <hr>
+
+          <p>This form uses a custom validator.</p>
+
+          <form data-abide novalidate>
+            <label>
+              <input required type="number" placeholder="Positive Number Required" data-validator="positive">
+              <span class="form-error" data-form-error-on="required">Required</span>
+              <span class="form-error" data-form-error-on="positive">Must be positive</span>
+            </label>
+            <button type="submit" class="button">Submit</button>
+            <button type="reset" class="button">Reset</button>
+          </form>
+        </div>
+      </div>
+    </div>
+
+    <script src="../assets/js/vendor.js"></script>
+    <script src="../assets/js/foundation.js"></script>
+    <script>
+      Foundation.Abide.defaults.validators['positive'] = function($el) {
+        return parseInt($el.val()) > 0;
+      }
+      $(document).foundation();
+    </script>
+  </body>
+</html>