diff --git a/code/lib/api/src/lib/shortcut.ts b/code/lib/api/src/lib/shortcut.ts index 2a16a9ddfa4..8a3416caca4 100644 --- a/code/lib/api/src/lib/shortcut.ts +++ b/code/lib/api/src/lib/shortcut.ts @@ -1,7 +1,7 @@ import global from 'global'; // The shortcut is our JSON-ifiable representation of a shortcut combination -import type { KeyCollection, Event } from '../modules/shortcuts'; +import type { KeyCollection } from '../modules/shortcuts'; const { navigator } = global; @@ -14,9 +14,15 @@ export const optionOrAltSymbol = () => (isMacLike() ? '⌥' : 'alt'); export const isShortcutTaken = (arr1: string[], arr2: string[]): boolean => JSON.stringify(arr1) === JSON.stringify(arr2); +// A subset of `KeyboardEvent` that's serialized over the channel, see `PreviewWeb.tsx` +export type KeyboardEventLike = Pick< + KeyboardEvent, + 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey' | 'key' | 'code' | 'keyCode' | 'preventDefault' +>; + // Map a keyboard event to a keyboard shortcut // NOTE: if we change the fields on the event that we need, we'll need to update the serialization in core/preview/start.js -export const eventToShortcut = (e: KeyboardEvent): KeyCollection | null => { +export const eventToShortcut = (e: KeyboardEventLike): KeyCollection | null => { // Meta key only doesn't map to a shortcut if (['Meta', 'Alt', 'Control', 'Shift'].includes(e.key)) { return null; @@ -72,7 +78,7 @@ export const shortcutMatchesShortcut = ( }; // Should this keyboard event trigger this keyboard shortcut? -export const eventMatchesShortcut = (e: Event, shortcut: KeyCollection): boolean => { +export const eventMatchesShortcut = (e: KeyboardEventLike, shortcut: KeyCollection): boolean => { return shortcutMatchesShortcut(eventToShortcut(e), shortcut); }; diff --git a/code/lib/api/src/modules/shortcuts.ts b/code/lib/api/src/modules/shortcuts.ts index 626dede58ce..6e891e428f7 100644 --- a/code/lib/api/src/modules/shortcuts.ts +++ b/code/lib/api/src/modules/shortcuts.ts @@ -1,9 +1,10 @@ import global from 'global'; import { PREVIEW_KEYDOWN } from '@storybook/core-events'; +import { DOMElement } from 'react'; import type { ModuleFn } from '../index'; -import { shortcutMatchesShortcut, eventToShortcut } from '../lib/shortcut'; +import { shortcutMatchesShortcut, eventToShortcut, KeyboardEventLike } from '../lib/shortcut'; import { focusableUIElements } from './layout'; const { navigator, document } = global; @@ -31,7 +32,7 @@ export interface SubAPI { setAddonShortcut(addon: string, shortcut: AddonShortcut): Promise; restoreAllDefaultShortcuts(): Promise; restoreDefaultShortcut(action: Action): Promise; - handleKeydownEvent(event: Event): void; + handleKeydownEvent(event: KeyboardEventLike): void; handleShortcutFeature(feature: Action): void; } export type KeyCollection = string[]; @@ -92,21 +93,10 @@ export const defaultShortcuts: Shortcuts = Object.freeze({ }); const addonsShortcuts: AddonShortcuts = {}; -export interface Event extends KeyboardEvent { - target: { - tagName: string; - addEventListener(): void; - removeEventListener(): boolean; - dispatchEvent(event: Event): boolean; - getAttribute(attr: string): string | null; - }; -} -function focusInInput(event: Event) { - return ( - /input|textarea/i.test(event.target.tagName) || - event.target.getAttribute('contenteditable') !== null - ); +function focusInInput(event: KeyboardEvent) { + const target = event.target as Element; + return /input|textarea/i.test(target.tagName) || target.getAttribute('contenteditable') !== null; } export const init: ModuleFn = ({ store, fullAPI }) => { @@ -347,14 +337,14 @@ export const init: ModuleFn = ({ store, fullAPI }) => { const initModule = () => { // Listen for keydown events in the manager - document.addEventListener('keydown', (event: Event) => { + document.addEventListener('keydown', (event: KeyboardEvent) => { if (!focusInInput(event)) { fullAPI.handleKeydownEvent(event); } }); // Also listen to keydown events sent over the channel - fullAPI.on(PREVIEW_KEYDOWN, (data: { event: Event }) => { + fullAPI.on(PREVIEW_KEYDOWN, (data: { event: KeyboardEventLike }) => { fullAPI.handleKeydownEvent(data.event); }); };