diff --git a/src/core/form-data-event.js b/src/core/form-data-event.js new file mode 100644 index 000000000..c5dff35da --- /dev/null +++ b/src/core/form-data-event.js @@ -0,0 +1,72 @@ +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); + +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.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); + }); +});