Skip to content

Commit b01819a

Browse files
committed
fix(pat validation): Fix problem with submitting invalid forms with pat-inject.
Stop submit event propagation if the form is invalid. This fixes a problem where a invalid form could be submitted via pat-inject.
1 parent e6f8ba3 commit b01819a

File tree

3 files changed

+153
-3
lines changed

3 files changed

+153
-3
lines changed

src/pat/validation/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@
251251
</form>
252252

253253
<div class="pat-modal">
254-
<form class="pat-validation vertical pat-inject"
254+
<form class="pat-inject vertical pat-validation"
255255
action="."
256256
method="get"
257257
data-pat-validation="disable-selector: [type=submit], button:not([type=button])"

src/pat/validation/validation.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,10 @@ export default Base.extend({
255255
}
256256

257257
if (event?.type === "submit") {
258-
// Do not submit in error case.
258+
// Do not submit in error case and prevent other handlers to take action.
259259
event.preventDefault();
260+
event.stopPropagation();
261+
event.stopImmediatePropagation();
260262
}
261263
this.set_error_message(input, input_options);
262264
},

src/pat/validation/validation.test.js

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Pattern, { parser } from "./validation";
2-
import utils from "../../core/utils";
32
import events from "../../core/events";
3+
import utils from "../../core/utils";
4+
import { jest } from "@jest/globals";
45

56
describe("pat-validation", function () {
67
let orig_delay;
@@ -287,6 +288,153 @@ describe("pat-validation", function () {
287288
expect(el.querySelectorAll("em.warning").length).toBe(0);
288289
});
289290

291+
it("1.13 - Prevents other event handlers when invalid.", async function () {
292+
document.body.innerHTML = `
293+
<form class="pat-validation">
294+
<input name="ok" required />
295+
<button>submit</button>
296+
</form>
297+
`;
298+
const el = document.querySelector(".pat-validation");
299+
300+
new Pattern(el);
301+
302+
let submit_called = false;
303+
let click_called = false;
304+
305+
// Note: the handlers must be registered after Pattern initialization.
306+
// Otherwise the pattern will not be able to prevent the event.
307+
// In case of other patterns, the validation pattern will be reordered
308+
// first and submit prevention does work.
309+
el.addEventListener("submit", () => (submit_called = true));
310+
el.addEventListener("click", () => (click_called = true));
311+
312+
el.querySelector("button").click();
313+
await utils.timeout(1); // wait a tick for async to settle.
314+
315+
expect(el.querySelectorAll("em.warning").length).toBe(1);
316+
expect(submit_called).toBe(false);
317+
expect(click_called).toBe(true);
318+
});
319+
320+
it("1.14 - Prevents pat-inject form submission when invalid.", async function () {
321+
const pat_inject = (await import("../inject/inject")).default;
322+
const registry = (await import("../../core/registry")).default;
323+
324+
document.body.innerHTML = `
325+
<form action="." class="pat-inject pat-validation">
326+
<input name="ok" required />
327+
<button>submit</button>
328+
</form>
329+
`;
330+
const el = document.querySelector(".pat-validation");
331+
332+
const spy_inject_submit = jest.spyOn(pat_inject, "onTrigger");
333+
334+
registry.scan(document.body);
335+
await utils.timeout(1); // wait a tick for async to settle.
336+
337+
el.querySelector("button").click();
338+
await utils.timeout(1); // wait a tick for async to settle.
339+
340+
expect(el.querySelectorAll("em.warning").length).toBe(1);
341+
expect(spy_inject_submit).not.toHaveBeenCalled();
342+
});
343+
344+
it("1.15 - Prevents pat-modal closing with a pat-inject when invalid.", async function () {
345+
await import("../close-panel/close-panel");
346+
const pat_inject = (await import("../inject/inject")).default;
347+
const pat_modal = (await import("../modal/modal")).default;
348+
const registry = (await import("../../core/registry")).default;
349+
350+
document.body.innerHTML = `
351+
<div class="pat-modal">
352+
<form action="." class="pat-inject pat-validation">
353+
<input name="ok" required />
354+
<button class="close-panel">submit</button>
355+
</form>
356+
</div>
357+
`;
358+
const el = document.querySelector("form");
359+
360+
const spy_inject_submit = jest.spyOn(pat_inject, "onTrigger");
361+
const spy_destroy_modal = jest.spyOn(pat_modal.prototype, "destroy");
362+
363+
registry.scan(document.body);
364+
await utils.timeout(1); // wait a tick for async to settle.
365+
366+
el.querySelector("button").click();
367+
await utils.timeout(1); // wait a tick for async to settle.
368+
369+
expect(el.querySelectorAll("em.warning").length).toBe(1);
370+
expect(spy_inject_submit).not.toHaveBeenCalled();
371+
expect(spy_destroy_modal).not.toHaveBeenCalled();
372+
});
373+
374+
it("1.16 - Prevents pat-modal closing when invalid.", async function () {
375+
await import("../close-panel/close-panel");
376+
const pat_modal = (await import("../modal/modal")).default;
377+
const registry = (await import("../../core/registry")).default;
378+
379+
document.body.innerHTML = `
380+
<div class="pat-modal">
381+
<form action="." class="pat-validation">
382+
<input name="ok" required />
383+
<button class="close-panel">submit</button>
384+
</form>
385+
</div>
386+
`;
387+
const el = document.querySelector("form");
388+
389+
const spy_destroy_modal = jest.spyOn(pat_modal.prototype, "destroy");
390+
391+
registry.scan(document.body);
392+
await utils.timeout(1); // wait a tick for async to settle.
393+
394+
el.querySelector("button").click();
395+
await utils.timeout(1); // wait a tick for async to settle.
396+
397+
expect(el.querySelectorAll("em.warning").length).toBe(1);
398+
expect(spy_destroy_modal).not.toHaveBeenCalled();
399+
});
400+
401+
it("1.17 - Prevents pat-modal closing when invalid with custom validation rule.", async function () {
402+
await import("../close-panel/close-panel");
403+
const pat_modal = (await import("../modal/modal")).default;
404+
const registry = (await import("../../core/registry")).default;
405+
406+
const spy_destroy_modal = jest.spyOn(pat_modal.prototype, "destroy");
407+
408+
document.body.innerHTML = `
409+
<div class="pat-modal">
410+
<form action="." class="pat-validation">
411+
<input name="ok" />
412+
<input name="nok" data-pat-validation="equality: ok" />
413+
<button class="close-panel submit">submit</button>
414+
<button class="close-panel cancel" type="button">cancel</button>
415+
</form>
416+
</div>
417+
`;
418+
const el = document.querySelector("form");
419+
const inp_ok = document.querySelector("input[name=ok]");
420+
inp_ok.value = "foo";
421+
422+
registry.scan(document.body);
423+
await utils.timeout(1); // wait a tick for async to settle.
424+
425+
el.querySelector("button.submit").click();
426+
await utils.timeout(1); // wait a tick for async to settle.
427+
428+
expect(el.querySelectorAll("em.warning").length).toBe(1);
429+
expect(spy_destroy_modal).not.toHaveBeenCalled();
430+
431+
// A non-submit close-panel button does not check for validity.
432+
el.querySelector("button.cancel").click();
433+
await utils.timeout(1); // wait a tick for async to settle.
434+
435+
expect(spy_destroy_modal).toHaveBeenCalled();
436+
});
437+
290438
it("2.1 - validates required inputs", async function () {
291439
document.body.innerHTML = `
292440
<form class="pat-validation">

0 commit comments

Comments
 (0)