diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
index cca10f327de..19a6d907c7a 100644
--- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
+++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
@@ -1233,21 +1233,21 @@ test.describe.parallel('Tables', () => {
aa
- bb
+ bb
|
- cc
+ cc
|
- d
+ d
|
- e
+ e
|
- f
+ f
|
diff --git a/packages/lexical-react/src/__tests__/unit/Collaboration.test.ts b/packages/lexical-react/src/__tests__/unit/Collaboration.test.ts
index 3291750ac60..7e5b7dd2e67 100644
--- a/packages/lexical-react/src/__tests__/unit/Collaboration.test.ts
+++ b/packages/lexical-react/src/__tests__/unit/Collaboration.test.ts
@@ -7,6 +7,7 @@
*/
import {
+ $createLineBreakNode,
$createParagraphNode,
$createTextNode,
$getRoot,
@@ -290,6 +291,55 @@ describe('Collaboration', () => {
client2.stop();
});
+ it('Should not sync direction of element node', async () => {
+ const connector = createTestConnection();
+ const client1 = connector.createClient('1');
+ const client2 = connector.createClient('2');
+ client1.start(container!);
+ client2.start(container!);
+
+ await expectCorrectInitialContent(client1, client2);
+
+ // Add paragraph with RTL text, then another with a non-TextNode child
+ await waitForReact(() => {
+ client1.update(() => {
+ const root = $getRoot().clear();
+ root.append(
+ $createParagraphNode().append($createTextNode('فرعي')),
+ $createParagraphNode().append($createLineBreakNode()),
+ );
+ });
+ });
+
+ // Check that the second paragraph has RTL direction
+ expect(client1.getHTML()).toEqual(
+ 'فرعي
',
+ );
+ expect(client2.getHTML()).toEqual(client1.getHTML());
+
+ // Mark the second paragraph's child as dirty to force the reconciler to run.
+ await waitForReact(() => {
+ client1.update(() => {
+ const pargraph = $getRoot().getChildAtIndex(1)!;
+ const lineBreak = pargraph.getFirstChildOrThrow();
+ lineBreak.markDirty();
+ });
+ });
+
+ // There was no activeEditorDirection when processing this node, so direction should be set back to null.
+ expect(client1.getHTML()).toEqual(
+ 'فرعي
',
+ );
+
+ // Check that the second paragraph still has RTL direction on client 2, as __dir is not synced.
+ expect(client2.getHTML()).toEqual(
+ 'فرعي
',
+ );
+
+ client1.stop();
+ client2.stop();
+ });
+
it('Should allow the passing of arbitrary awareness data', async () => {
const connector = createTestConnection();
diff --git a/packages/lexical-yjs/src/Utils.ts b/packages/lexical-yjs/src/Utils.ts
index f3c5153c6ed..6c1560ca6c3 100644
--- a/packages/lexical-yjs/src/Utils.ts
+++ b/packages/lexical-yjs/src/Utils.ts
@@ -51,6 +51,7 @@ const elementExcludedProperties = new Set([
'__first',
'__last',
'__size',
+ '__dir',
]);
const rootExcludedProperties = new Set(['__cachedText']);
const textExcludedProperties = new Set(['__text']);
diff --git a/packages/lexical/src/__tests__/unit/LexicalReconciler.test.ts b/packages/lexical/src/__tests__/unit/LexicalReconciler.test.ts
new file mode 100644
index 00000000000..1f92cdc2db8
--- /dev/null
+++ b/packages/lexical/src/__tests__/unit/LexicalReconciler.test.ts
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+ $createLineBreakNode,
+ $createParagraphNode,
+ $createTextNode,
+ $getRoot,
+ ParagraphNode,
+} from 'lexical';
+
+import {initializeUnitTest} from '../utils';
+
+describe('LexicalReconciler', () => {
+ initializeUnitTest((testEnv) => {
+ test('Should use activeEditorDirection as the direction for a node with no directioned text', async () => {
+ const {editor} = testEnv;
+
+ editor.update(() => {
+ const root = $getRoot().clear();
+ root.append(
+ $createParagraphNode().append($createTextNode('فرعي')),
+ $createParagraphNode().append($createTextNode('Hello')),
+ $createParagraphNode().append($createLineBreakNode()),
+ );
+ });
+
+ // The third paragraph has no directioned text, so it should be set to the direction of the previous sibling.
+ let para3Dir = editor.read(() => {
+ return $getRoot().getChildAtIndex(2)!.getDirection();
+ });
+ expect(para3Dir).toEqual('ltr');
+
+ // Mark the first and third paragraphs as dirty to force the reconciler to run.
+ editor.update(() => {
+ $getRoot().getChildAtIndex(0)!.markDirty();
+ $getRoot().getChildAtIndex(2)!.markDirty();
+ });
+
+ // Note: this is arguably a bug. It would be preferable for the node to keep its LTR direction. Added as a
+ // test so that the behaviour is at least documented.
+ para3Dir = editor.read(() => {
+ return $getRoot().getChildAtIndex(2)!.getDirection();
+ });
+ expect(para3Dir).toEqual('rtl');
+ });
+ });
+});