]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Make a form validation handler | handle form messages
authorGeoSot <geo.sotis@gmail.com>
Wed, 19 May 2021 15:37:26 +0000 (18:37 +0300)
committerGeoSot <geo.sotis@gmail.com>
Fri, 7 Oct 2022 21:06:56 +0000 (00:06 +0300)
add "aria-describedby" attribute on "supported elements" section

add "aria-describedby" attribute on server side succeed validation messages

js/index.esm.js
js/index.umd.js
js/src/forms/form-field.js [new file with mode: 0644]
js/src/forms/form.js [new file with mode: 0644]
site/content/docs/5.2/examples/checkout-rtl/index.html
site/content/docs/5.2/examples/checkout/checkout.js
site/content/docs/5.2/examples/checkout/index.html
site/content/docs/5.2/forms/validation.md
site/static/docs/5.2/assets/js/validate-forms.js

index 062b25408f09d5e67bdce6b36fca8ff9ccf02b23..1b3806c5edc43515a77746c07cd31874ce72fd46 100644 (file)
@@ -10,6 +10,7 @@ export { default as Button } from './src/button'
 export { default as Carousel } from './src/carousel'
 export { default as Collapse } from './src/collapse'
 export { default as Dropdown } from './src/dropdown'
+export { default as Form } from './src/forms/form'
 export { default as Modal } from './src/modal'
 export { default as Offcanvas } from './src/offcanvas'
 export { default as Popover } from './src/popover'
index c63d7c2079aed8b3313ef0619373faabe4088d9d..8e054aabae7d4ab5948f2585012c4066794d5255 100644 (file)
@@ -9,6 +9,7 @@ import Alert from './src/alert'
 import Button from './src/button'
 import Carousel from './src/carousel'
 import Collapse from './src/collapse'
+import Form from './src/forms/form'
 import Dropdown from './src/dropdown'
 import Modal from './src/modal'
 import Offcanvas from './src/offcanvas'
@@ -23,6 +24,7 @@ export default {
   Button,
   Carousel,
   Collapse,
+  Form,
   Dropdown,
   Modal,
   Offcanvas,
diff --git a/js/src/forms/form-field.js b/js/src/forms/form-field.js
new file mode 100644 (file)
index 0000000..e8fd522
--- /dev/null
@@ -0,0 +1,127 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v5.3.0): forms/field.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+import { getUID, isElement } from '../util/index'
+import EventHandler from '../dom/event-handler'
+import BaseComponent from '../base-component'
+import SelectorEngine from '../dom/selector-engine'
+import TemplateFactory from '../util/template-factory'
+
+const NAME = 'formField'
+const DATA_KEY = 'bs.field'
+const EVENT_KEY = `.${DATA_KEY}`
+const EVENT_INPUT = `input${EVENT_KEY}`
+const CLASS_FIELD_ERROR = 'is-invalid'
+const CLASS_FIELD_SUCCESS = 'is-valid'
+
+const ARIA_DESCRIBED_BY = 'aria-describedby'
+const Default = {
+  invalid: '', // invalid message to add
+  name: null,
+  valid: '', // valid message to add
+  type: 'feedback' // or tooltip
+}
+
+const DefaultType = {
+  invalid: 'string',
+  name: 'string',
+  valid: 'string',
+  type: 'string'
+}
+
+const MessageTypes = {
+  ERROR: { prefix: 'invalid', class: CLASS_FIELD_ERROR },
+  INFO: { prefix: 'info', class: '' },
+  SUCCESS: { prefix: 'valid', class: CLASS_FIELD_SUCCESS }
+}
+
+class FormField extends BaseComponent {
+  constructor(element, config) {
+    super(element, config)
+    if (!isElement(this._element)) {
+      throw new TypeError(`field "${this._config.name}" not found`)
+    }
+
+    this._tipId = getUID(`${this._config.name}-formTip-`)
+    this._initialDescribedBy = this._element.getAttribute(ARIA_DESCRIBED_BY) || ''
+
+    EventHandler.on(this._element, EVENT_INPUT, () => {
+      this.clearAppended()
+    })
+  }
+
+  static get NAME() {
+    return NAME
+  }
+
+  static get Default() {
+    return Default
+  }
+
+  static get DefaultType() {
+    return DefaultType
+  }
+
+  static get MessageTypes() {
+    return MessageTypes
+  }
+
+  getElement() {
+    return this._element
+  }
+
+  clearAppended() {
+    const appendedFeedback = SelectorEngine.findOne(`#${this._tipId}`, this._element.parentNode)
+    if (!appendedFeedback) {
+      return
+    }
+
+    appendedFeedback.remove()
+
+    this._element.classList.remove(CLASS_FIELD_ERROR, CLASS_FIELD_SUCCESS)
+
+    if (this._initialDescribedBy) {
+      this._element.setAttribute(ARIA_DESCRIBED_BY, this._initialDescribedBy)
+      return
+    }
+
+    this._element.removeAttribute(ARIA_DESCRIBED_BY)
+  }
+
+  appendError(message = this._config.invalid) {
+    return this.appendFeedback(message, this.constructor.MessageTypes.ERROR)
+  }
+
+  appendSuccess(message = this._config.valid) {
+    return this.appendFeedback(message, this.constructor.MessageTypes.SUCCESS)
+  }
+
+  appendFeedback(feedback, classes = this.constructor.MessageTypes.INFO) {
+    if (!feedback) {
+      return false
+    }
+
+    this.clearAppended()
+
+    const config = {
+      extraClass: `${classes.prefix}-${this._config.type} ${classes.class}`,
+      content: { div: feedback }
+    }
+    feedback = new TemplateFactory(config)
+
+    const feedbackElement = feedback.toHtml()
+    feedbackElement.id = this._tipId
+
+    this._element.parentNode.append(feedbackElement)
+
+    const describedBy = `${this._initialDescribedBy} ${feedbackElement.id}`.trim()
+    this._element.setAttribute(ARIA_DESCRIBED_BY, describedBy)
+    return true
+  }
+}
+
+export default FormField
diff --git a/js/src/forms/form.js b/js/src/forms/form.js
new file mode 100644 (file)
index 0000000..da47037
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v5.3.0): util/form-validation.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+import BaseComponent from '../base-component'
+import EventHandler from '../dom/event-handler'
+import FormField from './form-field'
+import SelectorEngine from '../dom/selector-engine'
+
+const NAME = 'formValidation'
+const DATA_KEY = 'bs.formValidation'
+const EVENT_KEY = `.${DATA_KEY}`
+const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
+const EVENT_SUBMIT = `submit${EVENT_KEY}`
+const EVENT_RESET = `reset${EVENT_KEY}`
+
+const CLASS_VALIDATED = 'was-validated'
+const SELECTOR_DATA_TOGGLE = 'form[data-bs-toggle="form-validation"]'
+
+const Default = {
+  type: 'feedback', // or 'tooltip'
+  validateCallback: null
+}
+
+const DefaultType = {
+  type: 'string', validateCallback: '(function|null)'
+}
+
+class Form extends BaseComponent {
+  constructor(element, config) {
+    if (element.tagName !== 'FORM') {
+      throw new TypeError(`Need to be initialized in form elements. "${element.tagName}" given`)
+    }
+
+    super(element, config)
+
+    this._formFields = null // form field instances
+  }
+
+  static get NAME() {
+    return NAME
+  }
+
+  static get Default() {
+    return Default
+  }
+
+  static get DefaultType() {
+    return DefaultType
+  }
+
+  getFields() {
+    if (!this._formFields) {
+      this._formFields = this._initializeFields()
+    }
+
+    return this._formFields
+  }
+
+  getField(name) {
+    return this.getFields().get(name)
+  }
+
+  clear() {
+    this._element.classList.remove(CLASS_VALIDATED)
+    // eslint-disable-next-line no-unused-vars
+    for (const [name, field] of this.getFields()) {
+      field.clearAppended()
+    }
+  }
+
+  validate() {
+    this.clear()
+    const fetchedErrors = this._fetchErrors()
+    if (this._element.checkValidity() && !Object.keys(fetchedErrors).length) {
+      return true
+    }
+
+    for (const [name, field] of this.getFields()) {
+      this._appendErrorToField(field, fetchedErrors[name] || null)
+    }
+
+    this._element.classList.add(CLASS_VALIDATED)
+    return false
+  }
+
+  getDataForSubmission() {
+    return new FormData(this._element)
+  }
+
+  _appendErrorToField(field, givenMessage) {
+    const element = field.getElement()
+
+    if (givenMessage) { // if field is invalid check and return for default message
+      field.appendError(givenMessage)
+      return
+    }
+
+    if (element.checkValidity()) { // if field is valid, return first success message
+      field.appendSuccess()
+      return
+    }
+
+    if (field.appendError()) { // if field is invalid check and return for default message
+      return
+    }
+
+    field.appendError(element.validationMessage)
+  }
+
+  _initializeFields() {
+    const fields = new Map()
+    const formElements = Array.from(this._element.elements) // the DOM elements
+    for (const element of formElements) {
+      const name = element.name || element.id
+
+      const field = FormField.getOrCreateInstance(element, {
+        name, type: this._config.type
+      })
+      fields.set(name, field)
+    }
+
+    return fields
+  }
+
+  _fetchErrors() {
+    return typeof this._config.validateCallback === 'function' ? this._config.validateCallback(this.getDataForSubmission()) : {}
+  }
+}
+
+// On submit we want to auto-validate form
+EventHandler.on(document, EVENT_SUBMIT, SELECTOR_DATA_TOGGLE, event => {
+  const { target } = event
+  const instance = Form.getOrCreateInstance(target)
+  if (!target.checkValidity()) {
+    event.preventDefault()
+    event.stopPropagation()
+  }
+
+  if (instance.validate()) {
+    target.submit()
+  }
+})
+
+EventHandler.on(document, EVENT_RESET, SELECTOR_DATA_TOGGLE, event => {
+  const { target } = event
+  const instance = Form.getOrCreateInstance(target)
+
+  instance.clear()
+})
+
+// On load, add `novalidate` attribute to avoid browser validation
+EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+  for (const el of SelectorEngine.find(SELECTOR_DATA_TOGGLE)) {
+    el.setAttribute('novalidate', true)
+  }
+})
+export default Form
+
index e2a7971c1a19b0459c8034a78bc5ca3021472526..44120b272b60db13e33ad24ae92ade57b4ce8556 100644 (file)
@@ -4,8 +4,6 @@ title: مثال إتمام الشراء
 direction: rtl
 extra_css:
   - "../checkout/checkout.css"
-extra_js:
-  - src: "../checkout/checkout.js"
 body_class: "bg-light"
 ---
 
@@ -67,7 +65,7 @@ body_class: "bg-light"
       </div>
       <div class="col-md-7 col-lg-8">
         <h4 class="mb-3">عنوان الفوترة</h4>
-        <form class="needs-validation" novalidate>
+        <form data-bs-toggle="form-validation">
           <div class="row g-3">
             <div class="col-sm-6">
               <label for="firstName" class="form-label">الاسم الأول</label>
index 30ea0aa6b1e01a48ca950411ccf836e8c0274f4f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,19 +0,0 @@
-// Example starter JavaScript for disabling form submissions if there are invalid fields
-(() => {
-  'use strict'
-
-  // Fetch all the forms we want to apply custom Bootstrap validation styles to
-  const forms = document.querySelectorAll('.needs-validation')
-
-  // Loop over them and prevent submission
-  Array.from(forms).forEach(form => {
-    form.addEventListener('submit', event => {
-      if (!form.checkValidity()) {
-        event.preventDefault()
-        event.stopPropagation()
-      }
-
-      form.classList.add('was-validated')
-    }, false)
-  })
-})()
index ba415f0d9f7a9ae71f1abb2ba7d1d283822f1e2f..87b03634bd5267bbfdb963fa99151456a5efe1e7 100644 (file)
@@ -3,8 +3,6 @@ layout: examples
 title: Checkout example
 extra_css:
   - "checkout.css"
-extra_js:
-  - src: "checkout.js"
 body_class: "bg-light"
 ---
 
@@ -66,7 +64,7 @@ body_class: "bg-light"
       </div>
       <div class="col-md-7 col-lg-8">
         <h4 class="mb-3">Billing address</h4>
-        <form class="needs-validation" novalidate>
+        <form data-bs-toggle="form-validation">
           <div class="row g-3">
             <div class="col-sm-6">
               <label for="firstName" class="form-label">First name</label>
index f8c2200c00b26344023e521b6aec9f289d74a6dc..aa789676ec0eb789d0f9315cec872cd8696a8073 100644 (file)
@@ -4,9 +4,6 @@ title: Validation
 description: Provide valuable, actionable feedback to your users with HTML5 form validation, via browser default behaviors or custom styles and JavaScript.
 group: forms
 toc: true
-extra_js:
-  - src: "/docs/5.2/assets/js/validate-forms.js"
-    async: true
 ---
 
 {{< callout warning >}}
@@ -30,80 +27,59 @@ With that in mind, consider the following demos for our custom form validation s
 
 ## Custom styles
 
-For custom Bootstrap form validation messages, you'll need to add the `novalidate` boolean attribute to your `<form>`. This disables the browser default feedback tooltips, but still provides access to the form validation APIs in JavaScript. Try to submit the form below; our JavaScript will intercept the submit button and relay feedback to you. When attempting to submit, you'll see the `:invalid` and `:valid` styles applied to your form controls.
+For custom Bootstrap form validation messages, you'll need to add the data-bs-toggle="form-validation" `<form>`. This disables the browser default feedback tooltips, but still provides access to the form validation APIs in JavaScript. Try to submit the form below; our JavaScript will intercept the submit button and relay feedback to you. When attempting to submit, you'll see the `:invalid` and `:valid` styles applied to your form controls.
 
 Custom feedback styles apply custom colors, borders, focus styles, and background icons to better communicate feedback. Background icons for `<select>`s are only available with `.form-select`, and not `.form-control`.
 
 {{< example >}}
-<form class="row g-3 needs-validation" novalidate>
+<form class="row g-3" data-bs-toggle="form-validation">
   <div class="col-md-4">
     <label for="validationCustom01" class="form-label">First name</label>
-    <input type="text" class="form-control" id="validationCustom01" value="Mark" required>
-    <div class="valid-feedback">
-      Looks good!
-    </div>
+    <input type="text" class="form-control" id="validationCustom01" value="Mark" required data-bs-valid="Looks good!" data-bs-invalid="Please, provide a valid Name!">
   </div>
   <div class="col-md-4">
     <label for="validationCustom02" class="form-label">Last name</label>
-    <input type="text" class="form-control" id="validationCustom02" value="Otto" required>
-    <div class="valid-feedback">
-      Looks good!
-    </div>
+    <input type="text" class="form-control" id="validationCustom02" value="Otto" required data-bs-valid="Looks good!">
   </div>
   <div class="col-md-4">
     <label for="validationCustomUsername" class="form-label">Username</label>
     <div class="input-group has-validation">
       <span class="input-group-text" id="inputGroupPrepend">@</span>
-      <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
-      <div class="invalid-feedback">
-        Please choose a username.
-      </div>
+      <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required data-bs-invalid="Please choose a username.">
     </div>
   </div>
   <div class="col-md-6">
     <label for="validationCustom03" class="form-label">City</label>
-    <input type="text" class="form-control" id="validationCustom03" required>
-    <div class="invalid-feedback">
-      Please provide a valid city.
-    </div>
+    <input type="text" class="form-control" id="validationCustom03" required data-bs-invalid="Please provide a valid city.">
   </div>
   <div class="col-md-3">
     <label for="validationCustom04" class="form-label">State</label>
-    <select class="form-select" id="validationCustom04" required>
+    <select class="form-select" id="validationCustom04" required data-bs-invalid="Please select a valid state.">
       <option selected disabled value="">Choose...</option>
       <option>...</option>
     </select>
-    <div class="invalid-feedback">
-      Please select a valid state.
-    </div>
   </div>
   <div class="col-md-3">
     <label for="validationCustom05" class="form-label">Zip</label>
-    <input type="text" class="form-control" id="validationCustom05" required>
-    <div class="invalid-feedback">
-      Please provide a valid zip.
-    </div>
+    <input type="text" class="form-control" id="validationCustom05" required data-bs-invalid="Please provide a valid zip.">
   </div>
   <div class="col-12">
     <div class="form-check">
-      <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
+      <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required data-bs-invalid="You must agree before submitting.">
       <label class="form-check-label" for="invalidCheck">
         Agree to terms and conditions
       </label>
-      <div class="invalid-feedback">
-        You must agree before submitting.
-      </div>
     </div>
   </div>
   <div class="col-12">
     <button class="btn btn-primary" type="submit">Submit form</button>
+    <button class="btn btn-danger" type="reset">Reset form</button>
   </div>
 </form>
 {{< /example >}}
 
 {{< example lang="js" show_preview="false" >}}
 {{< js.inline >}}
-{{- readFile (path.Join "site/static/docs" .Site.Params.docs_version "assets/js/validate-forms.js") -}}
 {{< /js.inline >}}
 {{< /example >}}
 
@@ -171,15 +147,15 @@ To fix [issues with border radius](https://github.com/twbs/bootstrap/issues/2511
 <form class="row g-3">
   <div class="col-md-4">
     <label for="validationServer01" class="form-label">First name</label>
-    <input type="text" class="form-control is-valid" id="validationServer01" value="Mark" required>
-    <div class="valid-feedback">
+    <input type="text" class="form-control is-valid" id="validationServer01" value="Mark" aria-describedby="validationServerNameFeedback" required>
+    <div id="validationServerNameFeedback" class="valid-feedback">
       Looks good!
     </div>
   </div>
   <div class="col-md-4">
     <label for="validationServer02" class="form-label">Last name</label>
-    <input type="text" class="form-control is-valid" id="validationServer02" value="Otto" required>
-    <div class="valid-feedback">
+    <input type="text" class="form-control is-valid" id="validationServer02" aria-describedby="validationServerLastNameFeedback" value="Otto" required>
+    <div id="validationServerLastNameFeedback" class="valid-feedback">
       Looks good!
     </div>
   </div>
@@ -246,41 +222,41 @@ Validation styles are available for the following form controls and components:
 <form class="was-validated">
   <div class="mb-3">
     <label for="validationTextarea" class="form-label">Textarea</label>
-    <textarea class="form-control" id="validationTextarea" placeholder="Required example textarea" required></textarea>
-    <div class="invalid-feedback">
+    <textarea class="form-control" id="validationTextarea" placeholder="Required example textarea" aria-describedby="validationSupportedElementsTextArea" required></textarea>
+    <div id="validationSupportedElementsTextArea" class="invalid-feedback">
       Please enter a message in the textarea.
     </div>
   </div>
 
   <div class="form-check mb-3">
-    <input type="checkbox" class="form-check-input" id="validationFormCheck1" required>
+    <input type="checkbox" class="form-check-input" id="validationFormCheck1" aria-describedby="validationSupportedElementsCheckBox" required>
     <label class="form-check-label" for="validationFormCheck1">Check this checkbox</label>
-    <div class="invalid-feedback">Example invalid feedback text</div>
+    <div id="validationSupportedElementsCheckBox" class="invalid-feedback">Example invalid feedback text</div>
   </div>
 
   <div class="form-check">
-    <input type="radio" class="form-check-input" id="validationFormCheck2" name="radio-stacked" required>
+    <input type="radio" class="form-check-input" id="validationFormCheck2" name="radio-stacked" aria-describedby="validationSupportedElementsRadio" required>
     <label class="form-check-label" for="validationFormCheck2">Toggle this radio</label>
   </div>
   <div class="form-check mb-3">
-    <input type="radio" class="form-check-input" id="validationFormCheck3" name="radio-stacked" required>
+    <input type="radio" class="form-check-input" id="validationFormCheck3" name="radio-stacked" aria-describedby="validationSupportedElementsRadio" required>
     <label class="form-check-label" for="validationFormCheck3">Or toggle this other radio</label>
-    <div class="invalid-feedback">More example invalid feedback text</div>
+    <div id="validationSupportedElementsRadio" class="invalid-feedback">More example invalid feedback text</div>
   </div>
 
   <div class="mb-3">
-    <select class="form-select" required aria-label="select example">
+    <select class="form-select" required aria-label="select example" aria-describedby="validationSupportedElementsSelect">
       <option value="">Open this select menu</option>
       <option value="1">One</option>
       <option value="2">Two</option>
       <option value="3">Three</option>
     </select>
-    <div class="invalid-feedback">Example invalid select feedback</div>
+    <div id="validationSupportedElementsSelect" class="invalid-feedback">Example invalid select feedback</div>
   </div>
 
   <div class="mb-3">
-    <input type="file" class="form-control" aria-label="file example" required>
-    <div class="invalid-feedback">Example invalid form file feedback</div>
+    <input type="file" class="form-control" aria-label="file example" required aria-describedby="validationSupportedElementsFile">
+    <div id="validationSupportedElementsFile" class="invalid-feedback">Example invalid form file feedback</div>
   </div>
 
   <div class="mb-3">
@@ -294,57 +270,40 @@ Validation styles are available for the following form controls and components:
 If your form layout allows it, you can swap the `.{valid|invalid}-feedback` classes for `.{valid|invalid}-tooltip` classes to display validation feedback in a styled tooltip. Be sure to have a parent with `position: relative` on it for tooltip positioning. In the example below, our column classes have this already, but your project may require an alternative setup.
 
 {{< example >}}
-<form class="row g-3 needs-validation" novalidate>
+<form class="row g-3" data-bs-toggle="form-validation" data-bs-type="tooltip" >
   <div class="col-md-4 position-relative">
     <label for="validationTooltip01" class="form-label">First name</label>
-    <input type="text" class="form-control" id="validationTooltip01" value="Mark" required>
-    <div class="valid-tooltip">
-      Looks good!
-    </div>
+    <input type="text" class="form-control" id="validationTooltip01" value="Mark" required data-bs-valid="Looks good!">
   </div>
   <div class="col-md-4 position-relative">
     <label for="validationTooltip02" class="form-label">Last name</label>
-    <input type="text" class="form-control" id="validationTooltip02" value="Otto" required>
-    <div class="valid-tooltip">
-      Looks good!
-    </div>
+    <input type="text" class="form-control" id="validationTooltip02" value="Otto" required  data-bs-valid="Looks good!">
   </div>
   <div class="col-md-4 position-relative">
     <label for="validationTooltipUsername" class="form-label">Username</label>
     <div class="input-group has-validation">
       <span class="input-group-text" id="validationTooltipUsernamePrepend">@</span>
-      <input type="text" class="form-control" id="validationTooltipUsername" aria-describedby="validationTooltipUsernamePrepend" required>
-      <div class="invalid-tooltip">
-        Please choose a unique and valid username.
-      </div>
+      <input type="text" class="form-control" id="validationTooltipUsername" aria-describedby="validationTooltipUsernamePrepend" required data-bs-invalid="Please choose a username.">
     </div>
   </div>
   <div class="col-md-6 position-relative">
     <label for="validationTooltip03" class="form-label">City</label>
-    <input type="text" class="form-control" id="validationTooltip03" required>
-    <div class="invalid-tooltip">
-      Please provide a valid city.
-    </div>
+    <input type="text" class="form-control" id="validationTooltip03" required data-bs-invalid="Please provide a valid city.">
   </div>
   <div class="col-md-3 position-relative">
     <label for="validationTooltip04" class="form-label">State</label>
-    <select class="form-select" id="validationTooltip04" required>
+    <select class="form-select" id="validationTooltip04" required data-bs-invalid="Please select a valid state.">
       <option selected disabled value="">Choose...</option>
       <option>...</option>
     </select>
-    <div class="invalid-tooltip">
-      Please select a valid state.
-    </div>
   </div>
   <div class="col-md-3 position-relative">
     <label for="validationTooltip05" class="form-label">Zip</label>
-    <input type="text" class="form-control" id="validationTooltip05" required>
-    <div class="invalid-tooltip">
-      Please provide a valid zip.
-    </div>
+    <input type="text" class="form-control" id="validationTooltip05" required data-bs-invalid="Please provide a valid zip.">
   </div>
   <div class="col-12">
     <button class="btn btn-primary" type="submit">Submit form</button>
+    <button class="btn btn-danger" type="reset">Reset form</button>
   </div>
 </form>
 {{< /example >}}
index 30ea0aa6b1e01a48ca950411ccf836e8c0274f4f..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,19 +0,0 @@
-// Example starter JavaScript for disabling form submissions if there are invalid fields
-(() => {
-  'use strict'
-
-  // Fetch all the forms we want to apply custom Bootstrap validation styles to
-  const forms = document.querySelectorAll('.needs-validation')
-
-  // Loop over them and prevent submission
-  Array.from(forms).forEach(form => {
-    form.addEventListener('submit', event => {
-      if (!form.checkValidity()) {
-        event.preventDefault()
-        event.stopPropagation()
-      }
-
-      form.classList.add('was-validated')
-    }, false)
-  })
-})()