Browse Source

fix: popover positioning (#3399)

David Luzar 4 years ago
parent
commit
9733ecb3df

+ 27 - 14
src/components/App.tsx

@@ -474,6 +474,7 @@ class App extends React.Component<AppProps, AppState> {
           UIOptions={this.props.UIOptions}
         />
         <div className="excalidraw-textEditorContainer" />
+        <div className="excalidraw-contextMenuContainer" />
         {this.state.showStats && (
           <Stats
             appState={this.state}
@@ -3675,12 +3676,20 @@ class App extends React.Component<AppProps, AppState> {
 
     const type = element ? "element" : "canvas";
 
+    const container = this.excalidrawContainerRef.current!;
+    const {
+      top: offsetTop,
+      left: offsetLeft,
+    } = container.getBoundingClientRect();
+    const left = event.clientX - offsetLeft;
+    const top = event.clientY - offsetTop;
+
     if (element && !this.state.selectedElementIds[element.id]) {
       this.setState({ selectedElementIds: { [element.id]: true } }, () => {
-        this._openContextMenu(event, type);
+        this._openContextMenu({ top, left }, type);
       });
     } else {
-      this._openContextMenu(event, type);
+      this._openContextMenu({ top, left }, type);
     }
   };
 
@@ -3774,11 +3783,11 @@ class App extends React.Component<AppProps, AppState> {
   /** @private use this.handleCanvasContextMenu */
   private _openContextMenu = (
     {
-      clientX,
-      clientY,
+      left,
+      top,
     }: {
-      clientX: number;
-      clientY: number;
+      left: number;
+      top: number;
     },
     type: "canvas" | "element",
   ) => {
@@ -3829,10 +3838,11 @@ class App extends React.Component<AppProps, AppState> {
 
       ContextMenu.push({
         options: viewModeOptions,
-        top: clientY,
-        left: clientX,
+        top,
+        left,
         actionManager: this.actionManager,
         appState: this.state,
+        container: this.excalidrawContainerRef.current!,
       });
 
       if (this.state.viewModeEnabled) {
@@ -3872,10 +3882,11 @@ class App extends React.Component<AppProps, AppState> {
             actionToggleViewMode,
           actionToggleStats,
         ],
-        top: clientY,
-        left: clientX,
+        top,
+        left,
         actionManager: this.actionManager,
         appState: this.state,
+        container: this.excalidrawContainerRef.current!,
       });
       return;
     }
@@ -3883,10 +3894,11 @@ class App extends React.Component<AppProps, AppState> {
     if (this.state.viewModeEnabled) {
       ContextMenu.push({
         options: [navigator.clipboard && actionCopy, ...options],
-        top: clientY,
-        left: clientX,
+        top,
+        left,
         actionManager: this.actionManager,
         appState: this.state,
+        container: this.excalidrawContainerRef.current!,
       });
       return;
     }
@@ -3928,10 +3940,11 @@ class App extends React.Component<AppProps, AppState> {
         actionDuplicateSelection,
         actionDeleteSelected,
       ],
-      top: clientY,
-      left: clientX,
+      top,
+      left,
       actionManager: this.actionManager,
       appState: this.state,
+      container: this.excalidrawContainerRef.current!,
     });
   };
 

+ 57 - 55
src/components/ContextMenu.tsx

@@ -32,67 +32,63 @@ const ContextMenu = ({
   actionManager,
   appState,
 }: ContextMenuProps) => {
-  const isDarkTheme = !!document
-    .querySelector(".excalidraw")
-    ?.classList.contains("theme--dark");
   return (
-    <div
-      className={clsx("excalidraw", {
-        "theme--dark theme--dark-background-none": isDarkTheme,
-      })}
+    <Popover
+      onCloseRequest={onCloseRequest}
+      top={top}
+      left={left}
+      fitInViewport={true}
     >
-      <Popover
-        onCloseRequest={onCloseRequest}
-        top={top}
-        left={left}
-        fitInViewport={true}
+      <ul
+        className="context-menu"
+        onContextMenu={(event) => event.preventDefault()}
       >
-        <ul
-          className="context-menu"
-          onContextMenu={(event) => event.preventDefault()}
-        >
-          {options.map((option, idx) => {
-            if (option === "separator") {
-              return <hr key={idx} className="context-menu-option-separator" />;
-            }
+        {options.map((option, idx) => {
+          if (option === "separator") {
+            return <hr key={idx} className="context-menu-option-separator" />;
+          }
 
-            const actionName = option.name;
-            const label = option.contextItemLabel
-              ? t(option.contextItemLabel)
-              : "";
-            return (
-              <li key={idx} data-testid={actionName} onClick={onCloseRequest}>
-                <button
-                  className={clsx("context-menu-option", {
-                    dangerous: actionName === "deleteSelectedElements",
-                    checkmark: option.checked?.(appState),
-                  })}
-                  onClick={() => actionManager.executeAction(option)}
-                >
-                  <div className="context-menu-option__label">{label}</div>
-                  <kbd className="context-menu-option__shortcut">
-                    {actionName
-                      ? getShortcutFromShortcutName(actionName as ShortcutName)
-                      : ""}
-                  </kbd>
-                </button>
-              </li>
-            );
-          })}
-        </ul>
-      </Popover>
-    </div>
+          const actionName = option.name;
+          const label = option.contextItemLabel
+            ? t(option.contextItemLabel)
+            : "";
+          return (
+            <li key={idx} data-testid={actionName} onClick={onCloseRequest}>
+              <button
+                className={clsx("context-menu-option", {
+                  dangerous: actionName === "deleteSelectedElements",
+                  checkmark: option.checked?.(appState),
+                })}
+                onClick={() => actionManager.executeAction(option)}
+              >
+                <div className="context-menu-option__label">{label}</div>
+                <kbd className="context-menu-option__shortcut">
+                  {actionName
+                    ? getShortcutFromShortcutName(actionName as ShortcutName)
+                    : ""}
+                </kbd>
+              </button>
+            </li>
+          );
+        })}
+      </ul>
+    </Popover>
   );
 };
 
-let contextMenuNode: HTMLDivElement;
-const getContextMenuNode = (): HTMLDivElement => {
+const contextMenuNodeByContainer = new WeakMap<HTMLElement, HTMLDivElement>();
+
+const getContextMenuNode = (container: HTMLElement): HTMLDivElement => {
+  let contextMenuNode = contextMenuNodeByContainer.get(container);
   if (contextMenuNode) {
     return contextMenuNode;
   }
-  const div = document.createElement("div");
-  document.body.appendChild(div);
-  return (contextMenuNode = div);
+  contextMenuNode = document.createElement("div");
+  container
+    .querySelector(".excalidraw-contextMenuContainer")!
+    .appendChild(contextMenuNode);
+  contextMenuNodeByContainer.set(container, contextMenuNode);
+  return contextMenuNode;
 };
 
 type ContextMenuParams = {
@@ -101,10 +97,16 @@ type ContextMenuParams = {
   left: ContextMenuProps["left"];
   actionManager: ContextMenuProps["actionManager"];
   appState: Readonly<AppState>;
+  container: HTMLElement;
 };
 
-const handleClose = () => {
-  unmountComponentAtNode(getContextMenuNode());
+const handleClose = (container: HTMLElement) => {
+  const contextMenuNode = contextMenuNodeByContainer.get(container);
+  if (contextMenuNode) {
+    unmountComponentAtNode(contextMenuNode);
+    contextMenuNode.remove();
+    contextMenuNodeByContainer.delete(container);
+  }
 };
 
 export default {
@@ -121,11 +123,11 @@ export default {
           top={params.top}
           left={params.left}
           options={options}
-          onCloseRequest={handleClose}
+          onCloseRequest={() => handleClose(params.container)}
           actionManager={params.actionManager}
           appState={params.appState}
         />,
-        getContextMenuNode(),
+        getContextMenuNode(params.container),
       );
     }
   },

+ 1 - 1
src/components/Popover.scss

@@ -1,6 +1,6 @@
 .excalidraw {
   .popover {
-    position: fixed;
+    position: absolute;
     z-index: 10;
   }
 }

+ 4582 - 0
src/tests/__snapshots__/contextmenu.test.tsx.snap

@@ -0,0 +1,4582 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`contextMenu element selecting 'Add to library' in context menu adds element to library: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Add to library' in context menu adds element to library: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Add to library' in context menu adds element to library: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Add to library' in context menu adds element to library: [end of test] number of elements 1`] = `1`;
+
+exports[`contextMenu element selecting 'Add to library' in context menu adds element to library: [end of test] number of renders 1`] = `9`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 401146281,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Bring forward' in context menu brings element forward: [end of test] number of renders 1`] = `15`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 401146281,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Bring to front' in context menu brings element to front: [end of test] number of renders 1`] = `15`;
+
+exports[`contextMenu element selecting 'Copy styles' in context menu copies styles: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": "Copied styles.",
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Copy styles' in context menu copies styles: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 449462985,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 453191,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Copy styles' in context menu copies styles: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 453191,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Copy styles' in context menu copies styles: [end of test] number of elements 1`] = `1`;
+
+exports[`contextMenu element selecting 'Copy styles' in context menu copies styles: [end of test] number of renders 1`] = `10`;
+
+exports[`contextMenu element selecting 'Delete' in context menu deletes element: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {},
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Delete' in context menu deletes element: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": true,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 453191,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Delete' in context menu deletes element: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": true,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 453191,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Delete' in context menu deletes element: [end of test] number of elements 1`] = `1`;
+
+exports[`contextMenu element selecting 'Delete' in context menu deletes element: [end of test] number of renders 1`] = `10`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0_copy": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0_copy",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": 0,
+  "y": 10,
+}
+`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0_copy": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0_copy",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": 0,
+          "y": 10,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Duplicate' in context menu duplicates element: [end of test] number of renders 1`] = `10`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {
+    "id1": true,
+  },
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+    "id1": true,
+    "id2": true,
+  },
+  "selectedGroupIds": Object {
+    "id3": true,
+  },
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [
+    "id3",
+  ],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 1150084233,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [
+    "id3",
+  ],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 1116226695,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+          "id1": true,
+          "id2": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id3",
+          ],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 1150084233,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id3",
+          ],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 1116226695,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Group selection' in context menu groups selected elements: [end of test] number of renders 1`] = `16`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "#e64980",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "cross-hatch",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 60,
+  "currentItemRoughness": 2,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#c92a2a",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "dotted",
+  "currentItemStrokeWidth": 2,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": "Copied styles.",
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "#e64980",
+  "boundElementIds": null,
+  "fillStyle": "cross-hatch",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 60,
+  "roughness": 2,
+  "seed": 1278240551,
+  "strokeColor": "#c92a2a",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "dotted",
+  "strokeWidth": 2,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 23633383,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "#e64980",
+  "boundElementIds": null,
+  "fillStyle": "cross-hatch",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 60,
+  "roughness": 2,
+  "seed": 400692809,
+  "strokeColor": "#c92a2a",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "dotted",
+  "strokeWidth": 2,
+  "type": "rectangle",
+  "version": 9,
+  "versionNonce": 1505387817,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 2019559783,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 4,
+          "versionNonce": 1150084233,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 5,
+          "versionNonce": 1116226695,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 6,
+          "versionNonce": 1014066025,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "dotted",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 7,
+          "versionNonce": 238820263,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 2,
+          "seed": 400692809,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "dotted",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 8,
+          "versionNonce": 1604849351,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 60,
+          "roughness": 2,
+          "seed": 400692809,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "dotted",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 9,
+          "versionNonce": 1505387817,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 60,
+          "roughness": 2,
+          "seed": 1278240551,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "dotted",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 23633383,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "#e64980",
+          "boundElementIds": null,
+          "fillStyle": "cross-hatch",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 60,
+          "roughness": 2,
+          "seed": 400692809,
+          "strokeColor": "#c92a2a",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "dotted",
+          "strokeWidth": 2,
+          "type": "rectangle",
+          "version": 9,
+          "versionNonce": 1505387817,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] number of renders 1`] = `25`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id1": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 401146281,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] number of renders 1`] = `14`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id1": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 401146281,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Send to back' in context menu sends element to back: [end of test] number of renders 1`] = `14`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {
+    "id1": true,
+  },
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+    "id1": true,
+    "id2": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 449462985,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 4,
+  "versionNonce": 238820263,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 401146281,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 4,
+  "versionNonce": 400692809,
+  "width": 20,
+  "x": 20,
+  "y": 30,
+}
+`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 453191,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 453191,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 401146281,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 2019559783,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+          "id1": true,
+          "id2": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id3",
+          ],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 1116226695,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id3",
+          ],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 401146281,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 1014066025,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+          "id1": true,
+          "id2": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 4,
+          "versionNonce": 238820263,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 401146281,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 4,
+          "versionNonce": 400692809,
+          "width": 20,
+          "x": 20,
+          "y": 30,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of renders 1`] = `17`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {
+    "id0": true,
+    "id2": true,
+  },
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+    "id1": true,
+    "id2": true,
+    "id3": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 10,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 10,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 10,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 453191,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 401146281,
+  "width": 10,
+  "x": 10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 10,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 10,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 453191,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 401146281,
+          "width": 10,
+          "x": 10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element shows 'Group selection' in context menu for multiple selected elements: [end of test] number of renders 1`] = `17`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {
+    "id0": true,
+    "id2": true,
+  },
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+    "id1": true,
+    "id2": true,
+    "id3": true,
+  },
+  "selectedGroupIds": Object {
+    "id4": true,
+  },
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [
+    "id4",
+  ],
+  "height": 10,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 449462985,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 1014066025,
+  "width": 10,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] element 1 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [
+    "id4",
+  ],
+  "height": 10,
+  "id": "id1",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 401146281,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 3,
+  "versionNonce": 238820263,
+  "width": 10,
+  "x": 10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 0,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 1,
+          "versionNonce": 0,
+          "width": 0,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 453191,
+          "width": 10,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id1": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 453191,
+          "width": 10,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 10,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 401146281,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 2019559783,
+          "width": 10,
+          "x": 10,
+          "y": 0,
+        },
+      ],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+          "id1": true,
+          "id2": true,
+          "id3": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id4",
+          ],
+          "height": 10,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 449462985,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 1014066025,
+          "width": 10,
+          "x": -10,
+          "y": 0,
+        },
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [
+            "id4",
+          ],
+          "height": 10,
+          "id": "id1",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 401146281,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 3,
+          "versionNonce": 238820263,
+          "width": 10,
+          "x": 10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] number of elements 1`] = `2`;
+
+exports[`contextMenu element shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] number of renders 1`] = `18`;
+
+exports[`contextMenu element shows context menu for canvas: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {},
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element shows context menu for canvas: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element shows context menu for canvas: [end of test] number of elements 1`] = `0`;
+
+exports[`contextMenu element shows context menu for canvas: [end of test] number of renders 1`] = `3`;
+
+exports[`contextMenu element shows context menu for element: [end of test] appState 1`] = `
+Object {
+  "collaborators": Map {},
+  "currentChartType": "bar",
+  "currentItemBackgroundColor": "transparent",
+  "currentItemEndArrowhead": "arrow",
+  "currentItemFillStyle": "hachure",
+  "currentItemFontFamily": 1,
+  "currentItemFontSize": 20,
+  "currentItemLinearStrokeSharpness": "round",
+  "currentItemOpacity": 100,
+  "currentItemRoughness": 1,
+  "currentItemStartArrowhead": null,
+  "currentItemStrokeColor": "#000000",
+  "currentItemStrokeSharpness": "sharp",
+  "currentItemStrokeStyle": "solid",
+  "currentItemStrokeWidth": 1,
+  "currentItemTextAlign": "left",
+  "cursorButton": "up",
+  "draggingElement": null,
+  "editingElement": null,
+  "editingGroupId": null,
+  "editingLinearElement": null,
+  "elementLocked": false,
+  "elementType": "selection",
+  "errorMessage": null,
+  "exportBackground": true,
+  "exportEmbedScene": false,
+  "exportWithDarkMode": false,
+  "fileHandle": null,
+  "gridSize": null,
+  "height": 100,
+  "isBindingEnabled": true,
+  "isLibraryOpen": false,
+  "isLoading": false,
+  "isResizing": false,
+  "isRotating": false,
+  "lastPointerDownWith": "mouse",
+  "multiElement": null,
+  "name": "Untitled-201933152653",
+  "offsetLeft": 20,
+  "offsetTop": 10,
+  "openMenu": null,
+  "pasteDialog": Object {
+    "data": null,
+    "shown": false,
+  },
+  "previousSelectedElementIds": Object {},
+  "resizingElement": null,
+  "scrollX": 0,
+  "scrollY": 0,
+  "scrolledOutside": false,
+  "selectedElementIds": Object {
+    "id0": true,
+  },
+  "selectedGroupIds": Object {},
+  "selectionElement": null,
+  "shouldAddWatermark": false,
+  "shouldCacheIgnoreZoom": false,
+  "showHelpDialog": false,
+  "showStats": false,
+  "startBoundElement": null,
+  "suggestedBindings": Array [],
+  "theme": "light",
+  "toastMessage": null,
+  "viewBackgroundColor": "#ffffff",
+  "viewModeEnabled": false,
+  "width": 200,
+  "zenModeEnabled": false,
+  "zoom": Object {
+    "translation": Object {
+      "x": 0,
+      "y": 0,
+    },
+    "value": 1,
+  },
+}
+`;
+
+exports[`contextMenu element shows context menu for element: [end of test] element 0 1`] = `
+Object {
+  "angle": 0,
+  "backgroundColor": "transparent",
+  "boundElementIds": null,
+  "fillStyle": "hachure",
+  "groupIds": Array [],
+  "height": 20,
+  "id": "id0",
+  "isDeleted": false,
+  "opacity": 100,
+  "roughness": 1,
+  "seed": 1278240551,
+  "strokeColor": "#000000",
+  "strokeSharpness": "sharp",
+  "strokeStyle": "solid",
+  "strokeWidth": 1,
+  "type": "rectangle",
+  "version": 2,
+  "versionNonce": 449462985,
+  "width": 20,
+  "x": -10,
+  "y": 0,
+}
+`;
+
+exports[`contextMenu element shows context menu for element: [end of test] history 1`] = `
+Object {
+  "recording": false,
+  "redoStack": Array [],
+  "stateHistory": Array [
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {},
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [],
+    },
+    Object {
+      "appState": Object {
+        "editingGroupId": null,
+        "editingLinearElement": null,
+        "name": "Untitled-201933152653",
+        "selectedElementIds": Object {
+          "id0": true,
+        },
+        "viewBackgroundColor": "#ffffff",
+      },
+      "elements": Array [
+        Object {
+          "angle": 0,
+          "backgroundColor": "transparent",
+          "boundElementIds": null,
+          "fillStyle": "hachure",
+          "groupIds": Array [],
+          "height": 20,
+          "id": "id0",
+          "isDeleted": false,
+          "opacity": 100,
+          "roughness": 1,
+          "seed": 1278240551,
+          "strokeColor": "#000000",
+          "strokeSharpness": "sharp",
+          "strokeStyle": "solid",
+          "strokeWidth": 1,
+          "type": "rectangle",
+          "version": 2,
+          "versionNonce": 449462985,
+          "width": 20,
+          "x": -10,
+          "y": 0,
+        },
+      ],
+    },
+  ],
+}
+`;
+
+exports[`contextMenu element shows context menu for element: [end of test] number of elements 1`] = `1`;
+
+exports[`contextMenu element shows context menu for element: [end of test] number of renders 1`] = `9`;

File diff suppressed because it is too large
+ 105 - 4380
src/tests/__snapshots__/regressionTests.test.tsx.snap


+ 531 - 0
src/tests/contextmenu.test.tsx

@@ -0,0 +1,531 @@
+import ReactDOM from "react-dom";
+import {
+  render,
+  fireEvent,
+  mockBoundingClientRect,
+  restoreOriginalGetBoundingClientRect,
+  GlobalTestState,
+  screen,
+  queryByText,
+  queryAllByText,
+  waitFor,
+} from "./test-utils";
+import ExcalidrawApp from "../excalidraw-app";
+import * as Renderer from "../renderer/renderScene";
+import { reseed } from "../random";
+import { UI, Pointer, Keyboard } from "./helpers/ui";
+import { CODES } from "../keys";
+import { ShortcutName } from "../actions/shortcuts";
+import { copiedStyles } from "../actions/actionStyles";
+import { API } from "./helpers/api";
+import { setDateTimeForTests } from "../utils";
+
+const checkpoint = (name: string) => {
+  expect(renderScene.mock.calls.length).toMatchSnapshot(
+    `[${name}] number of renders`,
+  );
+  expect(h.state).toMatchSnapshot(`[${name}] appState`);
+  expect(h.history.getSnapshotForTest()).toMatchSnapshot(`[${name}] history`);
+  expect(h.elements.length).toMatchSnapshot(`[${name}] number of elements`);
+  h.elements.forEach((element, i) =>
+    expect(element).toMatchSnapshot(`[${name}] element ${i}`),
+  );
+};
+
+const mouse = new Pointer("mouse");
+
+const queryContextMenu = () => {
+  return GlobalTestState.renderResult.container.querySelector(".context-menu");
+};
+
+const clickLabeledElement = (label: string) => {
+  const element = document.querySelector(`[aria-label='${label}']`);
+  if (!element) {
+    throw new Error(`No labeled element found: ${label}`);
+  }
+  fireEvent.click(element);
+};
+
+// Unmount ReactDOM from root
+ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
+
+const renderScene = jest.spyOn(Renderer, "renderScene");
+beforeEach(() => {
+  localStorage.clear();
+  renderScene.mockClear();
+  reseed(7);
+});
+
+const { h } = window;
+
+describe("contextMenu element", () => {
+  beforeEach(async () => {
+    localStorage.clear();
+    renderScene.mockClear();
+    h.history.clear();
+    reseed(7);
+    setDateTimeForTests("201933152653");
+
+    await render(<ExcalidrawApp />);
+  });
+
+  beforeAll(() => {
+    mockBoundingClientRect();
+  });
+
+  afterAll(() => {
+    restoreOriginalGetBoundingClientRect();
+  });
+
+  afterEach(() => {
+    checkpoint("end of test");
+
+    mouse.reset();
+    mouse.down(0, 0);
+  });
+
+  it("shows context menu for canvas", () => {
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    const contextMenuOptions = contextMenu?.querySelectorAll(
+      ".context-menu li",
+    );
+    const expectedShortcutNames: ShortcutName[] = [
+      "selectAll",
+      "gridMode",
+      "zenMode",
+      "viewMode",
+      "stats",
+    ];
+
+    expect(contextMenu).not.toBeNull();
+    expect(contextMenuOptions?.length).toBe(expectedShortcutNames.length);
+    expectedShortcutNames.forEach((shortcutName) => {
+      expect(
+        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
+      ).not.toBeNull();
+    });
+  });
+
+  it("shows context menu for element", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    const contextMenuOptions = contextMenu?.querySelectorAll(
+      ".context-menu li",
+    );
+    const expectedShortcutNames: ShortcutName[] = [
+      "copyStyles",
+      "pasteStyles",
+      "deleteSelectedElements",
+      "addToLibrary",
+      "flipHorizontal",
+      "flipVertical",
+      "sendBackward",
+      "bringForward",
+      "sendToBack",
+      "bringToFront",
+      "duplicateSelection",
+    ];
+
+    expect(contextMenu).not.toBeNull();
+    expect(contextMenuOptions?.length).toBe(expectedShortcutNames.length);
+    expectedShortcutNames.forEach((shortcutName) => {
+      expect(
+        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
+      ).not.toBeNull();
+    });
+  });
+
+  it("shows 'Group selection' in context menu for multiple selected elements", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(10, 10);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, -10);
+    mouse.up(10, 10);
+
+    mouse.reset();
+    mouse.click(10, 10);
+    Keyboard.withModifierKeys({ shift: true }, () => {
+      mouse.click(20, 0);
+    });
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+
+    const contextMenu = queryContextMenu();
+    const contextMenuOptions = contextMenu?.querySelectorAll(
+      ".context-menu li",
+    );
+    const expectedShortcutNames: ShortcutName[] = [
+      "copyStyles",
+      "pasteStyles",
+      "deleteSelectedElements",
+      "group",
+      "addToLibrary",
+      "sendBackward",
+      "bringForward",
+      "sendToBack",
+      "bringToFront",
+      "duplicateSelection",
+    ];
+
+    expect(contextMenu).not.toBeNull();
+    expect(contextMenuOptions?.length).toBe(expectedShortcutNames.length);
+    expectedShortcutNames.forEach((shortcutName) => {
+      expect(
+        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
+      ).not.toBeNull();
+    });
+  });
+
+  it("shows 'Ungroup selection' in context menu for group inside selected elements", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(10, 10);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, -10);
+    mouse.up(10, 10);
+
+    mouse.reset();
+    mouse.click(10, 10);
+    Keyboard.withModifierKeys({ shift: true }, () => {
+      mouse.click(20, 0);
+    });
+
+    Keyboard.withModifierKeys({ ctrl: true }, () => {
+      Keyboard.codePress(CODES.G);
+    });
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+
+    const contextMenu = queryContextMenu();
+    const contextMenuOptions = contextMenu?.querySelectorAll(
+      ".context-menu li",
+    );
+    const expectedShortcutNames: ShortcutName[] = [
+      "copyStyles",
+      "pasteStyles",
+      "deleteSelectedElements",
+      "ungroup",
+      "addToLibrary",
+      "sendBackward",
+      "bringForward",
+      "sendToBack",
+      "bringToFront",
+      "duplicateSelection",
+    ];
+
+    expect(contextMenu).not.toBeNull();
+    expect(contextMenuOptions?.length).toBe(expectedShortcutNames.length);
+    expectedShortcutNames.forEach((shortcutName) => {
+      expect(
+        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
+      ).not.toBeNull();
+    });
+  });
+
+  it("selecting 'Copy styles' in context menu copies styles", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    expect(copiedStyles).toBe("{}");
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
+    expect(copiedStyles).not.toBe("{}");
+    const element = JSON.parse(copiedStyles);
+    expect(element).toEqual(API.getSelectedElement());
+  });
+
+  it("selecting 'Paste styles' in context menu pastes styles", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    // Change some styles of second rectangle
+    clickLabeledElement("Stroke");
+    clickLabeledElement("#c92a2a");
+    clickLabeledElement("Background");
+    clickLabeledElement("#e64980");
+    // Fill style
+    fireEvent.click(screen.getByTitle("Cross-hatch"));
+    // Stroke width
+    fireEvent.click(screen.getByTitle("Bold"));
+    // Stroke style
+    fireEvent.click(screen.getByTitle("Dotted"));
+    // Roughness
+    fireEvent.click(screen.getByTitle("Cartoonist"));
+    // Opacity
+    fireEvent.change(screen.getByLabelText("Opacity"), {
+      target: { value: "60" },
+    });
+
+    mouse.reset();
+    // Copy styles of second rectangle
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 40,
+      clientY: 40,
+    });
+    let contextMenu = queryContextMenu();
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
+    const secondRect = JSON.parse(copiedStyles);
+    expect(secondRect.id).toBe(h.elements[1].id);
+
+    mouse.reset();
+    // Paste styles to first rectangle
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 10,
+      clientY: 10,
+    });
+    contextMenu = queryContextMenu();
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Paste styles")!);
+
+    const firstRect = API.getSelectedElement();
+    expect(firstRect.id).toBe(h.elements[0].id);
+    expect(firstRect.strokeColor).toBe("#c92a2a");
+    expect(firstRect.backgroundColor).toBe("#e64980");
+    expect(firstRect.fillStyle).toBe("cross-hatch");
+    expect(firstRect.strokeWidth).toBe(2); // Bold: 2
+    expect(firstRect.strokeStyle).toBe("dotted");
+    expect(firstRect.roughness).toBe(2); // Cartoonist: 2
+    expect(firstRect.opacity).toBe(60);
+  });
+
+  it("selecting 'Delete' in context menu deletes element", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    fireEvent.click(queryAllByText(contextMenu as HTMLElement, "Delete")[0]);
+    expect(API.getSelectedElements()).toHaveLength(0);
+    expect(h.elements[0].isDeleted).toBe(true);
+  });
+
+  it("selecting 'Add to library' in context menu adds element to library", async () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Add to library")!);
+
+    await waitFor(() => {
+      const library = localStorage.getItem("excalidraw-library");
+      expect(library).not.toBeNull();
+      const addedElement = JSON.parse(library!)[0][0];
+      expect(addedElement).toEqual(h.elements[0]);
+    });
+  });
+
+  it("selecting 'Duplicate' in context menu duplicates element", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Duplicate")!);
+    expect(h.elements).toHaveLength(2);
+    const { id: _id0, seed: _seed0, x: _x0, y: _y0, ...rect1 } = h.elements[0];
+    const { id: _id1, seed: _seed1, x: _x1, y: _y1, ...rect2 } = h.elements[1];
+    expect(rect1).toEqual(rect2);
+  });
+
+  it("selecting 'Send backward' in context menu sends element backward", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 40,
+      clientY: 40,
+    });
+    const contextMenu = queryContextMenu();
+    const elementsBefore = h.elements;
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Send backward")!);
+    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
+    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
+  });
+
+  it("selecting 'Bring forward' in context menu brings element forward", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 10,
+      clientY: 10,
+    });
+    const contextMenu = queryContextMenu();
+    const elementsBefore = h.elements;
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring forward")!);
+    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
+    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
+  });
+
+  it("selecting 'Send to back' in context menu sends element to back", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 40,
+      clientY: 40,
+    });
+    const contextMenu = queryContextMenu();
+    const elementsBefore = h.elements;
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Send to back")!);
+    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
+  });
+
+  it("selecting 'Bring to front' in context menu brings element to front", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 10,
+      clientY: 10,
+    });
+    const contextMenu = queryContextMenu();
+    const elementsBefore = h.elements;
+    fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring to front")!);
+    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
+  });
+
+  it("selecting 'Group selection' in context menu groups selected elements", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    Keyboard.withModifierKeys({ shift: true }, () => {
+      mouse.click(10, 10);
+    });
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+    const contextMenu = queryContextMenu();
+    fireEvent.click(
+      queryByText(contextMenu as HTMLElement, "Group selection")!,
+    );
+    const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
+    expect(h.elements[0].groupIds).toEqual(selectedGroupIds);
+    expect(h.elements[1].groupIds).toEqual(selectedGroupIds);
+  });
+
+  it("selecting 'Ungroup selection' in context menu ungroups selected group", () => {
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    UI.clickTool("rectangle");
+    mouse.down(10, 10);
+    mouse.up(20, 20);
+
+    mouse.reset();
+    Keyboard.withModifierKeys({ shift: true }, () => {
+      mouse.click(10, 10);
+    });
+
+    Keyboard.withModifierKeys({ ctrl: true }, () => {
+      Keyboard.codePress(CODES.G);
+    });
+
+    fireEvent.contextMenu(GlobalTestState.canvas, {
+      button: 2,
+      clientX: 1,
+      clientY: 1,
+    });
+
+    const contextMenu = queryContextMenu();
+    expect(contextMenu).not.toBeNull();
+    fireEvent.click(
+      queryByText(contextMenu as HTMLElement, "Ungroup selection")!,
+    );
+
+    const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
+    expect(selectedGroupIds).toHaveLength(0);
+    expect(h.elements[0].groupIds).toHaveLength(0);
+    expect(h.elements[1].groupIds).toHaveLength(0);
+  });
+});

+ 1 - 446
src/tests/regressionTests.test.tsx

@@ -1,8 +1,4 @@
-import { queryAllByText, queryByText } from "@testing-library/react";
-import React from "react";
 import ReactDOM from "react-dom";
-import { copiedStyles } from "../actions/actionStyles";
-import { ShortcutName } from "../actions/shortcuts";
 import { ExcalidrawElement } from "../element/types";
 import { CODES, KEYS } from "../keys";
 import ExcalidrawApp from "../excalidraw-app";
@@ -11,13 +7,7 @@ import * as Renderer from "../renderer/renderScene";
 import { setDateTimeForTests } from "../utils";
 import { API } from "./helpers/api";
 import { Keyboard, Pointer, UI } from "./helpers/ui";
-import {
-  fireEvent,
-  GlobalTestState,
-  render,
-  screen,
-  waitFor,
-} from "./test-utils";
+import { fireEvent, render, screen, waitFor } from "./test-utils";
 import { defaultLang } from "../i18n";
 
 const { h } = window;
@@ -612,441 +602,6 @@ describe("regression tests", () => {
     expect(h.state.currentItemFontFamily).toEqual(3); // Cascadia
   });
 
-  it("shows context menu for canvas", () => {
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const contextMenuOptions = document.querySelectorAll(".context-menu li");
-    const expectedShortcutNames: ShortcutName[] = [
-      "selectAll",
-      "gridMode",
-      "zenMode",
-      "viewMode",
-      "stats",
-    ];
-
-    expect(contextMenu).not.toBeNull();
-    expect(contextMenuOptions.length).toBe(expectedShortcutNames.length);
-    expectedShortcutNames.forEach((shortcutName) => {
-      expect(
-        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
-      ).not.toBeNull();
-    });
-  });
-
-  it("shows context menu for element", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const contextMenuOptions = document.querySelectorAll(".context-menu li");
-    const expectedShortcutNames: ShortcutName[] = [
-      "copyStyles",
-      "pasteStyles",
-      "deleteSelectedElements",
-      "addToLibrary",
-      "flipHorizontal",
-      "flipVertical",
-      "sendBackward",
-      "bringForward",
-      "sendToBack",
-      "bringToFront",
-      "duplicateSelection",
-    ];
-
-    expect(contextMenu).not.toBeNull();
-    expect(contextMenuOptions.length).toBe(expectedShortcutNames.length);
-    expectedShortcutNames.forEach((shortcutName) => {
-      expect(
-        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
-      ).not.toBeNull();
-    });
-  });
-
-  it("shows 'Group selection' in context menu for multiple selected elements", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(10, 10);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, -10);
-    mouse.up(10, 10);
-
-    mouse.reset();
-    mouse.click(10, 10);
-    Keyboard.withModifierKeys({ shift: true }, () => {
-      mouse.click(20, 0);
-    });
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-
-    const contextMenu = document.querySelector(".context-menu");
-    const contextMenuOptions = document.querySelectorAll(".context-menu li");
-    const expectedShortcutNames: ShortcutName[] = [
-      "copyStyles",
-      "pasteStyles",
-      "deleteSelectedElements",
-      "group",
-      "addToLibrary",
-      "sendBackward",
-      "bringForward",
-      "sendToBack",
-      "bringToFront",
-      "duplicateSelection",
-    ];
-
-    expect(contextMenu).not.toBeNull();
-    expect(contextMenuOptions.length).toBe(expectedShortcutNames.length);
-    expectedShortcutNames.forEach((shortcutName) => {
-      expect(
-        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
-      ).not.toBeNull();
-    });
-  });
-
-  it("shows 'Ungroup selection' in context menu for group inside selected elements", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(10, 10);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, -10);
-    mouse.up(10, 10);
-
-    mouse.reset();
-    mouse.click(10, 10);
-    Keyboard.withModifierKeys({ shift: true }, () => {
-      mouse.click(20, 0);
-    });
-
-    Keyboard.withModifierKeys({ ctrl: true }, () => {
-      Keyboard.codePress(CODES.G);
-    });
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-
-    const contextMenu = document.querySelector(".context-menu");
-    const contextMenuOptions = document.querySelectorAll(".context-menu li");
-    const expectedShortcutNames: ShortcutName[] = [
-      "copyStyles",
-      "pasteStyles",
-      "deleteSelectedElements",
-      "ungroup",
-      "addToLibrary",
-      "sendBackward",
-      "bringForward",
-      "sendToBack",
-      "bringToFront",
-      "duplicateSelection",
-    ];
-
-    expect(contextMenu).not.toBeNull();
-    expect(contextMenuOptions.length).toBe(expectedShortcutNames.length);
-    expectedShortcutNames.forEach((shortcutName) => {
-      expect(
-        contextMenu?.querySelector(`li[data-testid="${shortcutName}"]`),
-      ).not.toBeNull();
-    });
-  });
-
-  it("selecting 'Copy styles' in context menu copies styles", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    expect(copiedStyles).toBe("{}");
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
-    expect(copiedStyles).not.toBe("{}");
-    const element = JSON.parse(copiedStyles);
-    expect(element).toEqual(API.getSelectedElement());
-  });
-
-  it("selecting 'Paste styles' in context menu pastes styles", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    // Change some styles of second rectangle
-    clickLabeledElement("Stroke");
-    clickLabeledElement("#c92a2a");
-    clickLabeledElement("Background");
-    clickLabeledElement("#e64980");
-    // Fill style
-    fireEvent.click(screen.getByTitle("Cross-hatch"));
-    // Stroke width
-    fireEvent.click(screen.getByTitle("Bold"));
-    // Stroke style
-    fireEvent.click(screen.getByTitle("Dotted"));
-    // Roughness
-    fireEvent.click(screen.getByTitle("Cartoonist"));
-    // Opacity
-    fireEvent.change(screen.getByLabelText("Opacity"), {
-      target: { value: "60" },
-    });
-
-    mouse.reset();
-    // Copy styles of second rectangle
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 40,
-      clientY: 40,
-    });
-    let contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Copy styles")!);
-    const secondRect = JSON.parse(copiedStyles);
-    expect(secondRect.id).toBe(h.elements[1].id);
-
-    mouse.reset();
-    // Paste styles to first rectangle
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 10,
-      clientY: 10,
-    });
-    contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Paste styles")!);
-
-    const firstRect = API.getSelectedElement();
-    expect(firstRect.id).toBe(h.elements[0].id);
-    expect(firstRect.strokeColor).toBe("#c92a2a");
-    expect(firstRect.backgroundColor).toBe("#e64980");
-    expect(firstRect.fillStyle).toBe("cross-hatch");
-    expect(firstRect.strokeWidth).toBe(2); // Bold: 2
-    expect(firstRect.strokeStyle).toBe("dotted");
-    expect(firstRect.roughness).toBe(2); // Cartoonist: 2
-    expect(firstRect.opacity).toBe(60);
-  });
-
-  it("selecting 'Delete' in context menu deletes element", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(queryAllByText(contextMenu as HTMLElement, "Delete")[0]);
-    expect(API.getSelectedElements()).toHaveLength(0);
-    expect(h.elements[0].isDeleted).toBe(true);
-  });
-
-  it("selecting 'Add to library' in context menu adds element to library", async () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Add to library")!);
-
-    await waitFor(() => {
-      const library = localStorage.getItem("excalidraw-library");
-      expect(library).not.toBeNull();
-      const addedElement = JSON.parse(library!)[0][0];
-      expect(addedElement).toEqual(h.elements[0]);
-    });
-  });
-
-  it("selecting 'Duplicate' in context menu duplicates element", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Duplicate")!);
-    expect(h.elements).toHaveLength(2);
-    const { id: _id0, seed: _seed0, x: _x0, y: _y0, ...rect1 } = h.elements[0];
-    const { id: _id1, seed: _seed1, x: _x1, y: _y1, ...rect2 } = h.elements[1];
-    expect(rect1).toEqual(rect2);
-  });
-
-  it("selecting 'Send backward' in context menu sends element backward", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 40,
-      clientY: 40,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const elementsBefore = h.elements;
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Send backward")!);
-    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
-    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
-  });
-
-  it("selecting 'Bring forward' in context menu brings element forward", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 10,
-      clientY: 10,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const elementsBefore = h.elements;
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring forward")!);
-    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
-    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
-  });
-
-  it("selecting 'Send to back' in context menu sends element to back", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 40,
-      clientY: 40,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const elementsBefore = h.elements;
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Send to back")!);
-    expect(elementsBefore[1].id).toEqual(h.elements[0].id);
-  });
-
-  it("selecting 'Bring to front' in context menu brings element to front", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 10,
-      clientY: 10,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    const elementsBefore = h.elements;
-    fireEvent.click(queryByText(contextMenu as HTMLElement, "Bring to front")!);
-    expect(elementsBefore[0].id).toEqual(h.elements[1].id);
-  });
-
-  it("selecting 'Group selection' in context menu groups selected elements", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    Keyboard.withModifierKeys({ shift: true }, () => {
-      mouse.click(10, 10);
-    });
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(
-      queryByText(contextMenu as HTMLElement, "Group selection")!,
-    );
-    const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
-    expect(h.elements[0].groupIds).toEqual(selectedGroupIds);
-    expect(h.elements[1].groupIds).toEqual(selectedGroupIds);
-  });
-
-  it("selecting 'Ungroup selection' in context menu ungroups selected group", () => {
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    UI.clickTool("rectangle");
-    mouse.down(10, 10);
-    mouse.up(20, 20);
-
-    mouse.reset();
-    Keyboard.withModifierKeys({ shift: true }, () => {
-      mouse.click(10, 10);
-    });
-
-    Keyboard.withModifierKeys({ ctrl: true }, () => {
-      Keyboard.codePress(CODES.G);
-    });
-
-    fireEvent.contextMenu(GlobalTestState.canvas, {
-      button: 2,
-      clientX: 1,
-      clientY: 1,
-    });
-    const contextMenu = document.querySelector(".context-menu");
-    fireEvent.click(
-      queryByText(contextMenu as HTMLElement, "Ungroup selection")!,
-    );
-
-    const selectedGroupIds = Object.keys(h.state.selectedGroupIds);
-    expect(selectedGroupIds).toHaveLength(0);
-    expect(h.elements[0].groupIds).toHaveLength(0);
-    expect(h.elements[1].groupIds).toHaveLength(0);
-  });
-
   it("deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element", () => {
     UI.clickTool("ellipse");
     mouse.down();

Some files were not shown because too many files changed in this diff