Skip to content

Commit 0d1a237

Browse files
committed
Lexical: Fixed auto-link issue
Added extra test helper to check the editor state directly via string notation access rather than juggling types/objects to access deep properties.
1 parent 786a434 commit 0d1a237

File tree

4 files changed

+82
-76
lines changed

4 files changed

+82
-76
lines changed

resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
3737
import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
3838
import {EditorUiContext} from "../../../../ui/framework/core";
3939
import {EditorUIManager} from "../../../../ui/framework/manager";
40-
import {turtle} from "@codemirror/legacy-modes/mode/turtle";
41-
4240

4341
type TestEnv = {
4442
readonly container: HTMLDivElement;
@@ -47,6 +45,9 @@ type TestEnv = {
4745
readonly innerHTML: string;
4846
};
4947

48+
/**
49+
* @deprecated - Consider using `createTestContext` instead within the test case.
50+
*/
5051
export function initializeUnitTest(
5152
runTests: (testEnv: TestEnv) => void,
5253
editorConfig: CreateEditorArgs = {namespace: 'test', theme: {}},
@@ -795,6 +796,30 @@ export function expectNodeShapeToMatch(editor: LexicalEditor, expected: nodeShap
795796
expect(shape.children).toMatchObject(expected);
796797
}
797798

799+
/**
800+
* Expect a given prop within the JSON editor state structure to be the given value.
801+
* Uses dot notation for the provided `propPath`. Example:
802+
* 0.5.cat => First child, Sixth child, cat property
803+
*/
804+
export function expectEditorStateJSONPropToEqual(editor: LexicalEditor, propPath: string, expected: any) {
805+
let currentItem: any = editor.getEditorState().toJSON().root;
806+
let currentPath = [];
807+
const pathParts = propPath.split('.');
808+
809+
for (const part of pathParts) {
810+
currentPath.push(part);
811+
const childAccess = Number.isInteger(Number(part)) && Array.isArray(currentItem.children);
812+
const target = childAccess ? currentItem.children : currentItem;
813+
814+
if (typeof target[part] === 'undefined') {
815+
throw new Error(`Could not resolve editor state at path ${currentPath.join('.')}`)
816+
}
817+
currentItem = target[part];
818+
}
819+
820+
expect(currentItem).toBe(expected);
821+
}
822+
798823
function formatHtml(s: string): string {
799824
return s.replace(/>\s+</g, '><').replace(/\s*\n\s*/g, ' ').trim();
800825
}
Lines changed: 53 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,76 @@
1-
import {initializeUnitTest} from "lexical/__tests__/utils";
2-
import {SerializedLinkNode} from "@lexical/link";
1+
import {
2+
createTestContext,
3+
dispatchKeydownEventForNode, expectEditorStateJSONPropToEqual,
4+
expectNodeShapeToMatch
5+
} from "lexical/__tests__/utils";
36
import {
47
$getRoot,
58
ParagraphNode,
6-
SerializedParagraphNode,
7-
SerializedTextNode,
89
TextNode
910
} from "lexical";
1011
import {registerAutoLinks} from "../auto-links";
1112

1213
describe('Auto-link service tests', () => {
13-
initializeUnitTest((testEnv) => {
14-
15-
test('space after link in text', async () => {
16-
const {editor} = testEnv;
17-
18-
registerAutoLinks(editor);
19-
let pNode!: ParagraphNode;
20-
21-
editor.update(() => {
22-
pNode = new ParagraphNode();
23-
const text = new TextNode('Some https://example.com?test=true text');
24-
pNode.append(text);
25-
$getRoot().append(pNode);
26-
27-
text.select(34, 34);
28-
});
14+
test('space after link in text', async () => {
15+
const {editor} = createTestContext();
16+
registerAutoLinks(editor);
17+
let pNode!: ParagraphNode;
18+
19+
editor.updateAndCommit(() => {
20+
pNode = new ParagraphNode();
21+
const text = new TextNode('Some https://example.com?test=true text');
22+
pNode.append(text);
23+
$getRoot().append(pNode);
24+
25+
text.select(34, 34);
26+
});
2927

30-
editor.commitUpdates();
28+
dispatchKeydownEventForNode(pNode, editor, ' ');
3129

32-
const pDomEl = editor.getElementByKey(pNode.getKey());
33-
const event = new KeyboardEvent('keydown', {
34-
bubbles: true,
35-
cancelable: true,
36-
key: ' ',
37-
keyCode: 62,
38-
});
39-
pDomEl?.dispatchEvent(event);
30+
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
31+
expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true');
32+
});
4033

41-
editor.commitUpdates();
34+
test('space after link at end of line', async () => {
35+
const {editor} = createTestContext();
36+
registerAutoLinks(editor);
37+
let pNode!: ParagraphNode;
4238

43-
const paragraph = editor!.getEditorState().toJSON().root
44-
.children[0] as SerializedParagraphNode;
45-
expect(paragraph.children[1].type).toBe('link');
39+
editor.updateAndCommit(() => {
40+
pNode = new ParagraphNode();
41+
const text = new TextNode('Some https://example.com?test=true');
42+
pNode.append(text);
43+
$getRoot().append(pNode);
4644

47-
const link = paragraph.children[1] as SerializedLinkNode;
48-
expect(link.url).toBe('https://example.com?test=true');
49-
const linkText = link.children[0] as SerializedTextNode;
50-
expect(linkText.text).toBe('https://example.com?test=true');
45+
text.selectEnd();
5146
});
5247

53-
test('enter after link in text', async () => {
54-
const {editor} = testEnv;
55-
56-
registerAutoLinks(editor);
57-
let pNode!: ParagraphNode;
58-
59-
editor.update(() => {
60-
pNode = new ParagraphNode();
61-
const text = new TextNode('Some https://example.com?test=true text');
62-
pNode.append(text);
63-
$getRoot().append(pNode);
48+
dispatchKeydownEventForNode(pNode, editor, ' ');
6449

65-
text.select(34, 34);
66-
});
50+
expectNodeShapeToMatch(editor, [{type: 'paragraph', children: [
51+
{text: 'Some '},
52+
{type: 'link', children: [{text: 'https://example.com?test=true'}]}
53+
]}]);
54+
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
55+
});
6756

68-
editor.commitUpdates();
57+
test('enter after link in text', async () => {
58+
const {editor} = createTestContext();
59+
registerAutoLinks(editor);
60+
let pNode!: ParagraphNode;
6961

70-
const pDomEl = editor.getElementByKey(pNode.getKey());
71-
const event = new KeyboardEvent('keydown', {
72-
bubbles: true,
73-
cancelable: true,
74-
key: 'Enter',
75-
keyCode: 66,
76-
});
77-
pDomEl?.dispatchEvent(event);
62+
editor.updateAndCommit(() => {
63+
pNode = new ParagraphNode();
64+
const text = new TextNode('Some https://example.com?test=true text');
65+
pNode.append(text);
66+
$getRoot().append(pNode);
7867

79-
editor.commitUpdates();
68+
text.select(34, 34);
69+
});
8070

81-
const paragraph = editor!.getEditorState().toJSON().root
82-
.children[0] as SerializedParagraphNode;
83-
expect(paragraph.children[1].type).toBe('link');
71+
dispatchKeydownEventForNode(pNode, editor, 'Enter');
8472

85-
const link = paragraph.children[1] as SerializedLinkNode;
86-
expect(link.url).toBe('https://example.com?test=true');
87-
const linkText = link.children[0] as SerializedTextNode;
88-
expect(linkText.text).toBe('https://example.com?test=true');
89-
});
73+
expectEditorStateJSONPropToEqual(editor, '0.1.url', 'https://example.com?test=true');
74+
expectEditorStateJSONPropToEqual(editor, '0.1.0.text', 'https://example.com?test=true');
9075
});
9176
});

resources/js/wysiwyg/services/auto-links.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function handlePotentialLinkEvent(node: TextNode, selection: BaseSelection, edit
4343
linkNode.append(new TextNode(textSegment));
4444

4545
const splits = node.splitText(startIndex, cursorPoint);
46-
const targetIndex = splits.length === 3 ? 1 : 0;
46+
const targetIndex = startIndex > 0 ? 1 : 0;
4747
const targetText = splits[targetIndex];
4848
if (targetText) {
4949
targetText.replace(linkNode);

resources/js/wysiwyg/todo.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
## In progress
44

5-
Reorg
6-
- Merge custom nodes into original nodes
7-
- Reduce down to use CommonBlockNode where possible
8-
- Remove existing formatType/ElementFormatType references (replaced with alignment).
9-
- Remove existing indent references (replaced with inset).
5+
//
106

117
## Main Todo
128

0 commit comments

Comments
 (0)