@handlewithcare/react-prosemirror 2.4.10 → 2.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/cjs/components/Editor.js +28 -0
- package/dist/cjs/components/NodeViews.js +73 -0
- package/dist/cjs/{contexts/__tests__/DeferredLayoutEffects.test.js → components/__tests__/LayoutGroup.test.js} +2 -2
- package/dist/cjs/components/__tests__/ProseMirror.test.js +136 -263
- package/dist/cjs/contexts/NodeViewsContext.js +10 -0
- package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +27 -28
- package/dist/cjs/hooks/__tests__/useNodeViews.test.js +159 -0
- package/dist/cjs/hooks/useEditor.js +2 -4
- package/dist/cjs/hooks/useEditorView.js +100 -0
- package/dist/cjs/hooks/useNodePos.js +69 -0
- package/dist/cjs/hooks/useNodeViews.js +100 -0
- package/dist/cjs/nodeViews/createReactNodeViewConstructor.js +244 -0
- package/dist/cjs/nodeViews/phrasingContentTags.js +57 -0
- package/dist/cjs/plugins/__tests__/react.test.js +139 -0
- package/dist/cjs/plugins/react.js +71 -0
- package/dist/esm/components/Editor.js +15 -0
- package/dist/esm/components/NodeViews.js +26 -0
- package/dist/esm/{contexts/__tests__/DeferredLayoutEffects.test.js → components/__tests__/LayoutGroup.test.js} +2 -2
- package/dist/esm/components/__tests__/ProseMirror.test.js +135 -267
- package/dist/esm/contexts/NodeViewsContext.js +9 -0
- package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +27 -28
- package/dist/esm/hooks/__tests__/useNodeViews.test.js +116 -0
- package/dist/esm/hooks/useEditor.js +2 -4
- package/dist/esm/hooks/useEditorView.js +99 -0
- package/dist/esm/hooks/useNodePos.js +16 -0
- package/dist/esm/hooks/useNodeViews.js +53 -0
- package/dist/esm/nodeViews/createReactNodeViewConstructor.js +214 -0
- package/dist/esm/nodeViews/phrasingContentTags.js +49 -0
- package/dist/esm/plugins/__tests__/react.test.js +135 -0
- package/dist/esm/plugins/react.js +64 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/components/Editor.d.ts +7 -0
- package/dist/types/components/NodeViews.d.ts +6 -0
- package/dist/types/components/__tests__/LayoutGroup.test.d.ts +1 -0
- package/dist/types/contexts/NodeViewsContext.d.ts +19 -0
- package/dist/types/decorations/ReactWidgetType.d.ts +1 -0
- package/dist/types/hooks/__tests__/useNodeViews.test.d.ts +1 -0
- package/dist/types/hooks/useEditorView.d.ts +23 -0
- package/dist/types/hooks/useNodePos.d.ts +9 -0
- package/dist/types/hooks/useNodeViews.d.ts +5 -0
- package/dist/types/nodeViews/createReactNodeViewConstructor.d.ts +48 -0
- package/dist/types/nodeViews/phrasingContentTags.d.ts +1 -0
- package/dist/types/plugins/__tests__/react.test.d.ts +1 -0
- package/dist/types/plugins/react.d.ts +21 -0
- package/dist/types/props.d.ts +27 -27
- package/package.json +1 -1
- package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +0 -398
- package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +0 -270
- package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +0 -1010
- package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +0 -337
- package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +0 -315
- package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +0 -444
- package/dist/cjs/plugins/__tests__/reactKeys.test.js +0 -81
- package/dist/esm/components/__tests__/ProseMirror.composition.test.js +0 -395
- package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +0 -266
- package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +0 -967
- package/dist/esm/components/__tests__/ProseMirror.draw.test.js +0 -294
- package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +0 -272
- package/dist/esm/components/__tests__/ProseMirror.selection.test.js +0 -440
- package/dist/esm/plugins/__tests__/reactKeys.test.js +0 -77
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
for(var i = 1; i < arguments.length; i++){
|
|
4
|
-
var source = arguments[i];
|
|
5
|
-
for(var key in source){
|
|
6
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
7
|
-
target[key] = source[key];
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return target;
|
|
12
|
-
};
|
|
13
|
-
return _extends.apply(this, arguments);
|
|
14
|
-
}
|
|
15
|
-
import { render, screen } from "@testing-library/react";
|
|
1
|
+
import { act, render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
16
3
|
import { Schema } from "prosemirror-model";
|
|
17
|
-
import { EditorState
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import { useStopEvent } from "../../hooks/useStopEvent.js";
|
|
22
|
-
import { reactKeys } from "../../plugins/reactKeys.js";
|
|
23
|
-
import { tempEditor } from "../../testing/editorViewTestHelpers.js";
|
|
4
|
+
import { EditorState } from "prosemirror-state";
|
|
5
|
+
import React, { useEffect, useState } from "react";
|
|
6
|
+
import { react } from "../../plugins/react.js";
|
|
7
|
+
import { setupProseMirrorView, teardownProseMirrorView } from "../../testing/setupProseMirrorView.js";
|
|
24
8
|
import { ProseMirror } from "../ProseMirror.js";
|
|
25
|
-
|
|
9
|
+
// Mock `ReactDOM.flushSync` to call `act` to flush updates from DOM mutations.
|
|
10
|
+
jest.mock("react-dom", ()=>({
|
|
11
|
+
...jest.requireActual("react-dom"),
|
|
12
|
+
flushSync: (fn)=>act(fn)
|
|
13
|
+
}));
|
|
26
14
|
describe("ProseMirror", ()=>{
|
|
15
|
+
beforeAll(()=>{
|
|
16
|
+
setupProseMirrorView();
|
|
17
|
+
});
|
|
27
18
|
it("renders a contenteditable", async ()=>{
|
|
28
19
|
const schema = new Schema({
|
|
29
20
|
nodes: {
|
|
@@ -33,35 +24,57 @@ describe("ProseMirror", ()=>{
|
|
|
33
24
|
}
|
|
34
25
|
}
|
|
35
26
|
});
|
|
36
|
-
const
|
|
27
|
+
const defaultState = EditorState.create({
|
|
28
|
+
schema
|
|
29
|
+
});
|
|
30
|
+
function TestEditor() {
|
|
31
|
+
const [mount, setMount] = useState(null);
|
|
32
|
+
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
33
|
+
mount: mount,
|
|
34
|
+
defaultState: defaultState
|
|
35
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
36
|
+
"data-testid": "editor",
|
|
37
|
+
ref: setMount
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
const user = userEvent.setup();
|
|
41
|
+
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
42
|
+
const editor = screen.getByTestId("editor");
|
|
43
|
+
await user.type(editor, "Hello, world!");
|
|
44
|
+
expect(editor.textContent).toBe("Hello, world!");
|
|
45
|
+
});
|
|
46
|
+
it("supports observing transaction dispatch", async ()=>{
|
|
47
|
+
const schema = new Schema({
|
|
48
|
+
nodes: {
|
|
49
|
+
text: {},
|
|
50
|
+
doc: {
|
|
51
|
+
content: "text*"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const defaultState = EditorState.create({
|
|
37
56
|
schema
|
|
38
57
|
});
|
|
58
|
+
const dispatchTransaction = jest.fn();
|
|
39
59
|
function TestEditor() {
|
|
60
|
+
const [mount, setMount] = useState(null);
|
|
40
61
|
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
62
|
+
mount: mount,
|
|
63
|
+
defaultState: defaultState,
|
|
64
|
+
dispatchTransaction: dispatchTransaction
|
|
65
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
66
|
+
"data-testid": "editor",
|
|
67
|
+
ref: setMount
|
|
44
68
|
}));
|
|
45
69
|
}
|
|
70
|
+
const user = userEvent.setup();
|
|
46
71
|
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
47
72
|
const editor = screen.getByTestId("editor");
|
|
48
|
-
|
|
49
|
-
await browser.keys("H");
|
|
50
|
-
await browser.keys("e");
|
|
51
|
-
await browser.keys("l");
|
|
52
|
-
await browser.keys("l");
|
|
53
|
-
await browser.keys("o");
|
|
54
|
-
await browser.keys(",");
|
|
55
|
-
await browser.keys(" ");
|
|
56
|
-
await browser.keys("w");
|
|
57
|
-
await browser.keys("o");
|
|
58
|
-
await browser.keys("r");
|
|
59
|
-
await browser.keys("l");
|
|
60
|
-
await browser.keys("d");
|
|
61
|
-
await browser.keys("!");
|
|
73
|
+
await user.type(editor, "Hello, world!");
|
|
62
74
|
expect(editor.textContent).toBe("Hello, world!");
|
|
75
|
+
expect(dispatchTransaction).toHaveBeenCalledTimes(13);
|
|
63
76
|
});
|
|
64
|
-
it("supports
|
|
77
|
+
it("supports controlling the editor state", async ()=>{
|
|
65
78
|
const schema = new Schema({
|
|
66
79
|
nodes: {
|
|
67
80
|
text: {},
|
|
@@ -70,270 +83,125 @@ describe("ProseMirror", ()=>{
|
|
|
70
83
|
}
|
|
71
84
|
}
|
|
72
85
|
});
|
|
73
|
-
let
|
|
86
|
+
let observedState = EditorState.create({
|
|
74
87
|
schema
|
|
75
88
|
});
|
|
76
89
|
function TestEditor() {
|
|
77
|
-
const [
|
|
90
|
+
const [state, setState] = useState(observedState);
|
|
91
|
+
const [mount, setMount] = useState(null);
|
|
78
92
|
useEffect(()=>{
|
|
79
|
-
|
|
93
|
+
observedState = state;
|
|
80
94
|
}, [
|
|
81
|
-
|
|
95
|
+
state
|
|
82
96
|
]);
|
|
83
97
|
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
mount: mount,
|
|
99
|
+
state: state,
|
|
100
|
+
dispatchTransaction: (tr)=>{
|
|
101
|
+
setState((s)=>s.apply(tr));
|
|
102
|
+
}
|
|
103
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
104
|
+
"data-testid": "editor",
|
|
105
|
+
ref: setMount
|
|
88
106
|
}));
|
|
89
107
|
}
|
|
108
|
+
const user = userEvent.setup();
|
|
90
109
|
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
91
110
|
const editor = screen.getByTestId("editor");
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
await user.type(editor, "Hello, world!");
|
|
112
|
+
expect(observedState.doc.textContent).toBe("Hello, world!");
|
|
113
|
+
});
|
|
114
|
+
it("updates props atomically", async ()=>{
|
|
115
|
+
const schema = new Schema({
|
|
116
|
+
nodes: {
|
|
117
|
+
text: {},
|
|
118
|
+
doc: {
|
|
119
|
+
content: "text*"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
const defaultState = EditorState.create({
|
|
124
|
+
schema
|
|
125
|
+
});
|
|
126
|
+
let allStatesMatched = true;
|
|
127
|
+
function TestEditor() {
|
|
128
|
+
const [state, setState] = useState(defaultState);
|
|
129
|
+
const [mount, setMount] = useState(null);
|
|
130
|
+
// Check that function props get invoked with the latest React state.
|
|
131
|
+
const editable = (viewState)=>{
|
|
132
|
+
allStatesMatched &&= viewState === state;
|
|
133
|
+
return true;
|
|
134
|
+
};
|
|
135
|
+
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
136
|
+
mount: mount,
|
|
137
|
+
editable: editable,
|
|
138
|
+
state: state,
|
|
139
|
+
dispatchTransaction: (tr)=>{
|
|
140
|
+
setState((s)=>s.apply(tr));
|
|
141
|
+
}
|
|
142
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
143
|
+
"data-testid": "editor",
|
|
144
|
+
ref: setMount
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
const user = userEvent.setup();
|
|
148
|
+
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
149
|
+
const editor = screen.getByTestId("editor");
|
|
150
|
+
await user.type(editor, "Hello, world!");
|
|
151
|
+
expect(allStatesMatched).toBe(true);
|
|
107
152
|
});
|
|
108
153
|
it("supports React NodeViews", async ()=>{
|
|
109
154
|
const schema = new Schema({
|
|
110
155
|
nodes: {
|
|
111
156
|
text: {},
|
|
112
157
|
paragraph: {
|
|
113
|
-
content: "text*"
|
|
114
|
-
toDOM () {
|
|
115
|
-
return [
|
|
116
|
-
"p",
|
|
117
|
-
0
|
|
118
|
-
];
|
|
119
|
-
}
|
|
158
|
+
content: "text*"
|
|
120
159
|
},
|
|
121
160
|
doc: {
|
|
122
161
|
content: "paragraph+"
|
|
123
162
|
}
|
|
124
163
|
}
|
|
125
164
|
});
|
|
126
|
-
const
|
|
127
|
-
schema
|
|
165
|
+
const defaultState = EditorState.create({
|
|
166
|
+
schema,
|
|
167
|
+
plugins: [
|
|
168
|
+
react()
|
|
169
|
+
]
|
|
128
170
|
});
|
|
129
|
-
|
|
171
|
+
function Paragraph(param) {
|
|
130
172
|
let { children } = param;
|
|
131
173
|
return /*#__PURE__*/ React.createElement("p", {
|
|
132
|
-
ref: ref,
|
|
133
174
|
"data-testid": "paragraph"
|
|
134
175
|
}, children);
|
|
135
|
-
}
|
|
136
|
-
const
|
|
137
|
-
paragraph:
|
|
176
|
+
}
|
|
177
|
+
const nodeViews = {
|
|
178
|
+
paragraph: ()=>({
|
|
179
|
+
component: Paragraph,
|
|
180
|
+
dom: document.createElement("div"),
|
|
181
|
+
contentDOM: document.createElement("span")
|
|
182
|
+
})
|
|
138
183
|
};
|
|
139
184
|
function TestEditor() {
|
|
185
|
+
const [mount, setMount] = useState(null);
|
|
140
186
|
return /*#__PURE__*/ React.createElement(ProseMirror, {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
187
|
+
mount: mount,
|
|
188
|
+
defaultState: defaultState,
|
|
189
|
+
nodeViews: nodeViews
|
|
190
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
191
|
+
"data-testid": "editor",
|
|
192
|
+
ref: setMount
|
|
145
193
|
}));
|
|
146
194
|
}
|
|
195
|
+
const user = userEvent.setup();
|
|
147
196
|
render(/*#__PURE__*/ React.createElement(TestEditor, null));
|
|
148
197
|
const editor = screen.getByTestId("editor");
|
|
149
|
-
|
|
150
|
-
await browser.keys("H");
|
|
151
|
-
await browser.keys("e");
|
|
152
|
-
await browser.keys("l");
|
|
153
|
-
await browser.keys("l");
|
|
154
|
-
await browser.keys("o");
|
|
155
|
-
await browser.keys(",");
|
|
156
|
-
await browser.keys(" ");
|
|
157
|
-
await browser.keys("w");
|
|
158
|
-
await browser.keys("o");
|
|
159
|
-
await browser.keys("r");
|
|
160
|
-
await browser.keys("l");
|
|
161
|
-
await browser.keys("d");
|
|
162
|
-
await browser.keys("!");
|
|
198
|
+
await user.type(editor, "Hello, world!");
|
|
163
199
|
expect(editor.textContent).toBe("Hello, world!");
|
|
164
200
|
// Ensure that ProseMirror really rendered our Paragraph
|
|
165
201
|
// component, not just any old <p> tag
|
|
166
202
|
expect(screen.getAllByTestId("paragraph").length).toBeGreaterThanOrEqual(1);
|
|
167
203
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
doc: doc(p())
|
|
171
|
-
});
|
|
172
|
-
expect(view.state).toBe(view.props.state);
|
|
173
|
-
});
|
|
174
|
-
it("calls handleScrollToSelection when appropriate", async ()=>{
|
|
175
|
-
let scrolled = 0;
|
|
176
|
-
const { view } = tempEditor({
|
|
177
|
-
doc: doc(p()),
|
|
178
|
-
handleScrollToSelection: ()=>{
|
|
179
|
-
scrolled++;
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
view.dispatch(view.state.tr.scrollIntoView());
|
|
184
|
-
expect(scrolled).toBe(1);
|
|
185
|
-
});
|
|
186
|
-
it("can be queried for the DOM position at a doc position", async ()=>{
|
|
187
|
-
const { view } = tempEditor({
|
|
188
|
-
doc: doc(ul(li(p(strong("foo")))))
|
|
189
|
-
});
|
|
190
|
-
const inText = view.domAtPos(4);
|
|
191
|
-
expect(inText.offset).toBe(1);
|
|
192
|
-
expect(inText.node.nodeValue).toBe("foo");
|
|
193
|
-
const beforeLI = view.domAtPos(1);
|
|
194
|
-
expect(beforeLI.offset).toBe(0);
|
|
195
|
-
expect(beforeLI.node.nodeName).toBe("UL");
|
|
196
|
-
const afterP = view.domAtPos(7);
|
|
197
|
-
expect(afterP.offset).toBe(1);
|
|
198
|
-
expect(afterP.node.nodeName).toBe("LI");
|
|
199
|
-
});
|
|
200
|
-
it("can bias DOM position queries to enter nodes", async ()=>{
|
|
201
|
-
const { view } = tempEditor({
|
|
202
|
-
doc: doc(p(em(strong("a"), "b"), "c"))
|
|
203
|
-
});
|
|
204
|
-
function get(pos, bias) {
|
|
205
|
-
const r = view.domAtPos(pos, bias);
|
|
206
|
-
return (r.node.nodeType == 1 ? r.node.nodeName : r.node.nodeValue) + "@" + r.offset;
|
|
207
|
-
}
|
|
208
|
-
expect(get(1, 0)).toBe("P@0");
|
|
209
|
-
expect(get(1, -1)).toBe("P@0");
|
|
210
|
-
expect(get(1, 1)).toBe("a@0");
|
|
211
|
-
expect(get(2, -1)).toBe("a@1");
|
|
212
|
-
expect(get(2, 0)).toBe("EM@1");
|
|
213
|
-
expect(get(2, 1)).toBe("b@0");
|
|
214
|
-
expect(get(3, -1)).toBe("b@1");
|
|
215
|
-
expect(get(3, 0)).toBe("P@1");
|
|
216
|
-
expect(get(3, 1)).toBe("c@0");
|
|
217
|
-
expect(get(4, -1)).toBe("c@1");
|
|
218
|
-
expect(get(4, 0)).toBe("P@2");
|
|
219
|
-
expect(get(4, 1)).toBe("P@2");
|
|
220
|
-
});
|
|
221
|
-
it("can be queried for a node's DOM representation", async ()=>{
|
|
222
|
-
const { view } = tempEditor({
|
|
223
|
-
doc: doc(p("foo"), hr())
|
|
224
|
-
});
|
|
225
|
-
expect(view.nodeDOM(0).nodeName).toBe("P");
|
|
226
|
-
expect(view.nodeDOM(5).nodeName).toBe("HR");
|
|
227
|
-
expect(view.nodeDOM(3)).toBeNull();
|
|
228
|
-
});
|
|
229
|
-
it("can map DOM positions to doc positions", async ()=>{
|
|
230
|
-
const { view } = tempEditor({
|
|
231
|
-
doc: doc(p("foo"), hr())
|
|
232
|
-
});
|
|
233
|
-
expect(view.posAtDOM(view.dom.firstChild.firstChild, 2)).toBe(3);
|
|
234
|
-
expect(view.posAtDOM(view.dom, 1)).toBe(5);
|
|
235
|
-
expect(view.posAtDOM(view.dom, 2)).toBe(6);
|
|
236
|
-
expect(view.posAtDOM(view.dom.lastChild, 0, -1)).toBe(5);
|
|
237
|
-
expect(view.posAtDOM(view.dom.lastChild, 0, 1)).toBe(6);
|
|
238
|
-
});
|
|
239
|
-
it("binds this to itself in dispatchTransaction prop", async ()=>{
|
|
240
|
-
let thisBinding;
|
|
241
|
-
const { view } = tempEditor({
|
|
242
|
-
doc: doc(p("foo"), hr()),
|
|
243
|
-
dispatchTransaction () {
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
245
|
-
thisBinding = this;
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
view.dispatch(view.state.tr.insertText("x"));
|
|
249
|
-
expect(view).toBe(thisBinding);
|
|
250
|
-
});
|
|
251
|
-
it("replaces the EditorView when ProseMirror would redraw", async ()=>{
|
|
252
|
-
const viewPlugin = ()=>new Plugin({
|
|
253
|
-
props: {
|
|
254
|
-
nodeViews: {
|
|
255
|
-
horizontal_rule () {
|
|
256
|
-
const dom = document.createElement("hr");
|
|
257
|
-
return {
|
|
258
|
-
dom
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
const startDoc = doc(p());
|
|
265
|
-
const firstState = EditorState.create({
|
|
266
|
-
doc: startDoc,
|
|
267
|
-
schema,
|
|
268
|
-
plugins: [
|
|
269
|
-
viewPlugin(),
|
|
270
|
-
reactKeys()
|
|
271
|
-
]
|
|
272
|
-
});
|
|
273
|
-
let firstView = null;
|
|
274
|
-
let secondView = null;
|
|
275
|
-
function Test() {
|
|
276
|
-
useEditorEffect((v)=>{
|
|
277
|
-
if (firstView === null) {
|
|
278
|
-
firstView = v;
|
|
279
|
-
} else {
|
|
280
|
-
secondView = v;
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
const Paragraph = /*#__PURE__*/ forwardRef(function Paragraph(param, ref) {
|
|
286
|
-
let { nodeProps , children , ...props } = param;
|
|
287
|
-
return /*#__PURE__*/ React.createElement("p", _extends({
|
|
288
|
-
ref: ref,
|
|
289
|
-
"data-testid": "node-view"
|
|
290
|
-
}, props), children);
|
|
291
|
-
});
|
|
292
|
-
const { rerender } = render(/*#__PURE__*/ React.createElement(ProseMirror, {
|
|
293
|
-
state: firstState,
|
|
294
|
-
nodeViews: {
|
|
295
|
-
paragraph: Paragraph
|
|
296
|
-
}
|
|
297
|
-
}, /*#__PURE__*/ React.createElement(Test, null), /*#__PURE__*/ React.createElement(ProseMirrorDoc, null)));
|
|
298
|
-
expect(()=>screen.getByTestId("node-view")).not.toThrow();
|
|
299
|
-
const secondState = EditorState.create({
|
|
300
|
-
doc: startDoc,
|
|
301
|
-
schema,
|
|
302
|
-
plugins: [
|
|
303
|
-
viewPlugin(),
|
|
304
|
-
reactKeys()
|
|
305
|
-
]
|
|
306
|
-
});
|
|
307
|
-
rerender(/*#__PURE__*/ React.createElement(ProseMirror, {
|
|
308
|
-
state: secondState,
|
|
309
|
-
nodeViews: {
|
|
310
|
-
paragraph: Paragraph
|
|
311
|
-
}
|
|
312
|
-
}, /*#__PURE__*/ React.createElement(Test, null), /*#__PURE__*/ React.createElement(ProseMirrorDoc, null)));
|
|
313
|
-
expect(()=>screen.getByTestId("node-view")).not.toThrow();
|
|
314
|
-
expect(firstView).not.toBeNull();
|
|
315
|
-
expect(secondView).not.toBeNull();
|
|
316
|
-
expect(firstView === secondView).toBeFalsy();
|
|
317
|
-
});
|
|
318
|
-
it("supports focusing interactive controls", async ()=>{
|
|
319
|
-
tempEditor({
|
|
320
|
-
doc: doc(hr()),
|
|
321
|
-
nodeViews: {
|
|
322
|
-
horizontal_rule: /*#__PURE__*/ forwardRef(function Button(param, ref) {
|
|
323
|
-
let { nodeProps , ...props } = param;
|
|
324
|
-
useStopEvent(()=>{
|
|
325
|
-
return true;
|
|
326
|
-
});
|
|
327
|
-
return /*#__PURE__*/ React.createElement("button", _extends({
|
|
328
|
-
id: "button",
|
|
329
|
-
ref: ref,
|
|
330
|
-
type: "button"
|
|
331
|
-
}, props), "Click me");
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
const button = screen.getByText("Click me");
|
|
336
|
-
await $("#button").click();
|
|
337
|
-
expect(document.activeElement === button).toBeTruthy();
|
|
204
|
+
afterAll(()=>{
|
|
205
|
+
teardownProseMirrorView();
|
|
338
206
|
});
|
|
339
207
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A context containing a map of node view keys to portals.
|
|
4
|
+
*
|
|
5
|
+
* Each node view registers a portal under its parent's
|
|
6
|
+
* key. Each can then retrieve the list of portals under their
|
|
7
|
+
* key, allowing portals to be rendered with the appropriate
|
|
8
|
+
* hierarchy.
|
|
9
|
+
*/ export const NodeViewsContext = createContext(null);
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { LayoutGroup } from "../../components/LayoutGroup.js";
|
|
4
4
|
import { EditorContext } from "../../contexts/EditorContext.js";
|
|
5
|
-
import { EditorStateContext } from "../../contexts/EditorStateContext.js";
|
|
6
5
|
import { useEditorEffect } from "../useEditorEffect.js";
|
|
7
6
|
function TestComponent(param) {
|
|
8
7
|
let { effect , dependencies =[] } = param;
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
useEditorEffect(effect, [
|
|
9
|
+
effect,
|
|
10
|
+
...dependencies
|
|
11
|
+
]);
|
|
11
12
|
return null;
|
|
12
13
|
}
|
|
13
14
|
describe("useEditorViewLayoutEffect", ()=>{
|
|
@@ -19,15 +20,14 @@ describe("useEditorViewLayoutEffect", ()=>{
|
|
|
19
20
|
const unregisterEventListener = ()=>{};
|
|
20
21
|
render(/*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
21
22
|
value: {
|
|
22
|
-
|
|
23
|
+
editorView,
|
|
24
|
+
editorState,
|
|
23
25
|
registerEventListener,
|
|
24
26
|
unregisterEventListener
|
|
25
27
|
}
|
|
26
|
-
}, /*#__PURE__*/ React.createElement(EditorStateContext.Provider, {
|
|
27
|
-
value: editorState
|
|
28
28
|
}, /*#__PURE__*/ React.createElement(TestComponent, {
|
|
29
29
|
effect: effect
|
|
30
|
-
}))))
|
|
30
|
+
}))));
|
|
31
31
|
expect(effect).toHaveBeenCalled();
|
|
32
32
|
expect(effect).toHaveBeenCalledWith(editorView);
|
|
33
33
|
});
|
|
@@ -37,27 +37,28 @@ describe("useEditorViewLayoutEffect", ()=>{
|
|
|
37
37
|
const editorState = {};
|
|
38
38
|
const registerEventListener = ()=>{};
|
|
39
39
|
const unregisterEventListener = ()=>{};
|
|
40
|
-
const contextValue = {
|
|
41
|
-
view: editorView,
|
|
42
|
-
registerEventListener,
|
|
43
|
-
unregisterEventListener
|
|
44
|
-
};
|
|
45
40
|
const { rerender } = render(/*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
46
|
-
value:
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
value: {
|
|
42
|
+
editorView,
|
|
43
|
+
editorState,
|
|
44
|
+
registerEventListener,
|
|
45
|
+
unregisterEventListener
|
|
46
|
+
}
|
|
49
47
|
}, /*#__PURE__*/ React.createElement(TestComponent, {
|
|
50
48
|
effect: effect,
|
|
51
49
|
dependencies: []
|
|
52
|
-
}))
|
|
50
|
+
}))));
|
|
53
51
|
rerender(/*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
54
|
-
value:
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
value: {
|
|
53
|
+
editorView,
|
|
54
|
+
editorState,
|
|
55
|
+
registerEventListener,
|
|
56
|
+
unregisterEventListener
|
|
57
|
+
}
|
|
57
58
|
}, /*#__PURE__*/ React.createElement(TestComponent, {
|
|
58
59
|
effect: effect,
|
|
59
60
|
dependencies: []
|
|
60
|
-
}))))
|
|
61
|
+
}))));
|
|
61
62
|
expect(effect).toHaveBeenCalledTimes(1);
|
|
62
63
|
});
|
|
63
64
|
it("should re-run the effect if dependencies change", ()=>{
|
|
@@ -68,32 +69,30 @@ describe("useEditorViewLayoutEffect", ()=>{
|
|
|
68
69
|
const unregisterEventListener = ()=>{};
|
|
69
70
|
const { rerender } = render(/*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
70
71
|
value: {
|
|
71
|
-
|
|
72
|
+
editorView,
|
|
73
|
+
editorState,
|
|
72
74
|
registerEventListener,
|
|
73
75
|
unregisterEventListener
|
|
74
76
|
}
|
|
75
|
-
}, /*#__PURE__*/ React.createElement(EditorStateContext.Provider, {
|
|
76
|
-
value: editorState
|
|
77
77
|
}, /*#__PURE__*/ React.createElement(TestComponent, {
|
|
78
78
|
effect: effect,
|
|
79
79
|
dependencies: [
|
|
80
80
|
"one"
|
|
81
81
|
]
|
|
82
|
-
}))))
|
|
82
|
+
}))));
|
|
83
83
|
rerender(/*#__PURE__*/ React.createElement(LayoutGroup, null, /*#__PURE__*/ React.createElement(EditorContext.Provider, {
|
|
84
84
|
value: {
|
|
85
|
-
|
|
85
|
+
editorView,
|
|
86
|
+
editorState,
|
|
86
87
|
registerEventListener,
|
|
87
88
|
unregisterEventListener
|
|
88
89
|
}
|
|
89
|
-
}, /*#__PURE__*/ React.createElement(EditorStateContext.Provider, {
|
|
90
|
-
value: editorState
|
|
91
90
|
}, /*#__PURE__*/ React.createElement(TestComponent, {
|
|
92
91
|
effect: effect,
|
|
93
92
|
dependencies: [
|
|
94
93
|
"two"
|
|
95
94
|
]
|
|
96
|
-
}))))
|
|
95
|
+
}))));
|
|
97
96
|
expect(effect).toHaveBeenCalledTimes(2);
|
|
98
97
|
});
|
|
99
98
|
});
|