diff --git a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts index b13bba697..d54a64ce8 100644 --- a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts +++ b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts @@ -37,8 +37,6 @@ import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode"; import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode"; import {EditorUiContext} from "../../../../ui/framework/core"; import {EditorUIManager} from "../../../../ui/framework/manager"; -import {turtle} from "@codemirror/legacy-modes/mode/turtle"; - type TestEnv = { readonly container: HTMLDivElement; @@ -47,6 +45,9 @@ type TestEnv = { readonly innerHTML: string; }; +/** + * @deprecated - Consider using `createTestContext` instead within the test case. + */ export function initializeUnitTest( runTests: (testEnv: TestEnv) => void, editorConfig: CreateEditorArgs = {namespace: 'test', theme: {}}, @@ -795,6 +796,30 @@ export function expectNodeShapeToMatch(editor: LexicalEditor, expected: nodeShap expect(shape.children).toMatchObject(expected); } +/** + * Expect a given prop within the JSON editor state structure to be the given value. + * Uses dot notation for the provided `propPath`. Example: + * 0.5.cat => First child, Sixth child, cat property + */ +export function expectEditorStateJSONPropToEqual(editor: LexicalEditor, propPath: string, expected: any) { + let currentItem: any = editor.getEditorState().toJSON().root; + let currentPath = []; + const pathParts = propPath.split('.'); + + for (const part of pathParts) { + currentPath.push(part); + const childAccess = Number.isInteger(Number(part)) && Array.isArray(currentItem.children); + const target = childAccess ? currentItem.children : currentItem; + + if (typeof target[part] === 'undefined') { + throw new Error(`Could not resolve editor state at path ${currentPath.join('.')}`) + } + currentItem = target[part]; + } + + expect(currentItem).toBe(expected); +} + function formatHtml(s: string): string { return s.replace(/>\s+<').replace(/\s*\n\s*/g, ' ').trim(); } diff --git a/resources/js/wysiwyg/services/__tests__/auto-links.test.ts b/resources/js/wysiwyg/services/__tests__/auto-links.test.ts index 30dc92565..add61c495 100644 --- a/resources/js/wysiwyg/services/__tests__/auto-links.test.ts +++ b/resources/js/wysiwyg/services/__tests__/auto-links.test.ts @@ -1,91 +1,76 @@ -import {initializeUnitTest} from "lexical/__tests__/utils"; -import {SerializedLinkNode} from "@lexical/link"; +import { + createTestContext, + dispatchKeydownEventForNode, expectEditorStateJSONPropToEqual, + expectNodeShapeToMatch +} from "lexical/__tests__/utils"; import { $getRoot, ParagraphNode, - SerializedParagraphNode, - SerializedTextNode, TextNode } from "lexical"; import {registerAutoLinks} from "../auto-links"; describe('Auto-link service tests', () => { - initializeUnitTest((testEnv) => { + test('space after link in text', async () => { + const {editor} = createTestContext(); + registerAutoLinks(editor); + let pNode!: ParagraphNode; - test('space after link in text', async () => { - const {editor} = testEnv; + editor.updateAndCommit(() => { + pNode = new ParagraphNode(); + const text = new TextNode('Some https://example.com?test=true text'); + pNode.append(text); + $getRoot().append(pNode); - registerAutoLinks(editor); - let pNode!: ParagraphNode; - - editor.update(() => { - pNode = new ParagraphNode(); - const text = new TextNode('Some https://example.com?test=true text'); - pNode.append(text); - $getRoot().append(pNode); - - text.select(34, 34); - }); - - editor.commitUpdates(); - - const pDomEl = editor.getElementByKey(pNode.getKey()); - const event = new KeyboardEvent('keydown', { - bubbles: true, - cancelable: true, - key: ' ', - keyCode: 62, - }); - pDomEl?.dispatchEvent(event); - - editor.commitUpdates(); - - const paragraph = editor!.getEditorState().toJSON().root - .children[0] as SerializedParagraphNode; - expect(paragraph.children[1].type).toBe('link'); - - const link = paragraph.children[1] as SerializedLinkNode; - expect(link.url).toBe('https://example.com?test=true'); - const linkText = link.children[0] as SerializedTextNode; - expect(linkText.text).toBe('https://example.com?test=true'); + text.select(34, 34); }); - test('enter after link in text', async () => { - const {editor} = testEnv; + dispatchKeydownEventForNode(pNode, editor, ' '); - registerAutoLinks(editor); - let pNode!: ParagraphNode; + expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true'); + expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true'); + }); - editor.update(() => { - pNode = new ParagraphNode(); - const text = new TextNode('Some https://example.com?test=true text'); - pNode.append(text); - $getRoot().append(pNode); + test('space after link at end of line', async () => { + const {editor} = createTestContext(); + registerAutoLinks(editor); + let pNode!: ParagraphNode; - text.select(34, 34); - }); + editor.updateAndCommit(() => { + pNode = new ParagraphNode(); + const text = new TextNode('Some https://example.com?test=true'); + pNode.append(text); + $getRoot().append(pNode); - editor.commitUpdates(); - - const pDomEl = editor.getElementByKey(pNode.getKey()); - const event = new KeyboardEvent('keydown', { - bubbles: true, - cancelable: true, - key: 'Enter', - keyCode: 66, - }); - pDomEl?.dispatchEvent(event); - - editor.commitUpdates(); - - const paragraph = editor!.getEditorState().toJSON().root - .children[0] as SerializedParagraphNode; - expect(paragraph.children[1].type).toBe('link'); - - const link = paragraph.children[1] as SerializedLinkNode; - expect(link.url).toBe('https://example.com?test=true'); - const linkText = link.children[0] as SerializedTextNode; - expect(linkText.text).toBe('https://example.com?test=true'); + text.selectEnd(); }); + + dispatchKeydownEventForNode(pNode, editor, ' '); + + expectNodeShapeToMatch(editor, [{type: 'paragraph', children: [ + {text: 'Some '}, + {type: 'link', children: [{text: 'https://example.com?test=true'}]} + ]}]); + expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true'); + }); + + test('enter after link in text', async () => { + const {editor} = createTestContext(); + registerAutoLinks(editor); + let pNode!: ParagraphNode; + + editor.updateAndCommit(() => { + pNode = new ParagraphNode(); + const text = new TextNode('Some https://example.com?test=true text'); + pNode.append(text); + $getRoot().append(pNode); + + text.select(34, 34); + }); + + dispatchKeydownEventForNode(pNode, editor, 'Enter'); + + expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true'); + expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true'); }); }); \ No newline at end of file diff --git a/resources/js/wysiwyg/services/auto-links.ts b/resources/js/wysiwyg/services/auto-links.ts index 1c3b1c730..62cd45994 100644 --- a/resources/js/wysiwyg/services/auto-links.ts +++ b/resources/js/wysiwyg/services/auto-links.ts @@ -43,7 +43,7 @@ function handlePotentialLinkEvent(node: TextNode, selection: BaseSelection, edit linkNode.append(new TextNode(textSegment)); const splits = node.splitText(startIndex, cursorPoint); - const targetIndex = splits.length === 3 ? 1 : 0; + const targetIndex = startIndex > 0 ? 1 : 0; const targetText = splits[targetIndex]; if (targetText) { targetText.replace(linkNode); diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md index 817a235a7..a49cccd26 100644 --- a/resources/js/wysiwyg/todo.md +++ b/resources/js/wysiwyg/todo.md @@ -2,11 +2,7 @@ ## In progress -Reorg - - Merge custom nodes into original nodes - - Reduce down to use CommonBlockNode where possible - - Remove existing formatType/ElementFormatType references (replaced with alignment). - - Remove existing indent references (replaced with inset). +// ## Main Todo