Prechádzať zdrojové kódy

fix: keep binding for attached arrows after changing text (#3754)

Co-authored-by: David Luzar <luzar.david@gmail.com>
connorhanafee 3 rokov pred
rodič
commit
969d3c694a

+ 2 - 1
src/components/App.tsx

@@ -1747,7 +1747,8 @@ class App extends React.Component<AppProps, AppState> {
               [element.id]: true,
             },
           }));
-        } else {
+        }
+        if (isDeleted) {
           fixBindingsAfterDeletion(this.scene.getElements(), [element]);
         }
         if (!isDeleted || isExistingElement) {

+ 110 - 1
src/tests/binding.test.tsx

@@ -1,5 +1,5 @@
 import React from "react";
-import { render } from "./test-utils";
+import { fireEvent, render } from "./test-utils";
 import ExcalidrawApp from "../excalidraw-app";
 import { UI, Pointer, Keyboard } from "./helpers/ui";
 import { getTransformHandles } from "../element/transformHandles";
@@ -104,4 +104,113 @@ describe("element binding", () => {
     Keyboard.keyPress(KEYS.ARROW_LEFT);
     expect(arrow.endBinding).toBe(null);
   });
+
+  it("should unbind on bound element deletion", () => {
+    const rectangle = UI.createElement("rectangle", {
+      x: 60,
+      y: 0,
+      size: 100,
+    });
+
+    const arrow = UI.createElement("arrow", {
+      x: 0,
+      y: 0,
+      size: 50,
+    });
+
+    expect(arrow.endBinding?.elementId).toBe(rectangle.id);
+
+    mouse.select(rectangle);
+    expect(API.getSelectedElement().type).toBe("rectangle");
+    Keyboard.keyDown(KEYS.DELETE);
+    expect(arrow.endBinding).toBe(null);
+  });
+
+  it("should unbind on text element deletion by submitting empty text", async () => {
+    const text = API.createElement({
+      type: "text",
+      text: "ola",
+      x: 60,
+      y: 0,
+      width: 100,
+      height: 100,
+    });
+
+    h.elements = [text];
+
+    const arrow = UI.createElement("arrow", {
+      x: 0,
+      y: 0,
+      size: 50,
+    });
+
+    expect(arrow.endBinding?.elementId).toBe(text.id);
+
+    // edit text element and submit
+    // -------------------------------------------------------------------------
+
+    UI.clickTool("text");
+
+    mouse.clickAt(text.x + 50, text.y + 50);
+    const editor = document.querySelector(
+      ".excalidraw-textEditorContainer > textarea",
+    ) as HTMLTextAreaElement;
+
+    expect(editor).not.toBe(null);
+
+    // we defer binding blur event on wysiwyg, hence wait a bit
+    await new Promise((r) => setTimeout(r, 30));
+
+    fireEvent.change(editor, { target: { value: "" } });
+    editor.blur();
+
+    expect(
+      document.querySelector(".excalidraw-textEditorContainer > textarea"),
+    ).toBe(null);
+    expect(arrow.endBinding).toBe(null);
+  });
+
+  it("should keep binding on text update", async () => {
+    const text = API.createElement({
+      type: "text",
+      text: "ola",
+      x: 60,
+      y: 0,
+      width: 100,
+      height: 100,
+    });
+
+    h.elements = [text];
+
+    const arrow = UI.createElement("arrow", {
+      x: 0,
+      y: 0,
+      size: 50,
+    });
+
+    expect(arrow.endBinding?.elementId).toBe(text.id);
+
+    // delete text element by submitting empty text
+    // -------------------------------------------------------------------------
+
+    UI.clickTool("text");
+
+    mouse.clickAt(text.x + 50, text.y + 50);
+    const editor = document.querySelector(
+      ".excalidraw-textEditorContainer > textarea",
+    ) as HTMLTextAreaElement;
+
+    expect(editor).not.toBe(null);
+
+    // we defer binding blur event on wysiwyg, hence wait a bit
+    await new Promise((r) => setTimeout(r, 30));
+
+    fireEvent.change(editor, { target: { value: "asdasdasdasdas" } });
+    editor.blur();
+
+    expect(
+      document.querySelector(".excalidraw-textEditorContainer > textarea"),
+    ).toBe(null);
+    expect(arrow.endBinding?.elementId).toBe(text.id);
+  });
 });

+ 4 - 4
src/tests/data/__snapshots__/restore.test.ts.snap

@@ -253,7 +253,7 @@ Object {
   "fontFamily": 1,
   "fontSize": 14,
   "groupIds": Array [],
-  "height": 0,
+  "height": 100,
   "id": "id-text01",
   "isDeleted": false,
   "opacity": 100,
@@ -269,7 +269,7 @@ Object {
   "version": 1,
   "versionNonce": 0,
   "verticalAlign": "middle",
-  "width": 0,
+  "width": 100,
   "x": 0,
   "y": 0,
 }
@@ -285,7 +285,7 @@ Object {
   "fontFamily": 1,
   "fontSize": 10,
   "groupIds": Array [],
-  "height": 0,
+  "height": 100,
   "id": "id-text01",
   "isDeleted": false,
   "opacity": 100,
@@ -301,7 +301,7 @@ Object {
   "version": 1,
   "versionNonce": 0,
   "verticalAlign": "top",
-  "width": 0,
+  "width": 100,
   "x": 0,
   "y": 0,
 }

+ 2 - 0
src/tests/helpers/api.ts

@@ -139,6 +139,8 @@ export class API {
           textAlign: rest.textAlign ?? appState.currentItemTextAlign,
           verticalAlign: rest.verticalAlign ?? DEFAULT_VERTICAL_ALIGN,
         });
+        element.width = width;
+        element.height = height;
         break;
       case "freedraw":
         element = newFreeDrawElement({