diff --git a/src/plugins/hotkey/index.js b/src/plugins/hotkey/index.js index 7da40e6..4be88f9 100644 --- a/src/plugins/hotkey/index.js +++ b/src/plugins/hotkey/index.js @@ -26,7 +26,7 @@ function plugin({ directive }) { hotkey, e => { has_modifier(modifiers, "prevent") && e.preventDefault(); - has_modifier(modifiers, "stop") && e.stopPropogation(); + has_modifier(modifiers, "stop") && e.stopPropagation(); e.hotkey = hotkey; listener(e); diff --git a/tests/playwright/assets/page.html b/tests/playwright/assets/page.html index bca7431..d3f1f2d 100644 --- a/tests/playwright/assets/page.html +++ b/tests/playwright/assets/page.html @@ -15,6 +15,7 @@ + diff --git a/tests/playwright/x-hotkey.spec.js b/tests/playwright/x-hotkey.spec.js new file mode 100644 index 0000000..3a87fe7 --- /dev/null +++ b/tests/playwright/x-hotkey.spec.js @@ -0,0 +1,205 @@ +import { expect, test } from "@playwright/test"; +import { set_html } from "./assets/utils"; + +test("x-hotkey triggers expression on matching key", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("1"); +}); + +test("x-hotkey does not trigger on non-matching key", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+j"); + await page.keyboard.press("Control+Shift+k"); + await page.keyboard.press("k"); + + await expect(page.locator("span")).toHaveText("0"); +}); + +test("x-hotkey exposes $event in expression", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("keydown"); +}); + +test("x-hotkey sets $event.hotkey to the matched hotkey string", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("ctrl+k"); +}); + +test("x-hotkey .prevent modifier calls preventDefault()", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("true"); +}); + +test("x-hotkey .stop modifier stops event propagation", async ({ page }) => { + await set_html(page, ` +
+
+ +
+ + +
`); + + await page.locator("#btn").focus(); + await page.keyboard.press("Control+k"); + + await expect(page.locator("#inner")).toHaveText("1"); + await expect(page.locator("#outer")).toHaveText("0"); +}); + +test("x-hotkey without .stop modifier does not stop propagation", async ({ page }) => { + await set_html(page, ` +
+
+ +
+ + +
`); + + await page.locator("#btn").focus(); + await page.keyboard.press("Control+k"); + + await expect(page.locator("#inner")).toHaveText("1"); + await expect(page.locator("#outer")).toHaveText("1"); +}); + +test("x-hotkey .window modifier listens on window", async ({ page }) => { + await set_html(page, ` +
+ +
+ +
`); + + await page.locator("#other").focus(); + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("1"); +}); + +test("x-hotkey .document modifier listens on document", async ({ page }) => { + await set_html(page, ` +
+ +
+ +
`); + + await page.locator("#other").focus(); + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("1"); +}); + +test("x-hotkey:keyup listens on keyup event", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("keyup"); +}); + +test("x-hotkey supports multiple hotkeys via comma-separated values", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+j"); + await page.keyboard.press("Control+k"); + + await expect(page.locator("span")).toHaveText("2"); +}); + +test("x-hotkey with no expression does not throw", async ({ page }) => { + const errors = []; + page.on("pageerror", e => errors.push(e)); + + await set_html(page, ` +
+
+
`); + + await page.keyboard.press("Control+k"); + + expect(errors).toHaveLength(0); +}); + +test("x-hotkey cleans up listener when element is removed", async ({ page }) => { + await set_html(page, ` +
+ + + +
`); + + await page.keyboard.press("Control+k"); + await expect(page.locator("span")).toHaveText("1"); + + const hotkey_el = page.locator("#id_1"); + await expect(hotkey_el).toBeAttached(); + + await page.locator("button").click(); + await hotkey_el.waitFor({ state: "detached" }); + + await page.keyboard.press("Control+k"); + await expect(page.locator("span")).toHaveText("1"); +}); + +test("x-hotkey .once modifier fires only once", async ({ page }) => { + await set_html(page, ` +
+
+ +
`); + + await page.keyboard.press("Control+k"); + await expect(page.locator("span")).toHaveText("1"); + + await page.keyboard.press("Control+k"); + await expect(page.locator("span")).toHaveText("1"); + + await page.keyboard.press("Control+k"); + await expect(page.locator("span")).toHaveText("1"); +});