diff --git a/jest.config.ts b/jest.config.ts
index 11a86c672..3c04f05b2 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -21,7 +21,7 @@ const config: Config = {
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
- collectCoverage: true,
+ collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
@@ -164,10 +164,9 @@ const config: Config = {
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
- // testMatch: [
- // "**/__tests__/**/*.[jt]s?(x)",
- // "**/?(*.)+(spec|test).[tj]s?(x)"
- // ],
+ testMatch: [
+ "**/__tests__/**/*.test.[jt]s",
+ ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
diff --git a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
index 30b0bc8c8..f7230595a 100644
--- a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
+++ b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
@@ -717,3 +717,11 @@ export function html(
}
return output;
}
+
+export function expectHtmlToBeEqual(expected: string, actual: string): void {
+ expect(formatHtml(expected)).toBe(formatHtml(actual));
+}
+
+function formatHtml(s: string): string {
+ return s.replace(/>\s+<').replace(/\s*\n\s*/g, ' ').trim();
+}
\ No newline at end of file
diff --git a/resources/js/wysiwyg/lexical/core/nodes/__tests__/unit/LexicalTextNode.test.ts b/resources/js/wysiwyg/lexical/core/nodes/__tests__/unit/LexicalTextNode.test.ts
index 337c96a41..57e1dcb3b 100644
--- a/resources/js/wysiwyg/lexical/core/nodes/__tests__/unit/LexicalTextNode.test.ts
+++ b/resources/js/wysiwyg/lexical/core/nodes/__tests__/unit/LexicalTextNode.test.ts
@@ -21,7 +21,6 @@ import {
TextModeType,
TextNode,
} from 'lexical';
-import * as ReactTestUtils from 'lexical/shared/react-test-utils';
import {
$createTestSegmentedNode,
diff --git a/resources/js/wysiwyg/lexical/core/shared/react-test-utils.ts b/resources/js/wysiwyg/lexical/core/shared/react-test-utils.ts
deleted file mode 100644
index 8e086744d..000000000
--- a/resources/js/wysiwyg/lexical/core/shared/react-test-utils.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * 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 * as React from 'react';
-import * as ReactTestUtils from 'react-dom/test-utils';
-
-/**
- * React 19 moved act from react-dom/test-utils to react
- * https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-react-dom-test-utils
- */
-export const act =
- 'act' in React
- ? (React.act as typeof ReactTestUtils.act)
- : ReactTestUtils.act;
diff --git a/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalAutoLinkNode.test.ts b/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalAutoLinkNode.test.ts
index ffcefd7c8..0f3513682 100644
--- a/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalAutoLinkNode.test.ts
+++ b/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalAutoLinkNode.test.ts
@@ -19,7 +19,7 @@ import {
ParagraphNode,
SerializedParagraphNode,
TextNode,
-} from 'lexical/src';
+} from 'lexical';
import {initializeUnitTest} from 'lexical/__tests__/utils';
const editorConfig = Object.freeze({
diff --git a/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalLinkNode.test.ts b/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalLinkNode.test.ts
index fe978849b..1aff91863 100644
--- a/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalLinkNode.test.ts
+++ b/resources/js/wysiwyg/lexical/link/__tests__/unit/LexicalLinkNode.test.ts
@@ -19,7 +19,7 @@ import {
ParagraphNode,
SerializedParagraphNode,
TextNode,
-} from 'lexical/src';
+} from 'lexical';
import {initializeUnitTest} from 'lexical/__tests__/utils';
const editorConfig = Object.freeze({
diff --git a/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListItemNode.test.ts b/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListItemNode.test.ts
index a1ccd5020..22e555f35 100644
--- a/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListItemNode.test.ts
+++ b/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListItemNode.test.ts
@@ -62,7 +62,7 @@ describe('LexicalListItemNode tests', () => {
expectHtmlToBeEqual(
listItemNode.createDOM(editorConfig).outerHTML,
html`
-
+
`,
);
@@ -90,7 +90,7 @@ describe('LexicalListItemNode tests', () => {
expectHtmlToBeEqual(
domElement.outerHTML,
html`
-
+
`,
);
const newListItemNode = new ListItemNode();
@@ -106,7 +106,7 @@ describe('LexicalListItemNode tests', () => {
expectHtmlToBeEqual(
domElement.outerHTML,
html`
-
+
`,
);
});
@@ -125,7 +125,7 @@ describe('LexicalListItemNode tests', () => {
expectHtmlToBeEqual(
domElement.outerHTML,
html`
-
+
`,
);
const nestedListNode = new ListNode('bullet', 1);
@@ -142,9 +142,7 @@ describe('LexicalListItemNode tests', () => {
expectHtmlToBeEqual(
domElement.outerHTML,
html`
-
+
`,
);
});
@@ -184,13 +182,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
- -
+
-
two
- -
+
-
three
@@ -217,13 +215,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
bar
- -
+
-
two
- -
+
-
three
@@ -247,13 +245,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
- -
+
-
two
- -
+
-
three
@@ -273,12 +271,12 @@ describe('LexicalListItemNode tests', () => {
contenteditable="true"
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
-
+
@@ -303,14 +301,14 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
-
+
`,
);
@@ -332,13 +330,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
-
+
@@ -363,7 +361,7 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
@@ -383,7 +381,7 @@ describe('LexicalListItemNode tests', () => {
contenteditable="true"
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
-
+
`,
);
@@ -423,13 +421,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
@@ -447,10 +445,10 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
@@ -497,15 +495,15 @@ describe('LexicalListItemNode tests', () => {
@@ -525,12 +523,12 @@ describe('LexicalListItemNode tests', () => {
@@ -575,15 +573,15 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
A
- -
+
-
x
-
@@ -603,12 +601,12 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
A
-
@@ -661,17 +659,17 @@ describe('LexicalListItemNode tests', () => {
-
- -
+
-
x
-
@@ -693,10 +691,10 @@ describe('LexicalListItemNode tests', () => {
-
@@ -757,24 +755,24 @@ describe('LexicalListItemNode tests', () => {
-
- -
+
-
x
-
@@ -796,17 +794,17 @@ describe('LexicalListItemNode tests', () => {
-
@@ -867,24 +865,24 @@ describe('LexicalListItemNode tests', () => {
-
- -
+
-
x
-
@@ -906,17 +904,17 @@ describe('LexicalListItemNode tests', () => {
-
@@ -985,31 +983,31 @@ describe('LexicalListItemNode tests', () => {
-
- -
+
-
x
-
@@ -1031,20 +1029,20 @@ describe('LexicalListItemNode tests', () => {
-
@@ -1089,13 +1087,13 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
- -
+
-
two
- -
+
-
three
@@ -1119,14 +1117,14 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
-
- -
+
+ -
two
- -
+
-
three
@@ -1150,16 +1148,16 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
- -
+
-
two
- -
+
-
three
-
+
`,
@@ -1181,16 +1179,16 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
- -
+
-
one
- -
+
-
two
- -
+
-
three
-
+
`,
@@ -1213,7 +1211,7 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
@@ -1233,10 +1231,10 @@ describe('LexicalListItemNode tests', () => {
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
data-lexical-editor="true">
`,
@@ -1310,7 +1308,7 @@ describe('LexicalListItemNode tests', () => {
-
@@ -1319,7 +1317,7 @@ describe('LexicalListItemNode tests', () => {
- -
+
-
two
@@ -1338,10 +1336,10 @@ describe('LexicalListItemNode tests', () => {
editor.getRootElement()!.innerHTML,
html`
diff --git a/resources/js/wysiwyg/lexical/list/__tests__/utils.ts b/resources/js/wysiwyg/lexical/list/__tests__/utils.ts
deleted file mode 100644
index aa95a7a01..000000000
--- a/resources/js/wysiwyg/lexical/list/__tests__/utils.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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 {expect} from '@playwright/test';
-import prettier from 'prettier';
-
-// This tag function is just used to trigger prettier auto-formatting.
-// (https://prettier.io/blog/2020/08/24/2.1.0.html#api)
-export function html(
- partials: TemplateStringsArray,
- ...params: string[]
-): string {
- let output = '';
- for (let i = 0; i < partials.length; i++) {
- output += partials[i];
- if (i < partials.length - 1) {
- output += params[i];
- }
- }
- return output;
-}
-
-export function expectHtmlToBeEqual(expected: string, actual: string): void {
- expect(prettifyHtml(expected)).toBe(prettifyHtml(actual));
-}
-
-export function prettifyHtml(s: string): string {
- return prettier.format(s.replace(/\n/g, ''), {parser: 'html'});
-}
diff --git a/resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.tsx b/resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.ts
similarity index 73%
rename from resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.tsx
rename to resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.ts
index 68e9dcab5..665f5d854 100644
--- a/resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.tsx
+++ b/resources/js/wysiwyg/lexical/selection/__tests__/unit/LexicalSelection.test.ts
@@ -8,13 +8,7 @@
import {$createLinkNode} from '@lexical/link';
import {$createListItemNode, $createListNode} from '@lexical/list';
-import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
-import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-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 {$createHeadingNode} from '@lexical/rich-text';
+import {$createHeadingNode, registerRichText} from '@lexical/rich-text';
import {
$addNodeStyle,
$getSelectionStyleValueForProperty,
@@ -28,7 +22,7 @@ import {
$createRangeSelection,
$createTextNode,
$getRoot,
- $getSelection,
+ $getSelection, $insertNodes,
$isElementNode,
$isRangeSelection,
$isTextNode,
@@ -49,10 +43,7 @@ import {
createTestEditor,
initializeClipboard,
invariant,
- TestComposer,
} from 'lexical/__tests__/utils';
-import {createRoot, Root} from 'react-dom/client';
-import * as ReactTestUtils from 'lexical/shared/react-test-utils';
import {
$setAnchorPoint,
@@ -81,6 +72,8 @@ import {
setNativeSelectionWithPaths,
undo,
} from '../utils';
+import {createEmptyHistoryState, registerHistory} from "@lexical/history";
+import {mergeRegister} from "@lexical/utils";
interface ExpectedSelection {
anchorPath: number[];
@@ -118,89 +111,72 @@ Range.prototype.getBoundingClientRect = function (): DOMRect {
describe('LexicalSelection tests', () => {
let container: HTMLElement;
- let reactRoot: Root;
+ let root: HTMLDivElement;
let editor: LexicalEditor | null = null;
beforeEach(async () => {
container = document.createElement('div');
document.body.appendChild(container);
- reactRoot = createRoot(container);
+
+ root = document.createElement('div');
+ root.setAttribute('contenteditable', 'true');
+ container.append(root);
+
await init();
});
afterEach(async () => {
- // Ensure we are clearing out any React state and running effects with
- // act
- await ReactTestUtils.act(async () => {
- reactRoot.unmount();
- await Promise.resolve().then();
- });
document.body.removeChild(container);
});
async function init() {
- function TestBase() {
- function TestPlugin() {
- [editor] = useLexicalComposerContext();
- return null;
+ editor = createTestEditor({
+ nodes: [],
+ theme: {
+ code: 'editor-code',
+ heading: {
+ h1: 'editor-heading-h1',
+ h2: 'editor-heading-h2',
+ h3: 'editor-heading-h3',
+ h4: 'editor-heading-h4',
+ h5: 'editor-heading-h5',
+ h6: 'editor-heading-h6',
+ },
+ image: 'editor-image',
+ list: {
+ ol: 'editor-list-ol',
+ ul: 'editor-list-ul',
+ },
+ listitem: 'editor-listitem',
+ paragraph: 'editor-paragraph',
+ quote: 'editor-quote',
+ text: {
+ bold: 'editor-text-bold',
+ code: 'editor-text-code',
+ hashtag: 'editor-text-hashtag',
+ italic: 'editor-text-italic',
+ link: 'editor-text-link',
+ strikethrough: 'editor-text-strikethrough',
+ underline: 'editor-text-underline',
+ underlineStrikethrough: 'editor-text-underlineStrikethrough',
+ },
}
-
- return (
-
-
- }
- placeholder={null}
- ErrorBoundary={LexicalErrorBoundary}
- />
-
-
-
-
- );
- }
-
- await ReactTestUtils.act(async () => {
- reactRoot.render();
- await Promise.resolve().then();
});
- await Promise.resolve().then();
+ mergeRegister(
+ registerHistory(editor, createEmptyHistoryState(), 300),
+ registerRichText(editor),
+ );
+
+ editor.setRootElement(root);
+ editor.update(() => {
+ const p = $createParagraphNode();
+ $insertNodes([p]);
+ });
+ editor.commitUpdates();
+ editor.focus();
+
// Focus first element
setNativeSelectionWithPaths(
editor!.getRootElement()!,
@@ -212,9 +188,8 @@ describe('LexicalSelection tests', () => {
}
async function update(fn: () => void) {
- await ReactTestUtils.act(async () => {
- await editor!.update(fn);
- });
+ editor!.update(fn);
+ editor!.commitUpdates();
}
test('Expect initial output to be a block with no text.', () => {
@@ -734,301 +709,6 @@ describe('LexicalSelection tests', () => {
name: 'Format selection that contains a token node in the middle should format the token node',
},
- // Tests need fixing:
- // ...GRAPHEME_SCENARIOS.flatMap(({description, grapheme}) => [
- // {
- // name: `Delete backward eliminates entire ${description} (${grapheme})`,
- // inputs: [insertText(grapheme + grapheme), deleteBackward(1)],
- // expectedHTML: ``,
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: grapheme.length,
- // focusPath: [0, 0, 0],
- // focusOffset: grapheme.length,
- // },
- // setup: emptySetup,
- // },
- // {
- // name: `Delete forward eliminates entire ${description} (${grapheme})`,
- // inputs: [
- // insertText(grapheme + grapheme),
- // moveNativeSelection([0, 0, 0], 0, [0, 0, 0], 0),
- // deleteForward(),
- // ],
- // expectedHTML: ``,
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 0,
- // focusPath: [0, 0, 0],
- // focusOffset: 0,
- // },
- // setup: emptySetup,
- // },
- // {
- // name: `Move backward skips over grapheme cluster (${grapheme})`,
- // inputs: [insertText(grapheme + grapheme), moveBackward(1)],
- // expectedHTML: ``,
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: grapheme.length,
- // focusPath: [0, 0, 0],
- // focusOffset: grapheme.length,
- // },
- // setup: emptySetup,
- // },
- // {
- // name: `Move forward skips over grapheme cluster (${grapheme})`,
- // inputs: [
- // insertText(grapheme + grapheme),
- // moveNativeSelection([0, 0, 0], 0, [0, 0, 0], 0),
- // moveForward(),
- // ],
- // expectedHTML: ``,
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: grapheme.length,
- // focusPath: [0, 0, 0],
- // focusOffset: grapheme.length,
- // },
- // setup: emptySetup,
- // },
- // ]),
- // {
- // name: 'Jump to beginning and insert',
- // inputs: [
- // insertText('1'),
- // insertText('1'),
- // insertText('2'),
- // insertText('3'),
- // moveNativeSelection([0, 0, 0], 0, [0, 0, 0], 0),
- // insertText('a'),
- // insertText('b'),
- // insertText('c'),
- // deleteForward(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 3,
- // focusPath: [0, 0, 0],
- // focusOffset: 3,
- // },
- // },
- // {
- // name: 'Select and replace',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // insertText('lexical'),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 13,
- // focusPath: [0, 0, 0],
- // focusOffset: 13,
- // },
- // },
- // {
- // name: 'Select and bold',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatBold(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and italic',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatItalic(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and bold + italic',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatBold(),
- // formatItalic(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and underline',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatUnderline(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and strikethrough',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatStrikeThrough(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and underline + strikethrough',
- // inputs: [
- // insertText('Hello draft!'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 11),
- // formatUnderline(),
- // formatStrikeThrough(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 1, 0],
- // anchorOffset: 0,
- // focusPath: [0, 1, 0],
- // focusOffset: 5,
- // },
- // },
- // {
- // name: 'Select and replace all',
- // inputs: [
- // insertText('This is broken.'),
- // moveNativeSelection([0, 0, 0], 0, [0, 0, 0], 15),
- // insertText('This works!'),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 11,
- // focusPath: [0, 0, 0],
- // focusOffset: 11,
- // },
- // },
- // {
- // name: 'Select and delete',
- // inputs: [
- // insertText('A lion.'),
- // moveNativeSelection([0, 0, 0], 2, [0, 0, 0], 6),
- // deleteForward(),
- // insertText('duck'),
- // moveNativeSelection([0, 0, 0], 2, [0, 0, 0], 6),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 2,
- // focusPath: [0, 0, 0],
- // focusOffset: 6,
- // },
- // },
- // {
- // name: 'Inserting a paragraph',
- // inputs: [insertParagraph()],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [1, 0, 0],
- // anchorOffset: 0,
- // focusPath: [1, 0, 0],
- // focusOffset: 0,
- // },
- // },
- // {
- // name: 'Inserting a paragraph and then removing it',
- // inputs: [insertParagraph(), deleteBackward(1)],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 0,
- // focusPath: [0, 0, 0],
- // focusOffset: 0,
- // },
- // },
- // {
- // name: 'Inserting a paragraph part way through text',
- // inputs: [
- // insertText('Hello world'),
- // moveNativeSelection([0, 0, 0], 6, [0, 0, 0], 6),
- // insertParagraph(),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [1, 0, 0],
- // anchorOffset: 0,
- // focusPath: [1, 0, 0],
- // focusOffset: 0,
- // },
- // },
- // {
- // name: 'Inserting two paragraphs and then deleting via selection',
- // inputs: [
- // insertText('123'),
- // insertParagraph(),
- // insertText('456'),
- // moveNativeSelection([0, 0, 0], 0, [1, 0, 0], 3),
- // deleteBackward(1),
- // ],
- // expectedHTML:
- // '',
- // expectedSelection: {
- // anchorPath: [0, 0, 0],
- // anchorOffset: 0,
- // focusPath: [0, 0, 0],
- // focusOffset: 0,
- // },
- // },
...[
{
whitespaceCharacter: ' ',
@@ -1254,47 +934,43 @@ describe('LexicalSelection tests', () => {
});
test('insert text one selected node element selection', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
+ await editor!.update(() => {
+ const root = $getRoot();
- const paragraph = root.getFirstChild()!;
+ const paragraph = root.getFirstChild()!;
- const elementNode = $createTestElementNode();
- const text = $createTextNode('foo');
+ const elementNode = $createTestElementNode();
+ const text = $createTextNode('foo');
- paragraph.append(elementNode);
- elementNode.append(text);
+ paragraph.append(elementNode);
+ elementNode.append(text);
- const selection = $createRangeSelection();
- selection.anchor.set(text.__key, 0, 'text');
- selection.focus.set(paragraph.__key, 1, 'element');
+ const selection = $createRangeSelection();
+ selection.anchor.set(text.__key, 0, 'text');
+ selection.focus.set(paragraph.__key, 1, 'element');
- selection.insertText('');
+ selection.insertText('');
- expect(root.getTextContent()).toBe('');
- });
+ expect(root.getTextContent()).toBe('');
});
});
test('getNodes resolves nested block nodes', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
+ await editor!.update(() => {
+ const root = $getRoot();
- const paragraph = root.getFirstChild()!;
+ const paragraph = root.getFirstChild()!;
- const elementNode = $createTestElementNode();
- const text = $createTextNode();
+ const elementNode = $createTestElementNode();
+ const text = $createTextNode();
- paragraph.append(elementNode);
- elementNode.append(text);
+ paragraph.append(elementNode);
+ elementNode.append(text);
- const selectedNodes = $getSelection()!.getNodes();
+ const selectedNodes = $getSelection()!.getNodes();
- expect(selectedNodes.length).toBe(1);
- expect(selectedNodes[0].getKey()).toBe(text.getKey());
- });
+ expect(selectedNodes.length).toBe(1);
+ expect(selectedNodes[0].getKey()).toBe(text.getKey());
});
});
@@ -1851,50 +1527,48 @@ describe('LexicalSelection tests', () => {
// eslint-disable-next-line no-only-tests/no-only-tests
const test_ = only === true ? test.only : test;
test_(name, async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
+ await editor!.update(() => {
+ const root = $getRoot();
- const paragraph = root.getFirstChild()!;
- const textNode = $createTextNode('foo');
- // Note: line break can't be selected by the DOM
- const linebreak = $createLineBreakNode();
+ const paragraph = root.getFirstChild()!;
+ const textNode = $createTextNode('foo');
+ // Note: line break can't be selected by the DOM
+ const linebreak = $createLineBreakNode();
- const selection = $getSelection();
+ const selection = $getSelection();
- if (!$isRangeSelection(selection)) {
- return;
- }
+ if (!$isRangeSelection(selection)) {
+ return;
+ }
- const anchor = selection.anchor;
- const focus = selection.focus;
+ const anchor = selection.anchor;
+ const focus = selection.focus;
- paragraph.append(textNode, linebreak);
+ paragraph.append(textNode, linebreak);
- fnBefore(paragraph, textNode);
+ fnBefore(paragraph, textNode);
- anchor.set(paragraph.getKey(), anchorOffset, 'element');
- focus.set(paragraph.getKey(), focusOffset, 'element');
+ anchor.set(paragraph.getKey(), anchorOffset, 'element');
+ focus.set(paragraph.getKey(), focusOffset, 'element');
- const {
- expectedAnchor,
- expectedAnchorOffset,
- expectedFocus,
- expectedFocusOffset,
- } = fn(paragraph, textNode);
+ const {
+ expectedAnchor,
+ expectedAnchorOffset,
+ expectedFocus,
+ expectedFocusOffset,
+ } = fn(paragraph, textNode);
- if (invertSelection !== true) {
- expect(selection.anchor.key).toBe(expectedAnchor.__key);
- expect(selection.anchor.offset).toBe(expectedAnchorOffset);
- expect(selection.focus.key).toBe(expectedFocus.__key);
- expect(selection.focus.offset).toBe(expectedFocusOffset);
- } else {
- expect(selection.anchor.key).toBe(expectedFocus.__key);
- expect(selection.anchor.offset).toBe(expectedFocusOffset);
- expect(selection.focus.key).toBe(expectedAnchor.__key);
- expect(selection.focus.offset).toBe(expectedAnchorOffset);
- }
- });
+ if (invertSelection !== true) {
+ expect(selection.anchor.key).toBe(expectedAnchor.__key);
+ expect(selection.anchor.offset).toBe(expectedAnchorOffset);
+ expect(selection.focus.key).toBe(expectedFocus.__key);
+ expect(selection.focus.offset).toBe(expectedFocusOffset);
+ } else {
+ expect(selection.anchor.key).toBe(expectedFocus.__key);
+ expect(selection.anchor.offset).toBe(expectedFocusOffset);
+ expect(selection.focus.key).toBe(expectedAnchor.__key);
+ expect(selection.focus.offset).toBe(expectedAnchorOffset);
+ }
});
});
},
@@ -1903,132 +1577,19 @@ describe('LexicalSelection tests', () => {
describe('Selection correctly resolves to a sibling ElementNode when a node is removed', () => {
test('', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
-
- const listNode = $createListNode('bullet');
- const listItemNode = $createListItemNode();
- const paragraph = $createParagraphNode();
-
- root.append(listNode);
-
- listNode.append(listItemNode);
- listItemNode.select();
- listNode.insertAfter(paragraph);
- listItemNode.remove();
-
- const selection = $getSelection();
-
- if (!$isRangeSelection(selection)) {
- return;
- }
-
- expect(selection.anchor.getNode().__type).toBe('paragraph');
- expect(selection.focus.getNode().__type).toBe('paragraph');
- });
- });
- });
- });
-
- describe('Selection correctly resolves to a sibling ElementNode when a selected node child is removed', () => {
- test('', async () => {
- await ReactTestUtils.act(async () => {
- let paragraphNodeKey: string;
- await editor!.update(() => {
- const root = $getRoot();
-
- const paragraphNode = $createParagraphNode();
- paragraphNodeKey = paragraphNode.__key;
- const listNode = $createListNode('number');
- const listItemNode1 = $createListItemNode();
- const textNode1 = $createTextNode('foo');
- const listItemNode2 = $createListItemNode();
- const listNode2 = $createListNode('number');
- const listItemNode2x1 = $createListItemNode();
-
- listNode.append(listItemNode1, listItemNode2);
- listItemNode1.append(textNode1);
- listItemNode2.append(listNode2);
- listNode2.append(listItemNode2x1);
- root.append(paragraphNode, listNode);
-
- listItemNode2.select();
-
- listNode.remove();
- });
- await editor!.getEditorState().read(() => {
- const selection = $assertRangeSelection($getSelection());
- expect(selection.anchor.key).toBe(paragraphNodeKey);
- expect(selection.focus.key).toBe(paragraphNodeKey);
- });
- });
- });
- });
-
- describe('Selection correctly resolves to a sibling ElementNode that has multiple children with the correct offset when a node is removed', () => {
- test('', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- // Arrange
- // Root
- // |- Paragraph
- // |- Link
- // |- Text
- // |- LineBreak
- // |- Text
- // |- Text
- const root = $getRoot();
-
- const paragraph = $createParagraphNode();
- const link = $createLinkNode('bullet');
- const textOne = $createTextNode('Hello');
- const br = $createLineBreakNode();
- const textTwo = $createTextNode('world');
- const textThree = $createTextNode(' ');
-
- root.append(paragraph);
- link.append(textOne);
- link.append(br);
- link.append(textTwo);
-
- paragraph.append(link);
- paragraph.append(textThree);
-
- textThree.select();
- // Act
- textThree.remove();
- // Assert
- const expectedKey = link.getKey();
-
- const selection = $getSelection();
-
- if (!$isRangeSelection(selection)) {
- return;
- }
-
- const {anchor, focus} = selection;
-
- expect(anchor.getNode().getKey()).toBe(expectedKey);
- expect(focus.getNode().getKey()).toBe(expectedKey);
- expect(anchor.offset).toBe(3);
- expect(focus.offset).toBe(3);
- });
- });
- });
- });
-
- test('isBackward', async () => {
- await ReactTestUtils.act(async () => {
await editor!.update(() => {
const root = $getRoot();
- const paragraph = root.getFirstChild()!;
- const paragraphKey = paragraph.getKey();
- const textNode = $createTextNode('foo');
- const textNodeKey = textNode.getKey();
- // Note: line break can't be selected by the DOM
- const linebreak = $createLineBreakNode();
+ const listNode = $createListNode('bullet');
+ const listItemNode = $createListItemNode();
+ const paragraph = $createParagraphNode();
+
+ root.append(listNode);
+
+ listNode.append(listItemNode);
+ listItemNode.select();
+ listNode.insertAfter(paragraph);
+ listItemNode.remove();
const selection = $getSelection();
@@ -2036,32 +1597,137 @@ describe('LexicalSelection tests', () => {
return;
}
- const anchor = selection.anchor;
- const focus = selection.focus;
- paragraph.append(textNode, linebreak);
- anchor.set(textNodeKey, 0, 'text');
- focus.set(textNodeKey, 0, 'text');
-
- expect(selection.isBackward()).toBe(false);
-
- anchor.set(paragraphKey, 1, 'element');
- focus.set(paragraphKey, 1, 'element');
-
- expect(selection.isBackward()).toBe(false);
-
- anchor.set(paragraphKey, 0, 'element');
- focus.set(paragraphKey, 1, 'element');
-
- expect(selection.isBackward()).toBe(false);
-
- anchor.set(paragraphKey, 1, 'element');
- focus.set(paragraphKey, 0, 'element');
-
- expect(selection.isBackward()).toBe(true);
+ expect(selection.anchor.getNode().__type).toBe('paragraph');
+ expect(selection.focus.getNode().__type).toBe('paragraph');
});
});
});
+ describe('Selection correctly resolves to a sibling ElementNode when a selected node child is removed', () => {
+ test('', async () => {
+ let paragraphNodeKey: string;
+ await editor!.update(() => {
+ const root = $getRoot();
+
+ const paragraphNode = $createParagraphNode();
+ paragraphNodeKey = paragraphNode.__key;
+ const listNode = $createListNode('number');
+ const listItemNode1 = $createListItemNode();
+ const textNode1 = $createTextNode('foo');
+ const listItemNode2 = $createListItemNode();
+ const listNode2 = $createListNode('number');
+ const listItemNode2x1 = $createListItemNode();
+
+ listNode.append(listItemNode1, listItemNode2);
+ listItemNode1.append(textNode1);
+ listItemNode2.append(listNode2);
+ listNode2.append(listItemNode2x1);
+ root.append(paragraphNode, listNode);
+
+ listItemNode2.select();
+
+ listNode.remove();
+ });
+ await editor!.getEditorState().read(() => {
+ const selection = $assertRangeSelection($getSelection());
+ expect(selection.anchor.key).toBe(paragraphNodeKey);
+ expect(selection.focus.key).toBe(paragraphNodeKey);
+ });
+ });
+ });
+
+ describe('Selection correctly resolves to a sibling ElementNode that has multiple children with the correct offset when a node is removed', () => {
+ test('', async () => {
+ await editor!.update(() => {
+ // Arrange
+ // Root
+ // |- Paragraph
+ // |- Link
+ // |- Text
+ // |- LineBreak
+ // |- Text
+ // |- Text
+ const root = $getRoot();
+
+ const paragraph = $createParagraphNode();
+ const link = $createLinkNode('bullet');
+ const textOne = $createTextNode('Hello');
+ const br = $createLineBreakNode();
+ const textTwo = $createTextNode('world');
+ const textThree = $createTextNode(' ');
+
+ root.append(paragraph);
+ link.append(textOne);
+ link.append(br);
+ link.append(textTwo);
+
+ paragraph.append(link);
+ paragraph.append(textThree);
+
+ textThree.select();
+ // Act
+ textThree.remove();
+ // Assert
+ const expectedKey = link.getKey();
+
+ const selection = $getSelection();
+
+ if (!$isRangeSelection(selection)) {
+ return;
+ }
+
+ const {anchor, focus} = selection;
+
+ expect(anchor.getNode().getKey()).toBe(expectedKey);
+ expect(focus.getNode().getKey()).toBe(expectedKey);
+ expect(anchor.offset).toBe(3);
+ expect(focus.offset).toBe(3);
+ });
+ });
+ });
+
+ test('isBackward', async () => {
+ await editor!.update(() => {
+ const root = $getRoot();
+
+ const paragraph = root.getFirstChild()!;
+ const paragraphKey = paragraph.getKey();
+ const textNode = $createTextNode('foo');
+ const textNodeKey = textNode.getKey();
+ // Note: line break can't be selected by the DOM
+ const linebreak = $createLineBreakNode();
+
+ const selection = $getSelection();
+
+ if (!$isRangeSelection(selection)) {
+ return;
+ }
+
+ const anchor = selection.anchor;
+ const focus = selection.focus;
+ paragraph.append(textNode, linebreak);
+ anchor.set(textNodeKey, 0, 'text');
+ focus.set(textNodeKey, 0, 'text');
+
+ expect(selection.isBackward()).toBe(false);
+
+ anchor.set(paragraphKey, 1, 'element');
+ focus.set(paragraphKey, 1, 'element');
+
+ expect(selection.isBackward()).toBe(false);
+
+ anchor.set(paragraphKey, 0, 'element');
+ focus.set(paragraphKey, 1, 'element');
+
+ expect(selection.isBackward()).toBe(false);
+
+ anchor.set(paragraphKey, 1, 'element');
+ focus.set(paragraphKey, 0, 'element');
+
+ expect(selection.isBackward()).toBe(true);
+ });
+ });
+
describe('Decorator text content for selection', () => {
const baseCases: {
name: string;
@@ -2144,34 +1810,32 @@ describe('LexicalSelection tests', () => {
})
.forEach(({name, fn, invertSelection}) => {
it(name, async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
+ await editor!.update(() => {
+ const root = $getRoot();
- const paragraph = root.getFirstChild()!;
- const textNode1 = $createTextNode('1');
- const textNode2 = $createTextNode('2');
- const decorator = $createTestDecoratorNode();
+ const paragraph = root.getFirstChild()!;
+ const textNode1 = $createTextNode('1');
+ const textNode2 = $createTextNode('2');
+ const decorator = $createTestDecoratorNode();
- paragraph.append(textNode1, decorator, textNode2);
+ paragraph.append(textNode1, decorator, textNode2);
- const selection = $getSelection();
+ const selection = $getSelection();
- if (!$isRangeSelection(selection)) {
- return;
- }
+ if (!$isRangeSelection(selection)) {
+ return;
+ }
- const expectedTextContent = fn({
- anchor: invertSelection ? selection.focus : selection.anchor,
- decorator,
- focus: invertSelection ? selection.anchor : selection.focus,
- paragraph,
- textNode1,
- textNode2,
- });
-
- expect(selection.getTextContent()).toBe(expectedTextContent);
+ const expectedTextContent = fn({
+ anchor: invertSelection ? selection.focus : selection.anchor,
+ decorator,
+ focus: invertSelection ? selection.anchor : selection.focus,
+ paragraph,
+ textNode1,
+ textNode2,
});
+
+ expect(selection.getTextContent()).toBe(expectedTextContent);
});
});
});
@@ -2274,44 +1938,42 @@ describe('LexicalSelection tests', () => {
it('adjust offset for inline elements text formatting', async () => {
await init();
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
+ await editor!.update(() => {
+ const root = $getRoot();
- const text1 = $createTextNode('--');
- const text2 = $createTextNode('abc');
- const text3 = $createTextNode('--');
+ const text1 = $createTextNode('--');
+ const text2 = $createTextNode('abc');
+ const text3 = $createTextNode('--');
- root.append(
+ root.append(
$createParagraphNode().append(
- text1,
- $createLinkNode('https://lexical.dev').append(text2),
- text3,
+ text1,
+ $createLinkNode('https://lexical.dev').append(text2),
+ text3,
),
- );
+ );
- $setAnchorPoint({
- key: text1.getKey(),
- offset: 2,
- type: 'text',
- });
-
- $setFocusPoint({
- key: text3.getKey(),
- offset: 0,
- type: 'text',
- });
-
- const selection = $getSelection();
-
- if (!$isRangeSelection(selection)) {
- return;
- }
-
- selection.formatText('bold');
-
- expect(text2.hasFormat('bold')).toBe(true);
+ $setAnchorPoint({
+ key: text1.getKey(),
+ offset: 2,
+ type: 'text',
});
+
+ $setFocusPoint({
+ key: text3.getKey(),
+ offset: 0,
+ type: 'text',
+ });
+
+ const selection = $getSelection();
+
+ if (!$isRangeSelection(selection)) {
+ return;
+ }
+
+ selection.formatText('bold');
+
+ expect(text2.hasFormat('bold')).toBe(true);
});
});
});
@@ -2504,107 +2166,103 @@ describe('LexicalSelection tests', () => {
describe('$patchStyle', () => {
it('should patch the style with the new style object', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const root = $getRoot();
- const paragraph = $createParagraphNode();
- const textNode = $createTextNode('Hello, World!');
- textNode.setStyle('font-family: serif; color: red;');
- $addNodeStyle(textNode);
- paragraph.append(textNode);
- root.append(paragraph);
+ await editor!.update(() => {
+ const root = $getRoot();
+ const paragraph = $createParagraphNode();
+ const textNode = $createTextNode('Hello, World!');
+ textNode.setStyle('font-family: serif; color: red;');
+ $addNodeStyle(textNode);
+ paragraph.append(textNode);
+ root.append(paragraph);
- const selection = $createRangeSelection();
- $setSelection(selection);
- selection.insertParagraph();
- $setAnchorPoint({
- key: textNode.getKey(),
- offset: 0,
- type: 'text',
- });
+ const selection = $createRangeSelection();
+ $setSelection(selection);
+ selection.insertParagraph();
+ $setAnchorPoint({
+ key: textNode.getKey(),
+ offset: 0,
+ type: 'text',
+ });
- $setFocusPoint({
- key: textNode.getKey(),
- offset: 10,
- type: 'text',
- });
+ $setFocusPoint({
+ key: textNode.getKey(),
+ offset: 10,
+ type: 'text',
+ });
- const newStyle = {
- color: 'blue',
- 'font-family': 'Arial',
- };
+ const newStyle = {
+ color: 'blue',
+ 'font-family': 'Arial',
+ };
- $patchStyleText(selection, newStyle);
+ $patchStyleText(selection, newStyle);
- const cssFontFamilyValue = $getSelectionStyleValueForProperty(
+ const cssFontFamilyValue = $getSelectionStyleValueForProperty(
selection,
'font-family',
'',
- );
- expect(cssFontFamilyValue).toBe('Arial');
+ );
+ expect(cssFontFamilyValue).toBe('Arial');
- const cssColorValue = $getSelectionStyleValueForProperty(
+ const cssColorValue = $getSelectionStyleValueForProperty(
selection,
'color',
'',
- );
- expect(cssColorValue).toBe('blue');
- });
+ );
+ expect(cssColorValue).toBe('blue');
});
});
it('should patch the style with property function', async () => {
- await ReactTestUtils.act(async () => {
- await editor!.update(() => {
- const currentColor = 'red';
- const nextColor = 'blue';
+ await editor!.update(() => {
+ const currentColor = 'red';
+ const nextColor = 'blue';
- const root = $getRoot();
- const paragraph = $createParagraphNode();
- const textNode = $createTextNode('Hello, World!');
- textNode.setStyle(`color: ${currentColor};`);
- $addNodeStyle(textNode);
- paragraph.append(textNode);
- root.append(paragraph);
+ const root = $getRoot();
+ const paragraph = $createParagraphNode();
+ const textNode = $createTextNode('Hello, World!');
+ textNode.setStyle(`color: ${currentColor};`);
+ $addNodeStyle(textNode);
+ paragraph.append(textNode);
+ root.append(paragraph);
- const selection = $createRangeSelection();
- $setSelection(selection);
- selection.insertParagraph();
- $setAnchorPoint({
- key: textNode.getKey(),
- offset: 0,
- type: 'text',
- });
+ const selection = $createRangeSelection();
+ $setSelection(selection);
+ selection.insertParagraph();
+ $setAnchorPoint({
+ key: textNode.getKey(),
+ offset: 0,
+ type: 'text',
+ });
- $setFocusPoint({
- key: textNode.getKey(),
- offset: 10,
- type: 'text',
- });
+ $setFocusPoint({
+ key: textNode.getKey(),
+ offset: 10,
+ type: 'text',
+ });
- const newStyle = {
- color: jest.fn(
+ const newStyle = {
+ color: jest.fn(
(current: string | null, target: LexicalNode | RangeSelection) =>
- nextColor,
- ),
- };
+ nextColor,
+ ),
+ };
- $patchStyleText(selection, newStyle);
+ $patchStyleText(selection, newStyle);
- const cssColorValue = $getSelectionStyleValueForProperty(
+ const cssColorValue = $getSelectionStyleValueForProperty(
selection,
'color',
'',
- );
+ );
- expect(cssColorValue).toBe(nextColor);
- expect(newStyle.color).toHaveBeenCalledTimes(1);
+ expect(cssColorValue).toBe(nextColor);
+ expect(newStyle.color).toHaveBeenCalledTimes(1);
- const lastCall = newStyle.color.mock.lastCall!;
- expect(lastCall[0]).toBe(currentColor);
- // @ts-ignore - It expected to be a LexicalNode
- expect($isTextNode(lastCall[1])).toBeTruthy();
- });
+ const lastCall = newStyle.color.mock.lastCall!;
+ expect(lastCall[0]).toBe(currentColor);
+ // @ts-ignore - It expected to be a LexicalNode
+ expect($isTextNode(lastCall[1])).toBeTruthy();
});
});
});
diff --git a/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.tsx b/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.ts
similarity index 53%
rename from resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.tsx
rename to resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.ts
index 37049e598..6ce133d18 100644
--- a/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.tsx
+++ b/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableNode.test.ts
@@ -7,22 +7,14 @@
*/
import {$insertDataTransferForRichText} from '@lexical/clipboard';
-import {TablePlugin} from '@lexical/react/LexicalTablePlugin';
import {
$createTableNode,
- $createTableNodeWithDimensions,
- $createTableSelection,
} from '@lexical/table';
import {
$createParagraphNode,
- $createTextNode,
$getRoot,
$getSelection,
$isRangeSelection,
- $selectAll,
- $setSelection,
- CUT_COMMAND,
- ParagraphNode,
} from 'lexical';
import {
DataTransferMock,
@@ -30,8 +22,6 @@ import {
invariant,
} from 'lexical/__tests__/utils';
-import {$getElementForTableNode, TableNode} from '../../LexicalTableNode';
-
export class ClipboardDataMock {
getData: jest.Mock;
setData: jest.Mock;
@@ -149,203 +139,7 @@ describe('LexicalTableNode tests', () => {
`Surface | MWP_WORK_LS_COMPOSER | 77349 |
Lexical | XDS_RICH_TEXT_AREA | sdvd sdfvsfs |
`,
);
});
-
- test('Cut table in the middle of a range selection', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const paragraph = root.getFirstChild();
- const beforeText = $createTextNode('text before the table');
- const table = $createTableNodeWithDimensions(4, 4, true);
- const afterText = $createTextNode('text after the table');
-
- paragraph?.append(beforeText);
- paragraph?.append(table);
- paragraph?.append(afterText);
- });
- await editor.update(() => {
- editor.focus();
- $selectAll();
- });
- await editor.update(() => {
- editor.dispatchCommand(CUT_COMMAND, {} as ClipboardEvent);
- });
-
- expect(testEnv.innerHTML).toBe(`
`);
- });
-
- test('Cut table as last node in range selection ', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const paragraph = root.getFirstChild();
- const beforeText = $createTextNode('text before the table');
- const table = $createTableNodeWithDimensions(4, 4, true);
-
- paragraph?.append(beforeText);
- paragraph?.append(table);
- });
- await editor.update(() => {
- editor.focus();
- $selectAll();
- });
- await editor.update(() => {
- editor.dispatchCommand(CUT_COMMAND, {} as ClipboardEvent);
- });
-
- expect(testEnv.innerHTML).toBe(`
`);
- });
-
- test('Cut table as first node in range selection ', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const paragraph = root.getFirstChild();
- const table = $createTableNodeWithDimensions(4, 4, true);
- const afterText = $createTextNode('text after the table');
-
- paragraph?.append(table);
- paragraph?.append(afterText);
- });
- await editor.update(() => {
- editor.focus();
- $selectAll();
- });
- await editor.update(() => {
- editor.dispatchCommand(CUT_COMMAND, {} as ClipboardEvent);
- });
-
- expect(testEnv.innerHTML).toBe(`
`);
- });
-
- test('Cut table is whole selection, should remove it', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const table = $createTableNodeWithDimensions(4, 4, true);
- root.append(table);
- });
- await editor.update(() => {
- const root = $getRoot();
- const table = root.getLastChild();
- if (table) {
- const DOMTable = $getElementForTableNode(editor, table);
- if (DOMTable) {
- table
- ?.getCellNodeFromCords(0, 0, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('some text'));
- const selection = $createTableSelection();
- selection.set(
- table.__key,
- table?.getCellNodeFromCords(0, 0, DOMTable)?.__key || '',
- table?.getCellNodeFromCords(3, 3, DOMTable)?.__key || '',
- );
- $setSelection(selection);
- editor.dispatchCommand(CUT_COMMAND, {
- preventDefault: () => {},
- stopPropagation: () => {},
- } as ClipboardEvent);
- }
- }
- });
-
- expect(testEnv.innerHTML).toBe(`
`);
- });
-
- test('Cut subsection of table cells, should just clear contents', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const table = $createTableNodeWithDimensions(4, 4, true);
- root.append(table);
- });
- await editor.update(() => {
- const root = $getRoot();
- const table = root.getLastChild();
- if (table) {
- const DOMTable = $getElementForTableNode(editor, table);
- if (DOMTable) {
- table
- ?.getCellNodeFromCords(0, 0, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('some text'));
- const selection = $createTableSelection();
- selection.set(
- table.__key,
- table?.getCellNodeFromCords(0, 0, DOMTable)?.__key || '',
- table?.getCellNodeFromCords(2, 2, DOMTable)?.__key || '',
- );
- $setSelection(selection);
- editor.dispatchCommand(CUT_COMMAND, {
- preventDefault: () => {},
- stopPropagation: () => {},
- } as ClipboardEvent);
- }
- }
- });
-
- expect(testEnv.innerHTML).toBe(
- `
`,
- );
- });
-
- test('Table plain text output validation', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const root = $getRoot();
- const table = $createTableNodeWithDimensions(4, 4, true);
- root.append(table);
- });
- await editor.update(() => {
- const root = $getRoot();
- const table = root.getLastChild();
- if (table) {
- const DOMTable = $getElementForTableNode(editor, table);
- if (DOMTable) {
- table
- ?.getCellNodeFromCords(0, 0, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('1'));
- table
- ?.getCellNodeFromCords(1, 0, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode(''));
- table
- ?.getCellNodeFromCords(2, 0, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('2'));
- table
- ?.getCellNodeFromCords(0, 1, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('3'));
- table
- ?.getCellNodeFromCords(1, 1, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode('4'));
- table
- ?.getCellNodeFromCords(2, 1, DOMTable)
- ?.getLastChild()
- ?.append($createTextNode(''));
- const selection = $createTableSelection();
- selection.set(
- table.__key,
- table?.getCellNodeFromCords(0, 0, DOMTable)?.__key || '',
- table?.getCellNodeFromCords(2, 1, DOMTable)?.__key || '',
- );
- expect(selection.getTextContent()).toBe(`1\t\t2\n3\t4\t\n`);
- }
- }
- });
- });
},
undefined,
- ,
);
});
diff --git a/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.tsx b/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.ts
similarity index 73%
rename from resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.tsx
rename to resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.ts
index 9e9dbac81..35ee65b68 100644
--- a/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.tsx
+++ b/resources/js/wysiwyg/lexical/table/__tests__/unit/LexicalTableSelection.test.ts
@@ -19,9 +19,6 @@ import {
TextNode,
} from 'lexical';
import {createTestEditor} from 'lexical/__tests__/utils';
-import {createRef, useEffect, useMemo} from 'react';
-import {createRoot, Root} from 'react-dom/client';
-import * as ReactTestUtils from 'lexical/shared/react-test-utils';
describe('table selection', () => {
let originalText: TextNode;
@@ -31,57 +28,35 @@ describe('table selection', () => {
let paragraphKey: string;
let textKey: string;
let parsedEditorState: EditorState;
- let reactRoot: Root;
+ let root: HTMLDivElement;
let container: HTMLDivElement | null = null;
let editor: LexicalEditor | null = null;
beforeEach(() => {
container = document.createElement('div');
- reactRoot = createRoot(container);
+ root = document.createElement('div');
+ root.setAttribute('contenteditable', 'true');
document.body.appendChild(container);
});
- function useLexicalEditor(
- rootElementRef: React.RefObject,
- onError?: () => void,
- ) {
- const editorInHook = useMemo(
- () =>
- createTestEditor({
- nodes: [],
- onError: onError || jest.fn(),
- theme: {
- text: {
- bold: 'editor-text-bold',
- italic: 'editor-text-italic',
- underline: 'editor-text-underline',
- },
- },
- }),
- [onError],
- );
-
- useEffect(() => {
- const rootElement = rootElementRef.current;
-
- editorInHook.setRootElement(rootElement);
- }, [rootElementRef, editorInHook]);
-
- return editorInHook;
- }
+ afterEach(() => {
+ container?.remove();
+ });
function init(onError?: () => void) {
- const ref = createRef();
+ editor = createTestEditor({
+ nodes: [],
+ onError: onError || jest.fn(),
+ theme: {
+ text: {
+ bold: 'editor-text-bold',
+ italic: 'editor-text-italic',
+ underline: 'editor-text-underline',
+ },
+ },
+ })
- function TestBase() {
- editor = useLexicalEditor(ref, onError);
-
- return ;
- }
-
- ReactTestUtils.act(() => {
- reactRoot.render();
- });
+ editor.setRootElement(root);
}
async function update(fn: () => void) {
diff --git a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.tsx b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.ts
similarity index 85%
rename from resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.tsx
rename to resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.ts
index 2d5db2c69..53c743df6 100644
--- a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.tsx
+++ b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalEventHelpers.test.ts
@@ -5,27 +5,16 @@
* LICENSE file in the root directory of this source tree.
*
*/
-
-import {CodeHighlightNode, CodeNode} from '@lexical/code';
-import {HashtagNode} from '@lexical/hashtag';
import {AutoLinkNode, LinkNode} from '@lexical/link';
import {ListItemNode, ListNode} from '@lexical/list';
-import {OverflowNode} from '@lexical/overflow';
-import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
-import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-import {ContentEditable} from '@lexical/react/LexicalContentEditable';
-import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
-import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
-import {HeadingNode, QuoteNode} from '@lexical/rich-text';
+import {HeadingNode, QuoteNode, registerRichText} from '@lexical/rich-text';
import {
applySelectionInputs,
pasteHTML,
-} from '@lexical/selection/src/__tests__/utils';
+} from '@lexical/selection/__tests__/utils';
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
-import {LexicalEditor} from 'lexical';
-import {initializeClipboard, TestComposer} from 'lexical/__tests__/utils';
-import {createRoot} from 'react-dom/client';
-import * as ReactTestUtils from 'lexical/shared/react-test-utils';
+import {$createParagraphNode, $insertNodes, LexicalEditor} from 'lexical';
+import {createTestEditor, initializeClipboard} from 'lexical/__tests__/utils';
jest.mock('lexical/shared/environment', () => {
const originalModule = jest.requireActual('lexical/shared/environment');
@@ -89,85 +78,69 @@ describe('LexicalEventHelpers', () => {
let editor: LexicalEditor | null = null;
async function init() {
- function TestBase() {
- function TestPlugin(): null {
- [editor] = useLexicalComposerContext();
- return null;
- }
+ const config = {
+ nodes: [
+ LinkNode,
+ HeadingNode,
+ ListNode,
+ ListItemNode,
+ QuoteNode,
+ TableNode,
+ TableCellNode,
+ TableRowNode,
+ AutoLinkNode,
+ ],
+ theme: {
+ code: 'editor-code',
+ heading: {
+ h1: 'editor-heading-h1',
+ h2: 'editor-heading-h2',
+ h3: 'editor-heading-h3',
+ h4: 'editor-heading-h4',
+ h5: 'editor-heading-h5',
+ h6: 'editor-heading-h6',
+ },
+ image: 'editor-image',
+ list: {
+ listitem: 'editor-listitem',
+ olDepth: ['editor-list-ol'],
+ ulDepth: ['editor-list-ul'],
+ },
+ paragraph: 'editor-paragraph',
+ placeholder: 'editor-placeholder',
+ quote: 'editor-quote',
+ text: {
+ bold: 'editor-text-bold',
+ code: 'editor-text-code',
+ hashtag: 'editor-text-hashtag',
+ italic: 'editor-text-italic',
+ link: 'editor-text-link',
+ strikethrough: 'editor-text-strikethrough',
+ underline: 'editor-text-underline',
+ underlineStrikethrough: 'editor-text-underlineStrikethrough',
+ },
+ },
+ };
- return (
-
-
- }
- placeholder={null}
- ErrorBoundary={LexicalErrorBoundary}
- />
-
-
-
- );
- }
+ editor = createTestEditor(config);
+ registerRichText(editor);
- ReactTestUtils.act(() => {
- createRoot(container!).render();
+ const root = document.createElement('div');
+ root.setAttribute('contenteditable', 'true');
+ container?.append(root);
+
+ editor.setRootElement(root);
+
+ editor.update(() => {
+ $insertNodes([$createParagraphNode()])
});
+ editor.commitUpdates();
}
async function update(fn: () => void) {
- await ReactTestUtils.act(async () => {
- await editor!.update(fn);
- });
+ await editor!.update(fn);
+ editor?.commitUpdates();
return Promise.resolve().then();
}
@@ -549,24 +522,6 @@ describe('LexicalEventHelpers', () => {
],
name: 'collapsible spaces with nested structures',
},
- // TODO no proper support for divs #4465
- // {
- // expectedHTML:
- // 'a
b
',
- // inputs: [
- // pasteHTML(`
- //
- //
- // a
- //
- //
- // b
- //
- //
- // `),
- // ],
- // name: 'collapsible spaces with nested structures (2)',
- // },
{
expectedHTML:
'a b
',
@@ -612,32 +567,6 @@ describe('LexicalEventHelpers', () => {
],
name: 'forced line break with tabs',
},
- // The 3 below are not correct, they're missing the first \n ->
but that's a fault with
- // the implementation of DOMParser, it works correctly in Safari
- {
- expectedHTML:
- 'a
b
',
- inputs: [pasteHTML(`\na\r\nb\r\n
`)],
- name: 'pre (no touchy) (1)',
- },
- {
- expectedHTML:
- 'a
b
',
- inputs: [
- pasteHTML(`
- \na\r\nb\r\n
- `),
- ],
- name: 'pre (no touchy) (2)',
- },
- {
- expectedHTML:
- '
a
b
',
- inputs: [
- pasteHTML(`\na\r\nb\r\n`),
- ],
- name: 'white-space: pre (no touchy) (2)',
- },
{
expectedHTML:
'paragraph1
paragraph2
',
diff --git a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalRootHelpers.test.ts b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalRootHelpers.test.ts
index 369caaea4..1322b482b 100644
--- a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalRootHelpers.test.ts
+++ b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalRootHelpers.test.ts
@@ -6,14 +6,39 @@
*
*/
-import {
- $isRootTextContentEmpty,
- $isRootTextContentEmptyCurry,
- $rootTextContent,
-} from '@lexical/text';
import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
import {initializeUnitTest} from 'lexical/__tests__/utils';
+export function $rootTextContent(): string {
+ const root = $getRoot();
+
+ return root.getTextContent();
+}
+
+export function $isRootTextContentEmpty(
+ isEditorComposing: boolean,
+ trim = true,
+): boolean {
+ if (isEditorComposing) {
+ return false;
+ }
+
+ let text = $rootTextContent();
+
+ if (trim) {
+ text = text.trim();
+ }
+
+ return text === '';
+}
+
+export function $isRootTextContentEmptyCurry(
+ isEditorComposing: boolean,
+ trim?: boolean,
+): () => boolean {
+ return () => $isRootTextContentEmpty(isEditorComposing, trim);
+}
+
describe('LexicalRootHelpers tests', () => {
initializeUnitTest((testEnv) => {
it('textContent', async () => {
diff --git a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalUtilsSplitNode.test.tsx b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalUtilsSplitNode.test.ts
similarity index 100%
rename from resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalUtilsSplitNode.test.tsx
rename to resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalUtilsSplitNode.test.ts
diff --git a/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexlcaiUtilsInsertNodeToNearestRoot.test.tsx b/resources/js/wysiwyg/lexical/utils/__tests__/unit/LexlcaiUtilsInsertNodeToNearestRoot.test.ts
similarity index 100%
rename from resources/js/wysiwyg/lexical/utils/__tests__/unit/LexlcaiUtilsInsertNodeToNearestRoot.test.tsx
rename to resources/js/wysiwyg/lexical/utils/__tests__/unit/LexlcaiUtilsInsertNodeToNearestRoot.test.ts