From 5060fa4421ed581d5e64717e75eadff8f0c391d7 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Fri, 4 Mar 2022 17:25:58 +0100 Subject: [PATCH] fix(pat validation): With depending date validation also validate the dependend date. Fixes: #970 --- src/pat/validation/validation.js | 31 ++++++++++++-------- src/pat/validation/validation.test.js | 42 +++++++++++++++------------ 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/pat/validation/validation.js b/src/pat/validation/validation.js index 79c5511d2..16187e19b 100644 --- a/src/pat/validation/validation.js +++ b/src/pat/validation/validation.js @@ -57,7 +57,7 @@ export default Base.extend({ for (const [cnt, input] of this.inputs.entries()) { // Cancelable debouncer. const debouncer = utils.debounce((e) => { - this.check_input(input, e); + this.check_input({ input: input, event: e }); }, this.options.delay); events.add_event_listener( @@ -82,12 +82,12 @@ export default Base.extend({ this.el, "submit", `pat-validation--blur-${input.name}--${cnt}--validator`, - (e) => this.check_input(input, e) // immediate check with submit. Otherwise submit is not cancelable. + (e) => this.check_input({ input: input, event: e }) // immediate check with submit. Otherwise submit is not cancelable. ); } }, - check_input(input, e) { + check_input({ input, event, stop = false }) { if (input.disabled) { // No need to check disabled inputs. return; @@ -97,7 +97,7 @@ export default Base.extend({ this.set_validity({ input: input, msg: "" }); const validity_state = input.validity; - if (e.submitter?.hasAttribute("formnovalidate")) { + if (event?.submitter?.hasAttribute("formnovalidate")) { // Do not submit when a button with ``formnovalidate`` was used. return; } @@ -139,6 +139,8 @@ export default Base.extend({ let not_after; let not_before; + let not_after_el; + let not_before_el; const date = new Date(input.value); if (isNaN(date)) { // Should not happen or input only partially typed in. @@ -149,9 +151,8 @@ export default Base.extend({ not_after = new Date(input_options.not.after); if (isNaN(not_after)) { // Handle value as selector - not_after = document.querySelector( - input_options.not.after - )?.value; + not_after_el = document.querySelector(input_options.not.after); + not_after = not_after_el?.value; not_after = not_after && new Date( @@ -167,9 +168,8 @@ export default Base.extend({ not_before = new Date(input_options.not.before); if (isNaN(not_before)) { // Handle value as selector - not_before = document.querySelector( - input_options.not.before - )?.value; + not_before_el = document.querySelector(input_options.not.before); + not_before = not_before_el?.value; not_before = not_before && new Date( @@ -185,6 +185,13 @@ export default Base.extend({ } else if (not_before && date < not_before) { this.set_validity({ input: input, msg: msg }); } + // always check the other input to clear/set errors + !stop && // do not re-check when stop is set to avoid infinite loops + not_after_el && + this.check_input({ input: not_after_el, stop: true }); + !stop && + not_before_el && + this.check_input({ input: not_before_el, stop: true }); } if (!validity_state.customError) { @@ -248,9 +255,9 @@ export default Base.extend({ } } - if (e.type === "submit") { + if (event?.type === "submit") { // Do not submit in error case. - e.preventDefault(); + event.preventDefault(); } this.set_error_message(input, input_options); }, diff --git a/src/pat/validation/validation.test.js b/src/pat/validation/validation.test.js index 226e24ab7..bfdd5838e 100644 --- a/src/pat/validation/validation.test.js +++ b/src/pat/validation/validation.test.js @@ -824,15 +824,6 @@ describe("pat-validation", function () { inp_end.value = "2020-10-05"; inp_end.dispatchEvent(events.change_event()); await utils.timeout(1); // wait a tick for async to settle. - expect(el.querySelectorAll("em.warning").length).toBe(1); - expect(el.querySelectorAll("em.warning")[0].textContent).toBe( - "The end date must on or before the start date." - ); - - // Violate the before/after constraint - inp_start.value = "2020-10-06"; - inp_start.dispatchEvent(events.change_event()); - await utils.timeout(1); // wait a tick for async to settle. expect(el.querySelectorAll("em.warning").length).toBe(2); expect(el.querySelectorAll("em.warning")[0].textContent).toBe( "The start date must on or before the end date." @@ -849,6 +840,18 @@ describe("pat-validation", function () { await utils.timeout(1); // wait a tick for async to settle. expect(el.querySelectorAll("em.warning").length).toBe(0); + // Violate the before/after constraint + inp_start.value = "2020-10-11"; + inp_start.dispatchEvent(events.change_event()); + await utils.timeout(1); // wait a tick for async to settle. + expect(el.querySelectorAll("em.warning").length).toBe(2); + expect(el.querySelectorAll("em.warning")[0].textContent).toBe( + "The start date must on or before the end date." + ); + expect(el.querySelectorAll("em.warning")[1].textContent).toBe( + "The end date must on or before the start date." + ); + // Fulfill the before/after constraint - start before end inp_start.value = "2020-10-01"; inp_start.dispatchEvent(events.change_event()); @@ -947,15 +950,6 @@ describe("pat-validation", function () { inp_end.value = "2022-01-05T09:00"; inp_end.dispatchEvent(events.change_event()); await utils.timeout(1); // wait a tick for async to settle. - expect(el.querySelectorAll("em.warning").length).toBe(1); - expect(el.querySelectorAll("em.warning")[0].textContent).toBe( - "The end date/time must on or before the start date/time." - ); - - // Violate the before/after constraint - inp_start.value = "2022-01-05T11:00"; - inp_start.dispatchEvent(events.change_event()); - await utils.timeout(1); // wait a tick for async to settle. expect(el.querySelectorAll("em.warning").length).toBe(2); expect(el.querySelectorAll("em.warning")[0].textContent).toBe( "The start date/time must on or before the end date/time." @@ -972,6 +966,18 @@ describe("pat-validation", function () { await utils.timeout(1); // wait a tick for async to settle. expect(el.querySelectorAll("em.warning").length).toBe(0); + // Violate the before/after constraint + inp_start.value = "2022-01-05T11:00"; + inp_start.dispatchEvent(events.change_event()); + await utils.timeout(1); // wait a tick for async to settle. + expect(el.querySelectorAll("em.warning").length).toBe(2); + expect(el.querySelectorAll("em.warning")[0].textContent).toBe( + "The start date/time must on or before the end date/time." + ); + expect(el.querySelectorAll("em.warning")[1].textContent).toBe( + "The end date/time must on or before the start date/time." + ); + // Fulfill the before/after constraint - start before end inp_start.value = "2022-01-04T10:00"; inp_start.dispatchEvent(events.change_event());