Kaynağa Gözat

refactor: Stop using the deprecated keyCode (#2426)

Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: David Luzar <luzar.david@gmail.com>
Lipis 4 yıl önce
ebeveyn
işleme
014097a97e

+ 8 - 22
src/actions/actionAlign.tsx

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

+ 5 - 15
src/actions/actionCanvas.tsx

@@ -5,7 +5,7 @@ import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
 import { ToolButton } from "../components/ToolButton";
 import { t } from "../i18n";
 import { getNormalizedZoom } from "../scene";
-import { KEYS } from "../keys";
+import { CODES, KEYS } from "../keys";
 import { getShortcutKey } from "../utils";
 import useIsMobile from "../is-mobile";
 import { register } from "./register";
@@ -75,16 +75,6 @@ export const actionClearCanvas = register({
 
 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({
   name: "zoomIn",
   perform: (_elements, appState) => {
@@ -112,7 +102,7 @@ export const actionZoomIn = register({
     />
   ),
   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),
 });
 
@@ -143,7 +133,7 @@ export const actionZoomOut = register({
     />
   ),
   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),
 });
 
@@ -173,7 +163,7 @@ export const actionResetZoom = register({
     />
   ),
   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),
 });
 
@@ -229,7 +219,7 @@ export const actionZoomToFit = register({
     };
   },
   keyTest: (event) =>
-    event.code === KEY_CODES.ONE &&
+    event.code === CODES.ONE &&
     event.shiftKey &&
     !event.altKey &&
     !event[KEYS.CTRL_OR_CMD],

+ 3 - 7
src/actions/actionDistribute.tsx

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

+ 1 - 1
src/actions/actionDuplicateSelection.tsx

@@ -63,7 +63,7 @@ export const actionDuplicateSelection = register({
     };
   },
   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 }) => (
     <ToolButton
       type="button"

+ 4 - 6
src/actions/actionExport.tsx

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

+ 5 - 15
src/actions/actionGroup.tsx

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

+ 2 - 2
src/actions/actionMenu.tsx

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

+ 1 - 1
src/actions/actionSelectAll.ts

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

+ 3 - 7
src/actions/actionStyles.ts

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

+ 21 - 15
src/actions/actionZindex.tsx

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

+ 2 - 3
src/components/Actions.tsx

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

+ 6 - 14
src/components/App.tsx

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

+ 3 - 9
src/components/ColorPicker.tsx

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

+ 4 - 1
src/components/RoomDialog.tsx

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

+ 45 - 20
src/keys.ts

@@ -1,38 +1,63 @@
 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 = {
+  ARROW_DOWN: "ArrowDown",
   ARROW_LEFT: "ArrowLeft",
   ARROW_RIGHT: "ArrowRight",
-  ARROW_DOWN: "ArrowDown",
   ARROW_UP: "ArrowUp",
-  ENTER: "Enter",
-  ESCAPE: "Escape",
-  DELETE: "Delete",
   BACKSPACE: "Backspace",
   CTRL_OR_CMD: isDarwin ? "metaKey" : "ctrlKey",
-  TAB: "Tab",
-  SPACE: " ",
+  DELETE: "Delete",
+  ENTER: "Enter",
+  ESCAPE: "Escape",
   QUESTION_MARK: "?",
-  F_KEY_CODE: 70,
-  ALT_KEY_CODE: 18,
-  Z_KEY_CODE: 90,
-  GRID_KEY_CODE: 222,
-  H_KEY_CODE: 72,
-  G_KEY_CODE: 71,
-  C_KEY_CODE: 67,
-  V_KEY_CODE: 86,
+  SPACE: " ",
+  TAB: "Tab",
+
+  A: "a",
+  D: "d",
+  E: "e",
+  L: "l",
+  P: "p",
+  Q: "q",
+  R: "r",
+  S: "s",
+  T: "t",
+  V: "v",
+  X: "x",
+  Z: "z",
 } as const;
 
 export type Key = keyof typeof KEYS;
 
-export const isArrowKey = (keyCode: string) =>
-  keyCode === KEYS.ARROW_LEFT ||
-  keyCode === KEYS.ARROW_RIGHT ||
-  keyCode === KEYS.ARROW_DOWN ||
-  keyCode === KEYS.ARROW_UP;
+export const isArrowKey = (key: string) =>
+  key === KEYS.ARROW_LEFT ||
+  key === KEYS.ARROW_RIGHT ||
+  key === KEYS.ARROW_DOWN ||
+  key === KEYS.ARROW_UP;
 
 export const getResizeCenterPointKey = (event: MouseEvent | KeyboardEvent) =>
-  event.altKey || event.which === KEYS.ALT_KEY_CODE;
+  event.altKey;
 
 export const getResizeWithSidesSameLengthKey = (event: MouseEvent) =>
   event.shiftKey;

+ 9 - 8
src/shapes.tsx

@@ -1,4 +1,5 @@
 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
 export const SHAPES = [
@@ -10,7 +11,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "selection",
-    key: ["v", "s"],
+    key: [KEYS.V, KEYS.S],
   },
   {
     icon: (
@@ -20,7 +21,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "rectangle",
-    key: "r",
+    key: KEYS.R,
   },
   {
     icon: (
@@ -30,7 +31,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "diamond",
-    key: "d",
+    key: KEYS.D,
   },
   {
     icon: (
@@ -40,7 +41,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "ellipse",
-    key: "e",
+    key: KEYS.E,
   },
   {
     icon: (
@@ -50,7 +51,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "arrow",
-    key: "a",
+    key: KEYS.A,
   },
   {
     icon: (
@@ -67,7 +68,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "line",
-    key: ["p", "l"],
+    key: [KEYS.P, KEYS.L],
   },
   {
     icon: (
@@ -80,7 +81,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "draw",
-    key: ["P", "x"],
+    key: KEYS.X,
   },
   {
     icon: (
@@ -90,7 +91,7 @@ export const SHAPES = [
       </svg>
     ),
     value: "text",
-    key: "t",
+    key: KEYS.T,
   },
 ] as const;
 

+ 60 - 60
src/tests/__snapshots__/regressionTests.test.tsx.snap

@@ -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 hotkey 2 selects rectangle tool: [end of test] appState 1`] = `
+exports[`regression tests key 2 selects rectangle tool: [end of test] appState 1`] = `
 Object {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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 {
   "appearance": "light",
   "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 {
   "angle": 0,
   "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 {
   "recording": false,
   "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`] = `
 Object {

+ 3 - 2
src/tests/binding.test.tsx

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

+ 27 - 29
src/tests/helpers/ui.ts

@@ -1,11 +1,11 @@
-import { ToolName } from "../queries/toolQueries";
-import { fireEvent, GlobalTestState } from "../test-utils";
-import { KEYS, Key } from "../../keys";
 import {
   ExcalidrawElement,
   ExcalidrawLinearElement,
   ExcalidrawTextElement,
 } from "../../element/types";
+import { CODES } from "../../keys";
+import { ToolName } from "../queries/toolQueries";
+import { fireEvent, GlobalTestState } from "../test-utils";
 import { API } from "./api";
 
 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) => {
     fireEvent.keyDown(document, {
       key,
       ctrlKey,
       shiftKey,
       altKey,
-      keyCode: key.toUpperCase().charCodeAt(0),
-      which: key.toUpperCase().charCodeAt(0),
     });
   };
 
@@ -69,20 +51,36 @@ export class Keyboard {
       ctrlKey,
       shiftKey,
       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) => {
     Keyboard.keyDown(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 {
@@ -209,7 +207,7 @@ export class UI {
   static group(elements: ExcalidrawElement[]) {
     mouse.select(elements);
     Keyboard.withModifierKeys({ ctrl: true }, () => {
-      Keyboard.keyPress("g");
+      Keyboard.codePress(CODES.G);
     });
   }
 }

+ 4 - 3
src/tests/move.test.tsx

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

+ 41 - 41
src/tests/regressionTests.test.tsx

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