]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts
Lexical: Aligned new empty item behaviour for nested lists
[bookstack] / resources / js / wysiwyg / services / __tests__ / keyboard-handling.test.ts
1 import {
2     createTestContext, destroyFromContext,
3     dispatchKeydownEventForNode,
4     dispatchKeydownEventForSelectedNode,
5 } from "lexical/__tests__/utils";
6 import {
7     $createParagraphNode, $createTextNode,
8     $getRoot, $getSelection, LexicalEditor, LexicalNode,
9     ParagraphNode, TextNode,
10 } from "lexical";
11 import {$createDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
12 import {registerKeyboardHandling} from "../keyboard-handling";
13 import {registerRichText} from "@lexical/rich-text";
14 import {EditorUiContext} from "../../ui/framework/core";
15 import {$createListItemNode, $createListNode, ListItemNode, ListNode} from "@lexical/list";
16
17 describe('Keyboard-handling service tests', () => {
18
19     let context!: EditorUiContext;
20     let editor!: LexicalEditor;
21
22     beforeEach(() => {
23         context = createTestContext();
24         editor = context.editor;
25         registerRichText(editor);
26         registerKeyboardHandling(context);
27     });
28
29     afterEach(() => {
30         destroyFromContext(context);
31     });
32
33     test('Details: down key on last lines creates new sibling node', () => {
34         let lastRootChild!: LexicalNode|null;
35         let detailsPara!: ParagraphNode;
36
37         editor.updateAndCommit(() => {
38             const root = $getRoot()
39             const details = $createDetailsNode();
40             detailsPara = $createParagraphNode();
41             details.append(detailsPara);
42             $getRoot().append(details);
43             detailsPara.select();
44
45             lastRootChild = root.getLastChild();
46         });
47
48         expect(lastRootChild).toBeInstanceOf(DetailsNode);
49
50         dispatchKeydownEventForNode(detailsPara, editor, 'ArrowDown');
51
52         editor.getEditorState().read(() => {
53             lastRootChild = $getRoot().getLastChild();
54         });
55
56         expect(lastRootChild).toBeInstanceOf(ParagraphNode);
57     });
58
59     test('Details: enter on last empty block creates new sibling node', () => {
60         registerRichText(editor);
61
62         let lastRootChild!: LexicalNode|null;
63         let detailsPara!: ParagraphNode;
64
65         editor.updateAndCommit(() => {
66             const root = $getRoot()
67             const details = $createDetailsNode();
68             const text = $createTextNode('Hello!');
69             detailsPara = $createParagraphNode();
70             detailsPara.append(text);
71             details.append(detailsPara);
72             $getRoot().append(details);
73             text.selectEnd();
74
75             lastRootChild = root.getLastChild();
76         });
77
78         expect(lastRootChild).toBeInstanceOf(DetailsNode);
79
80         dispatchKeydownEventForNode(detailsPara, editor, 'Enter');
81         dispatchKeydownEventForSelectedNode(editor, 'Enter');
82
83         let detailsChildren!: LexicalNode[];
84         let lastDetailsText!: string;
85
86         editor.getEditorState().read(() => {
87             detailsChildren = (lastRootChild as DetailsNode).getChildren();
88             lastRootChild = $getRoot().getLastChild();
89             lastDetailsText = detailsChildren[0].getTextContent();
90         });
91
92         expect(lastRootChild).toBeInstanceOf(ParagraphNode);
93         expect(detailsChildren).toHaveLength(1);
94         expect(lastDetailsText).toBe('Hello!');
95     });
96
97     test('Lists: tab on empty list item insets item', () => {
98
99         let list!: ListNode;
100         let listItemB!: ListItemNode;
101
102         editor.updateAndCommit(() => {
103             const root = $getRoot();
104             list = $createListNode('bullet');
105             const listItemA = $createListItemNode();
106             listItemA.append($createTextNode('Hello!'));
107             listItemB = $createListItemNode();
108             list.append(listItemA, listItemB);
109             root.append(list);
110             listItemB.selectStart();
111         });
112
113         dispatchKeydownEventForNode(listItemB, editor, 'Tab');
114
115         editor.getEditorState().read(() => {
116             const list = $getRoot().getChildren()[0] as ListNode;
117             const listChild = list.getChildren()[0] as ListItemNode;
118             const children = listChild.getChildren();
119             expect(children).toHaveLength(2);
120             expect(children[0]).toBeInstanceOf(TextNode);
121             expect(children[0].getTextContent()).toBe('Hello!');
122             expect(children[1]).toBeInstanceOf(ListNode);
123
124             const innerList = children[1] as ListNode;
125             const selectedNode = $getSelection()?.getNodes()[0];
126             expect(selectedNode).toBeInstanceOf(ListItemNode);
127             expect(selectedNode?.getKey()).toBe(innerList.getChildren()[0].getKey());
128         });
129     });
130 });