@pie-lib/editable-html 11.1.2-next.0 → 11.2.0-beta.1

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 (158) hide show
  1. package/CHANGELOG.md +43 -167
  2. package/NEXT.CHANGELOG.json +1 -0
  3. package/lib/block-tags.js +25 -0
  4. package/lib/block-tags.js.map +1 -0
  5. package/lib/constants.js +16 -0
  6. package/lib/constants.js.map +1 -0
  7. package/lib/editor.js +348 -87
  8. package/lib/editor.js.map +1 -1
  9. package/lib/index.js +25 -9
  10. package/lib/index.js.map +1 -1
  11. package/lib/plugins/characters/index.js +8 -3
  12. package/lib/plugins/characters/index.js.map +1 -1
  13. package/lib/plugins/characters/utils.js +12 -12
  14. package/lib/plugins/characters/utils.js.map +1 -1
  15. package/lib/plugins/css/icons/index.js +37 -0
  16. package/lib/plugins/css/icons/index.js.map +1 -0
  17. package/lib/plugins/css/index.js +397 -0
  18. package/lib/plugins/css/index.js.map +1 -0
  19. package/lib/plugins/customPlugin/index.js +114 -0
  20. package/lib/plugins/customPlugin/index.js.map +1 -0
  21. package/lib/plugins/html/index.js +11 -7
  22. package/lib/plugins/html/index.js.map +1 -1
  23. package/lib/plugins/image/index.js +2 -1
  24. package/lib/plugins/image/index.js.map +1 -1
  25. package/lib/plugins/image/insert-image-handler.js +13 -4
  26. package/lib/plugins/image/insert-image-handler.js.map +1 -1
  27. package/lib/plugins/index.js +270 -11
  28. package/lib/plugins/index.js.map +1 -1
  29. package/lib/plugins/list/index.js +130 -0
  30. package/lib/plugins/list/index.js.map +1 -1
  31. package/lib/plugins/math/index.js +91 -56
  32. package/lib/plugins/math/index.js.map +1 -1
  33. package/lib/plugins/media/index.js +5 -2
  34. package/lib/plugins/media/index.js.map +1 -1
  35. package/lib/plugins/media/media-dialog.js +98 -57
  36. package/lib/plugins/media/media-dialog.js.map +1 -1
  37. package/lib/plugins/rendering/index.js +46 -0
  38. package/lib/plugins/rendering/index.js.map +1 -0
  39. package/lib/plugins/respArea/drag-in-the-blank/choice.js +5 -2
  40. package/lib/plugins/respArea/drag-in-the-blank/choice.js.map +1 -1
  41. package/lib/plugins/respArea/explicit-constructed-response/index.js +11 -9
  42. package/lib/plugins/respArea/explicit-constructed-response/index.js.map +1 -1
  43. package/lib/plugins/respArea/index.js +69 -21
  44. package/lib/plugins/respArea/index.js.map +1 -1
  45. package/lib/plugins/respArea/inline-dropdown/index.js +10 -5
  46. package/lib/plugins/respArea/inline-dropdown/index.js.map +1 -1
  47. package/lib/plugins/respArea/math-templated/index.js +130 -0
  48. package/lib/plugins/respArea/math-templated/index.js.map +1 -0
  49. package/lib/plugins/respArea/utils.js +16 -1
  50. package/lib/plugins/respArea/utils.js.map +1 -1
  51. package/lib/plugins/table/CustomTablePlugin.js +133 -0
  52. package/lib/plugins/table/CustomTablePlugin.js.map +1 -0
  53. package/lib/plugins/table/index.js +43 -59
  54. package/lib/plugins/table/index.js.map +1 -1
  55. package/lib/plugins/table/table-toolbar.js +33 -4
  56. package/lib/plugins/table/table-toolbar.js.map +1 -1
  57. package/lib/plugins/textAlign/icons/index.js +226 -0
  58. package/lib/plugins/textAlign/icons/index.js.map +1 -0
  59. package/lib/plugins/textAlign/index.js +34 -0
  60. package/lib/plugins/textAlign/index.js.map +1 -0
  61. package/lib/plugins/toolbar/default-toolbar.js +82 -27
  62. package/lib/plugins/toolbar/default-toolbar.js.map +1 -1
  63. package/lib/plugins/toolbar/done-button.js +5 -2
  64. package/lib/plugins/toolbar/done-button.js.map +1 -1
  65. package/lib/plugins/toolbar/editor-and-toolbar.js +18 -19
  66. package/lib/plugins/toolbar/editor-and-toolbar.js.map +1 -1
  67. package/lib/plugins/toolbar/toolbar-buttons.js +44 -11
  68. package/lib/plugins/toolbar/toolbar-buttons.js.map +1 -1
  69. package/lib/plugins/toolbar/toolbar.js +35 -11
  70. package/lib/plugins/toolbar/toolbar.js.map +1 -1
  71. package/lib/serialization.js +233 -44
  72. package/lib/serialization.js.map +1 -1
  73. package/package.json +11 -6
  74. package/src/__tests__/editor.test.jsx +363 -0
  75. package/src/__tests__/serialization.test.js +291 -0
  76. package/src/__tests__/utils.js +36 -0
  77. package/src/block-tags.js +17 -0
  78. package/src/constants.js +7 -0
  79. package/src/editor.jsx +303 -49
  80. package/src/index.jsx +19 -10
  81. package/src/plugins/characters/index.jsx +11 -3
  82. package/src/plugins/characters/utils.js +12 -12
  83. package/src/plugins/css/icons/index.jsx +17 -0
  84. package/src/plugins/css/index.jsx +346 -0
  85. package/src/plugins/customPlugin/index.jsx +85 -0
  86. package/src/plugins/html/index.jsx +9 -6
  87. package/src/plugins/image/__tests__/__snapshots__/component.test.jsx.snap +51 -0
  88. package/src/plugins/image/__tests__/__snapshots__/image-toolbar-logic.test.jsx.snap +27 -0
  89. package/src/plugins/image/__tests__/__snapshots__/image-toolbar.test.jsx.snap +44 -0
  90. package/src/plugins/image/__tests__/component.test.jsx +41 -0
  91. package/src/plugins/image/__tests__/image-toolbar-logic.test.jsx +42 -0
  92. package/src/plugins/image/__tests__/image-toolbar.test.jsx +11 -0
  93. package/src/plugins/image/__tests__/index.test.js +95 -0
  94. package/src/plugins/image/__tests__/insert-image-handler.test.js +113 -0
  95. package/src/plugins/image/__tests__/mock-change.js +15 -0
  96. package/src/plugins/image/index.jsx +2 -1
  97. package/src/plugins/image/insert-image-handler.js +13 -6
  98. package/src/plugins/index.jsx +248 -5
  99. package/src/plugins/list/__tests__/index.test.js +54 -0
  100. package/src/plugins/list/index.jsx +130 -0
  101. package/src/plugins/math/__tests__/__snapshots__/index.test.jsx.snap +48 -0
  102. package/src/plugins/math/__tests__/index.test.jsx +245 -0
  103. package/src/plugins/math/index.jsx +87 -56
  104. package/src/plugins/media/__tests__/index.test.js +75 -0
  105. package/src/plugins/media/index.jsx +3 -2
  106. package/src/plugins/media/media-dialog.js +106 -57
  107. package/src/plugins/rendering/index.js +31 -0
  108. package/src/plugins/respArea/drag-in-the-blank/choice.jsx +4 -1
  109. package/src/plugins/respArea/explicit-constructed-response/index.jsx +10 -8
  110. package/src/plugins/respArea/index.jsx +53 -7
  111. package/src/plugins/respArea/inline-dropdown/index.jsx +13 -6
  112. package/src/plugins/respArea/math-templated/index.jsx +104 -0
  113. package/src/plugins/respArea/utils.jsx +11 -0
  114. package/src/plugins/table/CustomTablePlugin.js +113 -0
  115. package/src/plugins/table/__tests__/__snapshots__/table-toolbar.test.jsx.snap +44 -0
  116. package/src/plugins/table/__tests__/index.test.jsx +401 -0
  117. package/src/plugins/table/__tests__/table-toolbar.test.jsx +42 -0
  118. package/src/plugins/table/index.jsx +46 -59
  119. package/src/plugins/table/table-toolbar.jsx +39 -2
  120. package/src/plugins/textAlign/icons/index.jsx +139 -0
  121. package/src/plugins/textAlign/index.jsx +23 -0
  122. package/src/plugins/toolbar/__tests__/__snapshots__/default-toolbar.test.jsx.snap +923 -0
  123. package/src/plugins/toolbar/__tests__/__snapshots__/editor-and-toolbar.test.jsx.snap +20 -0
  124. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar-buttons.test.jsx.snap +36 -0
  125. package/src/plugins/toolbar/__tests__/__snapshots__/toolbar.test.jsx.snap +46 -0
  126. package/src/plugins/toolbar/__tests__/default-toolbar.test.jsx +94 -0
  127. package/src/plugins/toolbar/__tests__/editor-and-toolbar.test.jsx +37 -0
  128. package/src/plugins/toolbar/__tests__/toolbar-buttons.test.jsx +51 -0
  129. package/src/plugins/toolbar/__tests__/toolbar.test.jsx +106 -0
  130. package/src/plugins/toolbar/default-toolbar.jsx +82 -20
  131. package/src/plugins/toolbar/done-button.jsx +3 -1
  132. package/src/plugins/toolbar/editor-and-toolbar.jsx +18 -13
  133. package/src/plugins/toolbar/toolbar-buttons.jsx +52 -11
  134. package/src/plugins/toolbar/toolbar.jsx +31 -8
  135. package/src/serialization.jsx +213 -38
  136. package/README.md +0 -45
  137. package/deploy.sh +0 -16
  138. package/playground/image/data.js +0 -59
  139. package/playground/image/index.html +0 -22
  140. package/playground/image/index.jsx +0 -81
  141. package/playground/index.html +0 -25
  142. package/playground/mathquill/index.html +0 -22
  143. package/playground/mathquill/index.jsx +0 -155
  144. package/playground/package.json +0 -15
  145. package/playground/prod-test/index.html +0 -22
  146. package/playground/prod-test/index.jsx +0 -28
  147. package/playground/schema-override/data.js +0 -29
  148. package/playground/schema-override/image-plugin.jsx +0 -41
  149. package/playground/schema-override/index.html +0 -21
  150. package/playground/schema-override/index.jsx +0 -97
  151. package/playground/serialization/data.js +0 -29
  152. package/playground/serialization/image-plugin.jsx +0 -41
  153. package/playground/serialization/index.html +0 -22
  154. package/playground/serialization/index.jsx +0 -12
  155. package/playground/static.json +0 -3
  156. package/playground/table-examples.html +0 -70
  157. package/playground/webpack.config.js +0 -42
  158. package/static.json +0 -1
@@ -0,0 +1,245 @@
1
+ import React from 'react';
2
+ import debug from 'debug';
3
+ import MockChange from '../../image/__tests__/mock-change';
4
+ import { Data } from 'slate';
5
+ import MathPlugin, { serialization, inlineMath, CustomToolbarComp } from '../index';
6
+ import { shallow } from 'enzyme';
7
+ import { MathToolbar } from '@pie-lib/math-toolbar';
8
+ jest.mock('@pie-framework/mathquill', () => ({
9
+ StaticMath: jest.fn(),
10
+ getInterface: jest.fn().mockReturnThis(),
11
+ registerEmbed: jest.fn(),
12
+ }));
13
+
14
+ jest.mock('@pie-lib/math-toolbar', () => ({
15
+ MathPreview: () => <div />,
16
+ MathToolbar: () => <div />,
17
+ }));
18
+
19
+ jest.mock('@pie-lib/math-rendering', () => ({
20
+ ...jest.requireActual('@pie-lib/math-rendering'),
21
+ mmlToLatex: jest.fn(() => {
22
+ return '/foobar/latex';
23
+ }),
24
+ }));
25
+ const log = debug('@pie-lib:editable-html:test:math');
26
+
27
+ // I believe @andrei is moving this stuff out.
28
+ describe('MathPlugin', () => {
29
+ describe('toolbar', () => {
30
+ describe('onClick', () => {
31
+ let plugin, mockChange, value, onChange;
32
+ beforeEach(() => {
33
+ plugin = MathPlugin({});
34
+ mockChange = new MockChange();
35
+ value = {
36
+ change: jest.fn(() => mockChange),
37
+ };
38
+ onChange = jest.fn();
39
+ plugin.toolbar.onClick(value, onChange);
40
+ });
41
+
42
+ test('calls insertInline', () => {
43
+ expect(mockChange.insertInline).toBeCalledWith(expect.objectContaining({ data: inlineMath().data }));
44
+ });
45
+
46
+ test('it calls onChange', () => {
47
+ expect(onChange).toHaveBeenCalledWith(mockChange);
48
+ });
49
+ });
50
+ });
51
+
52
+ describe('renderNode', () => {
53
+ test('the component has props', () => {
54
+ const plugin = MathPlugin({});
55
+ const { props } = plugin.renderNode({ node: { type: 'math' } });
56
+ expect(props.node).toEqual({ type: 'math' });
57
+ });
58
+ });
59
+
60
+ describe('serialization', () => {
61
+ describe('deserialize', () => {
62
+ const assertDeserialize = (html, expected, wrapType) => {
63
+ it(`innerHTML: ${html} is deserialized to: ${expected} with wrapType: ${wrapType}`, () => {
64
+ const el = {
65
+ tagName: 'span',
66
+ childNodes: [],
67
+ getAttribute: jest.fn(() => ''),
68
+ hasAttribute: jest.fn(() => true),
69
+ innerHTML: html,
70
+ };
71
+ const next = jest.fn();
72
+
73
+ const out = serialization.deserialize(el, next);
74
+ expect(out).toEqual({
75
+ object: 'inline',
76
+ type: 'math',
77
+ isVoid: true,
78
+ nodes: [],
79
+ data: {
80
+ latex: expected,
81
+ wrapper: wrapType,
82
+ },
83
+ });
84
+ });
85
+ };
86
+
87
+ assertDeserialize('$$&lt;$$', '<', MathPlugin.DOLLAR);
88
+ assertDeserialize('$&lt;$', '<', MathPlugin.DOLLAR);
89
+ assertDeserialize('\\(&lt;\\)', '<', MathPlugin.ROUND_BRACKETS);
90
+ assertDeserialize('\\[&lt;\\]', '<', MathPlugin.ROUND_BRACKETS);
91
+ assertDeserialize('latex', 'latex', MathPlugin.ROUND_BRACKETS);
92
+ assertDeserialize('\\displaystyle foo', 'foo', MathPlugin.ROUND_BRACKETS);
93
+
94
+ it('should make mathml editable if MathPlugin.mathMlOptions.mmlEditing is true', () => {
95
+ MathPlugin.mathMlOptions.mmlEditing = true;
96
+ const el = {
97
+ tagName: 'math',
98
+ outerHTML:
99
+ '<math xmlns="http://www.w3.org/1998/Math/MathML"> <mn>2</mn> <mi>x</mi> <mtext>&#xA0;</mtext> <mo>&#x2264;</mo> <mn>4</mn> <mi>y</mi> <mtext>&#xA0;</mtext> <mo>+</mo> <mtext>&#xA0;</mtext> <mn>8</mn> <msqrt> <mi>h</mi> </msqrt></math>',
100
+ };
101
+ const next = jest.fn();
102
+
103
+ const out = serialization.deserialize(el, next);
104
+
105
+ expect(out).toEqual({
106
+ object: 'inline',
107
+ type: 'math',
108
+ isVoid: true,
109
+ nodes: [],
110
+ data: {
111
+ latex: '/foobar/latex',
112
+ wrapper: 'round_brackets',
113
+ },
114
+ });
115
+ });
116
+
117
+ it('should make mathml readOnly if MathPlugin.mathMlOptions.mmlEditing is false', () => {
118
+ MathPlugin.mathMlOptions.mmlEditing = false;
119
+ const el = {
120
+ tagName: 'math',
121
+ outerHTML:
122
+ '<math xmlns="http://www.w3.org/1998/Math/MathML"> <mn>2</mn> <mi>x</mi> <mtext>&#xA0;</mtext> <mo>&#x2264;</mo> <mn>4</mn> <mi>y</mi> <mtext>&#xA0;</mtext> <mo>+</mo> <mtext>&#xA0;</mtext> <mn>8</mn> <msqrt> <mi>h</mi> </msqrt></math>',
123
+ };
124
+ const next = jest.fn();
125
+
126
+ const out = serialization.deserialize(el, next);
127
+
128
+ expect(out).toEqual({
129
+ object: 'inline',
130
+ isVoid: true,
131
+ type: 'mathml',
132
+ data: {
133
+ html: el.outerHTML,
134
+ },
135
+ });
136
+ });
137
+ });
138
+
139
+ describe('serialize', () => {
140
+ const assertSerialize = (latex, expectedHtml, wrapper) => {
141
+ wrapper = wrapper || MathPlugin.ROUND_BRACKETS;
142
+ it(`${latex} is serialized to: ${expectedHtml}`, () => {
143
+ const object = {
144
+ kind: 'inline',
145
+ type: 'math',
146
+ isVoid: true,
147
+ nodes: [],
148
+ data: Data.create({ latex, wrapper }),
149
+ };
150
+ const children = [];
151
+
152
+ const out = serialization.serialize(object, children);
153
+ log('out: ', out);
154
+ expect(out).toEqual(
155
+ <span data-latex="" data-raw={latex}>
156
+ {expectedHtml}
157
+ </span>,
158
+ );
159
+ });
160
+ };
161
+
162
+ assertSerialize('latex', '\\(latex\\)', MathPlugin.ROUND_BRACKETS);
163
+ assertSerialize('latex', '\\(latex\\)', MathPlugin.SQUARE_BRACKETS);
164
+ assertSerialize('latex', '$latex$', MathPlugin.DOLLAR);
165
+ assertSerialize('latex', '$latex$', MathPlugin.DOUBLE_DOLLAR);
166
+
167
+ /**
168
+ * Note that when this is converted to html it get's escaped - but that's an issue with the slate html-serializer.
169
+ */
170
+ assertSerialize('<', '\\(<\\)');
171
+ assertSerialize('x<y', '\\(x<y\\)');
172
+ });
173
+ });
174
+ });
175
+
176
+ describe('CustomToolbarComp', () => {
177
+ let onDataChange;
178
+ let onToolbarDone;
179
+
180
+ const wrapper = (extras) => {
181
+ let mockChange = new MockChange();
182
+ const defaults = {
183
+ node: {
184
+ key: '1',
185
+ data: Data.create({ latex: 'foo' }),
186
+ equals: () => true,
187
+ },
188
+ value: {
189
+ document: {
190
+ getNextText: jest.fn().mockReturnValue({ key: 'nt' }),
191
+ },
192
+ change: jest.fn().mockReturnValue(mockChange),
193
+ },
194
+ onDataChange,
195
+ onToolbarDone,
196
+ };
197
+
198
+ const props = {
199
+ ...defaults,
200
+ ...extras,
201
+ };
202
+
203
+ return shallow(<CustomToolbarComp {...props} />);
204
+ };
205
+
206
+ describe('render', () => {
207
+ it('renders without default keypadMode', () => {
208
+ const w = wrapper();
209
+
210
+ expect(w).toMatchSnapshot();
211
+
212
+ w.setProps({ pluginProps: { math: { keypadMode: 3 } } });
213
+
214
+ expect(w).toMatchSnapshot();
215
+ });
216
+
217
+ it('renders with default keypadMode', () => {
218
+ const w = wrapper({ pluginProps: { math: { keypadMode: 'geometry' } } });
219
+
220
+ expect(w).toMatchSnapshot();
221
+
222
+ w.setProps({ pluginProps: { math: { keypadMode: 3 } } });
223
+
224
+ expect(w).toMatchSnapshot();
225
+ });
226
+ });
227
+
228
+ describe('onDone', () => {
229
+ it('calls onToolbarDone', () => {
230
+ onToolbarDone = jest.fn();
231
+ const w = wrapper();
232
+ w.find(MathToolbar).prop('onDone')('oo');
233
+ expect(onToolbarDone).toHaveBeenCalledWith(expect.anything(), false);
234
+ });
235
+ });
236
+
237
+ describe('onChange', () => {
238
+ it('calls onDataChange', () => {
239
+ onDataChange = jest.fn();
240
+ const w = wrapper();
241
+ w.find(MathToolbar).prop('onChange')('oo');
242
+ expect(onDataChange).toHaveBeenCalledWith('1', { latex: 'oo' });
243
+ });
244
+ });
245
+ });
@@ -7,7 +7,7 @@ import debug from 'debug';
7
7
  import SlatePropTypes from 'slate-prop-types';
8
8
  import PropTypes from 'prop-types';
9
9
 
10
- import { BLOCK_TAGS } from '../../serialization';
10
+ import { BLOCK_TAGS } from '../../block-tags';
11
11
  import isEqual from 'lodash/isEqual';
12
12
 
13
13
  const log = debug('@pie-lib:editable-html:plugins:math');
@@ -36,6 +36,7 @@ export const CustomToolbarComp = React.memo(
36
36
  ...node.data.toObject(),
37
37
  latex,
38
38
  };
39
+
39
40
  const change = value.change().setNodeByKey(node.key, { data: update });
40
41
 
41
42
  const nextText = value.document.getNextText(node.key);
@@ -105,6 +106,7 @@ export default function MathPlugin(opts) {
105
106
  return {
106
107
  name: 'math',
107
108
  toolbar: {
109
+ ariaLabel: 'Math Toolbar',
108
110
  icon: <Functions />,
109
111
  onClick: (value, onChange) => {
110
112
  log('[insertMath]');
@@ -191,35 +193,79 @@ const getTagName = (el) => {
191
193
  * @param input
192
194
  * @returns {*}
193
195
  */
194
- const lessThanHandling = (input) => {
195
- const arrowSplit = input.split('<');
196
-
197
- // if we don't have at least 2 characters there's no point in checking
198
- if (input.length > 2) {
199
- return arrowSplit.reduce((st, part) => {
200
- /*
201
- We check if this element resulted is:
202
- div - continuation of a beginning of a HTML element
203
- /div - closing of a HTML tag
204
- br/> - beginning and closing of a html TAG
205
- */
206
- if (part.match(/<[a-zA-Z/][\s\S]*>/gi)) {
207
- return `${st}${st ? '<' : ''}${part}`;
208
- }
209
-
210
- return `${st}${st ? '&lt;' : ''}${part}`;
211
- }, '');
212
- }
213
-
214
- return input;
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');
215
202
  };
216
203
 
217
- function fixLatexExpression(latexInput) {
204
+ function replaceLeftRight(latexInput) {
218
205
  // for some reason, mmlToLatex parses () incorrectly - or at least in a way that our interpreter can not use them
219
206
  // Replace '\\left.' and '\\right.' with an empty string
220
207
  return latexInput.replace(/\\left\.\s*|\\right\.\s*/g, '');
221
208
  }
222
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
+
223
269
  export const serialization = {
224
270
  deserialize(el) {
225
271
  const tagName = getTagName(el);
@@ -237,16 +283,10 @@ export const serialization = {
237
283
  * This is here in order to be able to render mathml content
238
284
  */
239
285
  if (tagName === 'math' || (el.dataset && el.dataset.type === 'mathml') || hasMathChild) {
240
- const newHtml = hasMathChild ? el.innerHTML : el.outerHTML;
286
+ const mathMl = hasMathChild ? el.innerHTML : el.outerHTML;
241
287
 
242
288
  if (MathPlugin.mathMlOptions.mmlEditing) {
243
- // todo fix this in mathml-to-latex
244
- const htmlWithRemovedSpaces = newHtml.replaceAll("&nbsp;", " ");
245
- const htmlToUse = mmlToLatex(htmlWithRemovedSpaces);
246
- const latex = htmlDecode(htmlToUse);
247
- // todo fix this in mathml-to-latex
248
- const correctedLatex = fixLatexExpression(latex);
249
- const { unwrapped, wrapType } = unWrapMath(correctedLatex);
289
+ const { unwrapped, wrapType } = unWrapMath(convertMathMlToLatex(mathMl));
250
290
 
251
291
  return {
252
292
  object: 'inline',
@@ -265,7 +305,7 @@ export const serialization = {
265
305
  isVoid: true,
266
306
  type: 'mathml',
267
307
  data: {
268
- html: newHtml,
308
+ html: mathMl,
269
309
  },
270
310
  };
271
311
  }
@@ -298,35 +338,26 @@ export const serialization = {
298
338
  },
299
339
  serialize(object) {
300
340
  if (object.type === 'math') {
301
- const l = object.data.get('latex');
341
+ const latex = object.data.get('latex');
302
342
  const wrapper = object.data.get('wrapper');
303
- log('[serialize] latex: ', l);
304
- const decoded = htmlDecode(lessThanHandling(l));
343
+ const decoded = htmlDecode(arrowHandlingCase(latex));
344
+
345
+ log('[serialize] latex: ', latex);
305
346
 
306
347
  if (MathPlugin.mathMlOptions.mmlOutput) {
307
- const res = renderMath(`<span data-latex="" data-raw="${decoded}">${wrapMath(decoded, wrapper)}</span>`);
308
- const newLatex = mmlToLatex(res);
309
-
310
- // we need to remove all the spaces from the latex to be able to compare it
311
- const strippedL = l.replace(/\s/g, '');
312
- const strippedNewL = newLatex.replace(/\s/g, '');
313
-
314
- // we check if the latex keeps his form after being converted to mathml and back to latex
315
- // if it does we can safely convert it to mathml
316
- if (!isEqual(strippedL, strippedNewL)) {
317
- const correctedLatex = fixLatexExpression(newLatex);
318
-
319
- // As George requested in PD-3167, I will set the new mathML anyway, and also log differences
320
- // if it doesn't we keep the latex version
321
- console.log('This latex can not be safely converted to mathml so we will keep the latex version!!!', {
322
- initialLatex: l,
323
- newLatex: newLatex,
324
- correctedLatex,
325
- mathML: res,
326
- });
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
+ );
327
358
  }
328
359
 
329
- return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: res }} />;
360
+ return <span data-type="mathml" dangerouslySetInnerHTML={{ __html: mathMlFromLatex }} />;
330
361
  }
331
362
 
332
363
  return (
@@ -0,0 +1,75 @@
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
+ });
@@ -54,6 +54,7 @@ const types = ['audio', 'video'];
54
54
  export default function MediaPlugin(type, opts) {
55
55
  const toolbar = {
56
56
  icon: type === 'audio' ? <VolumeUpIcon /> : <TheatersIcon />,
57
+ ariaLabel: type === 'audio' ? 'Insert audio' : 'Insert video',
57
58
  onClick: (value, onChange) => {
58
59
  log('[toolbar] onClick');
59
60
  const inline = Inline.create({
@@ -162,7 +163,7 @@ export default function MediaPlugin(type, opts) {
162
163
  if (tag === 'audio') {
163
164
  return (
164
165
  <MediaWrapper editor data-type={type} width={style.width} {...rest}>
165
- <audio controls="controls">
166
+ <audio controls="controls" controlsList="nodownload">
166
167
  <source type="audio/mp3" src={src} />
167
168
  </audio>
168
169
  <MediaToolbar hideEdit onRemove={handleDelete} />
@@ -303,7 +304,7 @@ export const serialization = {
303
304
 
304
305
  if (tag === 'audio') {
305
306
  return (
306
- <audio controls="controls">
307
+ <audio controls="controls" controlsList="nodownload">
307
308
  <source type="audio/mp3" src={src} />
308
309
  </audio>
309
310
  );