Skip to content

Commit 0c3e245

Browse files
authored
Merge pull request #1245 from Patternslib/validation-upds
introduce pat-update event factory, use it in basepattern, emit after validation checks.
2 parents e585a67 + 2c15de1 commit 0c3e245

File tree

6 files changed

+143
-16
lines changed

6 files changed

+143
-16
lines changed

src/core/basepattern.js

+10
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ class BasePattern {
104104
// Extend this method in your pattern.
105105
}
106106

107+
emit_update(action = undefined, options = {}) {
108+
options = {
109+
pattern: this.name,
110+
dom: this.el,
111+
action: action,
112+
...options,
113+
}
114+
this.el.dispatchEvent(events.update_event(options));
115+
}
116+
107117
/**
108118
* Listen to an event on the element only once.
109119
*

src/core/basepattern.test.js

+25
Original file line numberDiff line numberDiff line change
@@ -364,4 +364,29 @@ describe("Basepattern class tests", function () {
364364
// If test reaches this expect statement, the init event catched.
365365
expect(true).toBe(true);
366366
});
367+
368+
it("7 - Emit update event.", async function () {
369+
const events = (await import("./events")).default;
370+
class Pat extends BasePattern {
371+
static name = "example";
372+
static trigger = ".example";
373+
}
374+
375+
const el = document.createElement("div");
376+
el.classList.add("example");
377+
378+
const pat = new Pat(el);
379+
await events.await_pattern_init(pat);
380+
381+
let event;
382+
el.addEventListener("pat-update", (e) => {
383+
event = e;
384+
});
385+
386+
pat.emit_update("test");
387+
388+
expect(event.detail.pattern).toBe("example");
389+
expect(event.detail.dom).toBe(el);
390+
expect(event.detail.action).toBe("test");
391+
});
367392
});

src/core/events.js

+23
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,28 @@ const generic_event = (name) => {
181181
});
182182
};
183183

184+
185+
/** Patternslib specifc event factories
186+
*/
187+
188+
class UpdateEvent extends CustomEvent {
189+
constructor(options) {
190+
super("pat-update", {
191+
bubbles: true,
192+
cancelable: true,
193+
detail: options
194+
});
195+
}
196+
}
197+
198+
const update_event = (options) => {
199+
return new UpdateEvent(options);
200+
}
201+
202+
203+
/** Web API event factories
204+
*/
205+
184206
const blur_event = () => {
185207
return new Event("blur", {
186208
bubbles: false,
@@ -266,6 +288,7 @@ export default {
266288
await_event: await_event,
267289
await_pattern_init: await_pattern_init,
268290
generic_event: generic_event,
291+
update_event: update_event,
269292
blur_event: blur_event,
270293
click_event: click_event,
271294
change_event: change_event,

src/core/events.test.js

+20
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,26 @@ describe("core.events tests", () => {
467467
expect(catched).toBe("outer");
468468
});
469469

470+
it("update event", async () => {
471+
outer.addEventListener("pat-update", () => {
472+
catched = "outer";
473+
});
474+
inner.dispatchEvent(events.update_event());
475+
await utils.timeout(1);
476+
expect(catched).toBe("outer");
477+
});
478+
479+
it("update event with data", async () => {
480+
let event;
481+
outer.addEventListener("pat-update", (e) => {
482+
event = e;
483+
});
484+
const data = {"foo": "bar"};
485+
inner.dispatchEvent(events.update_event(data));
486+
await utils.timeout(1);
487+
expect(event.detail).toBe(data);
488+
});
489+
470490
it("blur event", async () => {
471491
outer.addEventListener("blur", () => {
472492
catched = "outer";

src/pat/validation/validation.js

+30-16
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class Pattern extends BasePattern {
113113
}
114114

115115
// In any case, clear the custom validity first.
116-
this.set_validity({ input: input, msg: "" });
116+
this.set_error({ input: input, msg: "", skip_event: true });
117117
const validity_state = input.validity;
118118

119119
if (event?.submitter?.hasAttribute("formnovalidate")) {
@@ -148,7 +148,7 @@ class Pattern extends BasePattern {
148148
const message =
149149
input_options.message.equality ||
150150
`The value is not equal to %{attribute}`;
151-
this.set_validity({
151+
this.set_error({
152152
input: input,
153153
msg: message,
154154
attribute: input_options.equality,
@@ -216,7 +216,7 @@ class Pattern extends BasePattern {
216216
);
217217
msg_attr = msg_attr || not_after_el.name;
218218
}
219-
this.set_validity({
219+
this.set_error({
220220
input: input,
221221
msg: msg || msg_default_not_after,
222222
attribute: msg_attr.trim(),
@@ -237,7 +237,7 @@ class Pattern extends BasePattern {
237237
);
238238
msg_attr = msg_attr || not_before_el.name;
239239
}
240-
this.set_validity({
240+
this.set_error({
241241
input: input,
242242
msg: msg || msg_default_not_before,
243243
attribute: msg_attr.trim(),
@@ -266,15 +266,15 @@ class Pattern extends BasePattern {
266266
// Default error cases with custom messages.
267267

268268
if (validity_state.valueMissing && input_options.message.required) {
269-
this.set_validity({ input: input, msg: input_options.message.required });
269+
this.set_error({ input: input, msg: input_options.message.required });
270270
} else if (validity_state.rangeUnderflow && input_options.message.min) {
271-
this.set_validity({
271+
this.set_error({
272272
input: input,
273273
msg: input_options.message.min,
274274
min: input.getAttribute("min"),
275275
});
276276
} else if (validity_state.rangeOverflow && input_options.message.max) {
277-
this.set_validity({
277+
this.set_error({
278278
input: input,
279279
msg: input_options.message.max,
280280
max: input.getAttribute("max"),
@@ -284,38 +284,43 @@ class Pattern extends BasePattern {
284284
input.type === "number" &&
285285
input_options.message.number
286286
) {
287-
this.set_validity({ input: input, msg: input_options.message.number });
287+
this.set_error({ input: input, msg: input_options.message.number });
288288
} else if (
289289
validity_state.typeMismatch &&
290290
input.type === "email" &&
291291
input_options.message.email
292292
) {
293-
this.set_validity({ input: input, msg: input_options.message.email });
293+
this.set_error({ input: input, msg: input_options.message.email });
294294
} else if (
295295
validity_state.rangeUnderflow &&
296296
input.type === "date" &&
297297
input_options.message.date
298298
) {
299-
this.set_validity({ input: input, msg: input_options.message.date });
299+
this.set_error({ input: input, msg: input_options.message.date });
300300
} else if (
301301
validity_state.rangeOverflow &&
302302
input.type === "date" &&
303303
input_options.message.date
304304
) {
305-
this.set_validity({ input: input, msg: input_options.message.date });
305+
this.set_error({ input: input, msg: input_options.message.date });
306306
} else if (
307307
validity_state.rangeUnderflow &&
308308
input.type === "datetime" &&
309309
input_options.message.datetime
310310
) {
311-
this.set_validity({ input: input, msg: input_options.message.datetime });
311+
this.set_error({ input: input, msg: input_options.message.datetime });
312312
} else if (
313313
validity_state.rangeOverflow &&
314314
input.type === "datetime" &&
315315
input_options.message.datetime
316316
) {
317-
this.set_validity({ input: input, msg: input_options.message.datetime });
317+
this.set_error({ input: input, msg: input_options.message.datetime });
318+
} else {
319+
// Still an error, but without customized messages.
320+
// Call `emit_update` separately
321+
this.emit_update("invalid");
318322
}
323+
319324
}
320325

321326
if (event?.type === "submit") {
@@ -327,7 +332,7 @@ class Pattern extends BasePattern {
327332
this.set_error_message(input);
328333
}
329334

330-
set_validity({ input, msg, attribute = null, min = null, max = null }) {
335+
set_error({ input, msg, attribute = null, min = null, max = null, skip_event = false }) {
331336
// Replace some variables, as like validate.js
332337
if (attribute) {
333338
msg = msg.replace(/%{attribute}/g, attribute);
@@ -345,9 +350,13 @@ class Pattern extends BasePattern {
345350
// Hidden inputs do not participate in validation but we need this
346351
// (e.g. styled date input).
347352
input[KEY_ERROR_MSG] = msg;
353+
354+
if (!skip_event) {
355+
this.emit_update("invalid");
356+
}
348357
}
349358

350-
remove_error(input, all_of_group = false) {
359+
remove_error(input, all_of_group = false, skip_event = false) {
351360
// Remove error message and related referencesfrom input.
352361

353362
let inputs = [input];
@@ -370,10 +379,15 @@ class Pattern extends BasePattern {
370379
}
371380
}
372381
}
382+
383+
if (!skip_event) {
384+
this.emit_update("valid");
385+
}
373386
}
374387

375388
set_error_message(input) {
376-
this.remove_error(input);
389+
// First, remove the old error message.
390+
this.remove_error(input, false, true);
377391

378392
// Do not set a error message for a input group like radio buttons or
379393
// checkboxes where one has already been set.

src/pat/validation/validation.test.js

+35
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,41 @@ describe("pat-validation", function () {
503503
expect(el.querySelectorAll("em.warning").length).toBe(1);
504504
});
505505

506+
it("1.21 - Emits an update event when the validation state changes", async function () {
507+
document.body.innerHTML = `
508+
<form class="pat-validation">
509+
<input name="name" required>
510+
</form>
511+
`;
512+
const el = document.querySelector(".pat-validation");
513+
const inp = el.querySelector("[name=name]");
514+
515+
const instance = new Pattern(el);
516+
await events.await_pattern_init(instance);
517+
518+
let event;
519+
el.addEventListener("pat-update", (e) => {
520+
event = e;
521+
});
522+
523+
inp.value = "";
524+
inp.dispatchEvent(events.change_event());
525+
await utils.timeout(1); // wait a tick for async to settle.
526+
527+
expect(el.querySelectorAll("em.warning").length).toBe(1);
528+
expect(event.detail.pattern).toBe("validation");
529+
expect(event.detail.dom).toBe(el);
530+
expect(event.detail.action).toBe("invalid");
531+
532+
inp.value = "okay";
533+
inp.dispatchEvent(events.change_event());
534+
await utils.timeout(1); // wait a tick for async to settle.
535+
536+
expect(event.detail.pattern).toBe("validation");
537+
expect(event.detail.dom).toBe(el);
538+
expect(event.detail.action).toBe("valid");
539+
});
540+
506541
it("2.1 - validates required inputs", async function () {
507542
document.body.innerHTML = `
508543
<form class="pat-validation">

0 commit comments

Comments
 (0)