From fddb3b6fbe150fb462f71508a7318627e6a2a29e Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 9 Nov 2023 23:52:06 +0100 Subject: [PATCH 1/4] maint(pat inject): Add more debug messages. --- src/pat/inject/inject.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pat/inject/inject.js b/src/pat/inject/inject.js index 27160b2cf..c7040d759 100644 --- a/src/pat/inject/inject.js +++ b/src/pat/inject/inject.js @@ -105,6 +105,7 @@ const inject = { }); // setup event handlers if ($el[0]?.tagName === "FORM") { + log.debug("Initializing form with injection on", $el[0]); events.add_event_listener( $el[0], "submit", From 8c01058eac2bec6f6e5b6f3fd75088b21c845d30 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 9 Nov 2023 23:52:50 +0100 Subject: [PATCH 2/4] maint(pat autosubmit): Improve debug messages. --- src/pat/auto-submit/auto-submit.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pat/auto-submit/auto-submit.js b/src/pat/auto-submit/auto-submit.js index 3a9c787de..0f968031c 100644 --- a/src/pat/auto-submit/auto-submit.js +++ b/src/pat/auto-submit/auto-submit.js @@ -129,6 +129,10 @@ export default Base.extend({ onInputChange(e) { e.stopPropagation(); this.el.dispatchEvent(events.submit_event({ submitter: e.target })); - log.debug("triggered by " + e.type); + log.debug( + `submit event triggered by event ${e.type} by submitter (1) on (2)`, + e.target, + this.el + ); }, }); From 01b348db79381d0d9c3901ed48ddb531dccced3c Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 9 Nov 2023 23:46:17 +0100 Subject: [PATCH 3/4] feat(core dom): find_form - find a related form element. Add a function to find a related form element. If called on any element it would first try to get a input element and get it's associated form - which can be different and as a last resort to find the element's enclosing form. Otherwise return undefined. --- src/core/dom.js | 20 +++++++++ src/core/dom.test.js | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/src/core/dom.js b/src/core/dom.js index e1a89ab10..378809811 100644 --- a/src/core/dom.js +++ b/src/core/dom.js @@ -557,6 +557,25 @@ const element_uuid = (el) => { return get_data(el, "uuid"); }; +/** + * Find a related form element. + * + * @param {Node} el - The DOM node to start the search from. + * @returns {Node} - The closest form element. + * + * @example + * find_form(document.querySelector("input")); + */ +const find_form = (el) => { + // Prefer input.form which allows for input outside form elements and fall + // back to search for a parent form. + return ( + el.form || + el.querySelector("input, select, textarea, button")?.form || + el.closest("form") + ); +}; + const dom = { toNodeArray: toNodeArray, querySelectorAllAndMe: querySelectorAllAndMe, @@ -585,6 +604,7 @@ const dom = { get_visible_ratio: get_visible_ratio, escape_css_id: escape_css_id, element_uuid: element_uuid, + find_form: find_form, add_event_listener: events.add_event_listener, // BBB export. TODO: Remove in an upcoming version. remove_event_listener: events.remove_event_listener, // BBB export. TODO: Remove in an upcoming version. }; diff --git a/src/core/dom.test.js b/src/core/dom.test.js index c44e539d7..3bd03c90e 100644 --- a/src/core/dom.test.js +++ b/src/core/dom.test.js @@ -903,3 +903,103 @@ describe("element_uuid", function () { window.crypto.randomUUID = orig_randomUUID; }); }); + +describe("find_form", function () { + it("example 1", function () { + document.body.innerHTML = ` +
+
+
+ `; + const el = document.querySelector("#start"); + const form = document.querySelector("form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 2", function () { + document.body.innerHTML = ` +
+ +
+ `; + const el = document.querySelector("input"); + const form = document.querySelector("form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 3", function () { + document.body.innerHTML = ` +
+
+ + `; + const el = document.querySelector("input"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 4", function () { + document.body.innerHTML = ` +
+
+
+ +
+ `; + const el = document.querySelector("input"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 5", function () { + document.body.innerHTML = ` +
+
+
+ +
+ `; + const el = document.querySelector("#other-form"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 6", function () { + document.body.innerHTML = ` +
+
+
+ +
+ `; + const el = document.querySelector("#other-form"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 7", function () { + document.body.innerHTML = ` +
+
+
+ +
+ `; + const el = document.querySelector("#other-form"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); + + it("example 8", function () { + document.body.innerHTML = ` +
+
+
+ +
+ `; + const el = document.querySelector("#other-form"); + const form = document.querySelector("#the-form"); + expect(dom.find_form(el)).toBe(form); + }); +}); From a2530f3e4cd57943c3109e3ef5be58278f1252bb Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Fri, 10 Nov 2023 00:06:16 +0100 Subject: [PATCH 4/4] fix(pat autosubmit): Re-add support for autosubmit on non form elements. Fix a problem introduces in Patternslib 9.9.7 where pat-autosubmit did not work when not defined on a form or input element. As a side effect, the submit event is now always invoked on the related or nearest form of the element where pat-autosubmit was defined on. If that element is not a form element a related (input.form) or the nearest parent form is searched and the submit event invoked upon. --- src/pat/auto-submit/auto-submit.js | 7 +++-- src/pat/auto-submit/auto-submit.test.js | 37 +++++++++++++++-------- src/pat/auto-suggest/auto-suggest.test.js | 15 +++++---- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/pat/auto-submit/auto-submit.js b/src/pat/auto-submit/auto-submit.js index 0f968031c..29a4838c3 100644 --- a/src/pat/auto-submit/auto-submit.js +++ b/src/pat/auto-submit/auto-submit.js @@ -1,6 +1,7 @@ import "../../core/jquery-ext"; import $ from "jquery"; import Base from "../../core/base"; +import dom from "../../core/dom"; import events from "../../core/events"; import input_change_events from "../../lib/input-change-events"; import logging from "../../core/logging"; @@ -46,7 +47,7 @@ export default Base.extend({ data?.pattern === "sortable" ) { // Directly submit when removing a clone or changing the sorting. - this.el.dispatchEvent(events.submit_event()); + dom.find_form(this.el).dispatchEvent(events.submit_event()); log.debug( `triggered by pat-update, pattern: ${data.pattern}, action: ${data.action}` ); @@ -128,7 +129,9 @@ export default Base.extend({ onInputChange(e) { e.stopPropagation(); - this.el.dispatchEvent(events.submit_event({ submitter: e.target })); + dom.find_form(this.el).dispatchEvent( + events.submit_event({ submitter: e.target }) + ); log.debug( `submit event triggered by event ${e.type} by submitter (1) on (2)`, e.target, diff --git a/src/pat/auto-submit/auto-submit.test.js b/src/pat/auto-submit/auto-submit.test.js index f5f1e0828..63cfe3b63 100644 --- a/src/pat/auto-submit/auto-submit.test.js +++ b/src/pat/auto-submit/auto-submit.test.js @@ -82,17 +82,12 @@ describe("pat-autosubmit", function () { `; const input = document.querySelector(".pat-autosubmit"); new Pattern(input); - let submit_input_dispatched = false; let submit_form_dispatched = false; - input.addEventListener("submit", () => { - submit_input_dispatched = true; - }); document.querySelector("form").addEventListener("submit", () => { submit_form_dispatched = true; }); input.dispatchEvent(events.input_event()); await utils.timeout(1); - expect(submit_input_dispatched).toBe(true); expect(submit_form_dispatched).toBe(true); }); @@ -179,23 +174,41 @@ describe("pat-autosubmit", function () { `; const input = document.querySelector(".pat-autosubmit"); new Pattern(input); - let submit_input_dispatched = false; let submit_form_dispatched = false; - input.addEventListener("submit", () => { - submit_input_dispatched = true; - }); document.querySelector("form").addEventListener("submit", () => { submit_form_dispatched = true; }); input.dispatchEvent(events.input_event()); await utils.timeout(1); - expect(submit_input_dispatched).toBe(false); expect(submit_form_dispatched).toBe(false); await utils.timeout(9); - expect(submit_input_dispatched).toBe(false); expect(submit_form_dispatched).toBe(false); await utils.timeout(10); - expect(submit_input_dispatched).toBe(true); + expect(submit_form_dispatched).toBe(true); + }); + + it("2.6 - when pat-autosubmit is defined not on aform element", async function () { + document.body.innerHTML = ` +
+
+ +
+
+ `; + const input = document.querySelector("input"); + const autosubmit = document.querySelector(".pat-autosubmit"); + new Pattern(autosubmit); + + let submit_form_dispatched = false; + document.querySelector("form").addEventListener("submit", () => { + submit_form_dispatched = true; + }); + + input.dispatchEvent(events.input_event()); + await utils.timeout(1); expect(submit_form_dispatched).toBe(true); }); }); diff --git a/src/pat/auto-suggest/auto-suggest.test.js b/src/pat/auto-suggest/auto-suggest.test.js index 84678fe9e..e0daebae5 100644 --- a/src/pat/auto-suggest/auto-suggest.test.js +++ b/src/pat/auto-suggest/auto-suggest.test.js @@ -428,17 +428,20 @@ describe("pat-autosuggest", function () { describe("4 - Integration...", function () { it("4.1 - Works with pat-auto-submit", async function () { document.body.innerHTML = ` - +
+ +
`; const pattern_autosubmit = (await import("../auto-submit/auto-submit")).default; // prettier-ignore const input = document.querySelector("input"); + let submit_dispatched = false; - input.addEventListener("submit", () => { + document.querySelector("form").addEventListener("submit", () => { submit_dispatched = true; });