mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-22 12:51:16 +08:00
Lexical: Got a range of Editor tests working
This commit is contained in:
parent
22d078b47f
commit
ccd486f2a9
|
@ -93,10 +93,13 @@ const config: Config = {
|
||||||
// "node"
|
// "node"
|
||||||
// ],
|
// ],
|
||||||
|
|
||||||
modulePaths: ['/home/dan/web/bookstack/'],
|
modulePaths: ['./'],
|
||||||
|
|
||||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||||
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths),
|
moduleNameMapper: {
|
||||||
|
'lexical/shared/invariant': 'resources/js/wysiwyg/lexical/core/shared/__mocks__/invariant',
|
||||||
|
...pathsToModuleNameMapper(compilerOptions.paths),
|
||||||
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
// modulePathIgnorePatterns: [],
|
// modulePathIgnorePatterns: [],
|
||||||
|
|
|
@ -1236,6 +1236,13 @@ export class LexicalEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits any currently pending updates scheduled for the editor.
|
||||||
|
*/
|
||||||
|
commitUpdates(): void {
|
||||||
|
$commitPendingUpdates(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes focus from the editor.
|
* Removes focus from the editor.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,144 +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 {$insertDataTransferForRichText} from '@lexical/clipboard';
|
|
||||||
import {
|
|
||||||
$createParagraphNode,
|
|
||||||
$getRoot,
|
|
||||||
$getSelection,
|
|
||||||
$isRangeSelection,
|
|
||||||
} from 'lexical';
|
|
||||||
import {
|
|
||||||
DataTransferMock,
|
|
||||||
initializeUnitTest,
|
|
||||||
invariant,
|
|
||||||
} from 'lexical/__tests__/utils';
|
|
||||||
|
|
||||||
describe('CodeBlock tests', () => {
|
|
||||||
initializeUnitTest(
|
|
||||||
(testEnv) => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
await editor.update(() => {
|
|
||||||
const root = $getRoot();
|
|
||||||
const paragraph = $createParagraphNode();
|
|
||||||
root.append(paragraph);
|
|
||||||
paragraph.select();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Code example for tests:
|
|
||||||
*
|
|
||||||
* function run() {
|
|
||||||
* return [null, undefined, 2, ""];
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const EXPECTED_HTML = `<code spellcheck="false" dir="ltr"><span data-lexical-text="true">function run() {</span><br><span data-lexical-text="true"> return [null, undefined, 2, ""];</span><br><span data-lexical-text="true">}</span></code>`;
|
|
||||||
|
|
||||||
const CODE_PASTING_TESTS = [
|
|
||||||
{
|
|
||||||
expectedHTML: EXPECTED_HTML,
|
|
||||||
name: 'VS Code',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><div style="color: #d4d4d4;background-color: #1e1e1e;font-family: Menlo, Monaco, 'Courier New', monospace;font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><div><span style="color: #569cd6;">function</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">run</span><span style="color: #d4d4d4;">() {</span></div><div><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">return</span><span style="color: #d4d4d4;"> [</span><span style="color: #569cd6;">null</span><span style="color: #d4d4d4;">, </span><span style="color: #569cd6;">undefined</span><span style="color: #d4d4d4;">, </span><span style="color: #b5cea8;">2</span><span style="color: #d4d4d4;">, </span><span style="color: #ce9178;">""</span><span style="color: #d4d4d4;">];</span></div><div><span style="color: #d4d4d4;">}</span></div></div>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: EXPECTED_HTML,
|
|
||||||
name: 'Quip',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><pre>function run() {<br> return [null, undefined, 2, ""];<br>}</pre>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: EXPECTED_HTML,
|
|
||||||
name: 'WebStorm / Idea',
|
|
||||||
pastedHTML: `<html><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"></head><body><pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'JetBrains Mono',monospace;font-size:9.8pt;"><span style="color:#cc7832;">function </span><span style="color:#ffc66d;">run</span>() {<br>  <span style="color:#cc7832;">return </span>[<span style="color:#cc7832;">null, undefined, </span><span style="color:#6897bb;">2</span><span style="color:#cc7832;">, </span><span style="color:#6a8759;">""</span>]<span style="color:#cc7832;">;<br></span>}</pre></body></html>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<code spellcheck="false" dir="ltr"><strong class="editor-text-bold" data-lexical-text="true">function</strong><span data-lexical-text="true"> run() {</span><br><span data-lexical-text="true"> </span><strong class="editor-text-bold" data-lexical-text="true">return</strong><span data-lexical-text="true"> [</span><strong class="editor-text-bold" data-lexical-text="true">null</strong><span data-lexical-text="true">, </span><strong class="editor-text-bold" data-lexical-text="true">undefined</strong><span data-lexical-text="true">, 2, ""];</span><br><span data-lexical-text="true">}</span></code>`,
|
|
||||||
name: 'Postman IDE',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><div style="color: #000000;background-color: #fffffe;font-family: Menlo, Monaco, 'Courier New', monospace;font-weight: normal;font-size: 12px;line-height: 18px;white-space: pre;"><div><span style="color: #800555;font-weight: bold;">function</span><span style="color: #000000;"> run() {</span></div><div><span style="color: #000000;"> </span><span style="color: #800555;font-weight: bold;">return</span><span style="color: #000000;"> [</span><span style="color: #800555;font-weight: bold;">null</span><span style="color: #000000;">, </span><span style="color: #800555;font-weight: bold;">undefined</span><span style="color: #000000;">, </span><span style="color: #ff00aa;">2</span><span style="color: #000000;">, </span><span style="color: #2a00ff;">""</span><span style="color: #000000;">];</span></div><div><span style="color: #000000;">}</span></div></div>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: EXPECTED_HTML,
|
|
||||||
name: 'Slack message',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><pre class="c-mrkdwn__pre" data-stringify-type="pre" style="box-sizing: inherit; margin: 4px 0px; padding: 8px; --saf-0:rgba(var(--sk_foreground_low,29,28,29),0.13); font-size: 12px; line-height: 1.50001; font-variant-ligatures: none; white-space: pre-wrap; word-break: break-word; word-break: normal; tab-size: 4; font-family: Monaco, Menlo, Consolas, "Courier New", monospace !important; border: 1px solid var(--saf-0); border-radius: 4px; background: rgba(var(--sk_foreground_min,29,28,29),0.04); counter-reset: list-0 0 list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; color: rgb(29, 28, 29); font-style: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function run() {\n return [null, undefined, 2, ""];\n}</pre>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<code spellcheck="false" dir="ltr"><span data-lexical-text="true">const Lexical = requireCond('gk', 'runtime_is_dev', {</span><br><span data-lexical-text="true"> true: 'Lexical.dev',</span><br><span data-lexical-text="true"> false: 'Lexical.prod',</span><br><span data-lexical-text="true">});</span></code>`,
|
|
||||||
name: 'CodeHub',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><div style="color: #000000;background-color: #fffffe;font-family: 'monaco,monospace', Menlo, Monaco, 'Courier New', monospace;font-weight: normal;font-size: 13px;line-height: 20px;white-space: pre;"><div><span style="color: #ff0000;">const</span><span style="color: #000000;"> </span><span style="color: #800000;">Lexical</span><span style="color: #000000;"> = </span><span style="color: #383838;">requireCond</span><span style="color: #000000;">(</span><span style="color: #863b00;">'gk'</span><span style="color: #000000;">, </span><span style="color: #863b00;">'runtime_is_dev'</span><span style="color: #000000;">, {</span></div><div><span style="color: #000000;"> </span><span style="color: #863b00;">true</span><span style="color: #000000;">: </span><span style="color: #863b00;">'Lexical.dev'</span><span style="color: #000000;">,</span></div><div><span style="color: #000000;"> </span><span style="color: #863b00;">false</span><span style="color: #000000;">: </span><span style="color: #863b00;">'Lexical.prod'</span><span style="color: #000000;">,</span></div><div><span style="color: #000000;">});</span></div></div>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: EXPECTED_HTML,
|
|
||||||
name: 'GitHub / Gist',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><table class="highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file" data-tab-size="8" data-paste-markdown-skip="" data-tagsearch-lang="JavaScript" data-tagsearch-path="example.js" style="box-sizing: border-box; border-spacing: 0px; border-collapse: collapse; tab-size: 8; color: rgb(36, 41, 47); font-family: -apple-system, "system-ui", "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td id="file-example-js-LC1" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); overflow-wrap: normal; white-space: pre;"><span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">function</span> <span class="pl-en" style="box-sizing: border-box; color: var(--color-prettylights-syntax-entity);">run</span><span class="pl-kos" style="box-sizing: border-box;">(</span><span class="pl-kos" style="box-sizing: border-box;">)</span> <span class="pl-kos" style="box-sizing: border-box;">{</span></td></tr><tr style="box-sizing: border-box; background-color: transparent;"><td id="file-example-js-L2" class="blob-num js-line-number js-code-nav-line-number" data-line-number="2" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-example-js-LC2" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); overflow-wrap: normal; white-space: pre;"> <span class="pl-k" style="box-sizing: border-box; color: var(--color-prettylights-syntax-keyword);">return</span> <span class="pl-kos" style="box-sizing: border-box;">[</span><span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">null</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">undefined</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-c1" style="box-sizing: border-box; color: var(--color-prettylights-syntax-constant);">2</span><span class="pl-kos" style="box-sizing: border-box;">,</span> <span class="pl-s" style="box-sizing: border-box; color: var(--color-prettylights-syntax-string);">""</span><span class="pl-kos" style="box-sizing: border-box;">]</span><span class="pl-kos" style="box-sizing: border-box;">;</span></td></tr><tr style="box-sizing: border-box;"><td id="file-example-js-L3" class="blob-num js-line-number js-code-nav-line-number" data-line-number="3" style="box-sizing: border-box; padding: 0px 10px; width: 50px; min-width: 50px; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; line-height: 20px; color: var(--color-fg-subtle); text-align: right; white-space: nowrap; vertical-align: top; cursor: pointer; user-select: none;"></td><td id="file-example-js-LC3" class="blob-code blob-code-inner js-file-line" style="box-sizing: border-box; padding: 0px 10px; position: relative; line-height: 20px; vertical-align: top; overflow: visible; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 12px; color: var(--color-fg-default); overflow-wrap: normal; white-space: pre;"><span class="pl-kos" style="box-sizing: border-box;">}</span></td></tr></tbody></table>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<p><code spellcheck="false" data-lexical-text="true"><span>12</span></code></p>`,
|
|
||||||
name: 'Single line <code>',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><code>12</code>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<code spellcheck="false"><span data-lexical-text="true">1</span><br><span data-lexical-text="true">2</span></code>`,
|
|
||||||
name: 'Multiline <code>',
|
|
||||||
// TODO This is not correct. This resembles how Lexical exports code right now but
|
|
||||||
// semantically it should be wrapped in a pre
|
|
||||||
pastedHTML: `<meta charset='utf-8'><code>1<br>2</code>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<p dir="ltr"><strong class="editor-text-bold editor-text-italic editor-text-underline" data-lexical-text="true">Hello </strong><sub data-lexical-text="true"><strong class="editor-text-bold editor-text-italic">World </strong></sub><sup data-lexical-text="true"><strong class="editor-text-bold editor-text-italic editor-text-underline">Lexical</strong></sup></p>`,
|
|
||||||
name: 'Multiple text formats',
|
|
||||||
pastedHTML: `<strong style="font-weight: 700; font-style: italic; text-decoration: underline; color: rgb(0, 0, 0); font-size: 15px; text-align: left; text-indent: 0px; background-color: rgb(255, 255, 255);">Hello </strong><sub style="color: rgb(0, 0, 0); font-style: normal; font-weight: 400; text-align: left; text-indent: 0px; background-color: rgb(255, 255, 255);"><strong style="font-weight: 700; font-style: italic; text-decoration: line-through; font-size: 0.8em; vertical-align: sub !important;">World </strong></sub><sup style="color: rgb(0, 0, 0); font-style: normal; font-weight: 400; text-align: left; text-indent: 0px; background-color: rgb(255, 255, 255);"><strong style="font-weight: 700; font-style: italic; text-decoration: underline line-through; font-size: 0.8em; vertical-align: super;">Lexical</strong></sup>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<h1 dir="ltr"><span data-lexical-text="true">My document</span></h1>`,
|
|
||||||
name: 'Title from Google Docs',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-whatever"><span style="font-size:26pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">My document</span></b>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<h1 dir="ltr"><span data-lexical-text="true">My document</span></h1>`,
|
|
||||||
name: 'Title from Google Docs Wrapped in Paragraph',
|
|
||||||
pastedHTML: `<meta charset='utf-8'><meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-wjatever"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:3pt;"><span style="font-size:26pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">My document</span></p></b>`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
expectedHTML: `<p dir="ltr"><sub data-lexical-text="true"><span>subscript</span></sub><span data-lexical-text="true"> and </span><sup data-lexical-text="true"><span>superscript</span></sup></p>`,
|
|
||||||
name: 'Subscript and Superscript',
|
|
||||||
pastedHTML: `<b style="font-weight:normal;" id="docs-internal-guid-374b5f9d-7fff-9120-bcb0-1f5c1b6d59fa"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><span style="font-size:0.6em;vertical-align:sub;">subscript</span></span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> and </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><span style="font-size:0.6em;vertical-align:super;">superscript</span></span></b>`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
CODE_PASTING_TESTS.forEach((testCase, i) => {
|
|
||||||
test(`Code block html paste: ${testCase.name}`, async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
|
|
||||||
const dataTransfer = new DataTransferMock();
|
|
||||||
dataTransfer.setData('text/html', testCase.pastedHTML);
|
|
||||||
await editor.update(() => {
|
|
||||||
const selection = $getSelection();
|
|
||||||
invariant(
|
|
||||||
$isRangeSelection(selection),
|
|
||||||
'isRangeSelection(selection)',
|
|
||||||
);
|
|
||||||
$insertDataTransferForRichText(dataTransfer, selection, editor);
|
|
||||||
});
|
|
||||||
expect(testEnv.innerHTML).toBe(testCase.expectedHTML);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
namespace: 'test',
|
|
||||||
theme: {
|
|
||||||
text: {
|
|
||||||
bold: 'editor-text-bold',
|
|
||||||
italic: 'editor-text-italic',
|
|
||||||
underline: 'editor-text-underline',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
DataTransferMock,
|
DataTransferMock,
|
||||||
initializeUnitTest,
|
initializeUnitTest,
|
||||||
invariant,
|
invariant,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
describe('HTMLCopyAndPaste tests', () => {
|
describe('HTMLCopyAndPaste tests', () => {
|
||||||
initializeUnitTest(
|
initializeUnitTest(
|
||||||
|
|
|
@ -57,11 +57,13 @@ import {
|
||||||
|
|
||||||
describe('LexicalEditor tests', () => {
|
describe('LexicalEditor tests', () => {
|
||||||
let container: HTMLElement;
|
let container: HTMLElement;
|
||||||
let reactRoot: Root;
|
function setContainerChild(el: HTMLElement) {
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.append(el);
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
container = document.createElement('div');
|
container = document.createElement('div');
|
||||||
reactRoot = createRoot(container);
|
|
||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,49 +76,33 @@ describe('LexicalEditor tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function useLexicalEditor(
|
function useLexicalEditor(
|
||||||
rootElementRef: React.RefObject<HTMLDivElement>,
|
rootElement: HTMLDivElement,
|
||||||
onError?: (error: Error) => void,
|
onError?: (error: Error) => void,
|
||||||
nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,
|
nodes?: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>,
|
||||||
) {
|
) {
|
||||||
const editor = useMemo(
|
const editor = createTestEditor({
|
||||||
() =>
|
nodes: nodes ?? [],
|
||||||
createTestEditor({
|
onError: onError || jest.fn(),
|
||||||
nodes: nodes ?? [],
|
theme: {
|
||||||
onError: onError || jest.fn(),
|
text: {
|
||||||
theme: {
|
bold: 'editor-text-bold',
|
||||||
text: {
|
italic: 'editor-text-italic',
|
||||||
bold: 'editor-text-bold',
|
underline: 'editor-text-underline',
|
||||||
italic: 'editor-text-italic',
|
},
|
||||||
underline: 'editor-text-underline',
|
},
|
||||||
},
|
});
|
||||||
},
|
editor.setRootElement(rootElement);
|
||||||
}),
|
|
||||||
[onError, nodes],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const rootElement = rootElementRef.current;
|
|
||||||
|
|
||||||
editor.setRootElement(rootElement);
|
|
||||||
}, [rootElementRef, editor]);
|
|
||||||
|
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
let editor: LexicalEditor;
|
let editor: LexicalEditor;
|
||||||
|
|
||||||
function init(onError?: (error: Error) => void) {
|
function init(onError?: (error: Error) => void) {
|
||||||
const ref = createRef<HTMLDivElement>();
|
const edContainer = document.createElement('div');
|
||||||
|
edContainer.setAttribute('contenteditable', 'true');
|
||||||
|
|
||||||
function TestBase() {
|
setContainerChild(edContainer);
|
||||||
editor = useLexicalEditor(ref, onError);
|
editor = useLexicalEditor(edContainer, onError);
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
reactRoot.render(<TestBase />);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(fn: () => void) {
|
async function update(fn: () => void) {
|
||||||
|
@ -870,21 +856,12 @@ describe('LexicalEditor tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be able to update an editor state without a root element', () => {
|
it('Should be able to update an editor state without a root element', () => {
|
||||||
const ref = createRef<HTMLDivElement>();
|
const element = document.createElement('div');
|
||||||
|
element.setAttribute('contenteditable', 'true');
|
||||||
|
setContainerChild(element);
|
||||||
|
|
||||||
function TestBase({element}: {element: HTMLElement | null}) {
|
editor = createTestEditor();
|
||||||
editor = useMemo(() => createTestEditor(), []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editor.setRootElement(element);
|
|
||||||
}, [element]);
|
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
reactRoot.render(<TestBase element={null} />);
|
|
||||||
});
|
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
const root = $getRoot();
|
const root = $getRoot();
|
||||||
const paragraph = $createParagraphNode();
|
const paragraph = $createParagraphNode();
|
||||||
|
@ -895,9 +872,7 @@ describe('LexicalEditor tests', () => {
|
||||||
|
|
||||||
expect(container.innerHTML).toBe('<div contenteditable="true"></div>');
|
expect(container.innerHTML).toBe('<div contenteditable="true"></div>');
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
editor.setRootElement(element);
|
||||||
reactRoot.render(<TestBase element={ref.current} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p dir="ltr"><span data-lexical-text="true">This works!</span></p></div>',
|
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p dir="ltr"><span data-lexical-text="true">This works!</span></p></div>',
|
||||||
|
@ -945,57 +920,48 @@ describe('LexicalEditor tests', () => {
|
||||||
const rootListener = jest.fn();
|
const rootListener = jest.fn();
|
||||||
const updateListener = jest.fn();
|
const updateListener = jest.fn();
|
||||||
|
|
||||||
function TestBase({changeElement}: {changeElement: boolean}) {
|
let editorInstance = createTestEditor();
|
||||||
editor = useMemo(() => createTestEditor(), []);
|
editorInstance.registerRootListener(rootListener);
|
||||||
|
editorInstance.registerUpdateListener(updateListener);
|
||||||
|
|
||||||
useEffect(() => {
|
let edContainer: HTMLElement = document.createElement('div');
|
||||||
editor.update(() => {
|
edContainer.setAttribute('contenteditable', 'true');
|
||||||
const root = $getRoot();
|
setContainerChild(edContainer);
|
||||||
const firstChild = root.getFirstChild() as ParagraphNode | null;
|
editorInstance.setRootElement(edContainer);
|
||||||
const text = changeElement ? 'Change successful' : 'Not changed';
|
|
||||||
|
|
||||||
if (firstChild === null) {
|
function runUpdate(changeElement: boolean) {
|
||||||
const paragraph = $createParagraphNode();
|
editorInstance.update(() => {
|
||||||
const textNode = $createTextNode(text);
|
const root = $getRoot();
|
||||||
paragraph.append(textNode);
|
const firstChild = root.getFirstChild() as ParagraphNode | null;
|
||||||
root.append(paragraph);
|
const text = changeElement ? 'Change successful' : 'Not changed';
|
||||||
} else {
|
|
||||||
const textNode = firstChild.getFirstChild() as TextNode;
|
|
||||||
textNode.setTextContent(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [changeElement]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (firstChild === null) {
|
||||||
return editor.registerRootListener(rootListener);
|
const paragraph = $createParagraphNode();
|
||||||
}, []);
|
const textNode = $createTextNode(text);
|
||||||
|
paragraph.append(textNode);
|
||||||
useEffect(() => {
|
root.append(paragraph);
|
||||||
return editor.registerUpdateListener(updateListener);
|
} else {
|
||||||
}, []);
|
const textNode = firstChild.getFirstChild() as TextNode;
|
||||||
|
textNode.setTextContent(text);
|
||||||
const ref = useCallback((node: HTMLElement | null) => {
|
}
|
||||||
editor.setRootElement(node);
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
return changeElement ? (
|
|
||||||
<span ref={ref} contentEditable={true} />
|
|
||||||
) : (
|
|
||||||
<div ref={ref} contentEditable={true} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await ReactTestUtils.act(() => {
|
setContainerChild(edContainer);
|
||||||
reactRoot.render(<TestBase changeElement={false} />);
|
editorInstance.setRootElement(edContainer);
|
||||||
});
|
runUpdate(false);
|
||||||
|
editorInstance.commitUpdates();
|
||||||
|
|
||||||
expect(container.innerHTML).toBe(
|
expect(container.innerHTML).toBe(
|
||||||
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p dir="ltr"><span data-lexical-text="true">Not changed</span></p></div>',
|
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p dir="ltr"><span data-lexical-text="true">Not changed</span></p></div>',
|
||||||
);
|
);
|
||||||
|
|
||||||
await ReactTestUtils.act(() => {
|
edContainer = document.createElement('span');
|
||||||
reactRoot.render(<TestBase changeElement={true} />);
|
edContainer.setAttribute('contenteditable', 'true');
|
||||||
});
|
runUpdate(true);
|
||||||
|
editorInstance.setRootElement(edContainer);
|
||||||
|
setContainerChild(edContainer);
|
||||||
|
editorInstance.commitUpdates();
|
||||||
|
|
||||||
expect(rootListener).toHaveBeenCalledTimes(3);
|
expect(rootListener).toHaveBeenCalledTimes(3);
|
||||||
expect(updateListener).toHaveBeenCalledTimes(3);
|
expect(updateListener).toHaveBeenCalledTimes(3);
|
||||||
|
@ -1026,178 +992,6 @@ describe('LexicalEditor tests', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('With node decorators', () => {
|
|
||||||
function useDecorators() {
|
|
||||||
const [decorators, setDecorators] = useState(() =>
|
|
||||||
editor.getDecorators<ReactNode>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Subscribe to changes
|
|
||||||
useEffect(() => {
|
|
||||||
return editor.registerDecoratorListener<ReactNode>((nextDecorators) => {
|
|
||||||
setDecorators(nextDecorators);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const decoratedPortals = useMemo(
|
|
||||||
() =>
|
|
||||||
Object.keys(decorators).map((nodeKey) => {
|
|
||||||
const reactDecorator = decorators[nodeKey];
|
|
||||||
const element = editor.getElementByKey(nodeKey)!;
|
|
||||||
|
|
||||||
return createPortal(reactDecorator, element);
|
|
||||||
}),
|
|
||||||
[decorators],
|
|
||||||
);
|
|
||||||
|
|
||||||
return decoratedPortals;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// Clean up so we are not calling setState outside of act
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
reactRoot.render(null);
|
|
||||||
await Promise.resolve().then();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should correctly render React component into Lexical node #1', async () => {
|
|
||||||
const listener = jest.fn();
|
|
||||||
|
|
||||||
function Test() {
|
|
||||||
editor = useMemo(() => createTestEditor(), []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editor.registerRootListener(listener);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const ref = useCallback((node: HTMLDivElement | null) => {
|
|
||||||
editor.setRootElement(node);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const decorators = useDecorators();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div ref={ref} contentEditable={true} />
|
|
||||||
{decorators}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
reactRoot.render(<Test />);
|
|
||||||
});
|
|
||||||
// Update the editor with the decorator
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
const paragraph = $createParagraphNode();
|
|
||||||
const test = $createTestDecoratorNode();
|
|
||||||
paragraph.append(test);
|
|
||||||
$getRoot().append(paragraph);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(listener).toHaveBeenCalledTimes(1);
|
|
||||||
expect(container.innerHTML).toBe(
|
|
||||||
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p>' +
|
|
||||||
'<span data-lexical-decorator="true"><span>Hello world</span></span><br></p></div>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should correctly render React component into Lexical node #2', async () => {
|
|
||||||
const listener = jest.fn();
|
|
||||||
|
|
||||||
function Test({divKey}: {divKey: number}): JSX.Element {
|
|
||||||
function TestPlugin() {
|
|
||||||
[editor] = useLexicalComposerContext();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return editor.registerRootListener(listener);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TestComposer>
|
|
||||||
<RichTextPlugin
|
|
||||||
contentEditable={
|
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line jsx-a11y/aria-role
|
|
||||||
<ContentEditable key={divKey} role={null} spellCheck={null} />
|
|
||||||
}
|
|
||||||
placeholder={null}
|
|
||||||
ErrorBoundary={LexicalErrorBoundary}
|
|
||||||
/>
|
|
||||||
<TestPlugin />
|
|
||||||
</TestComposer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
reactRoot.render(<Test divKey={0} />);
|
|
||||||
// Wait for update to complete
|
|
||||||
await Promise.resolve().then();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(listener).toHaveBeenCalledTimes(1);
|
|
||||||
expect(container.innerHTML).toBe(
|
|
||||||
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p><br></p></div>',
|
|
||||||
);
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
reactRoot.render(<Test divKey={1} />);
|
|
||||||
// Wait for update to complete
|
|
||||||
await Promise.resolve().then();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(listener).toHaveBeenCalledTimes(5);
|
|
||||||
expect(container.innerHTML).toBe(
|
|
||||||
'<div contenteditable="true" style="user-select: text; white-space: pre-wrap; word-break: break-word;" data-lexical-editor="true"><p><br></p></div>',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait for update to complete
|
|
||||||
await Promise.resolve().then();
|
|
||||||
|
|
||||||
editor.getEditorState().read(() => {
|
|
||||||
const root = $getRoot();
|
|
||||||
const paragraph = root.getFirstChild()!;
|
|
||||||
expect(root).toEqual({
|
|
||||||
__cachedText: '',
|
|
||||||
__dir: null,
|
|
||||||
__first: paragraph.getKey(),
|
|
||||||
__format: 0,
|
|
||||||
__indent: 0,
|
|
||||||
__key: 'root',
|
|
||||||
__last: paragraph.getKey(),
|
|
||||||
__next: null,
|
|
||||||
__parent: null,
|
|
||||||
__prev: null,
|
|
||||||
__size: 1,
|
|
||||||
__style: '',
|
|
||||||
__type: 'root',
|
|
||||||
});
|
|
||||||
expect(paragraph).toEqual({
|
|
||||||
__dir: null,
|
|
||||||
__first: null,
|
|
||||||
__format: 0,
|
|
||||||
__indent: 0,
|
|
||||||
__key: paragraph.getKey(),
|
|
||||||
__last: null,
|
|
||||||
__next: null,
|
|
||||||
__parent: 'root',
|
|
||||||
__prev: null,
|
|
||||||
__size: 0,
|
|
||||||
__style: '',
|
|
||||||
__textFormat: 0,
|
|
||||||
__textStyle: '',
|
|
||||||
__type: 'paragraph',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('parseEditorState()', () => {
|
describe('parseEditorState()', () => {
|
||||||
let originalText: TextNode;
|
let originalText: TextNode;
|
||||||
let parsedParagraph: ParagraphNode;
|
let parsedParagraph: ParagraphNode;
|
||||||
|
@ -1872,10 +1666,12 @@ describe('LexicalEditor tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('mutation listener set for original node should work with the replaced node', async () => {
|
it('mutation listener set for original node should work with the replaced node', async () => {
|
||||||
const ref = createRef<HTMLDivElement>();
|
|
||||||
|
|
||||||
function TestBase() {
|
function TestBase() {
|
||||||
editor = useLexicalEditor(ref, undefined, [
|
const edContainer = document.createElement('div');
|
||||||
|
edContainer.contentEditable = 'true';
|
||||||
|
|
||||||
|
editor = useLexicalEditor(edContainer, undefined, [
|
||||||
TestTextNode,
|
TestTextNode,
|
||||||
{
|
{
|
||||||
replace: TextNode,
|
replace: TextNode,
|
||||||
|
@ -1884,12 +1680,10 @@ describe('LexicalEditor tests', () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
return edContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
setContainerChild(TestBase());
|
||||||
reactRoot.render(<TestBase />);
|
|
||||||
});
|
|
||||||
|
|
||||||
const textNodeMutations = jest.fn();
|
const textNodeMutations = jest.fn();
|
||||||
const textNodeMutationsB = jest.fn();
|
const textNodeMutationsB = jest.fn();
|
||||||
|
@ -1969,10 +1763,12 @@ describe('LexicalEditor tests', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('mutation listener should work with the replaced node', async () => {
|
it('mutation listener should work with the replaced node', async () => {
|
||||||
const ref = createRef<HTMLDivElement>();
|
|
||||||
|
|
||||||
function TestBase() {
|
function TestBase() {
|
||||||
editor = useLexicalEditor(ref, undefined, [
|
const edContainer = document.createElement('div');
|
||||||
|
edContainer.contentEditable = 'true';
|
||||||
|
|
||||||
|
editor = useLexicalEditor(edContainer, undefined, [
|
||||||
TestTextNode,
|
TestTextNode,
|
||||||
{
|
{
|
||||||
replace: TextNode,
|
replace: TextNode,
|
||||||
|
@ -1981,12 +1777,10 @@ describe('LexicalEditor tests', () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
return edContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
setContainerChild(TestBase());
|
||||||
reactRoot.render(<TestBase />);
|
|
||||||
});
|
|
||||||
|
|
||||||
const textNodeMutations = jest.fn();
|
const textNodeMutations = jest.fn();
|
||||||
const textNodeMutationsB = jest.fn();
|
const textNodeMutationsB = jest.fn();
|
||||||
|
@ -2581,6 +2375,8 @@ describe('LexicalEditor tests', () => {
|
||||||
expect(false).toBe('unreachable');
|
expect(false).toBe('unreachable');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newEditor.commitUpdates();
|
||||||
|
|
||||||
expect(onError).toHaveBeenCalledWith(
|
expect(onError).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
message: expect.stringMatching(/TestTextNode.*re-use key.*TextNode/),
|
message: expect.stringMatching(/TestTextNode.*re-use key.*TextNode/),
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
expectHtmlToBeEqual,
|
expectHtmlToBeEqual,
|
||||||
html,
|
html,
|
||||||
TestComposer,
|
TestComposer,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
import {createRoot, Root} from 'react-dom/client';
|
import {createRoot, Root} from 'react-dom/client';
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import {
|
||||||
SerializedTextNode,
|
SerializedTextNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateEditorArgs,
|
CreateEditorArgs,
|
||||||
|
@ -89,38 +88,13 @@ export function initializeUnitTest(
|
||||||
testEnv.container = document.createElement('div');
|
testEnv.container = document.createElement('div');
|
||||||
document.body.appendChild(testEnv.container);
|
document.body.appendChild(testEnv.container);
|
||||||
|
|
||||||
const useLexicalEditor = (
|
const editorEl = document.createElement('div');
|
||||||
rootElementRef: React.RefObject<HTMLDivElement>,
|
editorEl.contentEditable = 'true';
|
||||||
) => {
|
testEnv.container.append(editorEl);
|
||||||
const lexicalEditor = React.useMemo(() => {
|
|
||||||
const lexical = createTestEditor(editorConfig);
|
|
||||||
return lexical;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
const lexicalEditor = createTestEditor(editorConfig);
|
||||||
const rootElement = rootElementRef.current;
|
lexicalEditor.setRootElement(editorEl);
|
||||||
lexicalEditor.setRootElement(rootElement);
|
testEnv.editor = lexicalEditor;
|
||||||
}, [rootElementRef, lexicalEditor]);
|
|
||||||
return lexicalEditor;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Editor = () => {
|
|
||||||
testEnv.editor = useLexicalEditor(ref);
|
|
||||||
const context = createLexicalComposerContext(
|
|
||||||
null,
|
|
||||||
editorConfig?.theme ?? {},
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<LexicalComposerContext.Provider value={[testEnv.editor, context]}>
|
|
||||||
<div ref={ref} contentEditable={true} />
|
|
||||||
{plugins}
|
|
||||||
</LexicalComposerContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
createRoot(testEnv.container).render(<Editor />);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -381,7 +355,7 @@ export function $createTestExcludeFromCopyElementNode(): TestExcludeFromCopyElem
|
||||||
|
|
||||||
export type SerializedTestDecoratorNode = SerializedLexicalNode;
|
export type SerializedTestDecoratorNode = SerializedLexicalNode;
|
||||||
|
|
||||||
export class TestDecoratorNode extends DecoratorNode<JSX.Element> {
|
export class TestDecoratorNode extends DecoratorNode<HTMLElement> {
|
||||||
static getType(): string {
|
static getType(): string {
|
||||||
return 'test_decorator';
|
return 'test_decorator';
|
||||||
}
|
}
|
||||||
|
@ -433,32 +407,26 @@ export class TestDecoratorNode extends DecoratorNode<JSX.Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
decorate() {
|
decorate() {
|
||||||
return <Decorator text={'Hello world'} />;
|
const decorator = document.createElement('span');
|
||||||
|
decorator.textContent = 'Hello world';
|
||||||
|
return decorator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Decorator({text}: {text: string}): JSX.Element {
|
|
||||||
return <span>{text}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $createTestDecoratorNode(): TestDecoratorNode {
|
export function $createTestDecoratorNode(): TestDecoratorNode {
|
||||||
return new TestDecoratorNode();
|
return new TestDecoratorNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_NODES: NonNullable<InitialConfigType['nodes']> = [
|
const DEFAULT_NODES: NonNullable<ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement>> = [
|
||||||
HeadingNode,
|
HeadingNode,
|
||||||
ListNode,
|
ListNode,
|
||||||
ListItemNode,
|
ListItemNode,
|
||||||
QuoteNode,
|
QuoteNode,
|
||||||
CodeNode,
|
|
||||||
TableNode,
|
TableNode,
|
||||||
TableCellNode,
|
TableCellNode,
|
||||||
TableRowNode,
|
TableRowNode,
|
||||||
HashtagNode,
|
|
||||||
CodeHighlightNode,
|
|
||||||
AutoLinkNode,
|
AutoLinkNode,
|
||||||
LinkNode,
|
LinkNode,
|
||||||
OverflowNode,
|
|
||||||
TestElementNode,
|
TestElementNode,
|
||||||
TestSegmentedNode,
|
TestSegmentedNode,
|
||||||
TestExcludeFromCopyElementNode,
|
TestExcludeFromCopyElementNode,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
SerializedParagraphNode,
|
SerializedParagraphNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical/src';
|
} from 'lexical/src';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
SerializedParagraphNode,
|
SerializedParagraphNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical/src';
|
} from 'lexical/src';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
expectHtmlToBeEqual,
|
expectHtmlToBeEqual,
|
||||||
html,
|
html,
|
||||||
initializeUnitTest,
|
initializeUnitTest,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$createListItemNode,
|
$createListItemNode,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import {ParagraphNode, TextNode} from 'lexical';
|
import {ParagraphNode, TextNode} from 'lexical';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$createListItemNode,
|
$createListItemNode,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import {$createParagraphNode, $getRoot} from 'lexical';
|
import {$createParagraphNode, $getRoot} from 'lexical';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$createListItemNode, $createListNode} from '../..';
|
import {$createListItemNode, $createListNode} from '../..';
|
||||||
import {$getListDepth, $getTopListNode, $isLastItemInList} from '../../utils';
|
import {$getListDepth, $getTopListNode, $isLastItemInList} from '../../utils';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
ParagraphNode,
|
ParagraphNode,
|
||||||
RangeSelection,
|
RangeSelection,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {$createQuoteNode, QuoteNode} from '@lexical/rich-text';
|
import {$createQuoteNode, QuoteNode} from '@lexical/rich-text';
|
||||||
import {$createRangeSelection, $getRoot, ParagraphNode} from 'lexical';
|
import {$createRangeSelection, $getRoot, ParagraphNode} from 'lexical';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -50,7 +50,7 @@ import {
|
||||||
initializeClipboard,
|
initializeClipboard,
|
||||||
invariant,
|
invariant,
|
||||||
TestComposer,
|
TestComposer,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
import {createRoot, Root} from 'react-dom/client';
|
import {createRoot, Root} from 'react-dom/client';
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import {
|
||||||
createTestHeadlessEditor,
|
createTestHeadlessEditor,
|
||||||
invariant,
|
invariant,
|
||||||
TestDecoratorNode,
|
TestDecoratorNode,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$setAnchorPoint, $setFocusPoint} from '../utils';
|
import {$setAnchorPoint, $setFocusPoint} from '../utils';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {$createTableCellNode, TableCellHeaderStates} from '@lexical/table';
|
import {$createTableCellNode, TableCellHeaderStates} from '@lexical/table';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -28,7 +28,7 @@ import {
|
||||||
DataTransferMock,
|
DataTransferMock,
|
||||||
initializeUnitTest,
|
initializeUnitTest,
|
||||||
invariant,
|
invariant,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$getElementForTableNode, TableNode} from '../../LexicalTableNode';
|
import {$getElementForTableNode, TableNode} from '../../LexicalTableNode';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {$createTableRowNode} from '@lexical/table';
|
import {$createTableRowNode} from '@lexical/table';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
const editorConfig = Object.freeze({
|
const editorConfig = Object.freeze({
|
||||||
namespace: '',
|
namespace: '',
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
RootNode,
|
RootNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import {createTestEditor} from 'lexical/src/__tests__/utils';
|
import {createTestEditor} from 'lexical/__tests__/utils';
|
||||||
import {createRef, useEffect, useMemo} from 'react';
|
import {createRef, useEffect, useMemo} from 'react';
|
||||||
import {createRoot, Root} from 'react-dom/client';
|
import {createRoot, Root} from 'react-dom/client';
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {
|
||||||
} from '@lexical/selection/src/__tests__/utils';
|
} from '@lexical/selection/src/__tests__/utils';
|
||||||
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
|
import {TableCellNode, TableNode, TableRowNode} from '@lexical/table';
|
||||||
import {LexicalEditor} from 'lexical';
|
import {LexicalEditor} from 'lexical';
|
||||||
import {initializeClipboard, TestComposer} from 'lexical/src/__tests__/utils';
|
import {initializeClipboard, TestComposer} from 'lexical/__tests__/utils';
|
||||||
import {createRoot} from 'react-dom/client';
|
import {createRoot} from 'react-dom/client';
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
$createTestElementNode,
|
$createTestElementNode,
|
||||||
initializeUnitTest,
|
initializeUnitTest,
|
||||||
invariant,
|
invariant,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$dfs} from '../..';
|
import {$dfs} from '../..';
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
$rootTextContent,
|
$rootTextContent,
|
||||||
} from '@lexical/text';
|
} from '@lexical/text';
|
||||||
import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
|
import {$createParagraphNode, $createTextNode, $getRoot} from 'lexical';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
describe('LexicalRootHelpers tests', () => {
|
describe('LexicalRootHelpers tests', () => {
|
||||||
initializeUnitTest((testEnv) => {
|
initializeUnitTest((testEnv) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {objectKlassEquals} from '@lexical/utils';
|
import {objectKlassEquals} from '@lexical/utils';
|
||||||
import {initializeUnitTest} from 'lexical/src/__tests__/utils';
|
import {initializeUnitTest} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
class MyEvent extends Event {}
|
class MyEvent extends Event {}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type {ElementNode, LexicalEditor} from 'lexical';
|
||||||
|
|
||||||
import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
|
import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
|
||||||
import {$getRoot, $isElementNode} from 'lexical';
|
import {$getRoot, $isElementNode} from 'lexical';
|
||||||
import {createTestEditor} from 'lexical/src/__tests__/utils';
|
import {createTestEditor} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$splitNode} from '../../index';
|
import {$splitNode} from '../../index';
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
import {
|
import {
|
||||||
$createTestDecoratorNode,
|
$createTestDecoratorNode,
|
||||||
createTestEditor,
|
createTestEditor,
|
||||||
} from 'lexical/src/__tests__/utils';
|
} from 'lexical/__tests__/utils';
|
||||||
|
|
||||||
import {$insertNodeToNearestRoot} from '../..';
|
import {$insertNodeToNearestRoot} from '../..';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user