diff --git a/.flowconfig b/.flowconfig index 8d966996cb3..78723b4a8dd 100644 --- a/.flowconfig +++ b/.flowconfig @@ -68,6 +68,7 @@ module.name_mapper='^@lexical/react/LexicalNodeMenuPlugin$' -> '/p module.name_mapper='^@lexical/react/LexicalOnChangePlugin$' -> '/packages/lexical-react/flow/LexicalOnChangePlugin.js.flow' module.name_mapper='^@lexical/react/LexicalPlainTextPlugin$' -> '/packages/lexical-react/flow/LexicalPlainTextPlugin.js.flow' module.name_mapper='^@lexical/react/LexicalRichTextPlugin$' -> '/packages/lexical-react/flow/LexicalRichTextPlugin.js.flow' +module.name_mapper='^@lexical/react/LexicalScrollableNodePlugin$' -> '/packages/lexical-react/flow/LexicalScrollableNodePlugin.js.flow' module.name_mapper='^@lexical/react/LexicalTabIndentationPlugin$' -> '/packages/lexical-react/flow/LexicalTabIndentationPlugin.js.flow' module.name_mapper='^@lexical/react/LexicalTableOfContents$' -> '/packages/lexical-react/flow/LexicalTableOfContents.js.flow' module.name_mapper='^@lexical/react/LexicalTableOfContentsPlugin$' -> '/packages/lexical-react/flow/LexicalTableOfContentsPlugin.js.flow' diff --git a/examples/react-table/src/App.tsx b/examples/react-table/src/App.tsx index c42a31342f9..84429d486e0 100644 --- a/examples/react-table/src/App.tsx +++ b/examples/react-table/src/App.tsx @@ -12,6 +12,7 @@ import {ContentEditable} from '@lexical/react/LexicalContentEditable'; import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary'; import {HistoryPlugin} from '@lexical/react/LexicalHistoryPlugin'; import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin'; +import {ScrollableNodePlugin} from '@lexical/react/LexicalScrollableNodePlugin'; import {TablePlugin} from '@lexical/react/LexicalTablePlugin'; import { INSERT_TABLE_COMMAND, @@ -87,6 +88,7 @@ export default function App() { + diff --git a/packages/lexical-devtools/tsconfig.json b/packages/lexical-devtools/tsconfig.json index 74c92e8040d..82118929b17 100644 --- a/packages/lexical-devtools/tsconfig.json +++ b/packages/lexical-devtools/tsconfig.json @@ -120,6 +120,9 @@ "@lexical/react/LexicalRichTextPlugin": [ "../lexical-react/src/LexicalRichTextPlugin.tsx" ], + "@lexical/react/LexicalScrollableNodePlugin": [ + "../lexical-react/src/LexicalScrollableNodePlugin.ts" + ], "@lexical/react/LexicalTabIndentationPlugin": [ "../lexical-react/src/LexicalTabIndentationPlugin.tsx" ], diff --git a/packages/lexical-playground/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs b/packages/lexical-playground/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs index efe46564db1..1c9a8bd2548 100644 --- a/packages/lexical-playground/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs @@ -42,64 +42,66 @@ test.describe('HTML Tables CopyAndPaste', () => { await assertHTML( page, html` - - - - - - - - - - - - - - - - -
-

- a -

-
-

- b -

-

- b -

-
-

- c -

-
-

- d -

-
-

- e -

-
-

- f -

-
+
+ + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ b +

+

+ b +

+
+

+ c +

+
+

+ d +

+
+

+ e +

+
+

+ f +

+
+
`, ); }); @@ -117,71 +119,73 @@ test.describe('HTML Tables CopyAndPaste', () => { await assertHTML( page, html` - - - - - - - - - - - - - - - - -
-

- a -

-
-

- b -

-

- b -

-
-

- c -

-
-

- d -

-
-

- e -

-
-

- f -

-
+
+ + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ b +

+

+ b +

+
+

+ c +

+
+

+ d +

+
+

+ e +

+
+

+ f +

+
+
`, ); }); @@ -200,64 +204,66 @@ test.describe('HTML Tables CopyAndPaste', () => { await assertHTML( page, html` - - - - - - - - - - - - - - - - -
-

- a -

-
-

- b -

-

- b -

-
-

- c -

-
-

- d -

-
-

- e -

-
-

- f -

-
+
+ + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ b +

+

+ b +

+
+

+ c +

+
+

+ d +

+
+

+ e +

+
+

+ f +

+
+
`, ); }); @@ -290,211 +296,215 @@ test.describe('HTML Tables CopyAndPaste', () => { page, html`


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- a -

-
-

- b -

-
-


-
-


-
-

- c -

-
-

- d -

-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ b +

+
+


+
+


+
+

+ c +

+
+

+ d +

+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, html`


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- a -

-
-

- b -

-
-


-
-


-
-

- c -

-
-

- d -

-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ b +

+
+


+
+


+
+

+ c +

+
+

+ d +

+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -557,49 +567,51 @@ test.describe('HTML Tables CopyAndPaste', () => {

123

- - - - - - - - - - - - - -
-

- 456 -

-
-

- 789 -

-

- 000 -

-
-

- ABC -

-

- 000 -

-

- 000 -

-
-

- DEF -

-
+
+ + + + + + + + + + + + + +
+

+ 456 +

+
+

+ 789 +

+

+ 000 +

+
+

+ ABC +

+

+ 000 +

+

+ 000 +

+
+

+ DEF +

+
+
`, ); }); @@ -676,87 +688,89 @@ test.describe('HTML Tables CopyAndPaste', () => {

123

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- 1 -

-
-

- 2 -

-
-

- 3 -

-
-

- 4 -

-
-

- 7 -

-
-


-
-


-
-


-
-


-
-

- 8 -

-
-

- 9 -

-
-


-
-


-
-

- 0 -

-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ 1 +

+
+

+ 2 +

+
+

+ 3 +

+
+

+ 4 +

+
+

+ 7 +

+
+


+
+


+
+


+
+


+
+

+ 8 +

+
+

+ 9 +

+
+


+
+


+
+

+ 0 +

+
+


+
+


+
+
`, ); }); diff --git a/packages/lexical-playground/__tests__/e2e/Indentation.spec.mjs b/packages/lexical-playground/__tests__/e2e/Indentation.spec.mjs index 3a856bd19f3..80087aeeb4c 100644 --- a/packages/lexical-playground/__tests__/e2e/Indentation.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Indentation.spec.mjs @@ -104,23 +104,25 @@ test.describe('Identation', () => { code


- - - - - - - -
-

- foo -

-
+
+ + + + + + + +
+

+ foo +

+
+


`, ); @@ -189,24 +191,26 @@ test.describe('Identation', () => { style="padding-inline-start: calc(40px)">

- - - - - - - -
-

- foo -

-
+
+ + + + + + + +
+

+ foo +

+
+

@@ -285,24 +289,26 @@ test.describe('Identation', () => { style="padding-inline-start: calc(80px)">

- - - - - - - -
-

- foo -

-
+
+ + + + + + + +
+

+ foo +

+
+

@@ -375,24 +381,26 @@ test.describe('Identation', () => { style="padding-inline-start: calc(40px)">

- - - - - - - -
-

- foo -

-
+
+ + + + + + + +
+

+ foo +

+
+

@@ -455,24 +463,26 @@ test.describe('Identation', () => { code


- - - - - - - -
-

- foo -

-
+
+ + + + + + + +
+

+ foo +

+
+


`, ); diff --git a/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs b/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs index 5addcdfbc1a..3fd735201a9 100644 --- a/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs @@ -398,22 +398,24 @@ test.describe.parallel('Selection', () => { dir="ltr"> abc

- - - - - - - - - -
-


-
-


-
+
+ + + + + + + + + +
+


+
+


+
+


`, ); @@ -684,7 +686,8 @@ test.describe.parallel('Selection', () => { { anchorOffset: {desc: 'beginning of cell', value: 0}, anchorPath: [ - {desc: 'index of table in root', value: 1}, + {desc: 'index of scrollable in root', value: 1}, + {desc: 'table is firstChild of scrollable', value: 0}, {desc: 'first table row', value: 1}, {desc: 'second cell', value: 1}, {desc: 'first paragraph', value: 0}, @@ -693,7 +696,8 @@ test.describe.parallel('Selection', () => { ], focusOffset: {desc: 'full text length', value: cellText.length}, focusPath: [ - {desc: 'index of table in root', value: 1}, + {desc: 'index of scrollable in root', value: 1}, + {desc: 'table is firstChild of scrollable', value: 0}, {desc: 'first table row', value: 1}, {desc: 'second cell', value: 1}, {desc: 'first paragraph', value: 0}, @@ -714,14 +718,16 @@ test.describe.parallel('Selection', () => { { anchorOffset: {desc: 'beginning of cell', value: 0}, anchorPath: [ - {desc: 'index of table in root', value: 1}, + {desc: 'index of scrollable in root', value: 1}, + {desc: 'table is firstChild of scrollable', value: 0}, {desc: 'first table row', value: 1}, {desc: 'first cell', value: 0}, {desc: 'beginning of text', value: 0}, ], focusOffset: {desc: 'beginning of text', value: 0}, focusPath: [ - {desc: 'index of table in root', value: 1}, + {desc: 'index of scrollable in root', value: 1}, + {desc: 'table is firstChild of scrollable', value: 0}, {desc: 'first table row', value: 1}, {desc: 'first cell', value: 0}, {desc: 'beginning of text', value: 0}, @@ -770,7 +776,8 @@ test.describe.parallel('Selection', () => { { anchorOffset: {desc: 'beginning of cell', value: 0}, anchorPath: [ - {desc: 'index of table in root', value: 1}, + {desc: 'index of scrollable in root', value: 1}, + {desc: 'table is firstChild of scrollable', value: 0}, {desc: 'first table row', value: 1}, {desc: 'first cell', value: 0}, ], @@ -981,8 +988,8 @@ test.describe.parallel('Selection', () => { await assertSelection(page, { anchorOffset: 0, anchorPath: [0], - focusOffset: 0, - focusPath: [2], + focusOffset: 1, + focusPath: [1, 0, 2, 1], }); }); @@ -1006,8 +1013,8 @@ test.describe.parallel('Selection', () => { await assertSelection(page, { anchorOffset: 0, anchorPath: [2], - focusOffset: 0, - focusPath: [0], + focusOffset: 1, + focusPath: [1, 0, 1, 0], }); }); @@ -1029,7 +1036,7 @@ test.describe.parallel('Selection', () => { anchorOffset: 0, anchorPath: [0], focusOffset: 1, - focusPath: [1, 2, 1], + focusPath: [1, 0, 2, 1], }); }, ); @@ -1052,7 +1059,7 @@ test.describe.parallel('Selection', () => { anchorOffset: 0, anchorPath: [1], focusOffset: 1, - focusPath: [0, 1, 0], + focusPath: [0, 0, 1, 0], }); }, ); diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index fa5d8574235..0d7e6c83ec5 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -90,28 +90,30 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


`, undefined, @@ -136,28 +138,30 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-

abc

-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+

abc

+
+


+
+


+
+


+
+


`, undefined, @@ -180,9 +184,9 @@ test.describe.parallel('Tables', () => { await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); await moveLeft(page, 1); @@ -197,17 +201,17 @@ test.describe.parallel('Tables', () => { await page.keyboard.type('ab'); await assertSelection(page, { anchorOffset: 2, - anchorPath: [1, 1, 0, 0, 0, 0], + anchorPath: [1, 0, 1, 0, 0, 0, 0], focusOffset: 2, - focusPath: [1, 1, 0, 0, 0, 0], + focusPath: [1, 0, 1, 0, 0, 0, 0], }); await moveRight(page, 3); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 1, 0], + anchorPath: [1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 2, 1, 0], + focusPath: [1, 0, 2, 1, 0], }); }); @@ -225,9 +229,9 @@ test.describe.parallel('Tables', () => { await moveRight(page, 3); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 1, 0], + anchorPath: [1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 2, 1, 0], + focusPath: [1, 0, 2, 1, 0], }); await moveRight(page, 1); @@ -242,9 +246,9 @@ test.describe.parallel('Tables', () => { await page.keyboard.type('ab'); await assertSelection(page, { anchorOffset: 2, - anchorPath: [1, 2, 1, 0, 0, 0], + anchorPath: [1, 0, 2, 1, 0, 0, 0], focusOffset: 2, - focusPath: [1, 2, 1, 0, 0, 0], + focusPath: [1, 0, 2, 1, 0, 0, 0], }); await moveRight(page, 3); @@ -270,17 +274,17 @@ test.describe.parallel('Tables', () => { await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 1, 0, 1, 0, 0], }); await moveLeft(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); }); @@ -299,17 +303,17 @@ test.describe.parallel('Tables', () => { await moveRight(page, 3); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 1, 2, 1, 0], + anchorPath: [1, 0, 1, 0, 1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 1, 0, 1, 2, 1, 0], + focusPath: [1, 0, 1, 0, 1, 0, 2, 1, 0], }); await moveRight(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 2], + anchorPath: [1, 0, 1, 0, 2], focusOffset: 0, - focusPath: [1, 1, 0, 2], + focusPath: [1, 0, 1, 0, 2], }); }); }); @@ -344,36 +348,38 @@ test.describe.parallel('Tables', () => { await deleteBackward(page); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 1, 0], + anchorPath: [1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 2, 1, 0], + focusPath: [1, 0, 2, 1, 0], }); await assertHTML( page, html`


- - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+
`, undefined, {ignoreClasses: true}, @@ -383,12 +389,11 @@ test.describe.parallel('Tables', () => { // The native window selection should be on the root, whereas // the editor selection should be on the last cell of the table. await assertSelection(page, { - anchorOffset: 2, - anchorPath: [], - focusOffset: 2, - focusPath: [], + anchorOffset: 1, + anchorPath: [1], + focusOffset: 1, + focusPath: [1], }); - await page.keyboard.press('Enter'); await assertSelection(page, { anchorOffset: 0, @@ -401,28 +406,30 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


`, undefined, @@ -462,28 +469,30 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+

a

`, undefined, @@ -513,9 +522,9 @@ test.describe.parallel('Tables', () => { await moveLeft(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 1, 0], + anchorPath: [1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 2, 1, 0], + focusPath: [1, 0, 2, 1, 0], }); }); @@ -535,28 +544,30 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


`, undefined, @@ -565,57 +576,57 @@ test.describe.parallel('Tables', () => { await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); await moveRight(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 1, 0], + anchorPath: [1, 0, 1, 1, 0], focusOffset: 0, - focusPath: [1, 1, 1, 0], + focusPath: [1, 0, 1, 1, 0], }); await moveRight(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 0, 0], + anchorPath: [1, 0, 2, 0, 0], focusOffset: 0, - focusPath: [1, 2, 0, 0], + focusPath: [1, 0, 2, 0, 0], }); await moveRight(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 1, 0], + anchorPath: [1, 0, 2, 1, 0], focusOffset: 0, - focusPath: [1, 2, 1, 0], + focusPath: [1, 0, 2, 1, 0], }); await moveLeft(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 0, 0], + anchorPath: [1, 0, 2, 0, 0], focusOffset: 0, - focusPath: [1, 2, 0, 0], + focusPath: [1, 0, 2, 0, 0], }); await moveLeft(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 1, 0], + anchorPath: [1, 0, 1, 1, 0], focusOffset: 0, - focusPath: [1, 1, 1, 0], + focusPath: [1, 0, 1, 1, 0], }); await moveLeft(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); }); @@ -632,25 +643,25 @@ test.describe.parallel('Tables', () => { await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); await moveDown(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 2, 0, 0], + anchorPath: [1, 0, 2, 0, 0], focusOffset: 0, - focusPath: [1, 2, 0, 0], + focusPath: [1, 0, 2, 0, 0], }); await moveUp(page, 1); await assertSelection(page, { anchorOffset: 0, - anchorPath: [1, 1, 0, 0], + anchorPath: [1, 0, 1, 0, 0], focusOffset: 0, - focusPath: [1, 1, 0, 0], + focusPath: [1, 0, 1, 0, 0], }); }); @@ -668,9 +679,9 @@ test.describe.parallel('Tables', () => { await page.keyboard.type('@A'); await assertSelection(page, { anchorOffset: 2, - anchorPath: [1, 1, 0, 0, 0, 0], + anchorPath: [1, 0, 1, 0, 0, 0, 0], focusOffset: 2, - focusPath: [1, 1, 0, 0, 0, 0], + focusPath: [1, 0, 1, 0, 0, 0, 0], }); await waitForSelector(page, `#typeahead-menu ul li:first-child.selected`); @@ -678,9 +689,9 @@ test.describe.parallel('Tables', () => { await moveDown(page, 1); await assertSelection(page, { anchorOffset: 2, - anchorPath: [1, 1, 0, 0, 0, 0], + anchorPath: [1, 0, 1, 0, 0, 0, 0], focusOffset: 2, - focusPath: [1, 1, 0, 0, 0, 0], + focusPath: [1, 0, 1, 0, 0, 0, 0], }); await waitForSelector( @@ -715,6 +726,125 @@ test.describe.parallel('Tables', () => { page, html`


+
+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+
+


+ `, + html` +


+
+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+
+


+ `, + {ignoreClasses: true}, + ); + }, + ); + + test(`Can select cells using Table selection via keyboard`, async ({ + page, + isPlainText, + isCollab, + browserName, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 3, 3); + + await fillTablePartiallyWithText(page); + + let p = page; + + if (IS_COLLAB) { + await focusEditor(page); + p = await page.frame('left'); + } + + const firstRowFirstColumnCellBoundingBox = await p.locator( + 'table:first-of-type > :nth-match(tr, 1) > th:nth-child(1)', + ); + + // Focus on inside the iFrame or the boundingBox() below returns null. + await firstRowFirstColumnCellBoundingBox.click(); + + await page.keyboard.down('Shift'); + await page.keyboard.press('ArrowRight'); + // Firefox range selection spans across cells after two arrow key press + if (browserName === 'firefox') { + await page.keyboard.press('ArrowRight'); + } + await page.keyboard.press('ArrowDown'); + await page.keyboard.up('Shift'); + + await assertHTML( + page, + html` +


+
@@ -747,11 +877,24 @@ test.describe.parallel('Tables', () => {

f

+ + + + +
+


+
+


+
+


+
-


- `, - html` -


+
+


+ `, + html` +


+
@@ -780,157 +923,33 @@ test.describe.parallel('Tables', () => {

f

+ + + + +
+


+
+


+
+


+
-


- `, - {ignoreClasses: true}, - ); +
+


+ `, + {ignoreClasses: true, ignoreInlineStyles: true}, + ); + }); + + test( + `Can style text using Table selection`, + { + tag: '@flaky', }, - ); - - test(`Can select cells using Table selection via keyboard`, async ({ - page, - isPlainText, - isCollab, - browserName, - }) => { - await initialize({isCollab, page}); - test.skip(isPlainText); - - await focusEditor(page); - await insertTable(page, 3, 3); - - await fillTablePartiallyWithText(page); - - let p = page; - - if (IS_COLLAB) { - await focusEditor(page); - p = await page.frame('left'); - } - - const firstRowFirstColumnCellBoundingBox = await p.locator( - 'table:first-of-type > :nth-match(tr, 1) > th:nth-child(1)', - ); - - // Focus on inside the iFrame or the boundingBox() below returns null. - await firstRowFirstColumnCellBoundingBox.click(); - - await page.keyboard.down('Shift'); - await page.keyboard.press('ArrowRight'); - // Firefox range selection spans across cells after two arrow key press - if (browserName === 'firefox') { - await page.keyboard.press('ArrowRight'); - } - await page.keyboard.press('ArrowDown'); - await page.keyboard.up('Shift'); - - await assertHTML( - page, - html` -


- - - - - - - - - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
-


-
-


-
-


-
-


- `, - html` -


- - - - - - - - - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
-


-
-


-
-


-
-


- `, - {ignoreClasses: true, ignoreInlineStyles: true}, - ); - }); - - test( - `Can style text using Table selection`, - { - tag: '@flaky', - }, - async ({page, isPlainText, isCollab}) => { - await initialize({isCollab, page}); - test.skip(isPlainText); + async ({page, isPlainText, isCollab}) => { + await initialize({isCollab, page}); + test.skip(isPlainText); await focusEditor(page); await insertTable(page, 2, 3); @@ -953,6 +972,111 @@ test.describe.parallel('Tables', () => { page, html`


+
+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+
+


+ `, + html` +


+
+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+
+


+ `, + {ignoreClasses: true}, + ); + }, + ); + + test(`Can style on empty table cells and paragraphs with no text`, async ({ + page, + isPlainText, + isCollab, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 2, 3); + await selectAll(page); + + // Apply style on empty table + await clickSelectors(page, ['.bold']); + + // Add text after applying styles + await click(page, 'div[contenteditable="true"] p:first-of-type'); + await page.keyboard.type('abc'); + + await click(page, 'th p:first-of-type'); + await fillTablePartiallyWithText(page); + + // Check that the character styles are applied. + await assertHTML( + page, + html` +

abc

+
@@ -960,36 +1084,34 @@ test.describe.parallel('Tables', () => { - - - -
+

a

+

bb

-

cc

+

cc

+

d

+

e

-

f

+

f

-


- `, - html` -


+
+


+ `, + html` +

abc

+
@@ -1004,7 +1126,7 @@ test.describe.parallel('Tables', () => {

bb

@@ -1015,106 +1137,11 @@ test.describe.parallel('Tables', () => {

e

-

cc

+

cc

-

f

+

f

-


- `, - {ignoreClasses: true}, - ); - }, - ); - - test(`Can style on empty table cells and paragraphs with no text`, async ({ - page, - isPlainText, - isCollab, - }) => { - await initialize({isCollab, page}); - test.skip(isPlainText); - - await focusEditor(page); - await insertTable(page, 2, 3); - await selectAll(page); - - // Apply style on empty table - await clickSelectors(page, ['.bold']); - - // Add text after applying styles - await click(page, 'div[contenteditable="true"] p:first-of-type'); - await page.keyboard.type('abc'); - - await click(page, 'th p:first-of-type'); - await fillTablePartiallyWithText(page); - - // Check that the character styles are applied. - await assertHTML( - page, - html` -

abc

- - - - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
-


- `, - html` -

abc

- - - - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
+


`, {ignoreClasses: true}, @@ -1157,68 +1184,72 @@ test.describe.parallel('Tables', () => { page, html`

abc

- - - - - - - - - - - - - - - - -
-

aa

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
+
+ + + + + + + + + + + + + + + + +
+

aa

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


`, html`

abc

- - - - - - - - - - - - - - - - -
-

aa

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
+
+ + + + + + + + + + + + + + + + +
+

aa

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


`, {ignoreClasses: true}, @@ -1259,57 +1290,61 @@ test.describe.parallel('Tables', () => { await assertHTML( page, html` - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

d

-
-

e

-
- - - - - - - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
+
+ + + + + + + + + + + + + +
+

a

+
+

bb

+
+

d

+
+

e

+
+
+
+ + + + + + + + + + + + + + + + +
+

a

+
+

bb

+
+

cc

+
+

d

+
+

e

+
+

f

+
+


`, undefined, @@ -1346,35 +1381,37 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - -
-


-
-


-
-

cc

-
-


-
-


-
-

f

-
+
+ + + + + + + + + + + + + + + + +
+


+
+


+
+

cc

+
+


+
+


+
+

f

+
+


`, undefined, @@ -1403,40 +1440,44 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
-


-
-


-
+
+ + + + + + + + + +
+


+
+


+
+


`, html`


- - - - - - - - - -
-


-
-


-
+
+ + + + + + + + + +
+


+
+


+
+


`, {ignoreClasses: true}, @@ -1462,74 +1503,78 @@ test.describe.parallel('Tables', () => { page, html`

Hello World

- - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, html`

Hello World

- - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, {ignoreClasses: true}, @@ -1575,15 +1620,17 @@ test.describe.parallel('Tables', () => { await assertHTML( page, html` - - - - - -
-


-
+
+ + + + + +
+


+
+


`, ); @@ -1614,22 +1661,24 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
-

123

-
-


-
-


-
+
+ + + + + + + + + +
+

123

+
+


+
+


+
+


`, undefined, @@ -1671,51 +1720,53 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - -
-

- Hello -

-
-


-
-


-
-

- -

- Yellow flower in tilt shift lens -
- - <- it works! -

-
+
+ + + + + + + + + + + + + +
+

+ Hello +

+
+


+
+


+
+

+ +

+ Yellow flower in tilt shift lens +
+ + <- it works! +

+
+


`, ); @@ -1745,35 +1796,37 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
-

- cell one -

-
-

- first line -

-

- second line -

-
+
+ + + + + + + + + +
+

+ cell one +

+
+

+ first line +

+

+ second line +

+
+


`, ); @@ -1783,26 +1836,28 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
-

- cell one -

-
-


-
+
+ + + + + + + + + +
+

+ cell one +

+
+


+
+


`, ); @@ -1840,38 +1895,40 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - + +
-


- ${collapsibleOpeningTag} - -

- 123 -

-
-
-

- 123 -

+
+ + + + + + + + - - -


+ ${collapsibleOpeningTag} + +

+ 123 +

+
+
+

+ 123 +

+


+


+


+
+ ${collapsibleClosingTag}


+


- - ${collapsibleClosingTag} -


-
-


-
+
+


`, ); @@ -1881,33 +1938,35 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
-


- ${collapsibleOpeningTag} - -

- 123 -

-
-
-


-
- ${collapsibleClosingTag} -


-
-


-
+
+ + + + + + + + + +
+


+ ${collapsibleOpeningTag} + +

+ 123 +

+
+
+


+
+ ${collapsibleClosingTag} +


+
+


+
+


`, ); @@ -1958,42 +2017,44 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -2040,42 +2101,44 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -2124,42 +2187,44 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, undefined, @@ -2205,33 +2270,35 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - -
-


-
-

- first -

-

- second -

-
+
+ + + + + + + + + + +
+


+
+

+ first +

+

+ second +

+
+


`, ); @@ -2241,36 +2308,38 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - -
-


-
-

- first -

-

- second -

-
-


-
+
+ + + + + + + + + + + +
+


+
+

+ first +

+

+ second +

+
+


+
+


`, ); @@ -2305,53 +2374,55 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-

- first -

-
-

- second -

-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+

+ first +

+
+

+ second +

+
+


+
+


+
+


+
+


`, ); @@ -2361,59 +2432,61 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - - - -
-


-
-

- first -

-
-

- second -

-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + +
+


+
+

+ first +

+
+

+ second +

+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -2452,64 +2525,66 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-

- A -

-

- B -

-

- C -

-

- D -

-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+

+ A +

+

+ B +

+

+ C +

+

+ D +

+
+


+
+


`, ); @@ -2561,95 +2636,99 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, html`


- - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -2703,34 +2782,36 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - -
- - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + +
+ + + + + +
+


+
+


+
+


+
+


+
+


`, ); @@ -2748,53 +2829,55 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -2834,35 +2917,37 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


`, ); @@ -2902,32 +2987,34 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


`, ); @@ -2964,29 +3051,31 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - -
-


-
-


-
-


-
+
+ + + + + + + + + + + + +
+


+
+


+
+


+
+


`, ); @@ -3022,47 +3111,49 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -3108,27 +3199,29 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - -
-


-
-


-
-


-
+
+ + + + + + + + + + + + +
+


+
+


+
+


+
+


`, ); @@ -3174,27 +3267,29 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - -
-


-
-


-
-


-
+
+ + + + + + + + + + + + +
+


+
+


+
+


+
+


`, ); @@ -3231,23 +3326,25 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - -
-


-
-


-
+
+ + + + + + + + + + +
+


+
+


+
+


`, ); @@ -3284,23 +3381,25 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - -
-


-
-


-
+
+ + + + + + + + + + +
+


+
+


+
+


`, ); @@ -3353,18 +3452,20 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - -
-


-
+
+ + + + + + + +
+


+
+


`, ); @@ -3377,6 +3478,7 @@ test.describe.parallel('Tables', () => { await focusEditor(page); await pasteFromClipboard(page, { 'text/html': `
+
@@ -3403,74 +3505,77 @@ test.describe.parallel('Tables', () => {

e

- -
-
`, - }); - - await page.pause(); - - await assertHTML( - page, - html` - - - - - - - - - - - - - - - - - - - - - -
-

- Hello world -

-

-

- a -

-


-

- b -

-
-

- c -

-
-

- d -

-
-

- e -

-
+ + +
+ `, + }); + + await page.pause(); + + await assertHTML( + page, + html` +
+ + + + + + + + + + + + + + + + + + + + + +
+

+ Hello world +

+

+

+ a +

+


+

+ b +

+
+

+ c +

+
+

+ d +

+
+

+ e +

+
+
`, ); }); @@ -3486,6 +3591,7 @@ test.describe.parallel('Tables', () => { await focusEditor(page); await pasteFromClipboard(page, { 'text/html': `
+
@@ -3495,6 +3601,7 @@ test.describe.parallel('Tables', () => {
+
`, }); @@ -3503,20 +3610,22 @@ test.describe.parallel('Tables', () => { await assertHTML( page, html` - - - - - - - -
-

- Hello world -

-
+
+ + + + + + + +
+

+ Hello world +

+
+
`, ); }); @@ -3556,66 +3665,68 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -3648,88 +3759,92 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - -
-

- a -

-
-

- bb -

-
-

cc

-
-

- d -

-
-

- e -

-
-

f

-
+
+ + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ bb +

+
+

cc

+
+

+ d +

+
+

+ e +

+
+

f

+
+


`, html`


- - - - - - - - - - - - - - - - -
-

- a -

-
-

- bb -

-
-

cc

-
-

- d -

-
-

- e -

-
-

f

-
+
+ + + + + + + + + + + + + + + + +
+

+ a +

+
+

+ bb +

+
+

cc

+
+

+ d +

+
+

+ e +

+
+

f

+
+


`, {ignoreClasses: true}, @@ -3765,45 +3880,47 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -3847,62 +3964,64 @@ test.describe.parallel('Tables', () => { page, html`


- - - - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-

- HelloHelloHello -

-


-


-

- Hello -

-
+
+ + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+

+ HelloHelloHello +

+


+


+

+ Hello +

+
+

diff --git a/packages/lexical-playground/__tests__/e2e/Toolbar.spec.mjs b/packages/lexical-playground/__tests__/e2e/Toolbar.spec.mjs index 12cec473d34..817892ca0a7 100644 --- a/packages/lexical-playground/__tests__/e2e/Toolbar.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Toolbar.spec.mjs @@ -156,100 +156,102 @@ test.describe('Toolbar', () => {


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, undefined, diff --git a/packages/lexical-playground/__tests__/regression/4661-insert-column-selection.spec.mjs b/packages/lexical-playground/__tests__/regression/4661-insert-column-selection.spec.mjs index abcd0492817..529f7d0b147 100644 --- a/packages/lexical-playground/__tests__/regression/4661-insert-column-selection.spec.mjs +++ b/packages/lexical-playground/__tests__/regression/4661-insert-column-selection.spec.mjs @@ -47,47 +47,49 @@ test.describe('Regression test #4661', () => { page, html`


- - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); @@ -119,47 +121,49 @@ test.describe('Regression test #4661', () => { page, html`


- - - - - - - - - - - - - - - - - - - -
-


-
-


-
-


-
-


-
-


-
-


-
-


-
-


-
+
+ + + + + + + + + + + + + + + + + + + +
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


+
+


`, ); diff --git a/packages/lexical-playground/src/Editor.tsx b/packages/lexical-playground/src/Editor.tsx index 7fb6ae5c4a1..82d9abb70ff 100644 --- a/packages/lexical-playground/src/Editor.tsx +++ b/packages/lexical-playground/src/Editor.tsx @@ -19,6 +19,7 @@ import {HorizontalRulePlugin} from '@lexical/react/LexicalHorizontalRulePlugin'; import {ListPlugin} from '@lexical/react/LexicalListPlugin'; import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin'; import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin'; +import {ScrollableNodePlugin} from '@lexical/react/LexicalScrollableNodePlugin'; import {TabIndentationPlugin} from '@lexical/react/LexicalTabIndentationPlugin'; import {TablePlugin} from '@lexical/react/LexicalTablePlugin'; import {useLexicalEditable} from '@lexical/react/useLexicalEditable'; @@ -182,6 +183,7 @@ export default function Editor(): JSX.Element { hasCellMerge={tableCellMerge} hasCellBackgroundColor={tableCellBackgroundColor} /> + diff --git a/packages/lexical-playground/src/index.css b/packages/lexical-playground/src/index.css index a6c9d65c267..df58a9b5408 100644 --- a/packages/lexical-playground/src/index.css +++ b/packages/lexical-playground/src/index.css @@ -74,17 +74,18 @@ header h1 { .editor-scroller { min-height: 150px; + max-width: 100%; border: 0; display: flex; position: relative; outline: 0; z-index: 0; - overflow: auto; resize: vertical; } .editor { flex: auto; + max-width: 100%; position: relative; resize: vertical; z-index: -1; diff --git a/packages/lexical-playground/src/nodes/PlaygroundNodes.ts b/packages/lexical-playground/src/nodes/PlaygroundNodes.ts index e44931905ba..496d376b4e2 100644 --- a/packages/lexical-playground/src/nodes/PlaygroundNodes.ts +++ b/packages/lexical-playground/src/nodes/PlaygroundNodes.ts @@ -16,7 +16,12 @@ import {MarkNode} from '@lexical/mark'; import {OverflowNode} from '@lexical/overflow'; import {HorizontalRuleNode} from '@lexical/react/LexicalHorizontalRuleNode'; import {HeadingNode, QuoteNode} from '@lexical/rich-text'; -import {TableCellNode, TableNode, TableRowNode} from '@lexical/table'; +import { + ScrollableNode, + TableCellNode, + TableNode, + TableRowNode, +} from '@lexical/table'; import {CollapsibleContainerNode} from '../plugins/CollapsiblePlugin/CollapsibleContainerNode'; import {CollapsibleContentNode} from '../plugins/CollapsiblePlugin/CollapsibleContentNode'; @@ -44,6 +49,7 @@ const PlaygroundNodes: Array> = [ ListItemNode, QuoteNode, CodeNode, + ScrollableNode, TableNode, TableCellNode, TableRowNode, diff --git a/packages/lexical-react/flow/LexicalScrollableNodePlugin.js.flow b/packages/lexical-react/flow/LexicalScrollableNodePlugin.js.flow new file mode 100644 index 00000000000..165f2bf3fe0 --- /dev/null +++ b/packages/lexical-react/flow/LexicalScrollableNodePlugin.js.flow @@ -0,0 +1,12 @@ +/** + * 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. + * + * @flow strict + */ + +/** + * LexicalScrollableNodePlugin + */ diff --git a/packages/lexical-react/package.json b/packages/lexical-react/package.json index 6360d26502f..7b0ab7e5409 100644 --- a/packages/lexical-react/package.json +++ b/packages/lexical-react/package.json @@ -971,6 +971,36 @@ "default": "./LexicalRichTextPlugin.js" } }, + "./LexicalScrollableNodePlugin": { + "import": { + "types": "./LexicalScrollableNodePlugin.d.ts", + "development": "./LexicalScrollableNodePlugin.dev.mjs", + "production": "./LexicalScrollableNodePlugin.prod.mjs", + "node": "./LexicalScrollableNodePlugin.node.mjs", + "default": "./LexicalScrollableNodePlugin.mjs" + }, + "require": { + "types": "./LexicalScrollableNodePlugin.d.ts", + "development": "./LexicalScrollableNodePlugin.dev.js", + "production": "./LexicalScrollableNodePlugin.prod.js", + "default": "./LexicalScrollableNodePlugin.js" + } + }, + "./LexicalScrollableNodePlugin.js": { + "import": { + "types": "./LexicalScrollableNodePlugin.d.ts", + "development": "./LexicalScrollableNodePlugin.dev.mjs", + "production": "./LexicalScrollableNodePlugin.prod.mjs", + "node": "./LexicalScrollableNodePlugin.node.mjs", + "default": "./LexicalScrollableNodePlugin.mjs" + }, + "require": { + "types": "./LexicalScrollableNodePlugin.d.ts", + "development": "./LexicalScrollableNodePlugin.dev.js", + "production": "./LexicalScrollableNodePlugin.prod.js", + "default": "./LexicalScrollableNodePlugin.js" + } + }, "./LexicalTabIndentationPlugin": { "import": { "types": "./LexicalTabIndentationPlugin.d.ts", diff --git a/packages/lexical-react/src/LexicalScrollableNodePlugin.ts b/packages/lexical-react/src/LexicalScrollableNodePlugin.ts new file mode 100644 index 00000000000..b9fc0ff1145 --- /dev/null +++ b/packages/lexical-react/src/LexicalScrollableNodePlugin.ts @@ -0,0 +1,35 @@ +/** + * 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 { + $isTableNode, + registerScrollableNodeTransform, + type ScrollableNodeConfig, + TableNode, +} from '@lexical/table'; +import {useEffect} from 'react'; + +import {useLexicalComposerContext} from './LexicalComposerContext'; + +export const tableNodeConfig: ScrollableNodeConfig = { + $isScrollableChild: $isTableNode, + scrollableChildNodes: [TableNode], +}; + +export function ScrollableNodePlugin({ + config = tableNodeConfig, +}: { + config?: ScrollableNodeConfig; +}): null { + const [editor] = useLexicalComposerContext(); + useEffect( + () => registerScrollableNodeTransform(editor, config), + [editor, config], + ); + return null; +} diff --git a/packages/lexical-react/src/LexicalTablePlugin.ts b/packages/lexical-react/src/LexicalTablePlugin.ts index e8b512eb790..92a232d7e44 100644 --- a/packages/lexical-react/src/LexicalTablePlugin.ts +++ b/packages/lexical-react/src/LexicalTablePlugin.ts @@ -58,7 +58,7 @@ export function TablePlugin({ if (!editor.hasNodes([TableNode, TableCellNode, TableRowNode])) { invariant( false, - 'TablePlugin: TableNode, TableCellNode or TableRowNode not registered on editor', + 'TablePlugin: TableNode, TableCellNode, TableRowNode or ScrollableNode not registered on editor', ); } diff --git a/packages/lexical-table/src/LexicalScrollableNode.ts b/packages/lexical-table/src/LexicalScrollableNode.ts new file mode 100644 index 00000000000..34c617286bf --- /dev/null +++ b/packages/lexical-table/src/LexicalScrollableNode.ts @@ -0,0 +1,156 @@ +/** + * 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 {mergeRegister} from '@lexical/utils'; +import { + $applyNodeReplacement, + $createParagraphNode, + $getRoot, + $getSelection, + $normalizePoint__EXPERIMENTAL, + DOMConversionMap, + DOMConversionOutput, + DOMExportOutput, + EditorConfig, + ElementNode, + Klass, + LexicalEditor, + LexicalNode, + SerializedElementNode, +} from 'lexical'; +import invariant from 'shared/invariant'; + +export class ScrollableNode extends ElementNode { + static getType() { + return 'scrollable'; + } + + static clone(node: ScrollableNode): ScrollableNode { + return new ScrollableNode(node.__key); + } + + createDOM(_config: EditorConfig, _editor: LexicalEditor): HTMLElement { + const dom = document.createElement('div'); + dom.classList.add('lexical-scrollable'); + dom.style.overflowX = 'auto'; + return dom; + } + + updateDOM() { + return false; + } + + canIndent() { + return false; + } + + exportJSON() { + return { + ...super.exportJSON(), + type: ScrollableNode.getType(), + version: 1, + }; + } + + static importJSON(_serializedNode: SerializedElementNode): ScrollableNode { + return $createScrollableNode(); + } + + exportDOM(editor: LexicalEditor): DOMExportOutput { + return super.exportDOM(editor); + } + + static importDOM(): DOMConversionMap | null { + return { + div: (domElement: HTMLElement) => { + if (domElement.classList.contains('lexical-scrollable')) { + return { + conversion: $convertScrollableElement, + priority: 0, + }; + } else { + return null; + } + }, + }; + } +} + +export function $isScrollableNode( + node: LexicalNode | null | undefined, +): node is ScrollableNode { + return node instanceof ScrollableNode; +} + +export function $createScrollableNode(): ScrollableNode { + return $applyNodeReplacement(new ScrollableNode()); +} + +export interface ScrollableNodeConfig { + scrollableChildNodes: readonly Klass[]; + $isScrollableChild?: (node: LexicalNode | null) => boolean; +} + +export function registerScrollableNodeTransform( + editor: LexicalEditor, + config: ScrollableNodeConfig, +): () => void { + invariant( + editor.hasNodes([ScrollableNode]), + 'TablePlugin: ScrollableNode not registered on editor', + ); + const { + scrollableChildNodes, + $isScrollableChild = (node) => + node !== null && + scrollableChildNodes.some((klass) => node instanceof klass), + } = config; + return mergeRegister( + editor.registerNodeTransform(ScrollableNode, (node) => { + let onlyScrollableChild: LexicalNode | null = null; + for (const child of node.getChildren()) { + if (onlyScrollableChild === null && $isScrollableChild(child)) { + onlyScrollableChild = child; + } else { + child.remove(); + } + } + if (onlyScrollableChild === null) { + node.remove(); + const root = $getRoot(); + if (root.isEmpty()) { + root.append($createParagraphNode()); + } + return; + } + const selection = $getSelection(); + if (!selection) { + return; + } + const nodeKey = node.getKey(); + for (const point of selection.getStartEndPoints() || []) { + if (point.key === nodeKey) { + $normalizePoint__EXPERIMENTAL(point); + } + } + }), + ...scrollableChildNodes.map((klass) => + editor.registerNodeTransform(klass, (node) => { + const parent = node.getParent(); + if (!$isScrollableNode(parent)) { + const scrollable = $createScrollableNode(); + node.insertBefore(scrollable); + scrollable.append(node); + } + }), + ), + ); +} + +function $convertScrollableElement(domNode: HTMLElement): DOMConversionOutput { + return {node: $createScrollableNode()}; +} diff --git a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts index be84112c306..26816c2ef66 100644 --- a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts +++ b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts @@ -67,6 +67,7 @@ import { import {CAN_USE_DOM} from 'shared/canUseDOM'; import invariant from 'shared/invariant'; +import {$isScrollableNode} from './LexicalScrollableNode'; import {$isTableCellNode} from './LexicalTableCellNode'; import {$isTableNode} from './LexicalTableNode'; import {TableDOMTable, TableObserver} from './LexicalTableObserver'; @@ -1197,9 +1198,9 @@ const selectTableNodeInDirection = ( isForward, ); } else if (!isForward) { - tableNode.selectPrevious(); + tableNode.getParentOrThrow().selectPrevious(); } else { - tableNode.selectNext(); + tableNode.getParentOrThrow().selectNext(); } } @@ -1212,7 +1213,7 @@ const selectTableNodeInDirection = ( false, ); } else { - tableNode.selectPrevious(); + tableNode.getParentOrThrow().selectPrevious(); } return true; @@ -1224,7 +1225,7 @@ const selectTableNodeInDirection = ( true, ); } else { - tableNode.selectNext(); + tableNode.getParentOrThrow().selectNext(); } return true; @@ -1369,7 +1370,13 @@ function $handleArrowKey( if (!$isSelectionInTable(selection, tableNode)) { if ($isRangeSelection(selection)) { - if (selection.isCollapsed() && direction === 'backward') { + if ( + selection.isCollapsed() && + (direction === 'forward' || direction === 'backward') + ) { + if (event.shiftKey) { + return false; + } const anchorType = selection.anchor.type; const anchorOffset = selection.anchor.offset; if ( @@ -1389,18 +1396,48 @@ function $handleArrowKey( if (!parentNode) { return false; } - const siblingNode = parentNode.getPreviousSibling(); + const siblingNode0 = + direction === 'backward' + ? parentNode.getPreviousSibling() + : parentNode.getNextSibling(); + const siblingNode = $isScrollableNode(siblingNode0) + ? siblingNode0.getFirstChild() + : siblingNode0; if (!siblingNode || !$isTableNode(siblingNode)) { return false; } stopEvent(event); - siblingNode.selectEnd(); + if (direction === 'backward') { + siblingNode.selectEnd(); + } else { + const firstDescendant = siblingNode.getFirstDescendant()!; + invariant(firstDescendant !== null, 'TableNode is empty'); + if ($isTableCellNode(firstDescendant)) { + firstDescendant.selectEnd(); + } else { + firstDescendant.selectStart(); + } + } return true; } else if ( event.shiftKey && (direction === 'up' || direction === 'down') ) { const focusNode = selection.focus.getNode(); + const tableNode2 = $findMatchingParent(focusNode, $isTableNode); + if (tableNode2 && direction === 'up') { + const newFocus = tableNode2.getParentOrThrow().getPreviousSibling(); + if (newFocus) { + selection.focus.set(newFocus.getKey(), 0, 'element'); + } + return true; + } else if (tableNode2 && direction === 'down') { + const newFocus = tableNode2.getParentOrThrow().getNextSibling(); + if (newFocus) { + selection.focus.set(newFocus.getKey(), 0, 'element'); + } + return true; + } if ($isRootOrShadowRoot(focusNode)) { const selectedNode = selection.getNodes()[0]; if (selectedNode) { @@ -1790,8 +1827,8 @@ function $getExitingToNode( return anchorSibling && $isTableNode(anchorSibling) ? anchorSibling : direction === 'backward' - ? tableNode.getPreviousSibling() - : tableNode.getNextSibling(); + ? tableNode.getParentOrThrow().getPreviousSibling() + : tableNode.getParentOrThrow().getNextSibling(); } function $insertParagraphAtTableEdge( @@ -1801,9 +1838,9 @@ function $insertParagraphAtTableEdge( ) { const paragraphNode = $createParagraphNode(); if (edgePosition === 'first') { - tableNode.insertBefore(paragraphNode); + tableNode.getParentOrThrow().insertBefore(paragraphNode); } else { - tableNode.insertAfter(paragraphNode); + tableNode.getParentOrThrow().insertAfter(paragraphNode); } paragraphNode.append(...(children || [])); paragraphNode.selectEnd(); @@ -1814,7 +1851,7 @@ function $getTableEdgeCursorPosition( selection: RangeSelection, tableNode: TableNode, ) { - const tableNodeParent = tableNode.getParent(); + const tableNodeParent = tableNode.getParent(); // Should I change this to getParentOrThrow().getParent()? if (!tableNodeParent) { return undefined; } diff --git a/packages/lexical-table/src/__tests__/unit/LexicalScrollableNode.test.ts b/packages/lexical-table/src/__tests__/unit/LexicalScrollableNode.test.ts new file mode 100644 index 00000000000..17a18fe67e8 --- /dev/null +++ b/packages/lexical-table/src/__tests__/unit/LexicalScrollableNode.test.ts @@ -0,0 +1,67 @@ +/** + * 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 {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html'; +import { + $createTableNode, + $isScrollableNode, + $isTableNode, + ScrollableNode, +} from '@lexical/table'; +import {$getRoot, $insertNodes} from 'lexical'; +import {initializeUnitTest} from 'lexical/src/__tests__/utils'; + +describe('LexicalScrollableNode tests', () => { + initializeUnitTest((testEnv) => { + test('LexicalScrollableNode.constructor', async () => { + const {editor} = testEnv; + let htmlString; + + // tables should be automatically wrapped in scrollableNodes + await editor.update(() => { + const table = $createTableNode(); + const root = $getRoot(); + root.append(table); + }); + expect(testEnv.innerHTML).toBe( + '

', + ); + + // scrollableNode.exportDOM + const htmlConversion = + '
'; + await editor.update(() => { + htmlString = $generateHtmlFromNodes(editor); + }); + expect(htmlString).toBe(htmlConversion); + + // scrollableNodes should be automatically removed if they are empty + await editor.update(() => { + const root = $getRoot(); + const scrollableNode = root.getFirstChild()! as ScrollableNode; + expect($isScrollableNode(scrollableNode)).toBe(true); + const table = scrollableNode.getFirstChild()!; + expect($isTableNode(table)).toBe(true); + table.remove(); + }); + expect(testEnv.innerHTML).toBe('


'); + + // scrollableNode.importDOM + await editor.update(() => { + const parser = new DOMParser(); + const dom = parser.parseFromString(htmlConversion, 'text/html'); + const nodes = $generateNodesFromDOM(editor, dom); + $getRoot().select(); + $insertNodes(nodes); + }); + expect(testEnv.innerHTML).toBe( + '



', + ); + }); + }); +}); diff --git a/packages/lexical-table/src/__tests__/unit/LexicalTableNode.test.tsx b/packages/lexical-table/src/__tests__/unit/LexicalTableNode.test.tsx index fb1d9af2332..24662ee3363 100644 --- a/packages/lexical-table/src/__tests__/unit/LexicalTableNode.test.tsx +++ b/packages/lexical-table/src/__tests__/unit/LexicalTableNode.test.tsx @@ -7,12 +7,14 @@ */ import {$insertDataTransferForRichText} from '@lexical/clipboard'; +import {ScrollableNodePlugin} from '@lexical/react/LexicalScrollableNodePlugin'; import {TablePlugin} from '@lexical/react/LexicalTablePlugin'; import { $createTableNode, $createTableNodeWithDimensions, $createTableSelection, $insertTableColumn__EXPERIMENTAL, + ScrollableNode, } from '@lexical/table'; import { $createParagraphNode, @@ -125,9 +127,8 @@ describe('LexicalTableNode tests', () => { $insertDataTransferForRichText(dataTransfer, selection, editor); }); // Make sure paragraph is inserted inside empty cells - const emptyCell = '


'; expect(testEnv.innerHTML).toBe( - `${emptyCell}

Hello there

General Kenobi!

Lexical is nice

`, + `

Hello there

General Kenobi!

Lexical is nice


`, ); }); @@ -148,7 +149,7 @@ describe('LexicalTableNode tests', () => { $insertDataTransferForRichText(dataTransfer, selection, editor); }); expect(testEnv.innerHTML).toBe( - `

Surface

MWP_WORK_LS_COMPOSER

77349

Lexical

XDS_RICH_TEXT_AREA

sdvd sdfvsfs

`, + `

Surface

MWP_WORK_LS_COMPOSER

77349

Lexical

XDS_RICH_TEXT_AREA

sdvd sdfvsfs

`, ); }); @@ -233,7 +234,9 @@ describe('LexicalTableNode tests', () => { }); await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); if (table) { const DOMTable = $getElementForTableNode(editor, table); if (DOMTable) { @@ -269,7 +272,9 @@ describe('LexicalTableNode tests', () => { }); await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); if (table) { const DOMTable = $getElementForTableNode(editor, table); if (DOMTable) { @@ -293,7 +298,7 @@ describe('LexicalTableNode tests', () => { }); expect(testEnv.innerHTML).toBe( - `


















`, + `


















`, ); }); @@ -307,7 +312,9 @@ describe('LexicalTableNode tests', () => { }); await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); if (table) { const DOMTable = $getElementForTableNode(editor, table); if (DOMTable) { @@ -357,7 +364,9 @@ describe('LexicalTableNode tests', () => { }); await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); if (table) { table.setRowStriping(true); } @@ -365,7 +374,9 @@ describe('LexicalTableNode tests', () => { await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); expect(table!.createDOM(editorConfig).outerHTML).toBe( `
`, ); @@ -373,7 +384,9 @@ describe('LexicalTableNode tests', () => { await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); if (table) { table.setRowStriping(false); } @@ -381,7 +394,9 @@ describe('LexicalTableNode tests', () => { await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); expect(table!.createDOM(editorConfig).outerHTML).toBe( `
`, ); @@ -400,13 +415,17 @@ describe('LexicalTableNode tests', () => { // Set widths await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); table!.setColWidths([50, 50]); }); await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); expect(table!.createDOM(editorConfig).outerHTML).toBe( `
`, ); @@ -423,7 +442,9 @@ describe('LexicalTableNode tests', () => { // Add a column await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); const DOMTable = $getElementForTableNode(editor, table!); const selection = $createTableSelection(); selection.set( @@ -438,7 +459,9 @@ describe('LexicalTableNode tests', () => { await editor.update(() => { const root = $getRoot(); - const table = root.getLastChild(); + const table = ( + root.getLastChild()! as ScrollableNode + ).getLastChild(); expect(table!.createDOM(editorConfig).outerHTML).toBe( `
`, ); @@ -448,6 +471,9 @@ describe('LexicalTableNode tests', () => { }); }, undefined, - , + <> + + + , ); }); diff --git a/packages/lexical-table/src/index.ts b/packages/lexical-table/src/index.ts index 2429eb608a9..01668557296 100644 --- a/packages/lexical-table/src/index.ts +++ b/packages/lexical-table/src/index.ts @@ -6,6 +6,13 @@ * */ +export { + $createScrollableNode, + $isScrollableNode, + registerScrollableNodeTransform, + ScrollableNode, + type ScrollableNodeConfig, +} from './LexicalScrollableNode'; export type {SerializedTableCellNode} from './LexicalTableCellNode'; export { $createTableCellNode, diff --git a/packages/lexical-website/docs/react/plugins.md b/packages/lexical-website/docs/react/plugins.md index d2dcb206afb..f9134503f0b 100644 --- a/packages/lexical-website/docs/react/plugins.md +++ b/packages/lexical-website/docs/react/plugins.md @@ -107,6 +107,14 @@ React wrapper for `@lexical/list` that adds support for check lists. Note that i ``` +### `LexicalScrollableNodePlugin` + +React wrapper for `@lexical/table` that adds horizontal scrolling support for tables + +```jsx + +``` + ### `LexicalTablePlugin` React wrapper for `@lexical/table` that adds support for tables diff --git a/packages/lexical/flow/Lexical.js.flow b/packages/lexical/flow/Lexical.js.flow index 33e5a952bd1..7b2939542b2 100644 --- a/packages/lexical/flow/Lexical.js.flow +++ b/packages/lexical/flow/Lexical.js.flow @@ -885,6 +885,10 @@ declare export function $normalizeSelection__EXPERIMENTAL( selection: RangeSelection, ): RangeSelection; +declare export function $normalizePoint__EXPERIMENTAL( + point: Point, +): void; + /** * Serialization/Deserialization * */ diff --git a/packages/lexical/src/LexicalNormalization.ts b/packages/lexical/src/LexicalNormalization.ts index 59a7be64489..2a9efd3a776 100644 --- a/packages/lexical/src/LexicalNormalization.ts +++ b/packages/lexical/src/LexicalNormalization.ts @@ -92,7 +92,7 @@ export function $normalizeSelection(selection: RangeSelection): RangeSelection { return selection; } -function $normalizePoint(point: PointType): void { +export function $normalizePoint(point: PointType): void { while (point.type === 'element') { const node = point.getNode(); const offset = point.offset; diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index e59cab5c83e..45fd6a61f9f 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -1156,6 +1156,16 @@ export class RangeSelection implements BaseSelection { fixText(firstNode, del); return; } + if ($getRoot().getChildrenSize() > 1) { + const firstEmpty = $isElementNode(firstBlock) && firstBlock.isEmpty(); + const lastEmpty = $isElementNode(lastBlock) && lastBlock.isEmpty(); + if (firstEmpty && (!lastBlock || !lastBlock.isAttached())) { + firstBlock.remove(); + } + if (lastEmpty && (!firstBlock || !firstBlock.isAttached())) { + lastBlock.remove(); + } + } if ($isTextNode(firstNode)) { const del = firstNode.getTextContentSize() - firstPoint.offset; firstNode.spliceText(firstPoint.offset, del, ''); diff --git a/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts b/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts index 9237bc9d3dd..b46db1d0b2a 100644 --- a/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts +++ b/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts @@ -110,7 +110,7 @@ describe('LexicalSerialization tests', () => { }); const stringifiedEditorState = JSON.stringify(editor.getEditorState()); - const expectedStringifiedEditorState = `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1}],"direction":"ltr","format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`; + const expectedStringifiedEditorState = `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1}],"direction":"ltr","format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"scrollable","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`; expect(stringifiedEditorState).toBe(expectedStringifiedEditorState); @@ -119,7 +119,7 @@ describe('LexicalSerialization tests', () => { const otherStringifiedEditorState = JSON.stringify(editorState); expect(otherStringifiedEditorState).toBe( - `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1}],"direction":null,"format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, + `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1}],"direction":null,"format":"","indent":0,"type":"table","version":1}],"direction":null,"format":"","indent":0,"type":"scrollable","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, ); }); }); diff --git a/packages/lexical/src/__tests__/utils/index.tsx b/packages/lexical/src/__tests__/utils/index.tsx index 5292fdd5a5f..2811d7d6d9d 100644 --- a/packages/lexical/src/__tests__/utils/index.tsx +++ b/packages/lexical/src/__tests__/utils/index.tsx @@ -21,7 +21,12 @@ import { LexicalComposerContext, } from '@lexical/react/LexicalComposerContext'; import {HeadingNode, QuoteNode} from '@lexical/rich-text'; -import {TableCellNode, TableNode, TableRowNode} from '@lexical/table'; +import { + ScrollableNode, + TableCellNode, + TableNode, + TableRowNode, +} from '@lexical/table'; import {expect} from '@playwright/test'; import { $isRangeSelection, @@ -471,6 +476,7 @@ const DEFAULT_NODES: NonNullable = [ ListItemNode, QuoteNode, CodeNode, + ScrollableNode, TableNode, TableCellNode, TableRowNode, diff --git a/packages/lexical/src/index.ts b/packages/lexical/src/index.ts index b3f5013cdd7..b2cd44b7825 100644 --- a/packages/lexical/src/index.ts +++ b/packages/lexical/src/index.ts @@ -137,7 +137,10 @@ export { createEditor, } from './LexicalEditor'; export type {EventHandler} from './LexicalEvents'; -export {$normalizeSelection as $normalizeSelection__EXPERIMENTAL} from './LexicalNormalization'; +export { + $normalizePoint as $normalizePoint__EXPERIMENTAL, + $normalizeSelection as $normalizeSelection__EXPERIMENTAL, +} from './LexicalNormalization'; export { $createNodeSelection, $createPoint, diff --git a/tsconfig.build.json b/tsconfig.build.json index 227dca5252a..ed8e1896039 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -121,6 +121,9 @@ "@lexical/react/LexicalRichTextPlugin": [ "./packages/lexical-react/src/LexicalRichTextPlugin.tsx" ], + "@lexical/react/LexicalScrollableNodePlugin": [ + "./packages/lexical-react/src/LexicalScrollableNodePlugin.ts" + ], "@lexical/react/LexicalTabIndentationPlugin": [ "./packages/lexical-react/src/LexicalTabIndentationPlugin.tsx" ], diff --git a/tsconfig.json b/tsconfig.json index 50f67a71a8c..c588756dc6c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -129,6 +129,9 @@ "@lexical/react/LexicalRichTextPlugin": [ "./packages/lexical-react/src/LexicalRichTextPlugin.tsx" ], + "@lexical/react/LexicalScrollableNodePlugin": [ + "./packages/lexical-react/src/LexicalScrollableNodePlugin.ts" + ], "@lexical/react/LexicalTabIndentationPlugin": [ "./packages/lexical-react/src/LexicalTabIndentationPlugin.tsx" ],