Skip to content

Commit 8c40b70

Browse files
authored
Merge pull request #1232 from Patternslib/fix-pat-depends
Fix pat depends
2 parents 8efe802 + af5ecc5 commit 8c40b70

File tree

5 files changed

+416
-9
lines changed

5 files changed

+416
-9
lines changed

src/core/dom.js

+19
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,24 @@ const is_input = (el) => {
141141
return re_input.test(el.nodeName);
142142
};
143143

144+
/**
145+
* Test, if a element is a button-like input type.
146+
*
147+
* @param {Node} el - The DOM node to test.
148+
* @returns {Boolean} - True if the element is a input-type element.
149+
*/
150+
const is_button = (el) => {
151+
return el.matches(`
152+
button,
153+
input[type=image],
154+
input[type=button],
155+
input[type=reset],
156+
input[type=submit]
157+
`);
158+
};
159+
160+
161+
144162
/**
145163
* Return all direct parents of ``el`` matching ``selector``.
146164
* This matches against all parents but not the element itself.
@@ -613,6 +631,7 @@ const dom = {
613631
acquire_attribute: acquire_attribute,
614632
is_visible: is_visible,
615633
is_input: is_input,
634+
is_button: is_button,
616635
create_from_string: create_from_string,
617636
get_css_value: get_css_value,
618637
find_scroll_container: find_scroll_container,

src/core/dom.test.js

+41
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,47 @@ describe("core.dom tests", () => {
547547
});
548548
});
549549

550+
describe("is_button", () => {
551+
it("checks, if an element is a button-like element or not.", (done) => {
552+
553+
const button = document.createElement("button");
554+
const button_button = document.createElement("button");
555+
button_button.setAttribute("type", "button");
556+
const button_submit = document.createElement("button");
557+
button_submit.setAttribute("type", "submit");
558+
559+
const input_button = document.createElement("input");
560+
input_button.setAttribute("type", "button");
561+
const input_submit = document.createElement("input");
562+
input_submit.setAttribute("type", "submit");
563+
const input_reset = document.createElement("input");
564+
input_reset.setAttribute("type", "reset");
565+
const input_image = document.createElement("input");
566+
input_image.setAttribute("type", "image");
567+
568+
expect(dom.is_button(button)).toBe(true);
569+
expect(dom.is_button(button_button)).toBe(true);
570+
expect(dom.is_button(button_submit)).toBe(true);
571+
expect(dom.is_button(input_button)).toBe(true);
572+
expect(dom.is_button(input_image)).toBe(true);
573+
expect(dom.is_button(input_reset)).toBe(true);
574+
expect(dom.is_button(input_submit)).toBe(true);
575+
576+
const input_text = document.createElement("input");
577+
input_text.setAttribute("type", "text");
578+
579+
expect(dom.is_button(input_text)).toBe(false);
580+
expect(dom.is_button(document.createElement("input"))).toBe(false);
581+
expect(dom.is_button(document.createElement("select"))).toBe(false);
582+
expect(dom.is_button(document.createElement("textarea"))).toBe(false);
583+
expect(dom.is_button(document.createElement("form"))).toBe(false);
584+
expect(dom.is_button(document.createElement("div"))).toBe(false);
585+
586+
done();
587+
});
588+
});
589+
590+
550591
describe("create_from_string", () => {
551592
it("Creates a DOM element from a string", (done) => {
552593
const res = dom.create_from_string(`

src/pat/depends/depends.js

+75-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ parser.addArgument("transition", "none", ["none", "css", "fade", "slide"]);
1616
parser.addArgument("effect-duration", "fast");
1717
parser.addArgument("effect-easing", "swing");
1818

19+
20+
// A custom input event which differs from the one in `core/events` in that it
21+
// accepts a `detail` object to pass arbitrary information around.
22+
// TODO: The events in `core/events` should be refactored to accept a `detail`
23+
// object.
24+
const input_event = (detail = {}) => {
25+
return new CustomEvent("input", {
26+
bubbles: true,
27+
cancelable: false,
28+
detail: detail,
29+
});
30+
};
31+
32+
1933
class Pattern extends BasePattern {
2034
static name = "depends";
2135
static trigger = ".pat-depends";
@@ -44,7 +58,14 @@ class Pattern extends BasePattern {
4458
input,
4559
"input",
4660
`pat-depends--input--${this.uuid}`,
47-
this.set_state.bind(this)
61+
(e) => {
62+
if (e?.detail?.pattern_uuid === this.uuid) {
63+
// Ignore input events invoked from this pattern
64+
// instance to avoid infinite loops.
65+
return;
66+
}
67+
this.set_state();
68+
}
4869
);
4970

5071
if (input.form) {
@@ -74,15 +95,40 @@ class Pattern extends BasePattern {
7495
enable() {
7596
const inputs = dom.find_inputs(this.el);
7697
for (const input of inputs) {
98+
if (input.disabled === false) {
99+
// Do not re-enable an already enabled input.
100+
continue;
101+
}
102+
103+
// Now, enable the input element.
77104
input.disabled = false;
78-
// Trigger the input after disabling so that any other bound
105+
106+
if (input === this.el) {
107+
// Do not re-trigger this pattern on it's own element to avoid
108+
// infinite loops.
109+
continue;
110+
}
111+
112+
if (dom.is_button(input)) {
113+
// Do not trigger the input event on buttons as they do not
114+
// support it.
115+
continue;
116+
}
117+
118+
// Trigger the input after enabling so that any other bound
79119
// actions can react on that.
80-
input.dispatchEvent(events.input_event());
120+
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
81121
}
122+
123+
// Restore the original click behavior for anchor elements.
82124
if (this.el.tagName === "A") {
83125
events.remove_event_listener(this.el, "pat-depends--click");
84126
}
127+
128+
// Remove the disabled class from the element.
85129
this.el.classList.remove("disabled");
130+
131+
// Trigger the pat-update event to notify other patterns about enabling.
86132
this.$el.trigger("pat-update", {
87133
pattern: "depends",
88134
action: "attribute-changed",
@@ -94,17 +140,42 @@ class Pattern extends BasePattern {
94140
disable() {
95141
const inputs = dom.find_inputs(this.el);
96142
for (const input of inputs) {
143+
if (input.disabled === true) {
144+
// Do not re-disable an already disabled input.
145+
continue;
146+
}
147+
148+
// Now, disable the input element.
97149
input.disabled = true;
150+
151+
if (input === this.el) {
152+
// Do not re-trigger this pattern on it's own element to avoid
153+
// infinite loops.
154+
continue;
155+
}
156+
157+
if (dom.is_button(input)) {
158+
// Do not trigger the input event on buttons as they do not
159+
// support it.
160+
continue;
161+
}
162+
98163
// Trigger the input after disabling so that any other bound
99164
// actions can react on that.
100-
input.dispatchEvent(events.input_event());
165+
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
101166
}
167+
168+
// Prevent the default click behavior for anchor elements.
102169
if (this.el.tagName === "A") {
103170
events.add_event_listener(this.el, "click", "pat-depends--click", (e) =>
104171
e.preventDefault()
105172
);
106173
}
174+
175+
// Add the disabled class to the element.
107176
this.el.classList.add("disabled");
177+
178+
// Trigger the pat-update event to notify other patterns about disabling.
108179
this.$el.trigger("pat-update", {
109180
pattern: "depends",
110181
action: "attribute-changed",

0 commit comments

Comments
 (0)