From f239eb95599fe9e6a8619127a00b0f813fa0f975 Mon Sep 17 00:00:00 2001 From: Seva Zaikov Date: Thu, 25 Jul 2024 18:31:00 -0700 Subject: [PATCH] fix event listeners not registering for events with multiple words (#73) --- integration-tests/assign-attributes.test.ts | 71 +++++++++++++++++++ src/create-element/assign-attributes.ts | 6 +- src/create-state/update-useattribute-value.ts | 3 +- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/integration-tests/assign-attributes.test.ts b/integration-tests/assign-attributes.test.ts index 177838c..41e41ef 100644 --- a/integration-tests/assign-attributes.test.ts +++ b/integration-tests/assign-attributes.test.ts @@ -298,4 +298,75 @@ describe("assign-attributes", () => { expect(spyFn).toHaveBeenCalledTimes(3); expect(state.getValue()).toBe(4); }); + + test("adds listeners with multiple words in them correctly", async () => { + const user = userEvent.setup(); + const spyFn = jest.fn(); + function App() { + const handler = () => { + spyFn(); + }; + return createElement("div", { + children: [ + createElement("button", { + "data-testid": "button", + onDblClick: handler, + }), + ], + }); + } + + cleanup = attachComponent({ + htmlElement: document.body, + component: createElement(App), + }); + + const btn = screen.getByTestId("button"); + + await user.dblClick(btn); + expect(spyFn).toHaveBeenCalledTimes(1); + }); + + it("allows to assign and remove event listeners dynamically passing the same callback with multiple words in event", async () => { + const user = userEvent.setup(); + const state = createState(0); + const spyFn = jest.fn(); + function App() { + const handler = () => { + spyFn(); + state.setValue((currentValue) => currentValue + 1); + }; + return createElement("div", { + children: [ + createElement("button", { + "data-testid": "button", + onDblClick: state.useAttribute((value) => + value !== 0 && value < 4 ? handler : undefined + ), + }), + ], + }); + } + + cleanup = attachComponent({ + htmlElement: document.body, + component: createElement(App), + }); + + const btn = screen.getByTestId("button"); + + await user.dblClick(btn); + await user.dblClick(btn); + + state.setValue(1); + + await user.dblClick(btn); + await user.dblClick(btn); + await user.dblClick(btn); + await user.dblClick(btn); + await user.dblClick(btn); + + expect(spyFn).toHaveBeenCalledTimes(3); + expect(state.getValue()).toBe(4); + }); }); diff --git a/src/create-element/assign-attributes.ts b/src/create-element/assign-attributes.ts index 1fd9070..45a46b3 100644 --- a/src/create-element/assign-attributes.ts +++ b/src/create-element/assign-attributes.ts @@ -34,11 +34,7 @@ function assignAttribute({ typeof value === "function" && key.startsWith("on") ) { - // TODO: think if this is robust enough - htmlElement.addEventListener( - key[2].toLocaleLowerCase() + key.slice(3), - value - ); + htmlElement.addEventListener(key.slice(2).toLocaleLowerCase(), value); } else { if (typeof value === "boolean") { // according to the spec, boolean values should just get either an empty string diff --git a/src/create-state/update-useattribute-value.ts b/src/create-state/update-useattribute-value.ts index 5151a56..962a7c4 100644 --- a/src/create-state/update-useattribute-value.ts +++ b/src/create-state/update-useattribute-value.ts @@ -28,8 +28,7 @@ function updateUseAttributeValue({ return; } - const eventName = - attributeName[2].toLocaleLowerCase() + attributeName.slice(3); + const eventName = attributeName.slice(2).toLocaleLowerCase(); if (attributeValue) { // we remove the previous value, `removeEventListener` needs // to have the same value as the one that was added