diff --git a/frontend/playwright/ui/specs/text-editor-v3.spec.js b/frontend/playwright/ui/specs/text-editor-v3.spec.js index 90b2316a88..35e5e1853a 100644 --- a/frontend/playwright/ui/specs/text-editor-v3.spec.js +++ b/frontend/playwright/ui/specs/text-editor-v3.spec.js @@ -67,3 +67,44 @@ test.describe("BUG 10502 - Mixed families and variants", () => { }); }); +test.describe("BUG 10530 - Empty text box left behind when leaving the editor", () => { + test("An empty text box is removed when leaving the editor by clicking outside", async ({ + page, + }) => { + const workspace = new WasmWorkspacePage(page, { textEditor: true }); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + await workspace.waitForFirstRender(); + + const layerRows = workspace.layers.getByTestId("layer-row"); + await expect(layerRows).toHaveCount(0); + + // Draw an empty text box + await workspace.createTextShape(200, 150, 320, 210); + // The shape exists while it is being edited + await expect(layerRows).toHaveCount(1); + + // Leave the editor by clicking outside + await workspace.clickAt(500, 400); + + await expect(layerRows).toHaveCount(0); + }); + + test("A non-empty text box is kept when leaving the editor by clicking outside", async ({ + page, + }) => { + const workspace = new WasmWorkspacePage(page, { textEditor: true }); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + await workspace.waitForFirstRender(); + + const layerRows = workspace.layers.getByTestId("layer-row"); + + // A text box with content must survive leaving the editor by clicking outside. + await workspace.createTextShape(200, 150, 320, 210, "hello"); + await workspace.clickAt(500, 400); + + await expect(layerRows).toHaveCount(1); + }); +}); + diff --git a/frontend/src/app/main/data/workspace/edition.cljs b/frontend/src/app/main/data/workspace/edition.cljs index d7822d4142..e1d33fa136 100644 --- a/frontend/src/app/main/data/workspace/edition.cljs +++ b/frontend/src/app/main/data/workspace/edition.cljs @@ -10,7 +10,6 @@ [app.main.data.workspace.path.common :as dwpc] [app.main.features :as features] [app.render-wasm.api :as wasm.api] - [app.render-wasm.text-editor :as text-editor] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -66,6 +65,7 @@ ptk/EffectEvent (effect [_ state _] (when (features/active-feature? state "text-editor-wasm/v1") - (text-editor/text-editor-dispose) + ;; NOTE: the WASM text editor is disposed by the v3 editor component on + ;; unmount, *after* it finalizes its content. (wasm.api/request-render "clear-edition-mode"))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs index 03cb433153..47cb26696f 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs @@ -318,11 +318,13 @@ (fn [] (when-let [node (mf/ref-val contenteditable-ref)] (.focus node)) - ;; Explicitly call on-blur here instead of relying on browser blur events, - ;; because in Firefox blur is not reliably fired when leaving the text editor - ;; by clicking elsewhere. The component does unmount when the shape is - ;; deselected, so we can safely call the blur handler here to finalize the editor. - on-blur)) + ;; On unmount, finalize the editor content and then dispose the WASM editor. + ;; We finalize on unmount instead of relying on the browser blur event, because + ;; it was not being reliable (timing issues, Firefox issues…) + (fn [] + (on-blur) + (text-editor/text-editor-dispose) + (wasm.api/request-render "text-editor-dispose")))) (mf/use-effect (fn []