diff --git a/README.md b/README.md index 9425263..ea37e48 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![MIT](https://img.shields.io/github/license/rameel/ramstack.hotkey.js)](https://github.com/rameel/ramstack.hotkey.js/blob/main/LICENSE) The `@ramstack/hotkey` package is a very small and lightweight library for handling hotkeys. -The library weighs around 1.3KB and approximately 750 bytes when gzipped. +The library weighs around 1.4KB and approximately 800 bytes when gzipped. ## Installation @@ -102,41 +102,39 @@ registerHotkey(window, "Win + PgUp", e => { #### hotkey (required) The combination of keys for the hotkey, e.g., `Ctrl + Alt + Delete` -The hotkey description is a case-insensitive. Spaces are not important. Standard key names are used. -You can find them here [Key values for keyboard events](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values) +The hotkey description is case-insensitive. Spaces are not important. Standard key names are used. +You can find them here [Key values for keyboard events](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values). In addition, there are also aliases for some key names: ```js const aliases: Record = { - "esc" : "escape", - "ins" : "insert", - "del" : "delete", - "up" : "arrowup", - "down" : "arrowdown", - "right" : "arrowright", - "left" : "arrowleft", - "pgup" : "pageup", - "pgdn" : "pagedown", - "break" : "pause", - "scroll" : "scrolllock", - "scrlk" : "scrolllock", - "prtscr" : "printscreen", - "win" : "meta", - "windows" : "meta", - "cmd" : "meta", - "command" : "meta", - "comma" : ",", - "period" : ".", - "quote" : "\"", - "singlequote" : "'", - "colon" : ":", - "semicolon" : ";", - "plus" : "+", - "minus" : "-", - "tilde" : "~", - "equal" : "=", - "slash" : "/" + "esc" : "escape", + "ins" : "insert", + "del" : "delete", + "up" : "arrowup", + "down" : "arrowdown", + "right" : "arrowright", + "left" : "arrowleft", + "pgup" : "pageup", + "pgdn" : "pagedown", + "break" : "pause", + "scroll" : "scrolllock", + "scrlk" : "scrolllock", + "prtscr" : "printscreen", + "win" : "meta", + "windows" : "meta", + "cmd" : "meta", + "command" : "meta", + "," : "comma", + "." : "period", + "/" : "slash", + "\\" : "backslash", + ";" : "semicolon", + "=" : "equal", + "`" : "backquote", + "]" : "bracketright", + "[" : "bracketleft", }; ``` diff --git a/src/hotkey.ts b/src/hotkey.ts index 3c1f3c0..cd64c33 100644 --- a/src/hotkey.ts +++ b/src/hotkey.ts @@ -16,17 +16,15 @@ const aliases: Record = { "windows": "meta", "cmd": "meta", "command": "meta", - "comma": ",", - "period": ".", - "quote": "\"", - "singlequote": "'", - "colon": ":", - "semicolon": ";", - "plus": "+", - "minus": "-", - "tilde": "~", - "equal": "=", - "slash": "/" + ",": "comma", + ".": "period", + "/": "slash", + "\\": "backslash", + ";": "semicolon", + "=": "equal", + "`": "backquote", + "]": "bracketright", + "[": "bracketleft", }; const control_keys: string[] = [ @@ -117,7 +115,9 @@ function describe(hotkey: string): Hotkey { k.length || error_invalid_key(hotkey); k = k.toUpperCase(); - data.code = k.length === 1 && k >= 'A' && k <= 'Z' ? `KEY${k}` : k; + data.code = /^[A-Z]$/.test(k) ? `KEY${k}` + : /^[0-9]$/.test(k) ? `DIGIT${k}` + : k; break; } return data; diff --git a/tests/aliases.spec.js b/tests/aliases.spec.js new file mode 100644 index 0000000..f95ae26 --- /dev/null +++ b/tests/aliases.spec.js @@ -0,0 +1,200 @@ +import process from "node:process"; +import { test, expect } from "@playwright/test"; + +test.beforeEach(async ({ page }) => { + await page.goto(`file://${process.cwd()}/tests/assets/generic.html`); + await page.evaluate(() => { + window.hotkeyTriggered = false; + }); +}); + +test("should trigger when using 'Esc' alias for Escape", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Esc", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Escape"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Del' alias for Delete", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Del", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Delete"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ins' alias for Insert", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ins", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("Insert"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'PgUp' alias for PageUp", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "PgUp", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("PageUp"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'PgDn' alias for PageDown", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "PgDn", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.press("PageDown"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+,' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+,", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down(","); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+.' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+.", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("."); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+/' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+/", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("/"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+\\' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+\\", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("\\"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+;' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+;", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down(";"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+=' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+=", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("="); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+`' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+`", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("`"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+]' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+]", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("]"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when using 'Ctrl+[' literal character alias", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+[", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("["); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); diff --git a/tests/generic.html b/tests/assets/generic.html similarity index 89% rename from tests/generic.html rename to tests/assets/generic.html index bebbcd2..0cb583b 100644 --- a/tests/generic.html +++ b/tests/assets/generic.html @@ -16,6 +16,6 @@ - + diff --git a/tests/hotkey.spec.js b/tests/hotkey.spec.js index f3aa8a7..2a60d06 100644 --- a/tests/hotkey.spec.js +++ b/tests/hotkey.spec.js @@ -1,7 +1,8 @@ +import process from "node:process"; import { test, expect } from "@playwright/test"; test.beforeEach(async ({ page }) => { - await page.goto(`file://${process.cwd()}/tests/generic.html`); + await page.goto(`file://${process.cwd()}/tests/assets/generic.html`); await page.evaluate(() => { window.hotkeyTriggered = false; }); @@ -15,7 +16,7 @@ test("should trigger when Ctrl+Up is pressed on document", async ({ page }) => { }); await page.keyboard.down("Control"); - await page.keyboard.press("ArrowUp"); + await page.keyboard.down("ArrowUp"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -29,7 +30,7 @@ test("should trigger when Ctrl+Up is pressed on window", async ({ page }) => { }); await page.keyboard.down("Control"); - await page.keyboard.press("ArrowUp"); + await page.keyboard.down("ArrowUp"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -47,7 +48,7 @@ test("should trigger when Ctrl+K is pressed on a specific element", async ({ pag await page.locator("#text").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -63,7 +64,7 @@ test("should trigger when Shift+K is pressed on an element matching the selector await page.locator("#text").focus(); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -81,7 +82,7 @@ test("should trigger when Ctrl+Alt+Shift+K is pressed", async ({ page }) => { await page.keyboard.down("Control"); await page.keyboard.down("Alt"); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -98,7 +99,7 @@ test("should trigger when Alt+Shift+K is pressed", async ({ page }) => { await page.keyboard.down("Alt"); await page.keyboard.down("Shift"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -114,7 +115,7 @@ test("should trigger when Alt+K is pressed", async ({ page }) => { await page.locator("#text").focus(); await page.keyboard.down("Alt"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -145,7 +146,7 @@ test("should not trigger hotkey when additional modifier keys are pressed", asyn await page.keyboard.down("Shift"); await page.keyboard.down("Alt"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -163,7 +164,7 @@ test("should not trigger on elements with [data-hotkey-ignore]", async ({ page } await page.locator("#ignored").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -181,7 +182,7 @@ test("should not trigger when parent has [data-hotkey-ignore]", async ({ page }) await page.locator("#ignored-via-parent").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(false); @@ -262,7 +263,7 @@ test("should only trigger on trusted events when 'trusted' option is set to true await page.locator("#text").focus(); await page.keyboard.down("Control"); - await page.keyboard.press("K"); + await page.keyboard.down("K"); const triggered = await page.evaluate(() => window.hotkeyTriggered); expect(triggered).toBe(true); @@ -393,9 +394,38 @@ test("should trigger multiple times without 'once' option", async ({ page }) => await page.locator("#text").focus(); + // Press the hotkey twice await page.keyboard.press("Control+k"); await page.keyboard.press("Control+k"); const count = await page.evaluate(() => window.hotkeyCount); expect(count).toBe(2); }); + +test("should trigger when Ctrl+1 is pressed", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Ctrl+1", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Control"); + await page.keyboard.down("1"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +}); + +test("should trigger when Alt+0 is pressed", async ({ page }) => { + await page.evaluate(() => { + window.registerHotkey(document, "Alt+0", () => { + window.hotkeyTriggered = true; + }); + }); + + await page.keyboard.down("Alt"); + await page.keyboard.down("0"); + + const triggered = await page.evaluate(() => window.hotkeyTriggered); + expect(triggered).toBe(true); +});