diff --git a/app/assets/javascripts/constraint_validations.es.js b/app/assets/javascripts/constraint_validations.es.js
index 0be3e21..b659d2e 100644
--- a/app/assets/javascripts/constraint_validations.es.js
+++ b/app/assets/javascripts/constraint_validations.es.js
@@ -2,6 +2,10 @@ function isFieldElement(element) {
return !element.disabled && "validity" in element && element.willValidate
}
+function isAriaInvalid(element) {
+ return element.getAttribute("aria-invalid") === "true"
+}
+
function readValidationMessages(input) {
try {
return JSON.parse(input.getAttribute("data-validation-messages")) || {}
@@ -11,7 +15,7 @@ function readValidationMessages(input) {
}
class CheckboxValidator {
- selector = "input[type=checkbox]:required"
+ selector = "input[type=checkbox]"
ignoringMutations = false
constructor(constraintValidations, predicate) {
@@ -23,15 +27,17 @@ class CheckboxValidator {
}
connect() {
+ this.element.addEventListener("invalid", this.handleInvalid, { capture: true, passive: true });
this.mutationObserver.observe(this.element, {
attributeFilter: ["required"],
childList: true,
subtree: true
});
- this.reportValidationMessages(this.element.querySelectorAll(this.selector));
+ this.reportValidationMessages(this.element.querySelectorAll(this.selector), isAriaInvalid);
}
disconnect() {
+ this.element.removeEventListener("invalid", this.handleInvalid, { capture: true, passive: true });
this.mutationObserver.disconnect();
}
@@ -40,8 +46,8 @@ class CheckboxValidator {
}
validate(target) {
- const checkboxesInGroup = checkboxGroup(target).filter(isFieldElement);
- const allRequired = checkboxesInGroup.every((checkbox) => checkbox.getAttribute("aria-required") === "true");
+ const checkboxesInGroup = checkboxGroup(target).filter(isCheckboxElement);
+ const allRequired = checkboxesInGroup.every((checkbox) => isRequired(checkbox));
const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked);
if (allRequired && someChecked) {
@@ -60,6 +66,18 @@ class CheckboxValidator {
// Private
+ handleInvalid = ({ target }) => {
+ const checkboxes = new Set;
+
+ for (const element of target.form.elements) {
+ if (isCheckboxElement(element) && this.willValidate(element)) {
+ checkboxes.add(element);
+ }
+ }
+
+ this.reportValidationMessages(checkboxes);
+ }
+
handleMutation = (mutationRecords) => {
if (this.ignoringMutations) return
@@ -71,22 +89,23 @@ class CheckboxValidator {
target.removeAttribute("aria-required");
}
} else if (addedNodes.length) {
- this.reportValidationMessages(addedNodes);
+ this.reportValidationMessages(addedNodes, isAriaInvalid);
}
}
}
- reportValidationMessages(nodes) {
+ reportValidationMessages(nodes, willReport = () => true) {
const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes);
for (const checkbox of requiredCheckboxes) {
- if (checkbox.required) {
+ if (isRequired(checkbox)) {
const group = checkboxGroup(checkbox);
if (this.willValidateGroup(group)) {
for (const checkboxInGroup of group) {
this.swapRequiredWithAriaRequired(checkboxInGroup);
- if (checkboxInGroup.getAttribute("aria-invalid") === "true") {
+
+ if (willReport(checkboxInGroup)) {
this.validate(checkboxInGroup);
}
}
@@ -149,6 +168,14 @@ function querySelectorAllNodes(selector, nodes, elements = new Set) {
return Array.from(elements)
}
+function isCheckboxElement(element) {
+ return isFieldElement(element) && element.type === "checkbox"
+}
+
+function isRequired(element) {
+ return element.required || element.getAttribute("aria-required") === "true"
+}
+
const defaultOptions = {
disableSubmitWhenInvalid: false,
validateOn: ["blur", "input"],
@@ -231,7 +258,7 @@ class ConstraintValidations {
for (const form of forms) {
for (const element of Array.from(form.elements).filter(isFieldElement)) {
- const serverRenderedInvalid = /true/i.test(element.getAttribute("aria-invalid"));
+ const serverRenderedInvalid = isAriaInvalid(element);
const id = element.getAttribute("aria-errormessage");
const errorMessageElement = document.getElementById(id);
const validationMessage = errorMessageElement?.textContent;
diff --git a/app/assets/javascripts/constraint_validations.es.js.map b/app/assets/javascripts/constraint_validations.es.js.map
index 9408405..2b7deba 100644
--- a/app/assets/javascripts/constraint_validations.es.js.map
+++ b/app/assets/javascripts/constraint_validations.es.js.map
@@ -1 +1 @@
-{"version":3,"file":"constraint_validations.es.js","sources":["../../javascript/constraint_validations/util.js","../../javascript/constraint_validations/validators/checkbox_validator.js","../../javascript/constraint_validations/index.js"],"sourcesContent":["export function isFieldElement(element) {\n return !element.disabled && \"validity\" in element && element.willValidate\n}\n\nexport function readValidationMessages(input) {\n try {\n return JSON.parse(input.getAttribute(\"data-validation-messages\")) || {}\n } catch(_) {\n return {}\n }\n}\n","import { isFieldElement, readValidationMessages } from \"../util\"\n\nexport default class {\n selector = \"input[type=checkbox]:required\"\n ignoringMutations = false\n\n constructor(constraintValidations, predicate) {\n this.constraintValidations = constraintValidations\n this.mutationObserver = new MutationObserver(this.handleMutation)\n this.enabled = typeof predicate === \"function\" ?\n predicate :\n (group) => !!predicate\n }\n\n connect() {\n this.mutationObserver.observe(this.element, {\n attributeFilter: [\"required\"],\n childList: true,\n subtree: true\n })\n this.reportValidationMessages(this.element.querySelectorAll(this.selector))\n }\n\n disconnect() {\n this.mutationObserver.disconnect()\n }\n\n willValidate(target) {\n return this.willValidateGroup(checkboxGroup(target))\n }\n\n validate(target) {\n const checkboxesInGroup = checkboxGroup(target).filter(isFieldElement)\n const allRequired = checkboxesInGroup.every((checkbox) => checkbox.getAttribute(\"aria-required\") === \"true\")\n const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked)\n\n if (allRequired && someChecked) {\n for (const checkbox of checkboxesInGroup) {\n this.constraintValidations.clearValidity(checkbox)\n }\n } else if (allRequired) {\n for (const checkbox of checkboxesInGroup) {\n const validationMessages = readValidationMessages(checkbox)\n\n checkbox.setCustomValidity(validationMessages.valueMissing)\n this.constraintValidations.reportValidity(checkbox)\n }\n }\n }\n\n // Private\n\n handleMutation = (mutationRecords) => {\n if (this.ignoringMutations) return\n\n for (const { addedNodes, target, type } of mutationRecords) {\n if (type === \"attributes\") {\n if (target.required) {\n this.swapRequiredWithAriaRequired(target)\n } else {\n target.removeAttribute(\"aria-required\")\n }\n } else if (addedNodes.length) {\n this.reportValidationMessages(addedNodes)\n }\n }\n }\n\n reportValidationMessages(nodes) {\n const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes)\n\n for (const checkbox of requiredCheckboxes) {\n if (checkbox.required) {\n const group = checkboxGroup(checkbox)\n\n if (this.willValidateGroup(group)) {\n for (const checkboxInGroup of group) {\n this.swapRequiredWithAriaRequired(checkboxInGroup)\n if (checkboxInGroup.getAttribute(\"aria-invalid\") === \"true\") {\n this.validate(checkboxInGroup)\n }\n }\n }\n }\n }\n }\n\n swapRequiredWithAriaRequired(element) {\n this.ignoringMutations = true\n element.required = false\n element.setAttribute(\"aria-required\", \"true\")\n setTimeout(() => this.ignoringMutations = false, 0)\n }\n\n willValidateGroup(group) {\n return group.length > 0 && this.enabled(group)\n }\n\n get element() {\n return this.constraintValidations.element\n }\n}\n\nfunction checkboxGroup(formControl) {\n const results = new Set\n const { name, form } = formControl\n\n if (name && form instanceof HTMLFormElement) {\n const group = form.elements.namedItem(name)\n const elements = Symbol.iterator in group ?\n group :\n [group]\n\n for (const element of elements) {\n if (element.type === \"checkbox\") {\n results.add(element)\n }\n }\n\n if (results.size === 1 && results.has(formControl)) {\n results.clear()\n }\n }\n\n return Array.from(results)\n}\n\nfunction querySelectorAllNodes(selector, nodes, elements = new Set) {\n for (const node of nodes) {\n if (node instanceof Element) {\n if (node.matches(selector)) {\n elements.add(node)\n }\n\n elements.add(...querySelectorAllNodes(selector, node.children, elements))\n }\n }\n\n return Array.from(elements)\n}\n","import { isFieldElement, readValidationMessages } from \"./util\"\nimport CheckboxValidator from \"./validators/checkbox_validator\"\n\nconst defaultOptions = {\n disableSubmitWhenInvalid: false,\n validateOn: [\"blur\", \"input\"],\n validators: {\n checkbox: false\n }\n}\n\nexport default class ConstraintValidations {\n static connect(element = document, options = {}) {\n new this(element, options).connect()\n }\n\n constructor(element = document, options = {}) {\n this.element = element\n this.options = { ...defaultOptions, ...options }\n this.validators = [\n new CheckboxValidator(this, this.options.validators.checkbox)\n ]\n }\n\n connect() {\n this.validators.forEach(validator => validator.connect())\n this.element.addEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.addEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.addEventListener(\"input\", this.toggleSubmitsDisabled)\n\n this.reportValidationMessages(\n this.element instanceof HTMLFormElement ?\n [this.element] :\n this.element.querySelectorAll(\"form\")\n )\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.removeEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.removeEventListener(\"input\", this.toggleSubmitsDisabled)\n this.validators.forEach(validator => validator.disconnect())\n }\n\n reportFieldValidity = (event) => {\n if (isFieldElement(event.target) && this.reportValidity(event.target)) {\n event.preventDefault()\n\n focusFirstInvalidField(event.target.form || event.target)\n }\n }\n\n clearAndReportFieldValidity = ({ target }) => {\n if (isFieldElement(target)) {\n this.clearValidity(target)\n\n for (const validator of this.validators) {\n if (validator.willValidate(target)) {\n validator.validate(target)\n }\n }\n\n this.reportValidity(target)\n }\n }\n\n toggleSubmitsDisabled = ({ target }) => {\n if (isFieldElement(target) && this.willDisableSubmitWhenInvalid(target)) {\n disableSubmitWhenInvalid(target.form)\n }\n }\n\n reportValidationMessages(forms) {\n const invalidFields = []\n\n for (const form of forms) {\n for (const element of Array.from(form.elements).filter(isFieldElement)) {\n const serverRenderedInvalid = /true/i.test(element.getAttribute(\"aria-invalid\"))\n const id = element.getAttribute(\"aria-errormessage\")\n const errorMessageElement = document.getElementById(id)\n const validationMessage = errorMessageElement?.textContent\n\n if (validationMessage) {\n element.setCustomValidity(validationMessage)\n }\n\n if (validationMessage || serverRenderedInvalid) {\n this.reportValidity(element)\n invalidFields.push(element)\n }\n\n if (this.willDisableSubmitWhenInvalid(element)) {\n disableSubmitWhenInvalid(form)\n }\n }\n }\n\n const [firstInvalidField] = invalidFields\n firstInvalidField?.focus()\n }\n\n willDisableSubmitWhenInvalid(target) {\n return typeof this.options.disableSubmitWhenInvalid === \"function\" ?\n this.options.disableSubmitWhenInvalid(target) :\n !!this.options.disableSubmitWhenInvalid\n }\n\n clearValidity(input) {\n input.setCustomValidity(\"\")\n\n this.reportValidity(input)\n }\n\n reportValidity(input) {\n const id = input.getAttribute(\"aria-errormessage\")\n const validationMessage = getValidationMessage(input)\n const element = document.getElementById(id) || createValidationMessageFragment(input.form)\n\n if (input.form?.noValidate) {\n return false\n } else if (id && element) {\n element.id = id\n element.innerHTML = validationMessage\n\n if (validationMessage) {\n input.setCustomValidity(validationMessage)\n input.setAttribute(\"aria-describedby\", id)\n input.setAttribute(\"aria-invalid\", \"true\")\n } else {\n input.removeAttribute(\"aria-describedby\")\n input.removeAttribute(\"aria-invalid\")\n }\n\n if (!element.parentElement) {\n input.insertAdjacentElement(\"afterend\", element)\n }\n\n if (input.form && this.willDisableSubmitWhenInvalid(input)) disableSubmitWhenInvalid(input.form)\n\n return true\n } else {\n return false\n }\n }\n}\n\nfunction focusFirstInvalidField(element) {\n if (element instanceof HTMLFormElement) {\n return Array.from(element.elements).some(field => focusFirstInvalidField(field))\n } else if (isFieldElement(element) && !element.validity.valid) {\n element.focus()\n element.scrollIntoView()\n return true\n } else {\n return false\n }\n}\n\nfunction disableSubmitWhenInvalid(form) {\n if (!form || form.noValidate) return\n\n const isValid = Array.from(form.elements).filter(isFieldElement).every(input => input.validity.valid)\n\n for (const element of form.elements) {\n if (element.type == \"submit\" && !element.formNoValidate) {\n element.disabled = !isValid\n }\n }\n}\n\nfunction createValidationMessageFragment(form) {\n if (form) {\n const template = form.querySelector(\"[data-validation-message-template]\")\n\n return template?.content.children[0].cloneNode()\n }\n}\n\nfunction getValidationMessage(input) {\n const validationMessages = Object.entries(readValidationMessages(input))\n\n const [ _, validationMessage ] = validationMessages.find(([ key ]) => input.validity[key]) || [ null, null ]\n\n return validationMessage || input.validationMessage\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"constraint_validations.es.js","sources":["../../javascript/constraint_validations/util.js","../../javascript/constraint_validations/validators/checkbox_validator.js","../../javascript/constraint_validations/index.js"],"sourcesContent":["export function isFieldElement(element) {\n return !element.disabled && \"validity\" in element && element.willValidate\n}\n\nexport function isAriaInvalid(element) {\n return element.getAttribute(\"aria-invalid\") === \"true\"\n}\n\nexport function readValidationMessages(input) {\n try {\n return JSON.parse(input.getAttribute(\"data-validation-messages\")) || {}\n } catch(_) {\n return {}\n }\n}\n","import { isAriaInvalid, isFieldElement, readValidationMessages } from \"../util\"\n\nexport default class {\n selector = \"input[type=checkbox]\"\n ignoringMutations = false\n\n constructor(constraintValidations, predicate) {\n this.constraintValidations = constraintValidations\n this.mutationObserver = new MutationObserver(this.handleMutation)\n this.enabled = typeof predicate === \"function\" ?\n predicate :\n (group) => !!predicate\n }\n\n connect() {\n this.element.addEventListener(\"invalid\", this.handleInvalid, { capture: true, passive: true })\n this.mutationObserver.observe(this.element, {\n attributeFilter: [\"required\"],\n childList: true,\n subtree: true\n })\n this.reportValidationMessages(this.element.querySelectorAll(this.selector), isAriaInvalid)\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.handleInvalid, { capture: true, passive: true })\n this.mutationObserver.disconnect()\n }\n\n willValidate(target) {\n return this.willValidateGroup(checkboxGroup(target))\n }\n\n validate(target) {\n const checkboxesInGroup = checkboxGroup(target).filter(isCheckboxElement)\n const allRequired = checkboxesInGroup.every((checkbox) => isRequired(checkbox))\n const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked)\n\n if (allRequired && someChecked) {\n for (const checkbox of checkboxesInGroup) {\n this.constraintValidations.clearValidity(checkbox)\n }\n } else if (allRequired) {\n for (const checkbox of checkboxesInGroup) {\n const validationMessages = readValidationMessages(checkbox)\n\n checkbox.setCustomValidity(validationMessages.valueMissing)\n this.constraintValidations.reportValidity(checkbox)\n }\n }\n }\n\n // Private\n\n handleInvalid = ({ target }) => {\n const checkboxes = new Set\n\n for (const element of target.form.elements) {\n if (isCheckboxElement(element) && this.willValidate(element)) {\n checkboxes.add(element)\n }\n }\n\n this.reportValidationMessages(checkboxes)\n }\n\n handleMutation = (mutationRecords) => {\n if (this.ignoringMutations) return\n\n for (const { addedNodes, target, type } of mutationRecords) {\n if (type === \"attributes\") {\n if (target.required) {\n this.swapRequiredWithAriaRequired(target)\n } else {\n target.removeAttribute(\"aria-required\")\n }\n } else if (addedNodes.length) {\n this.reportValidationMessages(addedNodes, isAriaInvalid)\n }\n }\n }\n\n reportValidationMessages(nodes, willReport = () => true) {\n const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes)\n\n for (const checkbox of requiredCheckboxes) {\n if (isRequired(checkbox)) {\n const group = checkboxGroup(checkbox)\n\n if (this.willValidateGroup(group)) {\n for (const checkboxInGroup of group) {\n this.swapRequiredWithAriaRequired(checkboxInGroup)\n\n if (willReport(checkboxInGroup)) {\n this.validate(checkboxInGroup)\n }\n }\n }\n }\n }\n }\n\n swapRequiredWithAriaRequired(element) {\n this.ignoringMutations = true\n element.required = false\n element.setAttribute(\"aria-required\", \"true\")\n setTimeout(() => this.ignoringMutations = false, 0)\n }\n\n willValidateGroup(group) {\n return group.length > 0 && this.enabled(group)\n }\n\n get element() {\n return this.constraintValidations.element\n }\n}\n\nfunction checkboxGroup(formControl) {\n const results = new Set\n const { name, form } = formControl\n\n if (name && form instanceof HTMLFormElement) {\n const group = form.elements.namedItem(name)\n const elements = Symbol.iterator in group ?\n group :\n [group]\n\n for (const element of elements) {\n if (element.type === \"checkbox\") {\n results.add(element)\n }\n }\n\n if (results.size === 1 && results.has(formControl)) {\n results.clear()\n }\n }\n\n return Array.from(results)\n}\n\nfunction querySelectorAllNodes(selector, nodes, elements = new Set) {\n for (const node of nodes) {\n if (node instanceof Element) {\n if (node.matches(selector)) {\n elements.add(node)\n }\n\n elements.add(...querySelectorAllNodes(selector, node.children, elements))\n }\n }\n\n return Array.from(elements)\n}\n\nfunction isCheckboxElement(element) {\n return isFieldElement(element) && element.type === \"checkbox\"\n}\n\nfunction isRequired(element) {\n return element.required || element.getAttribute(\"aria-required\") === \"true\"\n}\n","import { isAriaInvalid, isFieldElement, readValidationMessages } from \"./util\"\nimport CheckboxValidator from \"./validators/checkbox_validator\"\n\nconst defaultOptions = {\n disableSubmitWhenInvalid: false,\n validateOn: [\"blur\", \"input\"],\n validators: {\n checkbox: false\n }\n}\n\nexport default class ConstraintValidations {\n static connect(element = document, options = {}) {\n new this(element, options).connect()\n }\n\n constructor(element = document, options = {}) {\n this.element = element\n this.options = { ...defaultOptions, ...options }\n this.validators = [\n new CheckboxValidator(this, this.options.validators.checkbox)\n ]\n }\n\n connect() {\n this.validators.forEach(validator => validator.connect())\n this.element.addEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.addEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.addEventListener(\"input\", this.toggleSubmitsDisabled)\n\n this.reportValidationMessages(\n this.element instanceof HTMLFormElement ?\n [this.element] :\n this.element.querySelectorAll(\"form\")\n )\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.removeEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.removeEventListener(\"input\", this.toggleSubmitsDisabled)\n this.validators.forEach(validator => validator.disconnect())\n }\n\n reportFieldValidity = (event) => {\n if (isFieldElement(event.target) && this.reportValidity(event.target)) {\n event.preventDefault()\n\n focusFirstInvalidField(event.target.form || event.target)\n }\n }\n\n clearAndReportFieldValidity = ({ target }) => {\n if (isFieldElement(target)) {\n this.clearValidity(target)\n\n for (const validator of this.validators) {\n if (validator.willValidate(target)) {\n validator.validate(target)\n }\n }\n\n this.reportValidity(target)\n }\n }\n\n toggleSubmitsDisabled = ({ target }) => {\n if (isFieldElement(target) && this.willDisableSubmitWhenInvalid(target)) {\n disableSubmitWhenInvalid(target.form)\n }\n }\n\n reportValidationMessages(forms) {\n const invalidFields = []\n\n for (const form of forms) {\n for (const element of Array.from(form.elements).filter(isFieldElement)) {\n const serverRenderedInvalid = isAriaInvalid(element)\n const id = element.getAttribute(\"aria-errormessage\")\n const errorMessageElement = document.getElementById(id)\n const validationMessage = errorMessageElement?.textContent\n\n if (validationMessage) {\n element.setCustomValidity(validationMessage)\n }\n\n if (validationMessage || serverRenderedInvalid) {\n this.reportValidity(element)\n invalidFields.push(element)\n }\n\n if (this.willDisableSubmitWhenInvalid(element)) {\n disableSubmitWhenInvalid(form)\n }\n }\n }\n\n const [firstInvalidField] = invalidFields\n firstInvalidField?.focus()\n }\n\n willDisableSubmitWhenInvalid(target) {\n return typeof this.options.disableSubmitWhenInvalid === \"function\" ?\n this.options.disableSubmitWhenInvalid(target) :\n !!this.options.disableSubmitWhenInvalid\n }\n\n clearValidity(input) {\n input.setCustomValidity(\"\")\n\n this.reportValidity(input)\n }\n\n reportValidity(input) {\n const id = input.getAttribute(\"aria-errormessage\")\n const validationMessage = getValidationMessage(input)\n const element = document.getElementById(id) || createValidationMessageFragment(input.form)\n\n if (input.form?.noValidate) {\n return false\n } else if (id && element) {\n element.id = id\n element.innerHTML = validationMessage\n\n if (validationMessage) {\n input.setCustomValidity(validationMessage)\n input.setAttribute(\"aria-describedby\", id)\n input.setAttribute(\"aria-invalid\", \"true\")\n } else {\n input.removeAttribute(\"aria-describedby\")\n input.removeAttribute(\"aria-invalid\")\n }\n\n if (!element.parentElement) {\n input.insertAdjacentElement(\"afterend\", element)\n }\n\n if (input.form && this.willDisableSubmitWhenInvalid(input)) disableSubmitWhenInvalid(input.form)\n\n return true\n } else {\n return false\n }\n }\n}\n\nfunction focusFirstInvalidField(element) {\n if (element instanceof HTMLFormElement) {\n return Array.from(element.elements).some(field => focusFirstInvalidField(field))\n } else if (isFieldElement(element) && !element.validity.valid) {\n element.focus()\n element.scrollIntoView()\n return true\n } else {\n return false\n }\n}\n\nfunction disableSubmitWhenInvalid(form) {\n if (!form || form.noValidate) return\n\n const isValid = Array.from(form.elements).filter(isFieldElement).every(input => input.validity.valid)\n\n for (const element of form.elements) {\n if (element.type == \"submit\" && !element.formNoValidate) {\n element.disabled = !isValid\n }\n }\n}\n\nfunction createValidationMessageFragment(form) {\n if (form) {\n const template = form.querySelector(\"[data-validation-message-template]\")\n\n return template?.content.children[0].cloneNode()\n }\n}\n\nfunction getValidationMessage(input) {\n const validationMessages = Object.entries(readValidationMessages(input))\n\n const [ _, validationMessage ] = validationMessages.find(([ key ]) => input.validity[key]) || [ null, null ]\n\n return validationMessage || input.validationMessage\n}\n"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/app/assets/javascripts/constraint_validations.js b/app/assets/javascripts/constraint_validations.js
index c23463d..9ae99df 100644
--- a/app/assets/javascripts/constraint_validations.js
+++ b/app/assets/javascripts/constraint_validations.js
@@ -3,6 +3,9 @@ var ConstraintValidations = function() {
function isFieldElement(element) {
return !element.disabled && "validity" in element && element.willValidate;
}
+ function isAriaInvalid(element) {
+ return element.getAttribute("aria-invalid") === "true";
+ }
function readValidationMessages(input) {
try {
return JSON.parse(input.getAttribute("data-validation-messages")) || {};
@@ -11,7 +14,7 @@ var ConstraintValidations = function() {
}
}
class CheckboxValidator {
- selector="input[type=checkbox]:required";
+ selector="input[type=checkbox]";
ignoringMutations=false;
constructor(constraintValidations, predicate) {
this.constraintValidations = constraintValidations;
@@ -19,22 +22,30 @@ var ConstraintValidations = function() {
this.enabled = typeof predicate === "function" ? predicate : group => !!predicate;
}
connect() {
+ this.element.addEventListener("invalid", this.handleInvalid, {
+ capture: true,
+ passive: true
+ });
this.mutationObserver.observe(this.element, {
attributeFilter: [ "required" ],
childList: true,
subtree: true
});
- this.reportValidationMessages(this.element.querySelectorAll(this.selector));
+ this.reportValidationMessages(this.element.querySelectorAll(this.selector), isAriaInvalid);
}
disconnect() {
+ this.element.removeEventListener("invalid", this.handleInvalid, {
+ capture: true,
+ passive: true
+ });
this.mutationObserver.disconnect();
}
willValidate(target) {
return this.willValidateGroup(checkboxGroup(target));
}
validate(target) {
- const checkboxesInGroup = checkboxGroup(target).filter(isFieldElement);
- const allRequired = checkboxesInGroup.every((checkbox => checkbox.getAttribute("aria-required") === "true"));
+ const checkboxesInGroup = checkboxGroup(target).filter(isCheckboxElement);
+ const allRequired = checkboxesInGroup.every((checkbox => isRequired(checkbox)));
const someChecked = checkboxesInGroup.some((checkbox => checkbox.checked));
if (allRequired && someChecked) {
for (const checkbox of checkboxesInGroup) {
@@ -48,6 +59,15 @@ var ConstraintValidations = function() {
}
}
}
+ handleInvalid=({target: target}) => {
+ const checkboxes = new Set;
+ for (const element of target.form.elements) {
+ if (isCheckboxElement(element) && this.willValidate(element)) {
+ checkboxes.add(element);
+ }
+ }
+ this.reportValidationMessages(checkboxes);
+ };
handleMutation=mutationRecords => {
if (this.ignoringMutations) return;
for (const {addedNodes: addedNodes, target: target, type: type} of mutationRecords) {
@@ -58,19 +78,19 @@ var ConstraintValidations = function() {
target.removeAttribute("aria-required");
}
} else if (addedNodes.length) {
- this.reportValidationMessages(addedNodes);
+ this.reportValidationMessages(addedNodes, isAriaInvalid);
}
}
};
- reportValidationMessages(nodes) {
+ reportValidationMessages(nodes, willReport = (() => true)) {
const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes);
for (const checkbox of requiredCheckboxes) {
- if (checkbox.required) {
+ if (isRequired(checkbox)) {
const group = checkboxGroup(checkbox);
if (this.willValidateGroup(group)) {
for (const checkboxInGroup of group) {
this.swapRequiredWithAriaRequired(checkboxInGroup);
- if (checkboxInGroup.getAttribute("aria-invalid") === "true") {
+ if (willReport(checkboxInGroup)) {
this.validate(checkboxInGroup);
}
}
@@ -119,6 +139,12 @@ var ConstraintValidations = function() {
}
return Array.from(elements);
}
+ function isCheckboxElement(element) {
+ return isFieldElement(element) && element.type === "checkbox";
+ }
+ function isRequired(element) {
+ return element.required || element.getAttribute("aria-required") === "true";
+ }
const defaultOptions = {
disableSubmitWhenInvalid: false,
validateOn: [ "blur", "input" ],
@@ -193,7 +219,7 @@ var ConstraintValidations = function() {
const invalidFields = [];
for (const form of forms) {
for (const element of Array.from(form.elements).filter(isFieldElement)) {
- const serverRenderedInvalid = /true/i.test(element.getAttribute("aria-invalid"));
+ const serverRenderedInvalid = isAriaInvalid(element);
const id = element.getAttribute("aria-errormessage");
const errorMessageElement = document.getElementById(id);
const validationMessage = errorMessageElement?.textContent;
diff --git a/app/assets/javascripts/constraint_validations.js.map b/app/assets/javascripts/constraint_validations.js.map
index 3aff332..be0fb0f 100644
--- a/app/assets/javascripts/constraint_validations.js.map
+++ b/app/assets/javascripts/constraint_validations.js.map
@@ -1 +1 @@
-{"version":3,"file":"constraint_validations.js","sources":["../../javascript/constraint_validations/util.js","../../javascript/constraint_validations/validators/checkbox_validator.js","../../javascript/constraint_validations/index.js"],"sourcesContent":["export function isFieldElement(element) {\n return !element.disabled && \"validity\" in element && element.willValidate\n}\n\nexport function readValidationMessages(input) {\n try {\n return JSON.parse(input.getAttribute(\"data-validation-messages\")) || {}\n } catch(_) {\n return {}\n }\n}\n","import { isFieldElement, readValidationMessages } from \"../util\"\n\nexport default class {\n selector = \"input[type=checkbox]:required\"\n ignoringMutations = false\n\n constructor(constraintValidations, predicate) {\n this.constraintValidations = constraintValidations\n this.mutationObserver = new MutationObserver(this.handleMutation)\n this.enabled = typeof predicate === \"function\" ?\n predicate :\n (group) => !!predicate\n }\n\n connect() {\n this.mutationObserver.observe(this.element, {\n attributeFilter: [\"required\"],\n childList: true,\n subtree: true\n })\n this.reportValidationMessages(this.element.querySelectorAll(this.selector))\n }\n\n disconnect() {\n this.mutationObserver.disconnect()\n }\n\n willValidate(target) {\n return this.willValidateGroup(checkboxGroup(target))\n }\n\n validate(target) {\n const checkboxesInGroup = checkboxGroup(target).filter(isFieldElement)\n const allRequired = checkboxesInGroup.every((checkbox) => checkbox.getAttribute(\"aria-required\") === \"true\")\n const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked)\n\n if (allRequired && someChecked) {\n for (const checkbox of checkboxesInGroup) {\n this.constraintValidations.clearValidity(checkbox)\n }\n } else if (allRequired) {\n for (const checkbox of checkboxesInGroup) {\n const validationMessages = readValidationMessages(checkbox)\n\n checkbox.setCustomValidity(validationMessages.valueMissing)\n this.constraintValidations.reportValidity(checkbox)\n }\n }\n }\n\n // Private\n\n handleMutation = (mutationRecords) => {\n if (this.ignoringMutations) return\n\n for (const { addedNodes, target, type } of mutationRecords) {\n if (type === \"attributes\") {\n if (target.required) {\n this.swapRequiredWithAriaRequired(target)\n } else {\n target.removeAttribute(\"aria-required\")\n }\n } else if (addedNodes.length) {\n this.reportValidationMessages(addedNodes)\n }\n }\n }\n\n reportValidationMessages(nodes) {\n const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes)\n\n for (const checkbox of requiredCheckboxes) {\n if (checkbox.required) {\n const group = checkboxGroup(checkbox)\n\n if (this.willValidateGroup(group)) {\n for (const checkboxInGroup of group) {\n this.swapRequiredWithAriaRequired(checkboxInGroup)\n if (checkboxInGroup.getAttribute(\"aria-invalid\") === \"true\") {\n this.validate(checkboxInGroup)\n }\n }\n }\n }\n }\n }\n\n swapRequiredWithAriaRequired(element) {\n this.ignoringMutations = true\n element.required = false\n element.setAttribute(\"aria-required\", \"true\")\n setTimeout(() => this.ignoringMutations = false, 0)\n }\n\n willValidateGroup(group) {\n return group.length > 0 && this.enabled(group)\n }\n\n get element() {\n return this.constraintValidations.element\n }\n}\n\nfunction checkboxGroup(formControl) {\n const results = new Set\n const { name, form } = formControl\n\n if (name && form instanceof HTMLFormElement) {\n const group = form.elements.namedItem(name)\n const elements = Symbol.iterator in group ?\n group :\n [group]\n\n for (const element of elements) {\n if (element.type === \"checkbox\") {\n results.add(element)\n }\n }\n\n if (results.size === 1 && results.has(formControl)) {\n results.clear()\n }\n }\n\n return Array.from(results)\n}\n\nfunction querySelectorAllNodes(selector, nodes, elements = new Set) {\n for (const node of nodes) {\n if (node instanceof Element) {\n if (node.matches(selector)) {\n elements.add(node)\n }\n\n elements.add(...querySelectorAllNodes(selector, node.children, elements))\n }\n }\n\n return Array.from(elements)\n}\n","import { isFieldElement, readValidationMessages } from \"./util\"\nimport CheckboxValidator from \"./validators/checkbox_validator\"\n\nconst defaultOptions = {\n disableSubmitWhenInvalid: false,\n validateOn: [\"blur\", \"input\"],\n validators: {\n checkbox: false\n }\n}\n\nexport default class ConstraintValidations {\n static connect(element = document, options = {}) {\n new this(element, options).connect()\n }\n\n constructor(element = document, options = {}) {\n this.element = element\n this.options = { ...defaultOptions, ...options }\n this.validators = [\n new CheckboxValidator(this, this.options.validators.checkbox)\n ]\n }\n\n connect() {\n this.validators.forEach(validator => validator.connect())\n this.element.addEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.addEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.addEventListener(\"input\", this.toggleSubmitsDisabled)\n\n this.reportValidationMessages(\n this.element instanceof HTMLFormElement ?\n [this.element] :\n this.element.querySelectorAll(\"form\")\n )\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.removeEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.removeEventListener(\"input\", this.toggleSubmitsDisabled)\n this.validators.forEach(validator => validator.disconnect())\n }\n\n reportFieldValidity = (event) => {\n if (isFieldElement(event.target) && this.reportValidity(event.target)) {\n event.preventDefault()\n\n focusFirstInvalidField(event.target.form || event.target)\n }\n }\n\n clearAndReportFieldValidity = ({ target }) => {\n if (isFieldElement(target)) {\n this.clearValidity(target)\n\n for (const validator of this.validators) {\n if (validator.willValidate(target)) {\n validator.validate(target)\n }\n }\n\n this.reportValidity(target)\n }\n }\n\n toggleSubmitsDisabled = ({ target }) => {\n if (isFieldElement(target) && this.willDisableSubmitWhenInvalid(target)) {\n disableSubmitWhenInvalid(target.form)\n }\n }\n\n reportValidationMessages(forms) {\n const invalidFields = []\n\n for (const form of forms) {\n for (const element of Array.from(form.elements).filter(isFieldElement)) {\n const serverRenderedInvalid = /true/i.test(element.getAttribute(\"aria-invalid\"))\n const id = element.getAttribute(\"aria-errormessage\")\n const errorMessageElement = document.getElementById(id)\n const validationMessage = errorMessageElement?.textContent\n\n if (validationMessage) {\n element.setCustomValidity(validationMessage)\n }\n\n if (validationMessage || serverRenderedInvalid) {\n this.reportValidity(element)\n invalidFields.push(element)\n }\n\n if (this.willDisableSubmitWhenInvalid(element)) {\n disableSubmitWhenInvalid(form)\n }\n }\n }\n\n const [firstInvalidField] = invalidFields\n firstInvalidField?.focus()\n }\n\n willDisableSubmitWhenInvalid(target) {\n return typeof this.options.disableSubmitWhenInvalid === \"function\" ?\n this.options.disableSubmitWhenInvalid(target) :\n !!this.options.disableSubmitWhenInvalid\n }\n\n clearValidity(input) {\n input.setCustomValidity(\"\")\n\n this.reportValidity(input)\n }\n\n reportValidity(input) {\n const id = input.getAttribute(\"aria-errormessage\")\n const validationMessage = getValidationMessage(input)\n const element = document.getElementById(id) || createValidationMessageFragment(input.form)\n\n if (input.form?.noValidate) {\n return false\n } else if (id && element) {\n element.id = id\n element.innerHTML = validationMessage\n\n if (validationMessage) {\n input.setCustomValidity(validationMessage)\n input.setAttribute(\"aria-describedby\", id)\n input.setAttribute(\"aria-invalid\", \"true\")\n } else {\n input.removeAttribute(\"aria-describedby\")\n input.removeAttribute(\"aria-invalid\")\n }\n\n if (!element.parentElement) {\n input.insertAdjacentElement(\"afterend\", element)\n }\n\n if (input.form && this.willDisableSubmitWhenInvalid(input)) disableSubmitWhenInvalid(input.form)\n\n return true\n } else {\n return false\n }\n }\n}\n\nfunction focusFirstInvalidField(element) {\n if (element instanceof HTMLFormElement) {\n return Array.from(element.elements).some(field => focusFirstInvalidField(field))\n } else if (isFieldElement(element) && !element.validity.valid) {\n element.focus()\n element.scrollIntoView()\n return true\n } else {\n return false\n }\n}\n\nfunction disableSubmitWhenInvalid(form) {\n if (!form || form.noValidate) return\n\n const isValid = Array.from(form.elements).filter(isFieldElement).every(input => input.validity.valid)\n\n for (const element of form.elements) {\n if (element.type == \"submit\" && !element.formNoValidate) {\n element.disabled = !isValid\n }\n }\n}\n\nfunction createValidationMessageFragment(form) {\n if (form) {\n const template = form.querySelector(\"[data-validation-message-template]\")\n\n return template?.content.children[0].cloneNode()\n }\n}\n\nfunction getValidationMessage(input) {\n const validationMessages = Object.entries(readValidationMessages(input))\n\n const [ _, validationMessage ] = validationMessages.find(([ key ]) => input.validity[key]) || [ null, null ]\n\n return validationMessage || input.validationMessage\n}\n"],"names":["isFieldElement","element","disabled","willValidate","readValidationMessages","input","JSON","parse","getAttribute","_","[object Object]","constraintValidations","predicate","this","mutationObserver","MutationObserver","handleMutation","enabled","group","observe","attributeFilter","childList","subtree","reportValidationMessages","querySelectorAll","selector","disconnect","target","willValidateGroup","checkboxGroup","checkboxesInGroup","filter","allRequired","every","checkbox","someChecked","some","checked","clearValidity","validationMessages","setCustomValidity","valueMissing","reportValidity","mutationRecords","ignoringMutations","addedNodes","type","required","swapRequiredWithAriaRequired","removeAttribute","length","nodes","requiredCheckboxes","querySelectorAllNodes","checkboxInGroup","validate","setAttribute","setTimeout","formControl","results","Set","name","form","HTMLFormElement","elements","namedItem","Symbol","iterator","add","size","has","clear","Array","from","node","Element","matches","children","defaultOptions","disableSubmitWhenInvalid","validateOn","validators","ConstraintValidations","document","options","connect","CheckboxValidator","forEach","validator","addEventListener","reportFieldValidity","capture","passive","eventName","clearAndReportFieldValidity","toggleSubmitsDisabled","removeEventListener","event","preventDefault","focusFirstInvalidField","willDisableSubmitWhenInvalid","forms","invalidFields","serverRenderedInvalid","test","id","errorMessageElement","getElementById","validationMessage","textContent","push","firstInvalidField","focus","getValidationMessage","createValidationMessageFragment","noValidate","innerHTML","parentElement","insertAdjacentElement","field","validity","valid","scrollIntoView","isValid","formNoValidate","template","querySelector","content","cloneNode","Object","entries","find","key"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"constraint_validations.js","sources":["../../javascript/constraint_validations/util.js","../../javascript/constraint_validations/validators/checkbox_validator.js","../../javascript/constraint_validations/index.js"],"sourcesContent":["export function isFieldElement(element) {\n return !element.disabled && \"validity\" in element && element.willValidate\n}\n\nexport function isAriaInvalid(element) {\n return element.getAttribute(\"aria-invalid\") === \"true\"\n}\n\nexport function readValidationMessages(input) {\n try {\n return JSON.parse(input.getAttribute(\"data-validation-messages\")) || {}\n } catch(_) {\n return {}\n }\n}\n","import { isAriaInvalid, isFieldElement, readValidationMessages } from \"../util\"\n\nexport default class {\n selector = \"input[type=checkbox]\"\n ignoringMutations = false\n\n constructor(constraintValidations, predicate) {\n this.constraintValidations = constraintValidations\n this.mutationObserver = new MutationObserver(this.handleMutation)\n this.enabled = typeof predicate === \"function\" ?\n predicate :\n (group) => !!predicate\n }\n\n connect() {\n this.element.addEventListener(\"invalid\", this.handleInvalid, { capture: true, passive: true })\n this.mutationObserver.observe(this.element, {\n attributeFilter: [\"required\"],\n childList: true,\n subtree: true\n })\n this.reportValidationMessages(this.element.querySelectorAll(this.selector), isAriaInvalid)\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.handleInvalid, { capture: true, passive: true })\n this.mutationObserver.disconnect()\n }\n\n willValidate(target) {\n return this.willValidateGroup(checkboxGroup(target))\n }\n\n validate(target) {\n const checkboxesInGroup = checkboxGroup(target).filter(isCheckboxElement)\n const allRequired = checkboxesInGroup.every((checkbox) => isRequired(checkbox))\n const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked)\n\n if (allRequired && someChecked) {\n for (const checkbox of checkboxesInGroup) {\n this.constraintValidations.clearValidity(checkbox)\n }\n } else if (allRequired) {\n for (const checkbox of checkboxesInGroup) {\n const validationMessages = readValidationMessages(checkbox)\n\n checkbox.setCustomValidity(validationMessages.valueMissing)\n this.constraintValidations.reportValidity(checkbox)\n }\n }\n }\n\n // Private\n\n handleInvalid = ({ target }) => {\n const checkboxes = new Set\n\n for (const element of target.form.elements) {\n if (isCheckboxElement(element) && this.willValidate(element)) {\n checkboxes.add(element)\n }\n }\n\n this.reportValidationMessages(checkboxes)\n }\n\n handleMutation = (mutationRecords) => {\n if (this.ignoringMutations) return\n\n for (const { addedNodes, target, type } of mutationRecords) {\n if (type === \"attributes\") {\n if (target.required) {\n this.swapRequiredWithAriaRequired(target)\n } else {\n target.removeAttribute(\"aria-required\")\n }\n } else if (addedNodes.length) {\n this.reportValidationMessages(addedNodes, isAriaInvalid)\n }\n }\n }\n\n reportValidationMessages(nodes, willReport = () => true) {\n const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes)\n\n for (const checkbox of requiredCheckboxes) {\n if (isRequired(checkbox)) {\n const group = checkboxGroup(checkbox)\n\n if (this.willValidateGroup(group)) {\n for (const checkboxInGroup of group) {\n this.swapRequiredWithAriaRequired(checkboxInGroup)\n\n if (willReport(checkboxInGroup)) {\n this.validate(checkboxInGroup)\n }\n }\n }\n }\n }\n }\n\n swapRequiredWithAriaRequired(element) {\n this.ignoringMutations = true\n element.required = false\n element.setAttribute(\"aria-required\", \"true\")\n setTimeout(() => this.ignoringMutations = false, 0)\n }\n\n willValidateGroup(group) {\n return group.length > 0 && this.enabled(group)\n }\n\n get element() {\n return this.constraintValidations.element\n }\n}\n\nfunction checkboxGroup(formControl) {\n const results = new Set\n const { name, form } = formControl\n\n if (name && form instanceof HTMLFormElement) {\n const group = form.elements.namedItem(name)\n const elements = Symbol.iterator in group ?\n group :\n [group]\n\n for (const element of elements) {\n if (element.type === \"checkbox\") {\n results.add(element)\n }\n }\n\n if (results.size === 1 && results.has(formControl)) {\n results.clear()\n }\n }\n\n return Array.from(results)\n}\n\nfunction querySelectorAllNodes(selector, nodes, elements = new Set) {\n for (const node of nodes) {\n if (node instanceof Element) {\n if (node.matches(selector)) {\n elements.add(node)\n }\n\n elements.add(...querySelectorAllNodes(selector, node.children, elements))\n }\n }\n\n return Array.from(elements)\n}\n\nfunction isCheckboxElement(element) {\n return isFieldElement(element) && element.type === \"checkbox\"\n}\n\nfunction isRequired(element) {\n return element.required || element.getAttribute(\"aria-required\") === \"true\"\n}\n","import { isAriaInvalid, isFieldElement, readValidationMessages } from \"./util\"\nimport CheckboxValidator from \"./validators/checkbox_validator\"\n\nconst defaultOptions = {\n disableSubmitWhenInvalid: false,\n validateOn: [\"blur\", \"input\"],\n validators: {\n checkbox: false\n }\n}\n\nexport default class ConstraintValidations {\n static connect(element = document, options = {}) {\n new this(element, options).connect()\n }\n\n constructor(element = document, options = {}) {\n this.element = element\n this.options = { ...defaultOptions, ...options }\n this.validators = [\n new CheckboxValidator(this, this.options.validators.checkbox)\n ]\n }\n\n connect() {\n this.validators.forEach(validator => validator.connect())\n this.element.addEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.addEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.addEventListener(\"input\", this.toggleSubmitsDisabled)\n\n this.reportValidationMessages(\n this.element instanceof HTMLFormElement ?\n [this.element] :\n this.element.querySelectorAll(\"form\")\n )\n }\n\n disconnect() {\n this.element.removeEventListener(\"invalid\", this.reportFieldValidity, { capture: true, passive: false })\n\n for (const eventName of this.options.validateOn) {\n this.element.removeEventListener(eventName, this.clearAndReportFieldValidity, { capture: true, passive: true })\n }\n\n this.element.removeEventListener(\"input\", this.toggleSubmitsDisabled)\n this.validators.forEach(validator => validator.disconnect())\n }\n\n reportFieldValidity = (event) => {\n if (isFieldElement(event.target) && this.reportValidity(event.target)) {\n event.preventDefault()\n\n focusFirstInvalidField(event.target.form || event.target)\n }\n }\n\n clearAndReportFieldValidity = ({ target }) => {\n if (isFieldElement(target)) {\n this.clearValidity(target)\n\n for (const validator of this.validators) {\n if (validator.willValidate(target)) {\n validator.validate(target)\n }\n }\n\n this.reportValidity(target)\n }\n }\n\n toggleSubmitsDisabled = ({ target }) => {\n if (isFieldElement(target) && this.willDisableSubmitWhenInvalid(target)) {\n disableSubmitWhenInvalid(target.form)\n }\n }\n\n reportValidationMessages(forms) {\n const invalidFields = []\n\n for (const form of forms) {\n for (const element of Array.from(form.elements).filter(isFieldElement)) {\n const serverRenderedInvalid = isAriaInvalid(element)\n const id = element.getAttribute(\"aria-errormessage\")\n const errorMessageElement = document.getElementById(id)\n const validationMessage = errorMessageElement?.textContent\n\n if (validationMessage) {\n element.setCustomValidity(validationMessage)\n }\n\n if (validationMessage || serverRenderedInvalid) {\n this.reportValidity(element)\n invalidFields.push(element)\n }\n\n if (this.willDisableSubmitWhenInvalid(element)) {\n disableSubmitWhenInvalid(form)\n }\n }\n }\n\n const [firstInvalidField] = invalidFields\n firstInvalidField?.focus()\n }\n\n willDisableSubmitWhenInvalid(target) {\n return typeof this.options.disableSubmitWhenInvalid === \"function\" ?\n this.options.disableSubmitWhenInvalid(target) :\n !!this.options.disableSubmitWhenInvalid\n }\n\n clearValidity(input) {\n input.setCustomValidity(\"\")\n\n this.reportValidity(input)\n }\n\n reportValidity(input) {\n const id = input.getAttribute(\"aria-errormessage\")\n const validationMessage = getValidationMessage(input)\n const element = document.getElementById(id) || createValidationMessageFragment(input.form)\n\n if (input.form?.noValidate) {\n return false\n } else if (id && element) {\n element.id = id\n element.innerHTML = validationMessage\n\n if (validationMessage) {\n input.setCustomValidity(validationMessage)\n input.setAttribute(\"aria-describedby\", id)\n input.setAttribute(\"aria-invalid\", \"true\")\n } else {\n input.removeAttribute(\"aria-describedby\")\n input.removeAttribute(\"aria-invalid\")\n }\n\n if (!element.parentElement) {\n input.insertAdjacentElement(\"afterend\", element)\n }\n\n if (input.form && this.willDisableSubmitWhenInvalid(input)) disableSubmitWhenInvalid(input.form)\n\n return true\n } else {\n return false\n }\n }\n}\n\nfunction focusFirstInvalidField(element) {\n if (element instanceof HTMLFormElement) {\n return Array.from(element.elements).some(field => focusFirstInvalidField(field))\n } else if (isFieldElement(element) && !element.validity.valid) {\n element.focus()\n element.scrollIntoView()\n return true\n } else {\n return false\n }\n}\n\nfunction disableSubmitWhenInvalid(form) {\n if (!form || form.noValidate) return\n\n const isValid = Array.from(form.elements).filter(isFieldElement).every(input => input.validity.valid)\n\n for (const element of form.elements) {\n if (element.type == \"submit\" && !element.formNoValidate) {\n element.disabled = !isValid\n }\n }\n}\n\nfunction createValidationMessageFragment(form) {\n if (form) {\n const template = form.querySelector(\"[data-validation-message-template]\")\n\n return template?.content.children[0].cloneNode()\n }\n}\n\nfunction getValidationMessage(input) {\n const validationMessages = Object.entries(readValidationMessages(input))\n\n const [ _, validationMessage ] = validationMessages.find(([ key ]) => input.validity[key]) || [ null, null ]\n\n return validationMessage || input.validationMessage\n}\n"],"names":["isFieldElement","element","disabled","willValidate","isAriaInvalid","getAttribute","readValidationMessages","input","JSON","parse","_","[object Object]","constraintValidations","predicate","this","mutationObserver","MutationObserver","handleMutation","enabled","group","addEventListener","handleInvalid","capture","passive","observe","attributeFilter","childList","subtree","reportValidationMessages","querySelectorAll","selector","removeEventListener","disconnect","target","willValidateGroup","checkboxGroup","checkboxesInGroup","filter","isCheckboxElement","allRequired","every","checkbox","isRequired","someChecked","some","checked","clearValidity","validationMessages","setCustomValidity","valueMissing","reportValidity","checkboxes","Set","form","elements","add","mutationRecords","ignoringMutations","addedNodes","type","required","swapRequiredWithAriaRequired","removeAttribute","length","nodes","willReport","requiredCheckboxes","querySelectorAllNodes","checkboxInGroup","validate","setAttribute","setTimeout","formControl","results","name","HTMLFormElement","namedItem","Symbol","iterator","size","has","clear","Array","from","node","Element","matches","children","defaultOptions","disableSubmitWhenInvalid","validateOn","validators","ConstraintValidations","document","options","connect","CheckboxValidator","forEach","validator","reportFieldValidity","eventName","clearAndReportFieldValidity","toggleSubmitsDisabled","event","preventDefault","focusFirstInvalidField","willDisableSubmitWhenInvalid","forms","invalidFields","serverRenderedInvalid","id","errorMessageElement","getElementById","validationMessage","textContent","push","firstInvalidField","focus","getValidationMessage","createValidationMessageFragment","noValidate","innerHTML","parentElement","insertAdjacentElement","field","validity","valid","scrollIntoView","isValid","formNoValidate","template","querySelector","content","cloneNode","Object","entries","find","key"],"mappings":"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"}
\ No newline at end of file
diff --git a/app/javascript/constraint_validations/index.js b/app/javascript/constraint_validations/index.js
index 287fa3c..dfc094e 100644
--- a/app/javascript/constraint_validations/index.js
+++ b/app/javascript/constraint_validations/index.js
@@ -1,4 +1,4 @@
-import { isFieldElement, readValidationMessages } from "./util"
+import { isAriaInvalid, isFieldElement, readValidationMessages } from "./util"
import CheckboxValidator from "./validators/checkbox_validator"
const defaultOptions = {
@@ -83,7 +83,7 @@ export default class ConstraintValidations {
for (const form of forms) {
for (const element of Array.from(form.elements).filter(isFieldElement)) {
- const serverRenderedInvalid = /true/i.test(element.getAttribute("aria-invalid"))
+ const serverRenderedInvalid = isAriaInvalid(element)
const id = element.getAttribute("aria-errormessage")
const errorMessageElement = document.getElementById(id)
const validationMessage = errorMessageElement?.textContent
diff --git a/app/javascript/constraint_validations/util.js b/app/javascript/constraint_validations/util.js
index 513f835..b344dc8 100644
--- a/app/javascript/constraint_validations/util.js
+++ b/app/javascript/constraint_validations/util.js
@@ -2,6 +2,10 @@ export function isFieldElement(element) {
return !element.disabled && "validity" in element && element.willValidate
}
+export function isAriaInvalid(element) {
+ return element.getAttribute("aria-invalid") === "true"
+}
+
export function readValidationMessages(input) {
try {
return JSON.parse(input.getAttribute("data-validation-messages")) || {}
diff --git a/app/javascript/constraint_validations/validators/checkbox_validator.js b/app/javascript/constraint_validations/validators/checkbox_validator.js
index a3e9f13..5b3a17c 100644
--- a/app/javascript/constraint_validations/validators/checkbox_validator.js
+++ b/app/javascript/constraint_validations/validators/checkbox_validator.js
@@ -1,7 +1,7 @@
-import { isFieldElement, readValidationMessages } from "../util"
+import { isAriaInvalid, isFieldElement, readValidationMessages } from "../util"
export default class {
- selector = "input[type=checkbox]:required"
+ selector = "input[type=checkbox]"
ignoringMutations = false
constructor(constraintValidations, predicate) {
@@ -13,15 +13,17 @@ export default class {
}
connect() {
+ this.element.addEventListener("invalid", this.handleInvalid, { capture: true, passive: true })
this.mutationObserver.observe(this.element, {
attributeFilter: ["required"],
childList: true,
subtree: true
})
- this.reportValidationMessages(this.element.querySelectorAll(this.selector))
+ this.reportValidationMessages(this.element.querySelectorAll(this.selector), isAriaInvalid)
}
disconnect() {
+ this.element.removeEventListener("invalid", this.handleInvalid, { capture: true, passive: true })
this.mutationObserver.disconnect()
}
@@ -30,8 +32,8 @@ export default class {
}
validate(target) {
- const checkboxesInGroup = checkboxGroup(target).filter(isFieldElement)
- const allRequired = checkboxesInGroup.every((checkbox) => checkbox.getAttribute("aria-required") === "true")
+ const checkboxesInGroup = checkboxGroup(target).filter(isCheckboxElement)
+ const allRequired = checkboxesInGroup.every((checkbox) => isRequired(checkbox))
const someChecked = checkboxesInGroup.some((checkbox) => checkbox.checked)
if (allRequired && someChecked) {
@@ -50,6 +52,18 @@ export default class {
// Private
+ handleInvalid = ({ target }) => {
+ const checkboxes = new Set
+
+ for (const element of target.form.elements) {
+ if (isCheckboxElement(element) && this.willValidate(element)) {
+ checkboxes.add(element)
+ }
+ }
+
+ this.reportValidationMessages(checkboxes)
+ }
+
handleMutation = (mutationRecords) => {
if (this.ignoringMutations) return
@@ -61,22 +75,23 @@ export default class {
target.removeAttribute("aria-required")
}
} else if (addedNodes.length) {
- this.reportValidationMessages(addedNodes)
+ this.reportValidationMessages(addedNodes, isAriaInvalid)
}
}
}
- reportValidationMessages(nodes) {
+ reportValidationMessages(nodes, willReport = () => true) {
const requiredCheckboxes = querySelectorAllNodes(this.selector, nodes)
for (const checkbox of requiredCheckboxes) {
- if (checkbox.required) {
+ if (isRequired(checkbox)) {
const group = checkboxGroup(checkbox)
if (this.willValidateGroup(group)) {
for (const checkboxInGroup of group) {
this.swapRequiredWithAriaRequired(checkboxInGroup)
- if (checkboxInGroup.getAttribute("aria-invalid") === "true") {
+
+ if (willReport(checkboxInGroup)) {
this.validate(checkboxInGroup)
}
}
@@ -138,3 +153,11 @@ function querySelectorAllNodes(selector, nodes, elements = new Set) {
return Array.from(elements)
}
+
+function isCheckboxElement(element) {
+ return isFieldElement(element) && element.type === "checkbox"
+}
+
+function isRequired(element) {
+ return element.required || element.getAttribute("aria-required") === "true"
+}
diff --git a/test/dummy/app/views/forms/new.html.erb b/test/dummy/app/views/forms/new.html.erb
index a507610..d2efa8c 100644
--- a/test/dummy/app/views/forms/new.html.erb
+++ b/test/dummy/app/views/forms/new.html.erb
@@ -51,5 +51,6 @@
<% end %>
+
<% end %>
diff --git a/test/system/validations_test.rb b/test/system/validations_test.rb
index 37d0a6b..c32a49b 100644
--- a/test/system/validations_test.rb
+++ b/test/system/validations_test.rb
@@ -235,6 +235,39 @@ class ValidationsTest < ApplicationSystemTestCase
end
end
+ test "checkbox with multiple [required] checkbox validates on submit after initial connect" do
+ visit new_form_path(disableSubmitWhenInvalid: false, checkbox: true)
+
+ within_fieldset "Multiple [required] checkboxes" do
+ assert_no_text "can't be blank"
+ assert_unchecked_field "Multiple required checkbox", exact: false, count: 2 do |input|
+ input.assert_matches_selector :element, required: false, "aria-required": "true"
+ end
+ end
+
+ click_button "Validate and Submit"
+
+ within_fieldset "Multiple [required] checkboxes" do
+ assert_unchecked_field "Multiple required checkbox", exact: false, valid: false, described_by: "can't be blank", validation_message: "can't be blank", count: 2
+ end
+ end
+
+ test "checkbox with multiple [required] checkbox validates on submit after re-render" do
+ visit new_form_path(hotwire_enabled: true, disableSubmitWhenInvalid: false, checkbox: true)
+ click_button "Skip Validations"
+ check "Single required checkbox"
+ check "Multiple required checkbox #1"
+
+ assert_no_field valid: false
+
+ uncheck "Multiple required checkbox #1"
+ click_button "Validate and Submit"
+
+ within_fieldset "Multiple [required] checkboxes" do
+ assert_unchecked_field "Multiple required checkbox", exact: false, valid: false, described_by: "can't be blank", validation_message: "can't be blank", count: 2
+ end
+ end
+
test "checkbox with multiple [required] checkbox requires one to be checked" do
visit new_form_path(checkbox: true)