diff --git a/src/core/events.js b/src/core/events.js index 90a7b88e5..8106957fa 100644 --- a/src/core/events.js +++ b/src/core/events.js @@ -167,6 +167,20 @@ const await_pattern_init = (pattern) => { * Event factories */ +/** Generic event factory. + * + * A event factory for a bubbling and cancelable generic event. + * + * @param {string} name - The event name. + * @returns {Event} - Returns a blur event. + */ +const generic_event = (name) => { + return new Event(name, { + bubbles: true, + cancelable: true, + }); +}; + const blur_event = () => { return new Event("blur", { bubbles: false, @@ -249,6 +263,7 @@ export default { remove_event_listener: remove_event_listener, await_event: await_event, await_pattern_init: await_pattern_init, + generic_event: generic_event, blur_event: blur_event, click_event: click_event, change_event: change_event, diff --git a/src/core/events.test.js b/src/core/events.test.js index ba2bd8898..f03e53f49 100644 --- a/src/core/events.test.js +++ b/src/core/events.test.js @@ -457,6 +457,16 @@ describe("core.events tests", () => { inner = el.querySelector("#inner"); }); + it("generic event", async () => { + const name = "fantasy_event"; + outer.addEventListener(name, () => { + catched = "outer"; + }); + inner.dispatchEvent(events.generic_event(name)); + await utils.timeout(1); + expect(catched).toBe("outer"); + }); + it("blur event", async () => { outer.addEventListener("blur", () => { catched = "outer"; diff --git a/src/pat/auto-submit/auto-submit.js b/src/pat/auto-submit/auto-submit.js index 81c3ff396..1df3d5886 100644 --- a/src/pat/auto-submit/auto-submit.js +++ b/src/pat/auto-submit/auto-submit.js @@ -31,8 +31,10 @@ export default Base.extend({ }, registerListeners() { - this.$el.on( - "input-change-delayed.pat-autosubmit", + events.add_event_listener( + this.el, + "input-change-delayed", + "pat-autosubmit--input-change-delayed", this.onInputChange.bind(this) ); this.registerSubformListeners(); @@ -44,7 +46,7 @@ export default Base.extend({ data?.pattern === "sortable" ) { // Directly submit when removing a clone or changing the sorting. - this.$el.submit(); + this.el.dispatchEvent(events.submit_event()); log.debug( `triggered by pat-update, pattern: ${data.pattern}, action: ${data.action}` ); @@ -60,15 +62,21 @@ export default Base.extend({ * that only the subform gets submitted if an element inside it * changes. */ - const $el = typeof ev !== "undefined" ? $(ev.target) : this.$el; - $el.find(".pat-subform") - .not(".pat-autosubmit") - .each((idx, el) => { - $(el).on( - "input-change-delayed.pat-autosubmit", - this.onInputChange.bind(this) - ); - }); + const el = typeof ev !== "undefined" ? ev.target : this.el; + + // get all subforms whice are not yet auto submit forms. + const subforms = el.querySelectorAll( + ".pat-autosubmit:not(.pat-autosubmit):not(.pat-auto-submit)" + ); + for (const subform of subforms) { + // register autosubmit on subform + events.add_event_listener( + subform, + "input-change-delayed", + "pat-autosubmit--input-change-delayed", + this.onInputChange.bind(this) + ); + } }, refreshListeners(ev, cfg, el, injected) { @@ -87,10 +95,10 @@ export default Base.extend({ } function trigger_event(ev) { - if ($(ev.target).closest(".pat-autosubmit")[0] !== this) { + if (ev.target.closest(".pat-autosubmit") !== this) { return; } - $(ev.target).trigger("input-change-delayed"); + ev.target.dispatchEvent(events.generic_event("input-change-delayed")); } if (this.options.delay === "defocus") { this.$el.on("input-defocus.pat-autosubmit", trigger_event); diff --git a/src/pat/auto-submit/auto-submit.test.js b/src/pat/auto-submit/auto-submit.test.js index 0e91c0119..f5f1e0828 100644 --- a/src/pat/auto-submit/auto-submit.test.js +++ b/src/pat/auto-submit/auto-submit.test.js @@ -134,10 +134,17 @@ describe("pat-autosubmit", function () { `; const el = document.querySelector(".pat-autosubmit"); - const instance = new Pattern(el); - const spy = jest.spyOn(instance.$el, "submit"); + + let submit_dispatched = false; + el.addEventListener("submit", () => { + submit_dispatched = true; + }); + + new Pattern(el); + $(el).trigger("pat-update", { pattern: "clone", action: "removed" }); - expect(spy).toHaveBeenCalled(); + + expect(submit_dispatched).toBe(true); }); it("2.4 - when pat-sortable changes the sorting", function () { @@ -146,10 +153,50 @@ describe("pat-autosubmit", function () { `; const el = document.querySelector(".pat-autosubmit"); - const instance = new Pattern(el); - const spy = jest.spyOn(instance.$el, "submit"); + + let submit_dispatched = false; + el.addEventListener("submit", () => { + submit_dispatched = true; + }); + + new Pattern(el); + $(el).trigger("pat-update", { pattern: "sortable" }); - expect(spy).toHaveBeenCalled(); + + expect(submit_dispatched).toBe(true); + }); + + it("2.5 - when a change on a single input happens with delay option", async function () { + document.body.innerHTML = ` +
+ `; + 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); }); }); diff --git a/src/pat/auto-submit/index.html b/src/pat/auto-submit/index.html index 0eea53a5e..826a013f5 100644 --- a/src/pat/auto-submit/index.html +++ b/src/pat/auto-submit/index.html @@ -110,19 +110,19 @@