From c90651ab8d8505d0fa6278f808784cdcbf07a3b8 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 2 Sep 2021 11:50:34 +0200 Subject: [PATCH 1/2] feat(core form-data-event): New core pattern to allow passing form data via events. --- src/core/form-data-event.js | 79 +++++++++++++++++++++ src/core/form-data-event.test.js | 113 +++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 src/core/form-data-event.js create mode 100644 src/core/form-data-event.test.js diff --git a/src/core/form-data-event.js b/src/core/form-data-event.js new file mode 100644 index 000000000..8ddd931f8 --- /dev/null +++ b/src/core/form-data-event.js @@ -0,0 +1,79 @@ +import Base from "./base"; +import Parser from "./parser"; +import logging from "./logging"; + +const log = logging.getLogger("form-data-event"); + +export const parser = new Parser("form-data-event"); +parser.attribute = "data-form-data-event"; // do not require ``data-pat-`` +parser.addArgument("event-name-submit"); +parser.addArgument("event-name-init"); +parser.addArgument("prevent-submit", true); +parser.addArgument("close-modal", true); + +export default Base.extend({ + name: "form-data-event", + trigger: ".form-data-event", // note: no .pat- trigger! + + init() { + this.options = parser.parse(this.el, this.options); + if (this.el.tagName !== "FORM") { + log.warn("pattern must be initialized on a form element."); + return; + } + if (this.options.event["name-submit"]) { + this.el.addEventListener("submit", this.handle_submit.bind(this)); + } + if (this.options.event["name-init"]) { + document.addEventListener( + this.options.event["name-init"], + this.handle_init.bind(this), + { once: true } + ); + } + }, + + handle_submit(e) { + if (this.options.preventSubmit) { + e.preventDefault(); + } + // Note: line breaks might change from /n to /r + const form_data = new FormData(this.el); + if (e.submitter?.name) { + form_data.append("action", e.submitter.name); + } + const ev = new CustomEvent(this.options.event["name-submit"], { + detail: { form_data: form_data }, + }); + document.dispatchEvent(ev); + if (this.options.closeModal) { + const modal = this.el.closest(".pat-modal"); + if (modal) { + modal["pattern-modal"].destroy(); + } + } + if (this.options.event["name-init"]) { + document.removeEventListener( + this.options.event["name-init"], + this.handle_init + ); + } + }, + + handle_init(e) { + const form_data = e?.detail?.form_data; + for (const [key, value] of form_data.entries()) { + const _el = this.el.querySelector(`[name=${key}]`); + if (_el) { + if (_el.type === "checkbox") { + // TODO: + // should set checked=true if value exists and set the checkbox value to the form_data value? + // then we need some logic to unset the checkbox if value is omitted. + _el.checked = value === "true" || value === "on"; // default value for checked checkboxes is "on" + } else { + _el.value = value; + } + } + } + }, +}); diff --git a/src/core/form-data-event.test.js b/src/core/form-data-event.test.js new file mode 100644 index 000000000..81e4dcb58 --- /dev/null +++ b/src/core/form-data-event.test.js @@ -0,0 +1,113 @@ +import "./form-data-event"; +import registry from "./registry"; +import utils from "./utils"; + +describe("form-data-event: Pass form data via events.", () => { + afterEach(() => { + document.body.innerHTML = ""; + }); + + it("Initializes a form with data when receiving an initialization event, but only once", async () => { + document.body.innerHTML = ` +
+ + + + +
+ `; + + registry.scan(document.body); + + const longer_text = `HalloGallo +Sonderangebot +Weissensee +Jahresüberblick +Im Glück +Negativland +Lieber Honig`; + + const form_data = new FormData(); + form_data.append("inp1", "Neu!"); + form_data.append("inp2", "1972"); + form_data.append("inp3", true); + form_data.append("inp4", longer_text); + + const init_event = new CustomEvent("init-event", { + detail: { form_data: form_data }, + }); + document.dispatchEvent(init_event); + + await utils.timeout(1); + + const form = document.querySelector("form"); + expect(form.querySelector("[name=inp1]").value).toBe("Neu!"); + expect(form.querySelector("[name=inp2]").value).toBe("1972"); + expect(form.querySelector("[name=inp3]").checked).toBe(true); + expect(form.querySelector("[name=inp4]").value).toBe(longer_text); + + // Another initialization Event won't change the form again + // The reason for the once-registration of the event is to prevent + // unused event handlers to be hanging around. + + const form_data_2 = new FormData(); + form_data_2.append("inp1", "Kraftwerk"); + form_data_2.append("inp2", "Autobahn"); + form_data_2.append("inp3", false); + form_data_2.append("inp4", ""); + + const init_event_2 = new CustomEvent("init-event", { + detail: { form_data: form_data_2 }, + }); + document.dispatchEvent(init_event_2); + + await utils.timeout(1); + + expect(form.querySelector("[name=inp1]").value).toBe("Neu!"); + expect(form.querySelector("[name=inp2]").value).toBe("1972"); + expect(form.querySelector("[name=inp3]").checked).toBe(true); + expect(form.querySelector("[name=inp4]").value).toBe(longer_text); + }); + + it("Passes form data via an submit event", async () => { + // Note: line breaks might change from /n to /r + // Testing without line breaks for now. + const longer_text = `Autobahn, Kometenmelodie 1, Kometenmelodie 2, Mitternacht, Morgenspaziergang`; + + document.body.innerHTML = ` +
+ + + + +
+ `; + + registry.scan(document.body); + + let form_data; + document.addEventListener("submit-event", (e) => { + form_data = e.detail.form_data; + }); + + const form = document.querySelector("form"); + + form.dispatchEvent(new Event("submit")); + + await utils.timeout(1); + + expect(form_data instanceof FormData).toBeTruthy(); + expect(form_data.get("inp1")).toBe("Kraftwerk"); + expect(form_data.get("inp2")).toBe("Autobahn"); + expect(form_data.get("inp3")).toBe("on"); + expect(form_data.get("inp4")).toBe(longer_text); + }); +}); From 8ad3bbee7e7f02eb2d5cb28298c9d028ed737a0c Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Thu, 2 Sep 2021 16:38:04 +0200 Subject: [PATCH 2/2] form-data-event: Don't close a modal within the submit handler. This should only be done via pat-modal's .close-panel CSS class handler. --- src/core/form-data-event.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/form-data-event.js b/src/core/form-data-event.js index 8ddd931f8..c5dff35da 100644 --- a/src/core/form-data-event.js +++ b/src/core/form-data-event.js @@ -9,7 +9,6 @@ parser.attribute = "data-form-data-event"; // do not require ``data-pat-`` parser.addArgument("event-name-submit"); parser.addArgument("event-name-init"); parser.addArgument("prevent-submit", true); -parser.addArgument("close-modal", true); export default Base.extend({ name: "form-data-event", @@ -46,12 +45,6 @@ export default Base.extend({ detail: { form_data: form_data }, }); document.dispatchEvent(ev); - if (this.options.closeModal) { - const modal = this.el.closest(".pat-modal"); - if (modal) { - modal["pattern-modal"].destroy(); - } - } if (this.options.event["name-init"]) { document.removeEventListener( this.options.event["name-init"],