Skip to content

Commit e8640a9

Browse files
committed
feat(core events): Support await_pattern_init with pattern double registration attempt.
When a pattern is tried to be initialized on the same element twice, throw an event and use that event in await_pattern_init to reject the promise. When using await_pattern_init you might want to try/catch the block to handle any possible double-registration errors.
1 parent fc0e333 commit e8640a9

File tree

4 files changed

+176
-8
lines changed

4 files changed

+176
-8
lines changed

src/core/basepattern.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*
77
* For usage, see basepattern.md
88
*/
9+
import events from "./events";
910
import logging from "./logging";
1011

1112
const log = logging.getLogger("basepattern");
@@ -46,6 +47,14 @@ class BasePattern {
4647
if (typeof this.el[`pattern-${this.name}`] !== "undefined") {
4748
// Do not reinstantiate
4849
log.debug(`Not reinstatiating the pattern ${this.name}.`, this.el);
50+
51+
// Notify that not instantiated
52+
this.el.dispatchEvent(
53+
new Event(`not-init.${this.name}.patterns`, {
54+
bubbles: true,
55+
cancelable: false,
56+
})
57+
);
4958
return;
5059
}
5160

@@ -80,9 +89,15 @@ class BasePattern {
8089
* @param {function} callback - Callback to call when the event is thrown.
8190
*/
8291
one(event_name, event_callback) {
83-
this.el.addEventListener(`${event_name}.${this.name}.patterns`, event_callback, {
84-
once: true,
85-
});
92+
events.add_event_listener(
93+
this.el,
94+
`${event_name}.${this.name}.patterns`,
95+
`basepattern-one--${event_name}.${this.name}.patterns`,
96+
event_callback,
97+
{
98+
once: true,
99+
}
100+
);
86101
}
87102

88103
/**

src/core/basepattern.test.js

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,48 @@ describe("Basepattern class tests", function () {
185185
expect(el["pattern-example"]).toBeInstanceOf(Pat);
186186
});
187187

188-
it("6.1 - Registers a one-time event listener on the element.", async function () {
188+
it("6.1 - Can register a special namespaced one-time event listener on the element.", async function () {
189+
class Pat extends BasePattern {
190+
static name = "example";
191+
static trigger = ".example";
192+
}
193+
194+
const el = document.createElement("div");
195+
196+
const pat = new Pat(el);
197+
198+
let cnt = 0;
199+
pat.one("test", () => {
200+
cnt++;
201+
});
202+
203+
el.dispatchEvent(new Event("test.example.patterns")); // Note this special event name.
204+
205+
expect(cnt).toBe(1);
206+
207+
// The handler is now unregistered.
208+
el.dispatchEvent(new Event("test.example.patterns"));
209+
210+
expect(cnt).toBe(1);
211+
});
212+
213+
it("6.2 - Throws a init event after asynchronous initialization has finished.", async function () {
214+
const events = (await import("./events")).default;
215+
class Pat extends BasePattern {
216+
static name = "example";
217+
static trigger = ".example";
218+
}
219+
220+
const el = document.createElement("div");
221+
222+
const pat = new Pat(el);
223+
await events.await_pattern_init(pat);
224+
225+
// If test reaches this expect statement, the init event catched.
226+
expect(true).toBe(true);
227+
});
228+
229+
it("6.3 - Throws a not-init event in case of an double initialization event which is handled by await_pattern_init.", async function () {
189230
const events = (await import("./events")).default;
190231
class Pat extends BasePattern {
191232
static name = "example";
@@ -200,5 +241,55 @@ describe("Basepattern class tests", function () {
200241

201242
// If test reaches this expect statement, the init event catched.
202243
expect(true).toBe(true);
244+
245+
// The same pattern cannot be registered twice without destroying the
246+
// first one. If the same pattern is initialized twice on the same
247+
// element, a event is thrown.
248+
const pat2 = new Pat(el);
249+
try {
250+
await events.await_pattern_init(pat2);
251+
} catch (e) {
252+
expect(e instanceof Error).toBe(true);
253+
expect(e.message).toBe(`Pattern "example" not initialized.`);
254+
}
255+
256+
// We can also use the catch method of the promise to handle that case.
257+
const pat3 = new Pat(el);
258+
await events.await_pattern_init(pat3).catch((e) => {
259+
expect(e instanceof Error).toBe(true);
260+
expect(e.message).toBe(`Pattern "example" not initialized.`);
261+
return; // We need to return here.
262+
});
263+
264+
// If test reaches this expect statement, the not-init event was catched.
265+
expect(true).toBe(true);
266+
});
267+
268+
it("6.4 - Destroying a pattern before re-initializing again also works together with await_pattern_init.", async function () {
269+
const events = (await import("./events")).default;
270+
class Pat extends BasePattern {
271+
static name = "example";
272+
static trigger = ".example";
273+
}
274+
275+
const el = document.createElement("div");
276+
el.classList.add("example");
277+
278+
const pat = new Pat(el);
279+
await events.await_pattern_init(pat);
280+
281+
// If test reaches this expect statement, the init event catched.
282+
expect(true).toBe(true);
283+
284+
pat.destroy();
285+
286+
const pat2 = new Pat(el);
287+
await events.await_pattern_init(pat2).catch(() => {
288+
// this is not reached.
289+
expect(false).toBe(true);
290+
});
291+
292+
// If test reaches this expect statement, the init event catched.
293+
expect(true).toBe(true);
203294
});
204295
});

src/core/events.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,29 @@ const await_event = (el, event_name) => {
100100
*/
101101
const await_pattern_init = (pattern) => {
102102
// See: https://stackoverflow.com/a/44746691/1337474
103-
return new Promise((resolve) => pattern.one("init", resolve));
103+
return new Promise((resolve, reject) => {
104+
// Case initialized
105+
pattern.one("init", () => {
106+
// Resolve promise and unregister the not-init event handler.
107+
remove_event_listener(
108+
pattern.el,
109+
`basepattern-one--not-init.${pattern.name}.patterns`
110+
);
111+
resolve();
112+
});
113+
114+
// Case not initialized
115+
pattern.one("not-init", () => {
116+
// Reject promise and unregister the init event handler.
117+
remove_event_listener(
118+
pattern.el,
119+
`basepattern-one--init.${pattern.name}.patterns`
120+
);
121+
reject();
122+
});
123+
}).catch(() => {
124+
throw new Error(`Pattern "${pattern.name}" not initialized.`);
125+
});
104126
};
105127

106128
/**

src/core/events.test.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe("core.events tests", () => {
6565
expect(event_listener_map[el].test_once_event).not.toBeDefined();
6666
});
6767

68-
it("Awaits an event to happen", async () => {
68+
it("Awaits an event to happen.", async () => {
6969
const el = document.createElement("div");
7070

7171
window.setTimeout(() => {
@@ -78,7 +78,7 @@ describe("core.events tests", () => {
7878
expect(true).toBe(true);
7979
});
8080

81-
it("Awaits a pattern to be initialized", async () => {
81+
it("Awaits a pattern to be initialized.", async () => {
8282
const pat = Base.extend({
8383
name: "tmp",
8484
trigger: ".pat-tmp",
@@ -94,7 +94,47 @@ describe("core.events tests", () => {
9494
expect(true).toBe(true);
9595
});
9696

97-
it("Awaits a class based pattern to be initialized", async () => {
97+
it("Awaits a class based pattern to be initialized.", async () => {
98+
class Pat extends BasePattern {
99+
static name = "tmp";
100+
static trigger = ".pat-tmp";
101+
init() {}
102+
}
103+
104+
const el = document.createElement("div");
105+
const instance = new Pat(el);
106+
107+
await events.await_pattern_init(instance);
108+
109+
// If test reaches this expect statement, all is fine.
110+
expect(true).toBe(true);
111+
112+
// The same pattern cannot be registered twice without destroying
113+
// the first one. If the same pattern is initialized twice on the
114+
// same element, a event is thrown.
115+
const pat2 = new Pat(el);
116+
try {
117+
await events.await_pattern_init(pat2);
118+
} catch (e) {
119+
expect(e instanceof Error).toBe(true);
120+
expect(e.message).toBe(`Pattern "tmp" not initialized.`);
121+
}
122+
123+
// We can also use the catch method of the promise to handle that
124+
// case.
125+
const pat3 = new Pat(el);
126+
await events.await_pattern_init(pat3).catch((e) => {
127+
expect(e instanceof Error).toBe(true);
128+
expect(e.message).toBe(`Pattern "tmp" not initialized.`);
129+
return; // We need to return here.
130+
});
131+
132+
// If test reaches this expect statement, the not-init event was
133+
// catched.
134+
expect(true).toBe(true);
135+
});
136+
137+
it("Handles double-registration attempts by rejecting the await_pattern_init promise.", async () => {
98138
class Pat extends BasePattern {
99139
static name = "tmp";
100140
static trigger = ".pat-tmp";

0 commit comments

Comments
 (0)