diff --git a/static/app/utils/useHotkeys.spec.tsx b/static/app/utils/useHotkeys.spec.tsx index bb74a213644ee6..28bac13d4c9d6f 100644 --- a/static/app/utils/useHotkeys.spec.tsx +++ b/static/app/utils/useHotkeys.spec.tsx @@ -8,6 +8,7 @@ describe('useHotkeys', () => { function makeKeyEventFixture(keyCode: any, options: any) { return { + key: keyCode, keyCode: getKeyCode(keyCode), preventDefault: jest.fn(), ...options, @@ -170,4 +171,49 @@ describe('useHotkeys', () => { expect(evt.preventDefault).not.toHaveBeenCalled(); expect(callback).toHaveBeenCalled(); }); + + it('handles slash key on different keyboard layouts using key property', () => { + const callback = jest.fn(); + + renderHook(p => useHotkeys(p), { + initialProps: [{match: 'command+/', callback}], + }); + + // Simulate German keyboard where "/" has a different keyCode but evt.key is still "/" + const evt = { + key: '/', + keyCode: 55, // German keyboard: the "7" key + metaKey: true, + shiftKey: false, + ctrlKey: false, + altKey: false, + preventDefault: jest.fn(), + } as any; + events.keydown!(evt); + + expect(callback).toHaveBeenCalled(); + expect(evt.preventDefault).toHaveBeenCalled(); + }); + + it('still works with keyCode fallback for special keys', () => { + const callback = jest.fn(); + + renderHook(p => useHotkeys(p), { + initialProps: [{match: 'escape', callback}], + }); + + // Simulate escape key with both key and keyCode + const evt = { + key: 'Escape', + keyCode: 27, + metaKey: false, + shiftKey: false, + ctrlKey: false, + altKey: false, + preventDefault: jest.fn(), + } as any; + events.keydown!(evt); + + expect(callback).toHaveBeenCalled(); + }); }); diff --git a/static/app/utils/useHotkeys.tsx b/static/app/utils/useHotkeys.tsx index 2a6951bd841850..140c886605edeb 100644 --- a/static/app/utils/useHotkeys.tsx +++ b/static/app/utils/useHotkeys.tsx @@ -5,18 +5,33 @@ import toArray from 'sentry/utils/array/toArray'; import {getKeyCode} from './getKeyCode'; const isKeyPressed = (key: string, evt: KeyboardEvent): boolean => { - const keyCode = getKeyCode(key); - switch (keyCode) { - case getKeyCode('command'): + const normalizedKey = key.toLowerCase(); + + switch (normalizedKey) { + case 'command': + case 'cmd': + case '⌘': return evt.metaKey; - case getKeyCode('shift'): + case 'shift': + case '⇧': return evt.shiftKey; - case getKeyCode('ctrl'): + case 'ctrl': + case 'control': + case '⌃': return evt.ctrlKey; - case getKeyCode('alt'): + case 'alt': + case 'option': + case '⌥': return evt.altKey; - default: + default: { + // Use evt.key for better keyboard layout support (works with German keyboards, etc.) + // Fall back to keyCode for backwards compatibility with special keys + if (evt.key && evt.key.toLowerCase() === normalizedKey) { + return true; + } + const keyCode = getKeyCode(key); return keyCode === evt.keyCode; + } } };