Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/hotkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,27 @@ export function registerHotkey(
target = document.querySelector(target) ?? error(`No element found for selector '${target}'`);
}

return listen(target, eventName, function (this: EventTarget, e: KeyboardEvent) {
if (!(options as HotkeyEventListenerOptions)?.trusted || e.isTrusted) {
const require_trusted = (options as HotkeyEventListenerOptions)?.trusted;
const once = (options as HotkeyEventListenerOptions)?.once;

const native_options = options && typeof options === "object"
? { capture: options.capture, passive: options.passive }
: options;

const unregister = listen(target, eventName, function (this: EventTarget, e: KeyboardEvent) {
if (!require_trusted || e.isTrusted) {
if (!(e.target as HTMLElement)?.closest("[data-hotkey-ignore]")) {
if (info.code === e.code.toUpperCase()) {
if (control_keys.every(n => info[n as keyof Hotkey] === e[n as keyof KeyboardEvent])) {
once && unregister();
handler.call(this, e);
}
}
}
}
} as EventListener, options);
} as EventListener, native_options);

return unregister;
}

function describe(hotkey: string): Hotkey {
Expand Down
60 changes: 60 additions & 0 deletions tests/hotkey.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,63 @@ test("should not trigger on untrusted events when 'trusted' option is true", asy
const triggered = await page.evaluate(() => window.hotkeyTriggered);
expect(triggered).toBe(false);
});

test("should not be consumed by non-matching keys when 'once' option is set", async ({ page }) => {
await page.evaluate(() => {
window.hotkeyCount = 0;
const el = document.getElementById("text");

window.registerHotkey(el, "Ctrl + K", () => {
window.hotkeyCount++;
}, "keydown", { once: true });
});

await page.locator("#text").focus();

await page.keyboard.press("A");
await page.keyboard.press("B");

await page.keyboard.press("Control+k");

const count = await page.evaluate(() => window.hotkeyCount);
expect(count).toBe(1);
});

test("should trigger only once with 'once' option", async ({ page }) => {
await page.evaluate(() => {
window.hotkeyCount = 0;
const el = document.getElementById("text");

window.registerHotkey(el, "Ctrl + K", () => {
window.hotkeyCount++;
}, "keydown", { once: true });
});

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(1);
});

test("should trigger multiple times without 'once' option", async ({ page }) => {
await page.evaluate(() => {
window.hotkeyCount = 0;
const el = document.getElementById("text");

window.registerHotkey(el, "Ctrl + K", () => {
window.hotkeyCount++;
});
});

await page.locator("#text").focus();

await page.keyboard.press("Control+k");
await page.keyboard.press("Control+k");

const count = await page.evaluate(() => window.hotkeyCount);
expect(count).toBe(2);
});
Loading