@pie-lib/editable-html-tip-tap 1.0.2 → 1.0.3

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.
Files changed (129) hide show
  1. package/lib/components/CharacterPicker.js +221 -0
  2. package/lib/components/EditableHtml.js +323 -0
  3. package/lib/components/MenuBar.js +693 -0
  4. package/lib/components/TiptapContainer.js +90 -0
  5. package/lib/components/buttons/done-button.js +53 -0
  6. package/lib/components/characters/characterUtils.js +112 -0
  7. package/lib/components/characters/custom-popper.js +73 -0
  8. package/lib/components/common/done-button.js +53 -0
  9. package/lib/components/icons/CssIcon.js +37 -0
  10. package/lib/components/icons/RespArea.js +95 -0
  11. package/lib/components/icons/TableIcons.js +69 -0
  12. package/lib/components/icons/TextAlign.js +194 -0
  13. package/lib/components/icons/index.js +194 -0
  14. package/lib/components/image/ImageToolbar.js +16 -0
  15. package/lib/components/image/InsertImageHandler.js +16 -0
  16. package/lib/components/media/MediaDialog.js +16 -0
  17. package/lib/components/media/MediaToolbar.js +16 -0
  18. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +94 -0
  19. package/lib/components/respArea/DragInTheBlank/choice.js +289 -0
  20. package/lib/components/respArea/DragInTheBlank.js +94 -0
  21. package/lib/components/respArea/ExplicitConstructedResponse.js +120 -0
  22. package/lib/components/respArea/InlineDropdown.js +126 -0
  23. package/lib/components/respArea/ToolbarIcon.js +105 -0
  24. package/lib/components/respArea/choice.js +2 -0
  25. package/lib/extensions/component.js +5 -5
  26. package/lib/extensions/custom-toolbar-wrapper.js +2 -4
  27. package/lib/extensions/extended-table.js +30 -0
  28. package/lib/extensions/index.js +52 -0
  29. package/lib/extensions/media.js +5 -5
  30. package/lib/extensions/responseArea.js +7 -7
  31. package/lib/index.js +16 -1481
  32. package/lib/plugins/index.js +8 -80
  33. package/lib/styles/editorContainerStyles.js +200 -0
  34. package/lib/utils/size.js +34 -0
  35. package/package.json +1 -1
  36. package/src/components/CharacterPicker.jsx +185 -0
  37. package/src/components/EditableHtml.jsx +306 -0
  38. package/src/components/MenuBar.jsx +630 -0
  39. package/src/components/TiptapContainer.jsx +96 -0
  40. package/src/components/characters/characterUtils.js +127 -0
  41. package/src/components/image/ImageToolbar.jsx +1 -0
  42. package/src/components/image/InsertImageHandler.js +1 -0
  43. package/src/components/media/MediaDialog.js +1 -0
  44. package/src/components/media/MediaToolbar.jsx +1 -0
  45. package/src/{plugins/respArea/drag-in-the-blank → components/respArea/DragInTheBlank}/choice.jsx +1 -1
  46. package/src/{plugins/respArea/inline-dropdown/index.jsx → components/respArea/InlineDropdown.jsx} +1 -1
  47. package/src/components/respArea/ToolbarIcon.jsx +68 -0
  48. package/src/extensions/component.jsx +2 -2
  49. package/src/extensions/custom-toolbar-wrapper.jsx +6 -7
  50. package/src/extensions/extended-table.js +27 -0
  51. package/src/extensions/index.js +76 -0
  52. package/src/extensions/media.js +10 -4
  53. package/src/extensions/responseArea.js +7 -7
  54. package/src/index.jsx +3 -1440
  55. package/src/styles/editorContainerStyles.js +203 -0
  56. package/src/utils/size.js +32 -0
  57. package/src/__tests__/editor.test.jsx +0 -363
  58. package/src/__tests__/serialization.test.js +0 -291
  59. package/src/block-tags.js +0 -17
  60. package/src/editor.jsx +0 -1197
  61. package/src/extensions/characters.js +0 -46
  62. package/src/old-index.jsx +0 -162
  63. package/src/parse-html.js +0 -8
  64. package/src/plugins/README.md +0 -27
  65. package/src/plugins/characters/index.jsx +0 -284
  66. package/src/plugins/characters/utils.js +0 -447
  67. package/src/plugins/css/index.jsx +0 -340
  68. package/src/plugins/customPlugin/index.jsx +0 -85
  69. package/src/plugins/html/icons/index.jsx +0 -19
  70. package/src/plugins/html/index.jsx +0 -72
  71. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +0 -51
  72. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +0 -27
  73. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +0 -44
  74. package/src/plugins/image/__tests__/component.test.jsx +0 -41
  75. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +0 -42
  76. package/src/plugins/image/__tests__/image-toolbar.test.jsx +0 -11
  77. package/src/plugins/image/__tests__/index.test.js +0 -95
  78. package/src/plugins/image/__tests__/insert-image-handler.test.js +0 -113
  79. package/src/plugins/image/__tests__/mock-change.js +0 -15
  80. package/src/plugins/image/alt-dialog.jsx +0 -82
  81. package/src/plugins/image/component.jsx +0 -343
  82. package/src/plugins/image/image-toolbar.jsx +0 -100
  83. package/src/plugins/image/index.jsx +0 -227
  84. package/src/plugins/image/insert-image-handler.js +0 -79
  85. package/src/plugins/index.jsx +0 -377
  86. package/src/plugins/list/__tests__/index.test.js +0 -54
  87. package/src/plugins/list/index.jsx +0 -305
  88. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +0 -48
  89. package/src/plugins/math/__tests__/index.test.jsx +0 -245
  90. package/src/plugins/math/index.jsx +0 -379
  91. package/src/plugins/media/__tests__/index.test.js +0 -75
  92. package/src/plugins/media/index.jsx +0 -325
  93. package/src/plugins/media/media-dialog.js +0 -624
  94. package/src/plugins/media/media-toolbar.jsx +0 -56
  95. package/src/plugins/media/media-wrapper.jsx +0 -43
  96. package/src/plugins/rendering/index.js +0 -31
  97. package/src/plugins/respArea/index.jsx +0 -299
  98. package/src/plugins/respArea/math-templated/index.jsx +0 -104
  99. package/src/plugins/respArea/utils.jsx +0 -90
  100. package/src/plugins/table/CustomTablePlugin.js +0 -113
  101. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +0 -44
  102. package/src/plugins/table/__tests__/index.test.jsx +0 -401
  103. package/src/plugins/table/__tests__/table-toolbar.test.jsx +0 -42
  104. package/src/plugins/table/index.jsx +0 -427
  105. package/src/plugins/table/table-toolbar.jsx +0 -136
  106. package/src/plugins/textAlign/index.jsx +0 -23
  107. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +0 -923
  108. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +0 -20
  109. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +0 -36
  110. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +0 -46
  111. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +0 -94
  112. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +0 -37
  113. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +0 -51
  114. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +0 -106
  115. package/src/plugins/toolbar/default-toolbar.jsx +0 -206
  116. package/src/plugins/toolbar/editor-and-toolbar.jsx +0 -257
  117. package/src/plugins/toolbar/index.jsx +0 -23
  118. package/src/plugins/toolbar/toolbar-buttons.jsx +0 -138
  119. package/src/plugins/toolbar/toolbar.jsx +0 -338
  120. package/src/plugins/utils.js +0 -31
  121. package/src/serialization.jsx +0 -621
  122. /package/src/{plugins → components}/characters/custom-popper.js +0 -0
  123. /package/src/{plugins/toolbar → components/common}/done-button.jsx +0 -0
  124. /package/src/{plugins/css/icons/index.jsx → components/icons/CssIcon.jsx} +0 -0
  125. /package/src/{plugins/respArea/icons/index.jsx → components/icons/RespArea.jsx} +0 -0
  126. /package/src/{plugins/table/icons/index.jsx → components/icons/TableIcons.jsx} +0 -0
  127. /package/src/{plugins/textAlign/icons/index.jsx → components/icons/TextAlign.jsx} +0 -0
  128. /package/src/{plugins/respArea/drag-in-the-blank/index.jsx → components/respArea/DragInTheBlank/DragInTheBlank.jsx} +0 -0
  129. /package/src/{plugins/respArea/explicit-constructed-response/index.jsx → components/respArea/ExplicitConstructedResponse.jsx} +0 -0
@@ -1,379 +0,0 @@
1
- import Functions from '@material-ui/icons/Functions';
2
- import { Inline } from 'slate';
3
- import { MathPreview, MathToolbar } from '@pie-lib/math-toolbar';
4
- import { wrapMath, unWrapMath, mmlToLatex, renderMath } from '@pie-lib/math-rendering';
5
- import React from 'react';
6
- import debug from 'debug';
7
- import SlatePropTypes from 'slate-prop-types';
8
- import PropTypes from 'prop-types';
9
-
10
- import { BLOCK_TAGS } from '../../block-tags';
11
- import isEqual from 'lodash/isEqual';
12
-
13
- const log = debug('@pie-lib:editable-html:plugins:math');
14
-
15
- const TEXT_NODE = 3;
16
-
17
- function generateAdditionalKeys(keyData = []) {
18
- return keyData.map((key) => ({
19
- name: key,
20
- latex: key,
21
- write: key,
22
- label: key,
23
- }));
24
- }
25
-
26
- // eslint-disable-next-line react/display-name
27
- export const CustomToolbarComp = React.memo(
28
- (props) => {
29
- const { node, value, onFocus, onBlur, onClick } = props;
30
- const { pluginProps } = props || {};
31
- const { math } = pluginProps || {};
32
- const { keypadMode, customKeys, controlledKeypadMode = true } = math || {};
33
-
34
- const onDone = (latex) => {
35
- const update = {
36
- ...node.data.toObject(),
37
- latex,
38
- };
39
-
40
- const change = value.change().setNodeByKey(node.key, { data: update });
41
-
42
- const nextText = value.document.getNextText(node.key);
43
-
44
- change.moveFocusTo(nextText.key, 0).moveAnchorTo(nextText.key, 0);
45
-
46
- props.onToolbarDone(change, false);
47
- };
48
-
49
- const onChange = (latex) => {
50
- const update = {
51
- ...node.data.toObject(),
52
- latex,
53
- };
54
- const change = value.change().setNodeByKey(node.key, { data: update });
55
- log('call onToolbarChange:', change);
56
- props.onDataChange(node.key, update);
57
- };
58
-
59
- const latex = node.data.get('latex');
60
-
61
- return (
62
- <MathToolbar
63
- autoFocus
64
- additionalKeys={generateAdditionalKeys(customKeys)}
65
- latex={latex}
66
- onChange={onChange}
67
- onDone={onDone}
68
- onBlur={onBlur}
69
- onFocus={onFocus}
70
- onClick={onClick}
71
- keypadMode={keypadMode}
72
- controlledKeypadMode={controlledKeypadMode}
73
- />
74
- );
75
- },
76
- (prev, next) => {
77
- const { node, pluginProps: { math: { keypadMode, controlledKeypadMode } = {} } = {} } = prev;
78
- const {
79
- node: nodeNext,
80
- pluginProps: { math: { keypadMode: keypadModeNext, controlledKeypadMode: controlledKeypadModeNext } = {} } = {},
81
- } = next;
82
- const keypadModeChanged = keypadMode !== keypadModeNext;
83
- const controlledKeypadModeChanged = controlledKeypadMode !== controlledKeypadModeNext;
84
-
85
- const equal = node.equals(nodeNext);
86
- return equal && !keypadModeChanged && !controlledKeypadModeChanged;
87
- },
88
- );
89
-
90
- CustomToolbarComp.propTypes = {
91
- node: SlatePropTypes.node.isRequired,
92
- value: SlatePropTypes.value,
93
- onToolbarDone: PropTypes.func,
94
- onDataChange: PropTypes.func,
95
- onFocus: PropTypes.func,
96
- onClick: PropTypes.func,
97
- onBlur: PropTypes.func,
98
- };
99
-
100
- export default function MathPlugin(opts) {
101
- MathPlugin.mathMlOptions = {
102
- mmlOutput: opts.mmlOutput,
103
- mmlEditing: opts.mmlEditing,
104
- };
105
-
106
- return {
107
- name: 'math',
108
- toolbar: {
109
- ariaLabel: 'Math Toolbar',
110
- icon: <Functions />,
111
- onClick: (value, onChange) => {
112
- log('[insertMath]');
113
- const math = inlineMath();
114
- const change = value.change().insertInline(math);
115
- onChange(change);
116
- },
117
- supports: (node) => node && node.object === 'inline' && node.type === 'math',
118
- /**
119
- * Return a react component function
120
- * @param node {Slate.Node}
121
- * @param value {Slate.Value}
122
- * @param onDone {(change?: Slate.Change, finishEditing :boolea) => void} - a function to call once the toolbar
123
- * has made any changes, call with the node.key and a data object.
124
- */
125
- CustomToolbarComp,
126
- },
127
- schema: {
128
- document: { match: [{ type: 'math' }] },
129
- },
130
-
131
- pluginStyles: (node, parentNode, p) => {
132
- if (p) {
133
- return {
134
- position: 'absolute',
135
- top: 'initial',
136
- };
137
- }
138
- },
139
-
140
- renderNode: (props) => {
141
- if (props.node.type === 'math') {
142
- log('[renderNode]: data:', props.node.data);
143
- return <MathPreview {...props} />;
144
- }
145
-
146
- /**
147
- * Here for rendering mathml content
148
- */
149
- if (props.node.type === 'mathml') {
150
- const html = props.node.data.get('html');
151
-
152
- return <span {...props.attributes} dangerouslySetInnerHTML={{ __html: html }} />;
153
- }
154
- },
155
- };
156
- }
157
-
158
- MathPlugin.ROUND_BRACKETS = 'round_brackets';
159
- MathPlugin.SQUARE_BRACKETS = 'square_brackets';
160
- MathPlugin.DOLLAR = 'dollar';
161
- MathPlugin.DOUBLE_DOLLAR = 'double_dollar';
162
- MathPlugin.mathMlOptions = {};
163
-
164
- MathPlugin.propTypes = {
165
- attributes: PropTypes.object,
166
- node: SlatePropTypes.node,
167
- };
168
-
169
- export const inlineMath = () =>
170
- Inline.create({
171
- object: 'inline',
172
- type: 'math',
173
- isVoid: true,
174
- data: {
175
- latex: '',
176
- },
177
- });
178
-
179
- const htmlDecode = (input) => {
180
- const doc = new DOMParser().parseFromString(input, 'text/html');
181
-
182
- return doc.documentElement.textContent;
183
- };
184
-
185
- const getTagName = (el) => {
186
- return ((el && el.tagName) || '').toLowerCase();
187
- };
188
-
189
- /**
190
- * Makes sure that strings that contain stuff like:
191
- * x<y are not transformed into x by the DOMParser because it thinks
192
- * that <y is the start of a dom element tag
193
- * @param input
194
- * @returns {*}
195
- */
196
- const arrowHandlingCase = (input) => {
197
- /*
198
- If we have a < character followed by a letter
199
- we make sure to replace it with a &lt; sign instead
200
- */
201
- return input.replace(/<([a-zA-Z]*)/g, '&lt;$1');
202
- };
203
-
204
- function replaceLeftRight(latexInput) {
205
- // for some reason, mmlToLatex parses () incorrectly - or at least in a way that our interpreter can not use them
206
- // Replace '\\left.' and '\\right.' with an empty string
207
- return latexInput.replace(/\\left\.\s*|\\right\.\s*/g, '');
208
- }
209
-
210
- const convertLatexToMathMl = ({ latex, decoded, wrapper }) => {
211
- const removeEmptyMos = (mmlFromLatex) => {
212
- // Regular expression to match <mo>&#x2061;</mo> and <mo ...>&#x2061;</mo>, which get added when using log with base
213
- // not sure why they get added, but they add an extra space which is not needed
214
- const regex = /<mo(?: [^>]*)?>&#x2061;<\/mo>/g;
215
-
216
- // Replace all occurrences of the matched patterns
217
- return mmlFromLatex.replace(regex, '');
218
- };
219
- const handled = arrowHandlingCase(decoded);
220
-
221
- const latexToConvert = `<span data-latex="" data-raw="${handled}">${wrapMath(handled, wrapper)}</span>`;
222
-
223
- // use math rendering (MathJax) to convert latex to mathMl
224
- let mathMlFromLatex = renderMath(latexToConvert, {
225
- skipWaitForMathRenderingLib: true,
226
- });
227
-
228
- // if renderMath returned the exact same string that we sent, it just means that the conversion could not be done
229
- const conversionDidNotWork = isEqual(latexToConvert, mathMlFromLatex);
230
-
231
- mathMlFromLatex = removeEmptyMos(mathMlFromLatex);
232
-
233
- // we convert resulted mathml to latex to check if the resulted mathMl can be converted back to latex if user wants to edit it later
234
- const latexFromMathMl = mathMlFromLatex ? mmlToLatex(mathMlFromLatex) : '';
235
-
236
- // we need to remove all the spaces from the latex to be able to compare it
237
- const strippedL = latex.replace(/\s/g, '');
238
- const strippedNewL = latexFromMathMl.replace(/\s/g, '');
239
-
240
- // we check if the latex keeps his form after being converted to mathml and back to latex
241
- // if it does, we can safely convert it to mathml
242
- if (!isEqual(strippedL, strippedNewL)) {
243
- const correctedLatex = replaceLeftRight(latexFromMathMl);
244
-
245
- // As George requested in PD-3167, I will set the new mathML anyway, and also log differences
246
- // if it doesn't, we keep the latex version
247
- // eslint-disable-next-line no-console
248
- console.log('This latex can not be safely converted to mathml so we will keep the latex version!!!', {
249
- initialLatex: latex,
250
- newLatex: latexFromMathMl,
251
- correctedLatex,
252
- mathML: mathMlFromLatex,
253
- conversionDidNotWork,
254
- });
255
- }
256
-
257
- return { mathMlFromLatex, conversionDidNotWork };
258
- };
259
-
260
- const convertMathMlToLatex = (mathMl) => {
261
- const htmlWithRemovedSpaces = mathMl.replaceAll('&nbsp;', ' ');
262
- const htmlToUse = mmlToLatex(htmlWithRemovedSpaces);
263
- const latex = htmlDecode(htmlToUse);
264
-
265
- // todo fix this in mathml-to-latex
266
- return replaceLeftRight(latex);
267
- };
268
-
269
- export const serialization = {
270
- deserialize(el) {
271
- const tagName = getTagName(el);
272
- /**
273
- * This is used for when there's a wrapper over the mathml element.
274
- * Because of this slate rule: "Only allow block nodes or inline and text nodes in blocks."
275
- * The element that contains only the mathml is removed (along with the math) because it has
276
- * an inline child and the block is of type block
277
- * This is for legacy content only since our math rendering is valid for the core slate rules
278
- */
279
- const hasMathChild = BLOCK_TAGS[tagName] && el.childNodes.length === 1 && getTagName(el.firstChild) === 'math';
280
- log('[deserialize] name: ', tagName);
281
-
282
- /**
283
- * This is here in order to be able to render mathml content
284
- */
285
- if (tagName === 'math' || (el.dataset && el.dataset.type === 'mathml') || hasMathChild) {
286
- const mathMl = hasMathChild ? el.innerHTML : el.outerHTML;
287
-
288
- if (MathPlugin.mathMlOptions.mmlEditing) {
289
- const { unwrapped, wrapType } = unWrapMath(convertMathMlToLatex(mathMl));
290
-
291
- return {
292
- object: 'inline',
293
- type: 'math',
294
- isVoid: true,
295
- nodes: [],
296
- data: {
297
- latex: unwrapped,
298
- wrapper: wrapType,
299
- },
300
- };
301
- }
302
-
303
- return {
304
- object: 'inline',
305
- isVoid: true,
306
- type: 'mathml',
307
- data: {
308
- html: mathMl,
309
- },
310
- };
311
- }
312
-
313
- if (el.nodeType === TEXT_NODE) {
314
- return;
315
- }
316
-
317
- if (tagName !== 'span') {
318
- return;
319
- }
320
-
321
- const hasLatex = el.hasAttribute('data-latex') || el.hasAttribute('latex');
322
-
323
- if (hasLatex) {
324
- const latex = htmlDecode(el.innerHTML);
325
- const { unwrapped, wrapType } = unWrapMath(latex);
326
- log('[deserialize]: noBrackets: ', unwrapped, wrapType);
327
- return {
328
- object: 'inline',
329
- type: 'math',
330
- isVoid: true,
331
- nodes: [],
332
- data: {
333
- latex: unwrapped,
334
- wrapper: wrapType,
335
- },
336
- };
337
- }
338
- },
339
- serialize(object) {
340
- if (object.type === 'math') {
341
- const latex = object.data.get('latex');
342
- const wrapper = object.data.get('wrapper');
343
- const decoded = htmlDecode(arrowHandlingCase(latex));
344
-
345
- log('[serialize] latex: ', latex);
346
-
347
- if (MathPlugin.mathMlOptions.mmlOutput) {
348
- const { mathMlFromLatex, conversionDidNotWork } = convertLatexToMathMl({ latex, decoded, wrapper });
349
-
350
- if (conversionDidNotWork) {
351
- // this means we could not convert latex to mathMl, so just return the latex version,
352
- // same as we would do if mmlOutput would not be enabled
353
- return (
354
- <span data-latex="" data-raw={decoded}>
355
- {wrapMath(decoded, wrapper)}
356
- </span>
357
- );
358
- }
359
-
360
- return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: mathMlFromLatex }} />;
361
- }
362
-
363
- return (
364
- <span data-latex="" data-raw={decoded}>
365
- {wrapMath(decoded, wrapper)}
366
- </span>
367
- );
368
- }
369
-
370
- /**
371
- * Here for rendering mathml content
372
- */
373
- if (object.type === 'mathml') {
374
- const html = object.data.get('html');
375
-
376
- return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: html }} />;
377
- }
378
- },
379
- };
@@ -1,75 +0,0 @@
1
- import MediaPlugin from '../';
2
-
3
- describe('media plugin', () => {
4
- const imagePlugin = MediaPlugin('video');
5
-
6
- describe('normalizeNode', () => {
7
- it('should exit the function if the node is not of type document', () => {
8
- const returnValue = imagePlugin.normalizeNode({ object: 'image' });
9
-
10
- expect(returnValue).toEqual(undefined);
11
- });
12
-
13
- it('should exit there are no changes needed', () => {
14
- const nodes = [
15
- {
16
- object: 'text',
17
- text: 'Before Media',
18
- },
19
- {
20
- type: 'video',
21
- },
22
- {
23
- object: 'text',
24
- text: 'After Media',
25
- },
26
- ];
27
- const returnValue = imagePlugin.normalizeNode({
28
- object: 'document',
29
- findDescendant: jest.fn((callback) => {
30
- nodes.forEach((n) => callback(n));
31
- }),
32
- });
33
- expect(returnValue).toEqual(undefined);
34
- });
35
-
36
- it('should return a function if there is a node with an empty text before a media element', () => {
37
- const nodes = [
38
- {
39
- object: 'text',
40
- text: '',
41
- key: '1',
42
- },
43
- {
44
- type: 'video',
45
- key: '2',
46
- },
47
- {
48
- object: 'text',
49
- text: 'After Media',
50
- key: '3',
51
- },
52
- ];
53
- const findDescendant = jest.fn((callback) => {
54
- nodes.forEach((n) => callback(n));
55
- });
56
- const change = {
57
- withoutNormalization: jest.fn((callback) => {
58
- callback();
59
- }),
60
- insertTextByKey: jest.fn(),
61
- };
62
- const returnValue = imagePlugin.normalizeNode({
63
- object: 'document',
64
- findDescendant,
65
- });
66
-
67
- expect(returnValue).toEqual(expect.any(Function));
68
-
69
- returnValue(change);
70
-
71
- expect(change.withoutNormalization).toHaveBeenCalledWith(expect.any(Function));
72
- expect(change.insertTextByKey).toHaveBeenCalledWith('1', 0, ' ');
73
- });
74
- });
75
- });