From e7d5702847205e5bc02b7ffd9c74cdc8a9e0609d Mon Sep 17 00:00:00 2001 From: Karan Gandhi Date: Tue, 14 Jul 2026 17:52:59 +0530 Subject: [PATCH 1/2] fix(VB-1797): match caret direction to text when inline editing Inline-editable fields kept an LTR caret even when the content was right-to-left, so the caret ignored where RTL text (Arabic, Hebrew) was being inserted. Set dir="auto" on the editable element so the browser derives writing direction from the content, and clear it on teardown. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../utils/__test__/enableInlineEditing.test.ts | 15 +++++++++++++++ src/visualBuilder/utils/enableInlineEditing.ts | 3 +++ src/visualBuilder/utils/handleIndividualFields.ts | 1 + 3 files changed, 19 insertions(+) diff --git a/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts b/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts index c8565a53..b2b7ef6d 100644 --- a/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts +++ b/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts @@ -127,6 +127,21 @@ describe("enableInlineEditing", () => { expect(editableElement.focus).toHaveBeenCalled(); }); + it("should set dir=auto so the caret follows the text direction", () => { + enableInlineEditing({ + expectedFieldData: "Test content", + editableElement, + fieldType: FieldDataType.SINGLELINE, + elements: { + visualBuilderContainer, + resizeObserver, + lastEditedField: null, + }, + }); + + expect(editableElement.getAttribute("dir")).toBe("auto"); + }); + it("should handle multiline fields correctly", () => { enableInlineEditing({ expectedFieldData: "Test content", diff --git a/src/visualBuilder/utils/enableInlineEditing.ts b/src/visualBuilder/utils/enableInlineEditing.ts index ea020a92..b9f396cd 100644 --- a/src/visualBuilder/utils/enableInlineEditing.ts +++ b/src/visualBuilder/utils/enableInlineEditing.ts @@ -92,6 +92,9 @@ export function enableInlineEditing({ } actualEditableField.setAttribute("contenteditable", "true"); + // Let the caret follow the text's own direction so RTL content (e.g. + // Arabic, Hebrew) reads naturally instead of keeping an LTR caret. + actualEditableField.setAttribute("dir", "auto"); actualEditableField.addEventListener("input", handleFieldInput); actualEditableField.addEventListener("keydown", handleFieldKeyDown); diff --git a/src/visualBuilder/utils/handleIndividualFields.ts b/src/visualBuilder/utils/handleIndividualFields.ts index 3ddb6763..52f27869 100644 --- a/src/visualBuilder/utils/handleIndividualFields.ts +++ b/src/visualBuilder/utils/handleIndividualFields.ts @@ -133,6 +133,7 @@ export function cleanIndividualFieldResidual(elements: { VISUAL_BUILDER_FIELD_TYPE_ATTRIBUTE_KEY ); previousSelectedEditableDOM.removeAttribute("contenteditable"); + previousSelectedEditableDOM.removeAttribute("dir"); previousSelectedEditableDOM.removeEventListener( "input", handleFieldInput From a06ad3403106cad06d899034f76d2d9a180b7460 Mon Sep 17 00:00:00 2001 From: Karan Gandhi Date: Thu, 16 Jul 2026 17:28:21 +0530 Subject: [PATCH 2/2] fix(VB-1797): keep dir=auto working on the pseudo editable element The pseudo element used for truncated/mismatched fields copies the source element's computed direction/unicode-bidi inline, which overrode dir="auto" and left an LTR caret on RTL content. Exclude both from the copied styles so the attribute governs direction on that path too. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../utils/__test__/enableInlineEditing.test.ts | 18 ++++++++++++++++++ .../utils/__test__/getStyleOfAnElement.test.ts | 10 ++++++++++ src/visualBuilder/utils/enableInlineEditing.ts | 3 +-- src/visualBuilder/utils/getStyleOfAnElement.ts | 3 +++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts b/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts index b2b7ef6d..2bee0a0e 100644 --- a/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts +++ b/src/visualBuilder/utils/__test__/enableInlineEditing.test.ts @@ -142,6 +142,24 @@ describe("enableInlineEditing", () => { expect(editableElement.getAttribute("dir")).toBe("auto"); }); + it("should set dir=auto on the pseudo element when one is created", () => { + // Content differs from expected, so a pseudo editable element is used. + enableInlineEditing({ + expectedFieldData: "Different content", + editableElement, + fieldType: FieldDataType.SINGLELINE, + elements: { + visualBuilderContainer, + resizeObserver, + lastEditedField: null, + }, + }); + + const pseudoElement = + generatePseudoEditableElement.mock.results[0].value; + expect(pseudoElement.getAttribute("dir")).toBe("auto"); + }); + it("should handle multiline fields correctly", () => { enableInlineEditing({ expectedFieldData: "Test content", diff --git a/src/visualBuilder/utils/__test__/getStyleOfAnElement.test.ts b/src/visualBuilder/utils/__test__/getStyleOfAnElement.test.ts index bc449ba8..22decf8f 100644 --- a/src/visualBuilder/utils/__test__/getStyleOfAnElement.test.ts +++ b/src/visualBuilder/utils/__test__/getStyleOfAnElement.test.ts @@ -42,4 +42,14 @@ describe("getStyleOfAnElement", () => { width: "100px", }); }); + + test("it should not copy direction/unicode-bidi so dir=auto can govern", () => { + const elem = document.createElement("div"); + elem.style.direction = "rtl"; + elem.style.unicodeBidi = "isolate"; + + const style = getStyleOfAnElement(elem); + expect(style).not.toHaveProperty("direction"); + expect(style).not.toHaveProperty("unicode-bidi"); + }); }); diff --git a/src/visualBuilder/utils/enableInlineEditing.ts b/src/visualBuilder/utils/enableInlineEditing.ts index b9f396cd..92563775 100644 --- a/src/visualBuilder/utils/enableInlineEditing.ts +++ b/src/visualBuilder/utils/enableInlineEditing.ts @@ -92,8 +92,7 @@ export function enableInlineEditing({ } actualEditableField.setAttribute("contenteditable", "true"); - // Let the caret follow the text's own direction so RTL content (e.g. - // Arabic, Hebrew) reads naturally instead of keeping an LTR caret. + // caret follows the text direction (fixes LTR caret on RTL content) actualEditableField.setAttribute("dir", "auto"); actualEditableField.addEventListener("input", handleFieldInput); actualEditableField.addEventListener("keydown", handleFieldKeyDown); diff --git a/src/visualBuilder/utils/getStyleOfAnElement.ts b/src/visualBuilder/utils/getStyleOfAnElement.ts index d70a78b0..4451e249 100644 --- a/src/visualBuilder/utils/getStyleOfAnElement.ts +++ b/src/visualBuilder/utils/getStyleOfAnElement.ts @@ -30,6 +30,9 @@ export default function getStyleOfAnElement(element: HTMLElement): { "margin-bottom", "-webkit-user-modify", "cursor", + // let the pseudo element's dir="auto" decide direction, not inline copies + "direction", + "unicode-bidi", ]; const styles: { [key: string]: string } = {};