From 95a8323424b244945c49b103a0791545e607172a Mon Sep 17 00:00:00 2001 From: Josh Ferge Date: Fri, 5 Dec 2025 14:45:42 -0800 Subject: [PATCH 1/2] fix(ui): Support keyboard shortcuts on non-US keyboard layouts The keyboard shortcut system was using deprecated keyCode property which doesn't work correctly with different keyboard layouts (like German QWERTZ). On German keyboards, the "/" character requires Shift+7 and has a different keyCode, causing Cmd+/ shortcuts to fail. This change updates the useHotkeys hook to use the modern event.key property for better keyboard layout compatibility, while maintaining backwards compatibility with keyCode for special keys that may not have a key property. The fix ensures that shortcuts like Cmd+/ (used for the Explore panel) now work correctly on German and other non-US keyboards. --- static/app/utils/useHotkeys.spec.tsx | 39 ++++++++++++++++++++++++++++ static/app/utils/useHotkeys.tsx | 29 ++++++++++++++++----- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/static/app/utils/useHotkeys.spec.tsx b/static/app/utils/useHotkeys.spec.tsx index bb74a213644ee6..0371c4ee3ef936 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,42 @@ 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, + preventDefault: jest.fn(), + }; + 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, + preventDefault: jest.fn(), + }; + 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; + } } }; From dda535d0ec0fe3aad965d69427a51d12482a337a Mon Sep 17 00:00:00 2001 From: Josh Ferge Date: Fri, 5 Dec 2025 14:53:45 -0800 Subject: [PATCH 2/2] fix(ui): Add missing event properties for TypeScript compatibility --- static/app/utils/useHotkeys.spec.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/static/app/utils/useHotkeys.spec.tsx b/static/app/utils/useHotkeys.spec.tsx index 0371c4ee3ef936..28bac13d4c9d6f 100644 --- a/static/app/utils/useHotkeys.spec.tsx +++ b/static/app/utils/useHotkeys.spec.tsx @@ -184,8 +184,11 @@ describe('useHotkeys', () => { 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(); @@ -203,8 +206,12 @@ describe('useHotkeys', () => { 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();