refactor: Stop using the deprecated keyCode (#2426)

Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: David Luzar <luzar.david@gmail.com>
This commit is contained in:
Lipis 2020-12-01 23:36:06 +02:00 committed by GitHub
parent 58fcb44de0
commit 014097a97e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 257 additions and 281 deletions

View File

@ -52,11 +52,8 @@ export const actionAlignTop = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) =>
return ( event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_UP,
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_UP
);
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -84,11 +81,8 @@ export const actionAlignBottom = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) =>
return ( event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_DOWN,
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_DOWN
);
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -116,11 +110,8 @@ export const actionAlignLeft = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) =>
return ( event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_LEFT,
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_LEFT
);
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -148,13 +139,8 @@ export const actionAlignRight = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) =>
return ( event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_RIGHT,
event[KEYS.CTRL_OR_CMD] &&
event.shiftKey &&
event.key === KEYS.ARROW_RIGHT
);
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}

View File

@ -5,7 +5,7 @@ import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
import { ToolButton } from "../components/ToolButton"; import { ToolButton } from "../components/ToolButton";
import { t } from "../i18n"; import { t } from "../i18n";
import { getNormalizedZoom } from "../scene"; import { getNormalizedZoom } from "../scene";
import { KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { getShortcutKey } from "../utils"; import { getShortcutKey } from "../utils";
import useIsMobile from "../is-mobile"; import useIsMobile from "../is-mobile";
import { register } from "./register"; import { register } from "./register";
@ -75,16 +75,6 @@ export const actionClearCanvas = register({
const ZOOM_STEP = 0.1; const ZOOM_STEP = 0.1;
const KEY_CODES = {
MINUS: "Minus",
EQUAL: "Equal",
ONE: "Digit1",
ZERO: "Digit0",
NUM_SUBTRACT: "NumpadSubtract",
NUM_ADD: "NumpadAdd",
NUM_ZERO: "Numpad0",
};
export const actionZoomIn = register({ export const actionZoomIn = register({
name: "zoomIn", name: "zoomIn",
perform: (_elements, appState) => { perform: (_elements, appState) => {
@ -112,7 +102,7 @@ export const actionZoomIn = register({
/> />
), ),
keyTest: (event) => keyTest: (event) =>
(event.code === KEY_CODES.EQUAL || event.code === KEY_CODES.NUM_ADD) && (event.code === CODES.EQUAL || event.code === CODES.NUM_ADD) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey), (event[KEYS.CTRL_OR_CMD] || event.shiftKey),
}); });
@ -143,7 +133,7 @@ export const actionZoomOut = register({
/> />
), ),
keyTest: (event) => keyTest: (event) =>
(event.code === KEY_CODES.MINUS || event.code === KEY_CODES.NUM_SUBTRACT) && (event.code === CODES.MINUS || event.code === CODES.NUM_SUBTRACT) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey), (event[KEYS.CTRL_OR_CMD] || event.shiftKey),
}); });
@ -173,7 +163,7 @@ export const actionResetZoom = register({
/> />
), ),
keyTest: (event) => keyTest: (event) =>
(event.code === KEY_CODES.ZERO || event.code === KEY_CODES.NUM_ZERO) && (event.code === CODES.ZERO || event.code === CODES.NUM_ZERO) &&
(event[KEYS.CTRL_OR_CMD] || event.shiftKey), (event[KEYS.CTRL_OR_CMD] || event.shiftKey),
}); });
@ -229,7 +219,7 @@ export const actionZoomToFit = register({
}; };
}, },
keyTest: (event) => keyTest: (event) =>
event.code === KEY_CODES.ONE && event.code === CODES.ONE &&
event.shiftKey && event.shiftKey &&
!event.altKey && !event.altKey &&
!event[KEYS.CTRL_OR_CMD], !event[KEYS.CTRL_OR_CMD],

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { KEYS } from "../keys"; import { CODES } from "../keys";
import { t } from "../i18n"; import { t } from "../i18n";
import { register } from "./register"; import { register } from "./register";
import { import {
@ -48,9 +48,7 @@ export const distributeHorizontally = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) => event.altKey && event.code === CODES.H,
return event.altKey && event.keyCode === KEYS.H_KEY_CODE;
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -78,9 +76,7 @@ export const distributeVertically = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) => event.altKey && event.code === CODES.V,
return event.altKey && event.keyCode === KEYS.V_KEY_CODE;
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}

View File

@ -63,7 +63,7 @@ export const actionDuplicateSelection = register({
}; };
}, },
contextItemLabel: "labels.duplicateSelection", contextItemLabel: "labels.duplicateSelection",
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === "d", keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.D,
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
type="button" type="button"

View File

@ -96,9 +96,8 @@ export const actionSaveScene = register({
return { commitToHistory: false }; return { commitToHistory: false };
} }
}, },
keyTest: (event) => { keyTest: (event) =>
return event.key === "s" && event[KEYS.CTRL_OR_CMD] && !event.shiftKey; event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
},
PanelComponent: ({ updateData }) => ( PanelComponent: ({ updateData }) => (
<ToolButton <ToolButton
type="button" type="button"
@ -127,9 +126,8 @@ export const actionSaveAsScene = register({
return { commitToHistory: false }; return { commitToHistory: false };
} }
}, },
keyTest: (event) => { keyTest: (event) =>
return event.key === "s" && event.shiftKey && event[KEYS.CTRL_OR_CMD]; event.key === KEYS.S && event.shiftKey && event[KEYS.CTRL_OR_CMD],
},
PanelComponent: ({ updateData }) => ( PanelComponent: ({ updateData }) => (
<ToolButton <ToolButton
type="button" type="button"

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { t } from "../i18n"; import { t } from "../i18n";
import { getShortcutKey } from "../utils"; import { getShortcutKey } from "../utils";
import { register } from "./register"; import { register } from "./register";
@ -129,13 +129,8 @@ export const actionGroup = register({
contextItemLabel: "labels.group", contextItemLabel: "labels.group",
contextItemPredicate: (elements, appState) => contextItemPredicate: (elements, appState) =>
enableActionGroup(elements, appState), enableActionGroup(elements, appState),
keyTest: (event) => { keyTest: (event) =>
return ( !event.shiftKey && event[KEYS.CTRL_OR_CMD] && event.code === CODES.G,
!event.shiftKey &&
event[KEYS.CTRL_OR_CMD] &&
event.keyCode === KEYS.G_KEY_CODE
);
},
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -177,13 +172,8 @@ export const actionUngroup = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => { keyTest: (event) =>
return ( event.shiftKey && event[KEYS.CTRL_OR_CMD] && event.code === CODES.G,
event.shiftKey &&
event[KEYS.CTRL_OR_CMD] &&
event.keyCode === KEYS.G_KEY_CODE
);
},
contextMenuOrder: 5, contextMenuOrder: 5,
contextItemLabel: "labels.ungroup", contextItemLabel: "labels.ungroup",
contextItemPredicate: (elements, appState) => contextItemPredicate: (elements, appState) =>

View File

@ -5,7 +5,7 @@ import { t } from "../i18n";
import { showSelectedShapeActions, getNonDeletedElements } from "../element"; import { showSelectedShapeActions, getNonDeletedElements } from "../element";
import { register } from "./register"; import { register } from "./register";
import { allowFullScreen, exitFullScreen, isFullScreen } from "../utils"; import { allowFullScreen, exitFullScreen, isFullScreen } from "../utils";
import { KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { HelpIcon } from "../components/HelpIcon"; import { HelpIcon } from "../components/HelpIcon";
export const actionToggleCanvasMenu = register({ export const actionToggleCanvasMenu = register({
@ -65,7 +65,7 @@ export const actionFullScreen = register({
commitToHistory: false, commitToHistory: false,
}; };
}, },
keyTest: (event) => event.keyCode === KEYS.F_KEY_CODE, keyTest: (event) => event.code === CODES.F,
}); });
export const actionShortcuts = register({ export const actionShortcuts = register({

View File

@ -27,5 +27,5 @@ export const actionSelectAll = register({
}; };
}, },
contextItemLabel: "labels.selectAll", contextItemLabel: "labels.selectAll",
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === "a", keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.A,
}); });

View File

@ -3,7 +3,7 @@ import {
isExcalidrawElement, isExcalidrawElement,
redrawTextBoundingBox, redrawTextBoundingBox,
} from "../element"; } from "../element";
import { KEYS } from "../keys"; import { CODES, KEYS } from "../keys";
import { register } from "./register"; import { register } from "./register";
import { mutateElement, newElementWith } from "../element/mutateElement"; import { mutateElement, newElementWith } from "../element/mutateElement";
import { import {
@ -28,9 +28,7 @@ export const actionCopyStyles = register({
}, },
contextItemLabel: "labels.copyStyles", contextItemLabel: "labels.copyStyles",
keyTest: (event) => keyTest: (event) =>
event[KEYS.CTRL_OR_CMD] && event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.C,
event.altKey &&
event.keyCode === KEYS.C_KEY_CODE,
contextMenuOrder: 0, contextMenuOrder: 0,
}); });
@ -70,8 +68,6 @@ export const actionPasteStyles = register({
}, },
contextItemLabel: "labels.pasteStyles", contextItemLabel: "labels.pasteStyles",
keyTest: (event) => keyTest: (event) =>
event[KEYS.CTRL_OR_CMD] && event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.V,
event.altKey &&
event.keyCode === KEYS.V_KEY_CODE,
contextMenuOrder: 1, contextMenuOrder: 1,
}); });

View File

@ -5,7 +5,7 @@ import {
moveAllLeft, moveAllLeft,
moveAllRight, moveAllRight,
} from "../zindex"; } from "../zindex";
import { KEYS, isDarwin } from "../keys"; import { KEYS, isDarwin, CODES } from "../keys";
import { t } from "../i18n"; import { t } from "../i18n";
import { getShortcutKey } from "../utils"; import { getShortcutKey } from "../utils";
import { register } from "./register"; import { register } from "./register";
@ -28,7 +28,9 @@ export const actionSendBackward = register({
contextItemLabel: "labels.sendBackward", contextItemLabel: "labels.sendBackward",
keyPriority: 40, keyPriority: 40,
keyTest: (event) => keyTest: (event) =>
event[KEYS.CTRL_OR_CMD] && !event.shiftKey && event.code === "BracketLeft", event[KEYS.CTRL_OR_CMD] &&
!event.shiftKey &&
event.code === CODES.BRACKET_LEFT,
PanelComponent: ({ updateData, appState }) => ( PanelComponent: ({ updateData, appState }) => (
<button <button
type="button" type="button"
@ -53,7 +55,9 @@ export const actionBringForward = register({
contextItemLabel: "labels.bringForward", contextItemLabel: "labels.bringForward",
keyPriority: 40, keyPriority: 40,
keyTest: (event) => keyTest: (event) =>
event[KEYS.CTRL_OR_CMD] && !event.shiftKey && event.code === "BracketRight", event[KEYS.CTRL_OR_CMD] &&
!event.shiftKey &&
event.code === CODES.BRACKET_RIGHT,
PanelComponent: ({ updateData, appState }) => ( PanelComponent: ({ updateData, appState }) => (
<button <button
type="button" type="button"
@ -76,13 +80,14 @@ export const actionSendToBack = register({
}; };
}, },
contextItemLabel: "labels.sendToBack", contextItemLabel: "labels.sendToBack",
keyTest: (event) => { keyTest: (event) =>
return isDarwin isDarwin
? event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === "BracketLeft" ? event[KEYS.CTRL_OR_CMD] &&
event.altKey &&
event.code === CODES.BRACKET_LEFT
: event[KEYS.CTRL_OR_CMD] && : event[KEYS.CTRL_OR_CMD] &&
event.shiftKey && event.shiftKey &&
event.code === "BracketLeft"; event.code === CODES.BRACKET_LEFT,
},
PanelComponent: ({ updateData, appState }) => ( PanelComponent: ({ updateData, appState }) => (
<button <button
type="button" type="button"
@ -109,13 +114,14 @@ export const actionBringToFront = register({
}; };
}, },
contextItemLabel: "labels.bringToFront", contextItemLabel: "labels.bringToFront",
keyTest: (event) => { keyTest: (event) =>
return isDarwin isDarwin
? event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === "BracketRight" ? event[KEYS.CTRL_OR_CMD] &&
event.altKey &&
event.code === CODES.BRACKET_RIGHT
: event[KEYS.CTRL_OR_CMD] && : event[KEYS.CTRL_OR_CMD] &&
event.shiftKey && event.shiftKey &&
event.code === "BracketRight"; event.code === CODES.BRACKET_RIGHT,
},
PanelComponent: ({ updateData, appState }) => ( PanelComponent: ({ updateData, appState }) => (
<button <button
type="button" type="button"

View File

@ -156,8 +156,7 @@ export const ShapesSwitcher = ({
{SHAPES.map(({ value, icon, key }, index) => { {SHAPES.map(({ value, icon, key }, index) => {
const label = t(`toolBar.${value}`); const label = t(`toolBar.${value}`);
const letter = typeof key === "string" ? key : key[0]; const letter = typeof key === "string" ? key : key[0];
const letterShortcut = /[a-z]/.test(letter) ? letter : `Shift+${letter}`; const shortcut = `${capitalizeString(letter)} ${t(
const shortcut = `${capitalizeString(letterShortcut)} ${t(
"shortcutsDialog.or", "shortcutsDialog.or",
)} ${index + 1}`; )} ${index + 1}`;
return ( return (
@ -171,7 +170,7 @@ export const ShapesSwitcher = ({
title={`${capitalizeString(label)}${shortcut}`} title={`${capitalizeString(label)}${shortcut}`}
keyBindingLabel={`${index + 1}`} keyBindingLabel={`${index + 1}`}
aria-label={capitalizeString(label)} aria-label={capitalizeString(label)}
aria-keyshortcuts={`${key} ${index + 1}`} aria-keyshortcuts={shortcut}
data-testid={value} data-testid={value}
onChange={() => { onChange={() => {
setAppState({ setAppState({

View File

@ -82,6 +82,7 @@ import {
getResizeCenterPointKey, getResizeCenterPointKey,
getResizeWithSidesSameLengthKey, getResizeWithSidesSameLengthKey,
getRotateWithDiscreteAngleKey, getRotateWithDiscreteAngleKey,
CODES,
} from "../keys"; } from "../keys";
import { findShapeByKey } from "../shapes"; import { findShapeByKey } from "../shapes";
@ -1534,11 +1535,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}); });
} }
// ensures we don't prevent devTools select-element feature
if (event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === "C") {
return;
}
if ( if (
(isWritableElement(event.target) && event.key !== KEYS.ESCAPE) || (isWritableElement(event.target) && event.key !== KEYS.ESCAPE) ||
// case: using arrows to move between buttons // case: using arrows to move between buttons
@ -1553,22 +1549,18 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}); });
} }
if ( if (!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.Z) {
!event[KEYS.CTRL_OR_CMD] &&
event.altKey &&
event.keyCode === KEYS.Z_KEY_CODE
) {
this.toggleZenMode(); this.toggleZenMode();
} }
if (event[KEYS.CTRL_OR_CMD] && event.keyCode === KEYS.GRID_KEY_CODE) { if (event[KEYS.CTRL_OR_CMD] && event.code === CODES.QUOTE) {
this.toggleGridMode(); this.toggleGridMode();
} }
if (event[KEYS.CTRL_OR_CMD]) { if (event[KEYS.CTRL_OR_CMD]) {
this.setState({ isBindingEnabled: false }); this.setState({ isBindingEnabled: false });
} }
if (event.code === "KeyC" && event.altKey && event.shiftKey) { if (event.code === CODES.C && event.altKey && event.shiftKey) {
this.copyToClipboardAsPng(); this.copyToClipboardAsPng();
event.preventDefault(); event.preventDefault();
return; return;
@ -1578,7 +1570,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return; return;
} }
if (event.code === "Digit9") { if (event.code === CODES.NINE) {
this.setState({ isLibraryOpen: !this.state.isLibraryOpen }); this.setState({ isLibraryOpen: !this.state.isLibraryOpen });
} }
@ -1664,7 +1656,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
const shape = findShapeByKey(event.key); const shape = findShapeByKey(event.key);
if (shape) { if (shape) {
this.selectShapeTool(shape); this.selectShapeTool(shape);
} else if (event.key === "q") { } else if (event.key === KEYS.Q) {
this.toggleLock(); this.toggleLock();
} }
} }

View File

@ -2,7 +2,7 @@ import React from "react";
import { Popover } from "./Popover"; import { Popover } from "./Popover";
import "./ColorPicker.scss"; import "./ColorPicker.scss";
import { KEYS } from "../keys"; import { isArrowKey, KEYS } from "../keys";
import { t, getLanguage } from "../i18n"; import { t, getLanguage } from "../i18n";
import { isWritableElement } from "../utils"; import { isWritableElement } from "../utils";
import colors from "../colors"; import colors from "../colors";
@ -59,8 +59,7 @@ const Picker = ({
const colorInput = React.useRef<HTMLInputElement>(); const colorInput = React.useRef<HTMLInputElement>();
React.useEffect(() => { React.useEffect(() => {
// After the component is first mounted // After the component is first mounted focus on first input
// focus on first input
if (activeItem.current) { if (activeItem.current) {
activeItem.current.focus(); activeItem.current.focus();
} else if (colorInput.current) { } else if (colorInput.current) {
@ -82,12 +81,7 @@ const Picker = ({
firstItem.current?.focus(); firstItem.current?.focus();
event.preventDefault(); event.preventDefault();
} }
} else if ( } else if (isArrowKey(event.key)) {
event.key === KEYS.ARROW_RIGHT ||
event.key === KEYS.ARROW_LEFT ||
event.key === KEYS.ARROW_UP ||
event.key === KEYS.ARROW_DOWN
) {
const { activeElement } = document; const { activeElement } = document;
const isRTL = getLanguage().rtl; const isRTL = getLanguage().rtl;
const index = Array.prototype.indexOf.call( const index = Array.prototype.indexOf.call(

View File

@ -9,6 +9,7 @@ import "./RoomDialog.scss";
import { copyTextToSystemClipboard } from "../clipboard"; import { copyTextToSystemClipboard } from "../clipboard";
import { Dialog } from "./Dialog"; import { Dialog } from "./Dialog";
import { AppState } from "../types"; import { AppState } from "../types";
import { KEYS } from "../keys";
const RoomModal = ({ const RoomModal = ({
activeRoomLink, activeRoomLink,
@ -94,7 +95,9 @@ const RoomModal = ({
value={username || ""} value={username || ""}
className="RoomDialog-username TextInput" className="RoomDialog-username TextInput"
onChange={(event) => onUsernameChange(event.target.value)} onChange={(event) => onUsernameChange(event.target.value)}
onKeyPress={(event) => event.key === "Enter" && onPressingEnter()} onKeyPress={(event) =>
event.key === KEYS.ENTER && onPressingEnter()
}
/> />
</div> </div>
<p> <p>

View File

@ -1,38 +1,63 @@
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform); export const isDarwin = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
export const CODES = {
EQUAL: "Equal",
MINUS: "Minus",
NUM_ADD: "NumpadAdd",
NUM_SUBTRACT: "NumpadSubtract",
NUM_ZERO: "Numpad0",
BRACKET_RIGHT: "BracketRight",
BRACKET_LEFT: "BracketLeft",
ONE: "Digit1",
NINE: "Digit9",
QUOTE: "Quote",
ZERO: "Digit0",
C: "KeyC",
G: "KeyG",
F: "KeyF",
H: "KeyH",
V: "KeyV",
Z: "KeyZ",
} as const;
export const KEYS = { export const KEYS = {
ARROW_DOWN: "ArrowDown",
ARROW_LEFT: "ArrowLeft", ARROW_LEFT: "ArrowLeft",
ARROW_RIGHT: "ArrowRight", ARROW_RIGHT: "ArrowRight",
ARROW_DOWN: "ArrowDown",
ARROW_UP: "ArrowUp", ARROW_UP: "ArrowUp",
ENTER: "Enter",
ESCAPE: "Escape",
DELETE: "Delete",
BACKSPACE: "Backspace", BACKSPACE: "Backspace",
CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey", CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",
TAB: "Tab", DELETE: "Delete",
SPACE: " ", ENTER: "Enter",
ESCAPE: "Escape",
QUESTION_MARK: "?", QUESTION_MARK: "?",
F_KEY_CODE: 70, SPACE: " ",
ALT_KEY_CODE: 18, TAB: "Tab",
Z_KEY_CODE: 90,
GRID_KEY_CODE: 222, A: "a",
H_KEY_CODE: 72, D: "d",
G_KEY_CODE: 71, E: "e",
C_KEY_CODE: 67, L: "l",
V_KEY_CODE: 86, P: "p",
Q: "q",
R: "r",
S: "s",
T: "t",
V: "v",
X: "x",
Z: "z",
} as const; } as const;
export type Key = keyof typeof KEYS; export type Key = keyof typeof KEYS;
export const isArrowKey = (keyCode: string) => export const isArrowKey = (key: string) =>
keyCode === KEYS.ARROW_LEFT || key === KEYS.ARROW_LEFT ||
keyCode === KEYS.ARROW_RIGHT || key === KEYS.ARROW_RIGHT ||
keyCode === KEYS.ARROW_DOWN || key === KEYS.ARROW_DOWN ||
keyCode === KEYS.ARROW_UP; key === KEYS.ARROW_UP;
export const getResizeCenterPointKey = (event: MouseEvent | KeyboardEvent) => export const getResizeCenterPointKey = (event: MouseEvent | KeyboardEvent) =>
event.altKey || event.which === KEYS.ALT_KEY_CODE; event.altKey;
export const getResizeWithSidesSameLengthKey = (event: MouseEvent) => export const getResizeWithSidesSameLengthKey = (event: MouseEvent) =>
event.shiftKey; event.shiftKey;

View File

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { KEYS } from "./keys";
// We inline font-awesome icons in order to save on js size rather than including the font awesome react library // We inline font-awesome icons in order to save on js size rather than including the font awesome react library
export const SHAPES = [ export const SHAPES = [
@ -10,7 +11,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "selection", value: "selection",
key: ["v", "s"], key: [KEYS.V, KEYS.S],
}, },
{ {
icon: ( icon: (
@ -20,7 +21,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "rectangle", value: "rectangle",
key: "r", key: KEYS.R,
}, },
{ {
icon: ( icon: (
@ -30,7 +31,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "diamond", value: "diamond",
key: "d", key: KEYS.D,
}, },
{ {
icon: ( icon: (
@ -40,7 +41,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "ellipse", value: "ellipse",
key: "e", key: KEYS.E,
}, },
{ {
icon: ( icon: (
@ -50,7 +51,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "arrow", value: "arrow",
key: "a", key: KEYS.A,
}, },
{ {
icon: ( icon: (
@ -67,7 +68,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "line", value: "line",
key: ["p", "l"], key: [KEYS.P, KEYS.L],
}, },
{ {
icon: ( icon: (
@ -80,7 +81,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "draw", value: "draw",
key: ["P", "x"], key: KEYS.X,
}, },
{ {
icon: ( icon: (
@ -90,7 +91,7 @@ export const SHAPES = [
</svg> </svg>
), ),
value: "text", value: "text",
key: "t", key: KEYS.T,
}, },
] as const; ] as const;

View File

@ -9393,7 +9393,7 @@ exports[`regression tests given selected element A with lower z-index than unsel
exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `17`; exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `17`;
exports[`regression tests hotkey 2 selects rectangle tool: [end of test] appState 1`] = ` exports[`regression tests key 2 selects rectangle tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -9465,7 +9465,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 2 selects rectangle tool: [end of test] element 0 1`] = ` exports[`regression tests key 2 selects rectangle tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -9491,7 +9491,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 2 selects rectangle tool: [end of test] history 1`] = ` exports[`regression tests key 2 selects rectangle tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -9546,11 +9546,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 2 selects rectangle tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 2 selects rectangle tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 2 selects rectangle tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key 2 selects rectangle tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey 3 selects diamond tool: [end of test] appState 1`] = ` exports[`regression tests key 3 selects diamond tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -9622,7 +9622,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 3 selects diamond tool: [end of test] element 0 1`] = ` exports[`regression tests key 3 selects diamond tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -9648,7 +9648,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 3 selects diamond tool: [end of test] history 1`] = ` exports[`regression tests key 3 selects diamond tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -9703,11 +9703,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 3 selects diamond tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 3 selects diamond tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 3 selects diamond tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key 3 selects diamond tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey 4 selects ellipse tool: [end of test] appState 1`] = ` exports[`regression tests key 4 selects ellipse tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -9779,7 +9779,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 4 selects ellipse tool: [end of test] element 0 1`] = ` exports[`regression tests key 4 selects ellipse tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -9805,7 +9805,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 4 selects ellipse tool: [end of test] history 1`] = ` exports[`regression tests key 4 selects ellipse tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -9860,11 +9860,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 4 selects ellipse tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 4 selects ellipse tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 4 selects ellipse tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key 4 selects ellipse tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey 5 selects arrow tool: [end of test] appState 1`] = ` exports[`regression tests key 5 selects arrow tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -9936,7 +9936,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 5 selects arrow tool: [end of test] element 0 1`] = ` exports[`regression tests key 5 selects arrow tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -9975,7 +9975,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 5 selects arrow tool: [end of test] history 1`] = ` exports[`regression tests key 5 selects arrow tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10043,11 +10043,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 5 selects arrow tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 5 selects arrow tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 5 selects arrow tool: [end of test] number of renders 1`] = `7`; exports[`regression tests key 5 selects arrow tool: [end of test] number of renders 1`] = `7`;
exports[`regression tests hotkey 6 selects line tool: [end of test] appState 1`] = ` exports[`regression tests key 6 selects line tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10119,7 +10119,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 6 selects line tool: [end of test] element 0 1`] = ` exports[`regression tests key 6 selects line tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -10158,7 +10158,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 6 selects line tool: [end of test] history 1`] = ` exports[`regression tests key 6 selects line tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10226,11 +10226,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 6 selects line tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 6 selects line tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 6 selects line tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key 6 selects line tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey 7 selects draw tool: [end of test] appState 1`] = ` exports[`regression tests key 7 selects draw tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10302,7 +10302,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 7 selects draw tool: [end of test] element 0 1`] = ` exports[`regression tests key 7 selects draw tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -10341,7 +10341,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey 7 selects draw tool: [end of test] history 1`] = ` exports[`regression tests key 7 selects draw tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10409,11 +10409,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey 7 selects draw tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key 7 selects draw tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey 7 selects draw tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key 7 selects draw tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey a selects arrow tool: [end of test] appState 1`] = ` exports[`regression tests key a selects arrow tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10485,7 +10485,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey a selects arrow tool: [end of test] element 0 1`] = ` exports[`regression tests key a selects arrow tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -10524,7 +10524,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey a selects arrow tool: [end of test] history 1`] = ` exports[`regression tests key a selects arrow tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10592,11 +10592,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey a selects arrow tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key a selects arrow tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey a selects arrow tool: [end of test] number of renders 1`] = `7`; exports[`regression tests key a selects arrow tool: [end of test] number of renders 1`] = `7`;
exports[`regression tests hotkey d selects diamond tool: [end of test] appState 1`] = ` exports[`regression tests key d selects diamond tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10668,7 +10668,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey d selects diamond tool: [end of test] element 0 1`] = ` exports[`regression tests key d selects diamond tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -10694,7 +10694,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey d selects diamond tool: [end of test] history 1`] = ` exports[`regression tests key d selects diamond tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10749,11 +10749,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey d selects diamond tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key d selects diamond tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey d selects diamond tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key d selects diamond tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey e selects ellipse tool: [end of test] appState 1`] = ` exports[`regression tests key e selects ellipse tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10825,7 +10825,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey e selects ellipse tool: [end of test] element 0 1`] = ` exports[`regression tests key e selects ellipse tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -10851,7 +10851,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey e selects ellipse tool: [end of test] history 1`] = ` exports[`regression tests key e selects ellipse tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -10906,11 +10906,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey e selects ellipse tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key e selects ellipse tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey e selects ellipse tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key e selects ellipse tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey l selects line tool: [end of test] appState 1`] = ` exports[`regression tests key l selects line tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -10982,7 +10982,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey l selects line tool: [end of test] element 0 1`] = ` exports[`regression tests key l selects line tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -11021,7 +11021,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey l selects line tool: [end of test] history 1`] = ` exports[`regression tests key l selects line tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -11089,11 +11089,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey l selects line tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key l selects line tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey l selects line tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key l selects line tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey r selects rectangle tool: [end of test] appState 1`] = ` exports[`regression tests key r selects rectangle tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -11165,7 +11165,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey r selects rectangle tool: [end of test] element 0 1`] = ` exports[`regression tests key r selects rectangle tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -11191,7 +11191,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey r selects rectangle tool: [end of test] history 1`] = ` exports[`regression tests key r selects rectangle tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -11246,11 +11246,11 @@ Object {
} }
`; `;
exports[`regression tests hotkey r selects rectangle tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key r selects rectangle tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey r selects rectangle tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key r selects rectangle tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests hotkey x selects draw tool: [end of test] appState 1`] = ` exports[`regression tests key x selects draw tool: [end of test] appState 1`] = `
Object { Object {
"appearance": "light", "appearance": "light",
"collaborators": Map {}, "collaborators": Map {},
@ -11322,7 +11322,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey x selects draw tool: [end of test] element 0 1`] = ` exports[`regression tests key x selects draw tool: [end of test] element 0 1`] = `
Object { Object {
"angle": 0, "angle": 0,
"backgroundColor": "transparent", "backgroundColor": "transparent",
@ -11361,7 +11361,7 @@ Object {
} }
`; `;
exports[`regression tests hotkey x selects draw tool: [end of test] history 1`] = ` exports[`regression tests key x selects draw tool: [end of test] history 1`] = `
Object { Object {
"recording": false, "recording": false,
"redoStack": Array [], "redoStack": Array [],
@ -11429,9 +11429,9 @@ Object {
} }
`; `;
exports[`regression tests hotkey x selects draw tool: [end of test] number of elements 1`] = `1`; exports[`regression tests key x selects draw tool: [end of test] number of elements 1`] = `1`;
exports[`regression tests hotkey x selects draw tool: [end of test] number of renders 1`] = `6`; exports[`regression tests key x selects draw tool: [end of test] number of renders 1`] = `6`;
exports[`regression tests make a group and duplicate it: [end of test] appState 1`] = ` exports[`regression tests make a group and duplicate it: [end of test] appState 1`] = `
Object { Object {

View File

@ -4,6 +4,7 @@ import App from "../components/App";
import { UI, Pointer, Keyboard } from "./helpers/ui"; import { UI, Pointer, Keyboard } from "./helpers/ui";
import { getTransformHandles } from "../element/transformHandles"; import { getTransformHandles } from "../element/transformHandles";
import { API } from "./helpers/api"; import { API } from "./helpers/api";
import { KEYS } from "../keys";
const { h } = window; const { h } = window;
@ -97,10 +98,10 @@ describe("element binding", () => {
expect(arrow.endBinding).toBe(null); expect(arrow.endBinding).toBe(null);
expect(API.getSelectedElement().type).toBe("arrow"); expect(API.getSelectedElement().type).toBe("arrow");
Keyboard.hotkeyPress("ARROW_RIGHT"); Keyboard.keyPress(KEYS.ARROW_RIGHT);
expect(arrow.endBinding?.elementId).toBe(rectangle.id); expect(arrow.endBinding?.elementId).toBe(rectangle.id);
Keyboard.hotkeyPress("ARROW_LEFT"); Keyboard.keyPress(KEYS.ARROW_LEFT);
expect(arrow.endBinding).toBe(null); expect(arrow.endBinding).toBe(null);
}); });
}); });

View File

@ -1,11 +1,11 @@
import { ToolName } from "../queries/toolQueries";
import { fireEvent, GlobalTestState } from "../test-utils";
import { KEYS, Key } from "../../keys";
import { import {
ExcalidrawElement, ExcalidrawElement,
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawTextElement, ExcalidrawTextElement,
} from "../../element/types"; } from "../../element/types";
import { CODES } from "../../keys";
import { ToolName } from "../queries/toolQueries";
import { fireEvent, GlobalTestState } from "../test-utils";
import { API } from "./api"; import { API } from "./api";
const { h } = window; const { h } = window;
@ -36,30 +36,12 @@ export class Keyboard {
} }
}; };
static hotkeyDown = (hotkey: Key) => {
const key = KEYS[hotkey];
if (typeof key !== "string") {
throw new Error("must provide a hotkey, not a key code");
}
Keyboard.keyDown(key);
};
static hotkeyUp = (hotkey: Key) => {
const key = KEYS[hotkey];
if (typeof key !== "string") {
throw new Error("must provide a hotkey, not a key code");
}
Keyboard.keyUp(key);
};
static keyDown = (key: string) => { static keyDown = (key: string) => {
fireEvent.keyDown(document, { fireEvent.keyDown(document, {
key, key,
ctrlKey, ctrlKey,
shiftKey, shiftKey,
altKey, altKey,
keyCode: key.toUpperCase().charCodeAt(0),
which: key.toUpperCase().charCodeAt(0),
}); });
}; };
@ -69,20 +51,36 @@ export class Keyboard {
ctrlKey, ctrlKey,
shiftKey, shiftKey,
altKey, altKey,
keyCode: key.toUpperCase().charCodeAt(0),
which: key.toUpperCase().charCodeAt(0),
}); });
}; };
static hotkeyPress = (key: Key) => {
Keyboard.hotkeyDown(key);
Keyboard.hotkeyUp(key);
};
static keyPress = (key: string) => { static keyPress = (key: string) => {
Keyboard.keyDown(key); Keyboard.keyDown(key);
Keyboard.keyUp(key); Keyboard.keyUp(key);
}; };
static codeDown = (code: string) => {
fireEvent.keyDown(document, {
code,
ctrlKey,
shiftKey,
altKey,
});
};
static codeUp = (code: string) => {
fireEvent.keyUp(document, {
code,
ctrlKey,
shiftKey,
altKey,
});
};
static codePress = (code: string) => {
Keyboard.codeDown(code);
Keyboard.codeUp(code);
};
} }
export class Pointer { export class Pointer {
@ -209,7 +207,7 @@ export class UI {
static group(elements: ExcalidrawElement[]) { static group(elements: ExcalidrawElement[]) {
mouse.select(elements); mouse.select(elements);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
} }
} }

View File

@ -11,6 +11,7 @@ import {
ExcalidrawRectangleElement, ExcalidrawRectangleElement,
} from "../element/types"; } from "../element/types";
import { UI, Pointer, Keyboard } from "./helpers/ui"; import { UI, Pointer, Keyboard } from "./helpers/ui";
import { KEYS } from "../keys";
// Unmount ReactDOM from root // Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!); ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
@ -88,9 +89,9 @@ describe("move element", () => {
renderScene.mockClear(); renderScene.mockClear();
// Move selected rectangle // Move selected rectangle
Keyboard.keyDown("ArrowRight"); Keyboard.keyDown(KEYS.ARROW_RIGHT);
Keyboard.keyDown("ArrowDown"); Keyboard.keyDown(KEYS.ARROW_DOWN);
Keyboard.keyDown("ArrowDown"); Keyboard.keyDown(KEYS.ARROW_DOWN);
// Check that the arrow size has been changed according to moving the rectangle // Check that the arrow size has been changed according to moving the rectangle
expect(renderScene).toHaveBeenCalledTimes(3); expect(renderScene).toHaveBeenCalledTimes(3);

View File

@ -18,6 +18,7 @@ import { queryByText } from "@testing-library/react";
import { copiedStyles } from "../actions/actionStyles"; import { copiedStyles } from "../actions/actionStyles";
import { UI, Pointer, Keyboard } from "./helpers/ui"; import { UI, Pointer, Keyboard } from "./helpers/ui";
import { API } from "./helpers/api"; import { API } from "./helpers/api";
import { CODES, KEYS } from "../keys";
const { h } = window; const { h } = window;
@ -129,13 +130,13 @@ describe("regression tests", () => {
mouse.click(40, -10); mouse.click(40, -10);
mouse.click(50, 10); mouse.click(50, 10);
mouse.click(30, 10); mouse.click(30, 10);
Keyboard.hotkeyPress("ENTER"); Keyboard.keyPress(KEYS.ENTER);
UI.clickTool("line"); UI.clickTool("line");
mouse.click(40, -20); mouse.click(40, -20);
mouse.click(50, 10); mouse.click(50, 10);
mouse.click(30, 10); mouse.click(30, 10);
Keyboard.hotkeyPress("ENTER"); Keyboard.keyPress(KEYS.ENTER);
UI.clickTool("draw"); UI.clickTool("draw");
mouse.down(40, -20); mouse.down(40, -20);
@ -172,15 +173,15 @@ describe("regression tests", () => {
}); });
for (const [keys, shape] of [ for (const [keys, shape] of [
["2r", "rectangle"], [`2${KEYS.R}`, "rectangle"],
["3d", "diamond"], [`3${KEYS.D}`, "diamond"],
["4e", "ellipse"], [`4${KEYS.E}`, "ellipse"],
["5a", "arrow"], [`5${KEYS.A}`, "arrow"],
["6l", "line"], [`6${KEYS.L}`, "line"],
["7x", "draw"], [`7${KEYS.X}`, "draw"],
] as [string, ExcalidrawElement["type"]][]) { ] as [string, ExcalidrawElement["type"]][]) {
for (const key of keys) { for (const key of keys) {
it(`hotkey ${key} selects ${shape} tool`, () => { it(`key ${key} selects ${shape} tool`, () => {
Keyboard.keyPress(key); Keyboard.keyPress(key);
mouse.down(10, 10); mouse.down(10, 10);
@ -190,7 +191,6 @@ describe("regression tests", () => {
}); });
} }
} }
it("change the properties of a shape", () => { it("change the properties of a shape", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(10, 10);
@ -397,10 +397,10 @@ describe("regression tests", () => {
it("spacebar + drag scrolls the canvas", () => { it("spacebar + drag scrolls the canvas", () => {
const { scrollX: startScrollX, scrollY: startScrollY } = h.state; const { scrollX: startScrollX, scrollY: startScrollY } = h.state;
Keyboard.hotkeyDown("SPACE"); Keyboard.keyDown(KEYS.SPACE);
mouse.down(50, 50); mouse.down(50, 50);
mouse.up(60, 60); mouse.up(60, 60);
Keyboard.hotkeyUp("SPACE"); Keyboard.keyUp(KEYS.SPACE);
const { scrollX, scrollY } = h.state; const { scrollX, scrollY } = h.state;
expect(scrollX).not.toEqual(startScrollX); expect(scrollX).not.toEqual(startScrollX);
expect(scrollY).not.toEqual(startScrollY); expect(scrollY).not.toEqual(startScrollY);
@ -410,12 +410,12 @@ describe("regression tests", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
mouse.down(10, 10); mouse.down(10, 10);
mouse.up(10, 10); mouse.up(10, 10);
Keyboard.hotkeyPress("ARROW_LEFT"); Keyboard.keyPress(KEYS.ARROW_LEFT);
Keyboard.hotkeyPress("ARROW_LEFT"); Keyboard.keyPress(KEYS.ARROW_LEFT);
Keyboard.hotkeyPress("ARROW_RIGHT"); Keyboard.keyPress(KEYS.ARROW_RIGHT);
Keyboard.hotkeyPress("ARROW_UP"); Keyboard.keyPress(KEYS.ARROW_UP);
Keyboard.hotkeyPress("ARROW_UP"); Keyboard.keyPress(KEYS.ARROW_UP);
Keyboard.hotkeyPress("ARROW_DOWN"); Keyboard.keyPress(KEYS.ARROW_DOWN);
expect(h.elements[0].x).toBe(9); expect(h.elements[0].x).toBe(9);
expect(h.elements[0].y).toBe(9); expect(h.elements[0].y).toBe(9);
}); });
@ -433,20 +433,20 @@ describe("regression tests", () => {
mouse.click(60, -10); mouse.click(60, -10);
mouse.click(60, 10); mouse.click(60, 10);
mouse.click(40, 10); mouse.click(40, 10);
Keyboard.hotkeyPress("ENTER"); Keyboard.keyPress(KEYS.ENTER);
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(3); expect(h.elements.filter((element) => !element.isDeleted).length).toBe(3);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
}); });
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2); expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
}); });
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(1); expect(h.elements.filter((element) => !element.isDeleted).length).toBe(1);
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
}); });
expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2); expect(h.elements.filter((element) => !element.isDeleted).length).toBe(2);
}); });
@ -469,7 +469,7 @@ describe("regression tests", () => {
expect(API.getStateHistory().length).toBe(3); expect(API.getStateHistory().length).toBe(3);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
}); });
expect(API.getStateHistory().length).toBe(2); expect(API.getStateHistory().length).toBe(2);
@ -480,7 +480,7 @@ describe("regression tests", () => {
expect(API.getStateHistory().length).toBe(2); expect(API.getStateHistory().length).toBe(2);
Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => { Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
Keyboard.keyPress("z"); Keyboard.keyPress(KEYS.Z);
}); });
expect(API.getStateHistory().length).toBe(3); expect(API.getStateHistory().length).toBe(3);
@ -501,11 +501,11 @@ describe("regression tests", () => {
it("zoom hotkeys", () => { it("zoom hotkeys", () => {
expect(h.state.zoom.value).toBe(1); expect(h.state.zoom.value).toBe(1);
fireEvent.keyDown(document, { code: "Equal", ctrlKey: true }); fireEvent.keyDown(document, { code: CODES.EQUAL, ctrlKey: true });
fireEvent.keyUp(document, { code: "Equal", ctrlKey: true }); fireEvent.keyUp(document, { code: CODES.EQUAL, ctrlKey: true });
expect(h.state.zoom.value).toBeGreaterThan(1); expect(h.state.zoom.value).toBeGreaterThan(1);
fireEvent.keyDown(document, { code: "Minus", ctrlKey: true }); fireEvent.keyDown(document, { code: CODES.MINUS, ctrlKey: true });
fireEvent.keyUp(document, { code: "Minus", ctrlKey: true }); fireEvent.keyUp(document, { code: CODES.MINUS, ctrlKey: true });
expect(h.state.zoom.value).toBe(1); expect(h.state.zoom.value).toBe(1);
}); });
@ -553,7 +553,7 @@ describe("regression tests", () => {
} }
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
for (const element of h.elements) { for (const element of h.elements) {
@ -591,8 +591,8 @@ describe("regression tests", () => {
mouse.up(10, 10); mouse.up(10, 10);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("a"); Keyboard.keyPress(KEYS.A);
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
expect(API.getSelectedElements().length).toBe(3); expect(API.getSelectedElements().length).toBe(3);
@ -629,7 +629,7 @@ describe("regression tests", () => {
mouse.click(); mouse.click();
}); });
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
expect(h.elements.map((element) => element.id)).toEqual([ expect(h.elements.map((element) => element.id)).toEqual([
@ -658,8 +658,8 @@ describe("regression tests", () => {
positions.push(mouse.getPosition()); positions.push(mouse.getPosition());
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("a"); Keyboard.keyPress(KEYS.A);
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
mouse.doubleClick(); mouse.doubleClick();
@ -668,7 +668,7 @@ describe("regression tests", () => {
mouse.click(); mouse.click();
}); });
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
const groupIds = h.elements[2].groupIds; const groupIds = h.elements[2].groupIds;
@ -813,7 +813,7 @@ describe("regression tests", () => {
}); });
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
fireEvent.contextMenu(GlobalTestState.canvas, { fireEvent.contextMenu(GlobalTestState.canvas, {
@ -1106,7 +1106,7 @@ describe("regression tests", () => {
}); });
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
fireEvent.contextMenu(GlobalTestState.canvas, { fireEvent.contextMenu(GlobalTestState.canvas, {
@ -1502,8 +1502,8 @@ describe("regression tests", () => {
UI.group([rect3, rect4]); UI.group([rect3, rect4]);
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("a"); Keyboard.keyPress(KEYS.A);
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
const selectedGroupIds_prev = h.state.selectedGroupIds; const selectedGroupIds_prev = h.state.selectedGroupIds;
@ -1617,7 +1617,7 @@ it(
// Create group with first and third rectangle // Create group with first and third rectangle
Keyboard.withModifierKeys({ ctrl: true }, () => { Keyboard.withModifierKeys({ ctrl: true }, () => {
Keyboard.keyPress("g"); Keyboard.codePress(CODES.G);
}); });
expect(API.getSelectedElements().length).toBe(2); expect(API.getSelectedElements().length).toBe(2);