diff --git a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
index d54a64ce8..6a8e45724 100644
--- a/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
+++ b/resources/js/wysiwyg/lexical/core/__tests__/utils/index.ts
@@ -37,6 +37,7 @@ 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 {ImageNode} from "@lexical/rich-text/LexicalImageNode";
type TestEnv = {
readonly container: HTMLDivElement;
@@ -484,6 +485,9 @@ export function createTestContext(): EditorUiContext {
const editor = createTestEditor({
namespace: 'testing',
theme: {},
+ nodes: [
+ ImageNode,
+ ]
});
editor.setRootElement(editorDOM);
diff --git a/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListNode.test.ts b/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListNode.test.ts
index 8c7729dbf..b85383e7d 100644
--- a/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListNode.test.ts
+++ b/resources/js/wysiwyg/lexical/list/__tests__/unit/LexicalListNode.test.ts
@@ -6,7 +6,7 @@
*
*/
import {ParagraphNode, TextNode} from 'lexical';
-import {initializeUnitTest} from 'lexical/__tests__/utils';
+import {createTestContext} from 'lexical/__tests__/utils';
import {
$createListItemNode,
@@ -16,6 +16,7 @@ import {
ListItemNode,
ListNode,
} from '../..';
+import {$htmlToBlockNodes} from "../../../../utils/nodes";
const editorConfig = Object.freeze({
namespace: '',
@@ -46,123 +47,122 @@ const editorConfig = Object.freeze({
});
describe('LexicalListNode tests', () => {
- initializeUnitTest((testEnv) => {
- test('ListNode.constructor', async () => {
- const {editor} = testEnv;
+ test('ListNode.constructor', async () => {
+ const {editor} = createTestContext();
- await editor.update(() => {
- const listNode = $createListNode('bullet', 1);
- expect(listNode.getType()).toBe('list');
- expect(listNode.getTag()).toBe('ul');
- expect(listNode.getTextContent()).toBe('');
- });
-
- // @ts-expect-error
- expect(() => $createListNode()).toThrow();
+ await editor.update(() => {
+ const listNode = $createListNode('bullet', 1);
+ expect(listNode.getType()).toBe('list');
+ expect(listNode.getTag()).toBe('ul');
+ expect(listNode.getTextContent()).toBe('');
});
- test('ListNode.getTag()', async () => {
- const {editor} = testEnv;
+ // @ts-expect-error
+ expect(() => $createListNode()).toThrow();
+ });
- await editor.update(() => {
- const ulListNode = $createListNode('bullet', 1);
- expect(ulListNode.getTag()).toBe('ul');
- const olListNode = $createListNode('number', 1);
- expect(olListNode.getTag()).toBe('ol');
- const checkListNode = $createListNode('check', 1);
- expect(checkListNode.getTag()).toBe('ul');
- });
+ test('ListNode.getTag()', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const ulListNode = $createListNode('bullet', 1);
+ expect(ulListNode.getTag()).toBe('ul');
+ const olListNode = $createListNode('number', 1);
+ expect(olListNode.getTag()).toBe('ol');
+ const checkListNode = $createListNode('check', 1);
+ expect(checkListNode.getTag()).toBe('ul');
});
+ });
- test('ListNode.createDOM()', async () => {
- const {editor} = testEnv;
+ test('ListNode.createDOM()', async () => {
+ const {editor} = createTestContext();
- await editor.update(() => {
- const listNode = $createListNode('bullet', 1);
- expect(listNode.createDOM(editorConfig).outerHTML).toBe(
+ await editor.update(() => {
+ const listNode = $createListNode('bullet', 1);
+ expect(listNode.createDOM(editorConfig).outerHTML).toBe(
'
',
- );
- expect(
+ );
+ expect(
listNode.createDOM({
namespace: '',
theme: {
list: {},
},
}).outerHTML,
- ).toBe('');
- expect(
+ ).toBe('');
+ expect(
listNode.createDOM({
namespace: '',
theme: {},
}).outerHTML,
- ).toBe('');
- });
+ ).toBe('');
});
+ });
- test('ListNode.createDOM() correctly applies classes to a nested ListNode', async () => {
- const {editor} = testEnv;
+ test('ListNode.createDOM() correctly applies classes to a nested ListNode', async () => {
+ const {editor} = createTestContext();
- await editor.update(() => {
- const listNode1 = $createListNode('bullet');
- const listNode2 = $createListNode('bullet');
- const listNode3 = $createListNode('bullet');
- const listNode4 = $createListNode('bullet');
- const listNode5 = $createListNode('bullet');
- const listNode6 = $createListNode('bullet');
- const listNode7 = $createListNode('bullet');
+ await editor.update(() => {
+ const listNode1 = $createListNode('bullet');
+ const listNode2 = $createListNode('bullet');
+ const listNode3 = $createListNode('bullet');
+ const listNode4 = $createListNode('bullet');
+ const listNode5 = $createListNode('bullet');
+ const listNode6 = $createListNode('bullet');
+ const listNode7 = $createListNode('bullet');
- const listItem1 = $createListItemNode();
- const listItem2 = $createListItemNode();
- const listItem3 = $createListItemNode();
- const listItem4 = $createListItemNode();
+ const listItem1 = $createListItemNode();
+ const listItem2 = $createListItemNode();
+ const listItem3 = $createListItemNode();
+ const listItem4 = $createListItemNode();
- listNode1.append(listItem1);
- listItem1.append(listNode2);
- listNode2.append(listItem2);
- listItem2.append(listNode3);
- listNode3.append(listItem3);
- listItem3.append(listNode4);
- listNode4.append(listItem4);
- listNode4.append(listNode5);
- listNode5.append(listNode6);
- listNode6.append(listNode7);
+ listNode1.append(listItem1);
+ listItem1.append(listNode2);
+ listNode2.append(listItem2);
+ listItem2.append(listNode3);
+ listNode3.append(listItem3);
+ listItem3.append(listNode4);
+ listNode4.append(listItem4);
+ listNode4.append(listNode5);
+ listNode5.append(listNode6);
+ listNode6.append(listNode7);
- expect(listNode1.createDOM(editorConfig).outerHTML).toBe(
+ expect(listNode1.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(
+ );
+ expect(
listNode1.createDOM({
namespace: '',
theme: {
list: {},
},
}).outerHTML,
- ).toBe('');
- expect(
+ ).toBe('');
+ expect(
listNode1.createDOM({
namespace: '',
theme: {},
}).outerHTML,
- ).toBe('');
- expect(listNode2.createDOM(editorConfig).outerHTML).toBe(
+ ).toBe('');
+ expect(listNode2.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(listNode3.createDOM(editorConfig).outerHTML).toBe(
+ );
+ expect(listNode3.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(listNode4.createDOM(editorConfig).outerHTML).toBe(
+ );
+ expect(listNode4.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(listNode5.createDOM(editorConfig).outerHTML).toBe(
+ );
+ expect(listNode5.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(listNode6.createDOM(editorConfig).outerHTML).toBe(
+ );
+ expect(listNode6.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(listNode7.createDOM(editorConfig).outerHTML).toBe(
+ );
+ expect(listNode7.createDOM(editorConfig).outerHTML).toBe(
'',
- );
- expect(
+ );
+ expect(
listNode5.createDOM({
namespace: '',
theme: {
@@ -176,123 +176,135 @@ describe('LexicalListNode tests', () => {
},
},
}).outerHTML,
- ).toBe('');
- });
+ ).toBe('');
});
+ });
- test('ListNode.updateDOM()', async () => {
- const {editor} = testEnv;
+ test('ListNode.updateDOM()', async () => {
+ const {editor} = createTestContext();
- await editor.update(() => {
- const listNode = $createListNode('bullet', 1);
- const domElement = listNode.createDOM(editorConfig);
+ await editor.update(() => {
+ const listNode = $createListNode('bullet', 1);
+ const domElement = listNode.createDOM(editorConfig);
- expect(domElement.outerHTML).toBe(
+ expect(domElement.outerHTML).toBe(
'',
- );
+ );
- const newListNode = $createListNode('number', 1);
- const result = newListNode.updateDOM(
+ const newListNode = $createListNode('number', 1);
+ const result = newListNode.updateDOM(
listNode,
domElement,
editorConfig,
- );
+ );
- expect(result).toBe(true);
- expect(domElement.outerHTML).toBe(
+ expect(result).toBe(true);
+ expect(domElement.outerHTML).toBe(
'',
- );
- });
- });
-
- test('ListNode.append() should properly transform a ListItemNode', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const listNode = new ListNode('bullet', 1);
- const listItemNode = new ListItemNode();
- const textNode = new TextNode('Hello');
-
- listItemNode.append(textNode);
- const nodesToAppend = [listItemNode];
-
- expect(listNode.append(...nodesToAppend)).toBe(listNode);
- expect(listNode.getFirstChild()).toBe(listItemNode);
- expect(listNode.getFirstChild()?.getTextContent()).toBe('Hello');
- });
- });
-
- test('ListNode.append() should properly transform a ListNode', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const listNode = new ListNode('bullet', 1);
- const nestedListNode = new ListNode('bullet', 1);
- const listItemNode = new ListItemNode();
- const textNode = new TextNode('Hello');
-
- listItemNode.append(textNode);
- nestedListNode.append(listItemNode);
-
- const nodesToAppend = [nestedListNode];
-
- expect(listNode.append(...nodesToAppend)).toBe(listNode);
- expect($isListItemNode(listNode.getFirstChild())).toBe(true);
- expect(listNode.getFirstChild()!.getFirstChild()).toBe(
- nestedListNode,
- );
- });
- });
-
- test('ListNode.append() should properly transform a ParagraphNode', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const listNode = new ListNode('bullet', 1);
- const paragraph = new ParagraphNode();
- const textNode = new TextNode('Hello');
- paragraph.append(textNode);
- const nodesToAppend = [paragraph];
-
- expect(listNode.append(...nodesToAppend)).toBe(listNode);
- expect($isListItemNode(listNode.getFirstChild())).toBe(true);
- expect(listNode.getFirstChild()?.getTextContent()).toBe('Hello');
- });
- });
-
- test('$createListNode()', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const listNode = $createListNode('bullet', 1);
- const createdListNode = $createListNode('bullet');
-
- expect(listNode.__type).toEqual(createdListNode.__type);
- expect(listNode.__parent).toEqual(createdListNode.__parent);
- expect(listNode.__tag).toEqual(createdListNode.__tag);
- expect(listNode.__key).not.toEqual(createdListNode.__key);
- });
- });
-
- test('$isListNode()', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const listNode = $createListNode('bullet', 1);
-
- expect($isListNode(listNode)).toBe(true);
- });
- });
-
- test('$createListNode() with tag name (backward compatibility)', async () => {
- const {editor} = testEnv;
-
- await editor.update(() => {
- const numberList = $createListNode('number', 1);
- const bulletList = $createListNode('bullet', 1);
- expect(numberList.__listType).toBe('number');
- expect(bulletList.__listType).toBe('bullet');
- });
+ );
});
});
+
+ test('ListNode.append() should properly transform a ListItemNode', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const listNode = new ListNode('bullet', 1);
+ const listItemNode = new ListItemNode();
+ const textNode = new TextNode('Hello');
+
+ listItemNode.append(textNode);
+ const nodesToAppend = [listItemNode];
+
+ expect(listNode.append(...nodesToAppend)).toBe(listNode);
+ expect(listNode.getFirstChild()).toBe(listItemNode);
+ expect(listNode.getFirstChild()?.getTextContent()).toBe('Hello');
+ });
+ });
+
+ test('ListNode.append() should properly transform a ListNode', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const listNode = new ListNode('bullet', 1);
+ const nestedListNode = new ListNode('bullet', 1);
+ const listItemNode = new ListItemNode();
+ const textNode = new TextNode('Hello');
+
+ listItemNode.append(textNode);
+ nestedListNode.append(listItemNode);
+
+ const nodesToAppend = [nestedListNode];
+
+ expect(listNode.append(...nodesToAppend)).toBe(listNode);
+ expect($isListItemNode(listNode.getFirstChild())).toBe(true);
+ expect(listNode.getFirstChild()!.getFirstChild()).toBe(
+ nestedListNode,
+ );
+ });
+ });
+
+ test('ListNode.append() should properly transform a ParagraphNode', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const listNode = new ListNode('bullet', 1);
+ const paragraph = new ParagraphNode();
+ const textNode = new TextNode('Hello');
+ paragraph.append(textNode);
+ const nodesToAppend = [paragraph];
+
+ expect(listNode.append(...nodesToAppend)).toBe(listNode);
+ expect($isListItemNode(listNode.getFirstChild())).toBe(true);
+ expect(listNode.getFirstChild()?.getTextContent()).toBe('Hello');
+ });
+ });
+
+ test('$createListNode()', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const listNode = $createListNode('bullet', 1);
+ const createdListNode = $createListNode('bullet');
+
+ expect(listNode.__type).toEqual(createdListNode.__type);
+ expect(listNode.__parent).toEqual(createdListNode.__parent);
+ expect(listNode.__tag).toEqual(createdListNode.__tag);
+ expect(listNode.__key).not.toEqual(createdListNode.__key);
+ });
+ });
+
+ test('$isListNode()', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const listNode = $createListNode('bullet', 1);
+
+ expect($isListNode(listNode)).toBe(true);
+ });
+ });
+
+ test('$createListNode() with tag name (backward compatibility)', async () => {
+ const {editor} = createTestContext();
+
+ await editor.update(() => {
+ const numberList = $createListNode('number', 1);
+ const bulletList = $createListNode('bullet', 1);
+ expect(numberList.__listType).toBe('number');
+ expect(bulletList.__listType).toBe('bullet');
+ });
+ });
+
+ test('importDOM handles old editor expected task list format', async () => {
+ const {editor} = createTestContext();
+
+ let list!: ListNode;
+ editor.update(() => {
+ const nodes = $htmlToBlockNodes(editor, ``);
+ list = nodes[0] as ListNode;
+ });
+
+ expect(list).toBeInstanceOf(ListNode);
+ expect(list.getListType()).toBe('check');
+ });
});
diff --git a/resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts b/resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts
index 736c3573c..cd4235f2f 100644
--- a/resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts
+++ b/resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts
@@ -1,7 +1,7 @@
import {
createTestContext, destroyFromContext,
dispatchKeydownEventForNode,
- dispatchKeydownEventForSelectedNode,
+ dispatchKeydownEventForSelectedNode, expectNodeShapeToMatch,
} from "lexical/__tests__/utils";
import {
$createParagraphNode, $createTextNode,
@@ -13,6 +13,7 @@ import {registerKeyboardHandling} from "../keyboard-handling";
import {registerRichText} from "@lexical/rich-text";
import {EditorUiContext} from "../../ui/framework/core";
import {$createListItemNode, $createListNode, ListItemNode, ListNode} from "@lexical/list";
+import {$createImageNode, ImageNode} from "@lexical/rich-text/LexicalImageNode";
describe('Keyboard-handling service tests', () => {
@@ -127,4 +128,34 @@ describe('Keyboard-handling service tests', () => {
expect(selectedNode?.getKey()).toBe(innerList.getChildren()[0].getKey());
});
});
+
+ test('Images: up on selected image creates new paragraph if none above', () => {
+ let image!: ImageNode;
+ editor.updateAndCommit(() => {
+ const root = $getRoot();
+ const imageWrap = $createParagraphNode();
+ image = $createImageNode('https://example.com/cat.png');
+ imageWrap.append(image);
+ root.append(imageWrap);
+ image.select();
+ });
+
+ expectNodeShapeToMatch(editor, [{
+ type: 'paragraph',
+ children: [
+ {type: 'image'}
+ ],
+ }]);
+
+ dispatchKeydownEventForNode(image, editor, 'ArrowUp');
+
+ expectNodeShapeToMatch(editor, [{
+ type: 'paragraph',
+ }, {
+ type: 'paragraph',
+ children: [
+ {type: 'image'}
+ ],
+ }]);
+ });
});
\ No newline at end of file
diff --git a/resources/js/wysiwyg/services/keyboard-handling.ts b/resources/js/wysiwyg/services/keyboard-handling.ts
index 0ef0b81bf..a7f1ec7f0 100644
--- a/resources/js/wysiwyg/services/keyboard-handling.ts
+++ b/resources/js/wysiwyg/services/keyboard-handling.ts
@@ -79,22 +79,19 @@ function focusAdjacentOrInsertForSingleSelectNode(editor: LexicalEditor, event:
const nearestBlock = $getNearestNodeBlockParent(node) || node;
let target = after ? nearestBlock.getNextSibling() : nearestBlock.getPreviousSibling();
- requestAnimationFrame(() => {
- editor.update(() => {
- if (!target) {
- target = $createParagraphNode();
- if (after) {
- nearestBlock.insertAfter(target)
- } else {
- nearestBlock.insertBefore(target);
- }
+ editor.update(() => {
+ if (!target) {
+ target = $createParagraphNode();
+ if (after) {
+ nearestBlock.insertAfter(target)
+ } else {
+ nearestBlock.insertBefore(target);
}
+ }
- target.selectStart();
- });
+ target.selectStart();
});
-
return true;
}