@pie-lib/math-input-mathlive 1.0.10 → 1.0.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.
Files changed (41) hide show
  1. package/lib/keys/misc.js +9 -2
  2. package/lib/keys/misc.js.map +1 -1
  3. package/lib/latex-bridge.js +30 -0
  4. package/lib/latex-bridge.js.map +1 -1
  5. package/lib/mathlive-instance.js +67 -3
  6. package/lib/mathlive-instance.js.map +1 -1
  7. package/lib/mf/input.js +7 -0
  8. package/lib/mf/input.js.map +1 -1
  9. package/lib/mf/static.js +82 -1
  10. package/lib/mf/static.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/__tests__/keypad-label.test.jsx +58 -5
  13. package/src/__tests__/latex-bridge.test.js +22 -0
  14. package/src/__tests__/mathlive-instance.test.js +128 -0
  15. package/src/__tests__/mount-race.test.jsx +1 -0
  16. package/src/__tests__/static-prompts.test.jsx +113 -0
  17. package/src/keys/misc.js +8 -2
  18. package/src/latex-bridge.js +34 -0
  19. package/src/mathlive-instance.js +75 -1
  20. package/src/mf/input.jsx +15 -1
  21. package/src/mf/static.jsx +94 -2
  22. package/lib/__tests__/composites.test.js +0 -255
  23. package/lib/__tests__/composites.test.js.map +0 -1
  24. package/lib/__tests__/drop-in-compat.test.js +0 -133
  25. package/lib/__tests__/drop-in-compat.test.js.map +0 -1
  26. package/lib/__tests__/index.test.js +0 -118
  27. package/lib/__tests__/index.test.js.map +0 -1
  28. package/lib/__tests__/input-imperative.test.js +0 -155
  29. package/lib/__tests__/input-imperative.test.js.map +0 -1
  30. package/lib/__tests__/keypad-label.test.js +0 -118
  31. package/lib/__tests__/keypad-label.test.js.map +0 -1
  32. package/lib/__tests__/keypad-render.test.js +0 -206
  33. package/lib/__tests__/keypad-render.test.js.map +0 -1
  34. package/lib/__tests__/latex-bridge.test.js +0 -280
  35. package/lib/__tests__/latex-bridge.test.js.map +0 -1
  36. package/lib/__tests__/mathlive-instance.test.js +0 -279
  37. package/lib/__tests__/mathlive-instance.test.js.map +0 -1
  38. package/lib/__tests__/mount-race.test.js +0 -195
  39. package/lib/__tests__/mount-race.test.js.map +0 -1
  40. package/lib/__tests__/static-prompts.test.js +0 -282
  41. package/lib/__tests__/static-prompts.test.js.map +0 -1
package/src/keys/misc.js CHANGED
@@ -29,10 +29,16 @@ export const brackets = set({
29
29
  command: '[',
30
30
  });
31
31
 
32
+ // `%` must be escaped. MathQuill was not a real LaTeX parser: it defined
33
+ // `LatexCmds['%']` as a symbol that *serialised* to `\%`, so a bare `%` worked
34
+ // both as a label and as a command. MathLive parses LaTeX properly, where `%`
35
+ // begins a comment - it rendered nothing (a blank button) and, once inserted,
36
+ // silently swallowed the rest of the expression. `\%` is what MathQuill stored
37
+ // all along, so this changes the key only, not the saved latex.
32
38
  export const percentage = set({
33
39
  name: 'Percent',
34
- latex: '%',
35
- command: '%',
40
+ latex: '\\%',
41
+ command: '\\%',
36
42
  });
37
43
 
38
44
  export const approx = set({
@@ -114,6 +114,40 @@ export const toNativeCommands = (latex) => {
114
114
  out = replaceAbs(out);
115
115
  out = out.replace(/\\longdiv\{/g, '\\enclose{longdiv}{');
116
116
  out = out.replace(/\\overarc\{/g, '\\overparen{');
117
+ out = escapeBareComments(out);
118
+
119
+ return out;
120
+ };
121
+
122
+ /**
123
+ * Escape an unescaped `%`.
124
+ *
125
+ * In real LaTeX `%` starts a comment, so MathLive discards it *and everything
126
+ * after it*: `50%` renders as `50`, and `x%y` as just `x`. MathQuill had no
127
+ * such notion - it treated `%` as a symbol - so latex from anywhere outside the
128
+ * keypad may still carry a bare one.
129
+ *
130
+ * Only a `%` preceded by an even number of backslashes is bare: `\%` is already
131
+ * escaped, while the `%` in `\\%` (a line break, then a comment) is not.
132
+ *
133
+ * @param {string} latex
134
+ * @returns {string}
135
+ */
136
+ const escapeBareComments = (latex) => {
137
+ if (latex.indexOf('%') === -1) {
138
+ return latex;
139
+ }
140
+
141
+ let out = '';
142
+ // Consecutive backslashes immediately before the current character.
143
+ let backslashes = 0;
144
+
145
+ for (let i = 0; i < latex.length; i++) {
146
+ const ch = latex[i];
147
+
148
+ out += ch === '%' && backslashes % 2 === 0 ? '\\%' : ch;
149
+ backslashes = ch === '\\' ? backslashes + 1 : 0;
150
+ }
117
151
 
118
152
  return out;
119
153
  };
@@ -196,7 +196,81 @@ const configureLoaded = () => {
196
196
  * `:has(.ML__stretchy)` keeps the fix to stretchy accents; single-glyph accents
197
197
  * (`\hat{x}`, `\vec{x}`) still need their centring offset.
198
198
  */
199
- const SHADOW_CSS = '.ML__latex .ML__center:has(.ML__stretchy){margin-left:0 !important}';
199
+ /**
200
+ * Marker put on the host `<math-field>` while a `\placeholder{}` is selected,
201
+ * so the caret rules below apply to a placeholder and nothing else.
202
+ */
203
+ export const PLACEHOLDER_CARET_CLASS = 'pie-caret-in-placeholder';
204
+
205
+ /** What MathLive serialises a `#?` slot as. */
206
+ const PLACEHOLDER_LATEX = '\\placeholder{}';
207
+
208
+ /**
209
+ * A blinking caret inside a selected placeholder box.
210
+ *
211
+ * A keypad key such as `\frac` inserts `\frac{#?}{#?}`. MathLive turns each
212
+ * `#?` into a `\placeholder{}` atom and *selects* the first one. That atom is a
213
+ * leaf - the offsets around it are "before" and "after", there is no position
214
+ * inside it - so MathLive renders **no caret element at all** and shows only
215
+ * the selection highlight. The box therefore looked focused (blue) but dead,
216
+ * where MathQuill used to blink a cursor inside its empty box.
217
+ *
218
+ * So the caret is drawn here. `:not(:has(.ML__selected))` picks the innermost
219
+ * selected element (MathLive puts the class on a wrapper *and* the glyph span,
220
+ * which would otherwise give two carets), and the `ML__caret-blink` keyframes
221
+ * plus `--_caret-color` are MathLive's own, already in this shadow root.
222
+ *
223
+ * Typing still replaces the whole placeholder, which for an empty box is
224
+ * indistinguishable from inserting at the caret.
225
+ */
226
+ const PLACEHOLDER_CARET_CSS = [
227
+ `:host(.${PLACEHOLDER_CARET_CLASS}) .ML__selected:not(:has(.ML__selected)){position:relative}`,
228
+ `:host(.${PLACEHOLDER_CARET_CLASS}) .ML__selected:not(:has(.ML__selected))::after{`,
229
+ 'content:"";position:absolute;left:50%;top:12%;height:76%;',
230
+ 'border-right:2px solid var(--_caret-color, currentColor);',
231
+ 'animation:ML__caret-blink 1.05s step-end forwards infinite}',
232
+ ].join('');
233
+
234
+ const SHADOW_CSS = '.ML__latex .ML__center:has(.ML__stretchy){margin-left:0 !important}' + PLACEHOLDER_CARET_CSS;
235
+
236
+ /**
237
+ * Keep {@link PLACEHOLDER_CARET_CLASS} in step with the selection.
238
+ *
239
+ * Gating on the class is what keeps the caret specific to placeholders: an
240
+ * ordinary selection (`x+1`) must not sprout one. MathLive fires
241
+ * `selection-change` for every caret move, including the one that lands on the
242
+ * placeholder straight after an insert.
243
+ *
244
+ * @param {HTMLElement} mathField
245
+ * @returns {function|undefined} teardown, to call on unmount
246
+ */
247
+ export const trackPlaceholderCaret = (mathField) => {
248
+ if (!mathField || typeof mathField.addEventListener !== 'function') {
249
+ return undefined;
250
+ }
251
+
252
+ const update = () => {
253
+ let selected = '';
254
+
255
+ try {
256
+ // A collapsed selection already has a real MathLive caret.
257
+ if (!mathField.selectionIsCollapsed && mathField.selection) {
258
+ selected = mathField.getValue(mathField.selection, 'latex');
259
+ }
260
+ } catch (e) {
261
+ log('could not read the selection: %s', e && e.message);
262
+ }
263
+
264
+ if (mathField.classList) {
265
+ mathField.classList.toggle(PLACEHOLDER_CARET_CLASS, selected === PLACEHOLDER_LATEX);
266
+ }
267
+ };
268
+
269
+ mathField.addEventListener('selection-change', update);
270
+ update();
271
+
272
+ return () => mathField.removeEventListener('selection-change', update);
273
+ };
200
274
 
201
275
  /**
202
276
  * Apply {@link SHADOW_CSS} inside a mathfield's shadow root.
package/src/mf/input.jsx CHANGED
@@ -2,7 +2,13 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import debug from 'debug';
5
- import { loadMathLive, getMacros, MATH_MODE_SPACE, applyShadowStyles } from '../mathlive-instance';
5
+ import {
6
+ loadMathLive,
7
+ getMacros,
8
+ MATH_MODE_SPACE,
9
+ applyShadowStyles,
10
+ trackPlaceholderCaret,
11
+ } from '../mathlive-instance';
6
12
  import { toMathLive, fromMathLive, keyToAction, KEYSTROKES } from '../latex-bridge';
7
13
 
8
14
  const log = debug('pie-lib:math-input-mathlive:input');
@@ -79,6 +85,9 @@ export class Input extends React.Component {
79
85
  // stretchy-accent fix has to be injected there directly.
80
86
  applyShadowStyles(this.mathField);
81
87
 
88
+ // A selected `\placeholder{}` gets no MathLive caret; draw one.
89
+ this.untrackCaret = trackPlaceholderCaret(this.mathField);
90
+
82
91
  this.mathField.addEventListener('input', this.onInputEdit);
83
92
 
84
93
  // Belt and braces: drop anything a previous mount may have left behind.
@@ -117,6 +126,11 @@ export class Input extends React.Component {
117
126
  // append a field into a holder that is going away.
118
127
  this.mountGeneration = (this.mountGeneration || 0) + 1;
119
128
 
129
+ if (this.untrackCaret) {
130
+ this.untrackCaret();
131
+ this.untrackCaret = undefined;
132
+ }
133
+
120
134
  if (this.mathField) {
121
135
  this.mathField.removeEventListener('input', this.onInputEdit);
122
136
  this.mathField.remove();
package/src/mf/static.jsx CHANGED
@@ -2,7 +2,14 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import debug from 'debug';
5
- import { loadMathLive, getMacros, latexToMarkup, MATH_MODE_SPACE, applyShadowStyles } from '../mathlive-instance';
5
+ import {
6
+ loadMathLive,
7
+ getMacros,
8
+ latexToMarkup,
9
+ MATH_MODE_SPACE,
10
+ applyShadowStyles,
11
+ trackPlaceholderCaret,
12
+ } from '../mathlive-instance';
6
13
  import { toMathLive, fromMathLive, fieldIds } from '../latex-bridge';
7
14
  import { placeholderStyles } from './common-styles';
8
15
 
@@ -97,6 +104,10 @@ export default class Static extends React.Component {
97
104
  // stretchy-accent fix has to be injected there directly.
98
105
  applyShadowStyles(this.mathField);
99
106
 
107
+ // A selected `\placeholder{}` gets no MathLive caret; draw one. Answer
108
+ // blocks are typed into with the same keypad, so they grow placeholders too.
109
+ this.untrackCaret = trackPlaceholderCaret(this.mathField);
110
+
100
111
  this.mathField.addEventListener('input', this.onPromptInput);
101
112
  this.mathField.addEventListener('focusin', this.onPromptFocus);
102
113
  this.mathField.addEventListener('keydown', this.onKeyDown);
@@ -123,6 +134,11 @@ export default class Static extends React.Component {
123
134
  // Invalidate any mount still awaiting the MathLive load.
124
135
  this.mountGeneration = (this.mountGeneration || 0) + 1;
125
136
 
137
+ if (this.untrackCaret) {
138
+ this.untrackCaret();
139
+ this.untrackCaret = undefined;
140
+ }
141
+
126
142
  if (this.mathField) {
127
143
  this.mathField.removeEventListener('input', this.onPromptInput);
128
144
  this.mathField.removeEventListener('focusin', this.onPromptFocus);
@@ -254,6 +270,13 @@ export default class Static extends React.Component {
254
270
  promptHandle(id) {
255
271
  return {
256
272
  id,
273
+ // MathQuill's inner fields could be focused directly; keep that.
274
+ focus: () => {
275
+ if (this.mathField) {
276
+ this.mathField.focus();
277
+ this.focusPrompt(id);
278
+ }
279
+ },
257
280
  latex: (value) => {
258
281
  if (!this.mathField) {
259
282
  return '';
@@ -351,8 +374,77 @@ export default class Static extends React.Component {
351
374
  }
352
375
  };
353
376
 
377
+ /** The id of the prompt whose range contains `position`, if any. */
378
+ promptContaining(position) {
379
+ if (!this.mathField || typeof position !== 'number') {
380
+ return undefined;
381
+ }
382
+
383
+ return (this.mathField.getPrompts() || []).find((id) => {
384
+ const range = this.mathField.getPromptRange(id);
385
+
386
+ return range && position >= range[0] && position <= range[1];
387
+ });
388
+ }
389
+
390
+ /**
391
+ * Put a blinking caret inside an answer block.
392
+ *
393
+ * MathLive only makes the caret visible while the field element carries the
394
+ * `ML__focused` class, and it adds that class during a *render*, gated on
395
+ * `isSelectionEditable && hasFocus()`. In a read-only field only prompt
396
+ * interiors are editable, so the two things both have to be true:
397
+ *
398
+ * - the selection must sit inside a prompt, and
399
+ * - a render must run afterwards.
400
+ *
401
+ * Assigning `selection` satisfies the first but not the second - it updates
402
+ * the model without re-rendering, so the class stays off and the caret span
403
+ * remains `visibility: hidden`. Executing any command does trigger the
404
+ * render, and `scrollIntoView` is the one with no other effect: it keeps the
405
+ * caret on screen, which the preceding `focus()` already does anyway.
406
+ *
407
+ * @param {string} [id] prompt to focus; defaults to the first one
408
+ */
409
+ focusPrompt(id) {
410
+ if (!this.mathField) {
411
+ return;
412
+ }
413
+
414
+ const ids = this.mathField.getPrompts() || [];
415
+ const target = id && ids.indexOf(id) !== -1 ? id : ids[0];
416
+
417
+ if (!target) {
418
+ return;
419
+ }
420
+
421
+ const range = this.mathField.getPromptRange(target);
422
+
423
+ if (!range) {
424
+ return;
425
+ }
426
+
427
+ this.mathField.selection = { ranges: [[range[0], range[0]]] };
428
+
429
+ if (typeof this.mathField.executeCommand === 'function') {
430
+ this.mathField.executeCommand('scrollIntoView');
431
+ }
432
+ }
433
+
354
434
  focus() {
355
- this.mathField && this.mathField.focus();
435
+ if (!this.mathField) {
436
+ return;
437
+ }
438
+
439
+ this.mathField.focus();
440
+
441
+ // A bare focus() leaves the selection outside every prompt, which in a
442
+ // read-only field is not editable - the block looked focused but had no
443
+ // cursor. Clicking a prompt already works, because MathLive's own pointer
444
+ // handling moves the selection and re-renders.
445
+ if (!this.promptContaining(this.mathField.position)) {
446
+ this.focusPrompt();
447
+ }
356
448
  }
357
449
 
358
450
  blur() {
@@ -1,255 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- var _react = _interopRequireDefault(require("react"));
5
- var _react2 = require("@testing-library/react");
6
- var _mathInput = _interopRequireDefault(require("../math-input"));
7
- var _horizontalKeypad = _interopRequireDefault(require("../horizontal-keypad"));
8
- var _input = _interopRequireDefault(require("../mf/input"));
9
- /**
10
- * The pieces that sit between the keypad and the field: how a pressed key is
11
- * routed into the field, and the legacy `{value,type}` shape math-toolbar
12
- * consumes.
13
- */
14
- describe('MathInput.keypadPress routing', function () {
15
- var withInput = function withInput() {
16
- var mi = new _mathInput["default"]({});
17
- var calls = {
18
- write: [],
19
- keystroke: []
20
- };
21
- mi.input = {
22
- write: function write(v) {
23
- return calls.write.push(v);
24
- },
25
- keystroke: function keystroke(v) {
26
- return calls.keystroke.push(v);
27
- }
28
- };
29
- return {
30
- mi: mi,
31
- calls: calls
32
- };
33
- };
34
- it('routes an insert action to write()', function () {
35
- var _withInput = withInput(),
36
- mi = _withInput.mi,
37
- calls = _withInput.calls;
38
- mi.keypadPress({
39
- latex: '\\pi'
40
- });
41
- expect(calls.write).toEqual(['\\pi']);
42
- expect(calls.keystroke).toEqual([]);
43
- });
44
- it('expands an argument-taking command before writing', function () {
45
- var _withInput2 = withInput(),
46
- mi = _withInput2.mi,
47
- calls = _withInput2.calls;
48
- mi.keypadPress({
49
- command: '\\frac'
50
- });
51
- expect(calls.write).toEqual(['\\frac{#?}{#?}']);
52
- });
53
- it('routes a cursor keystroke to keystroke()', function () {
54
- var _withInput3 = withInput(),
55
- mi = _withInput3.mi,
56
- calls = _withInput3.calls;
57
- mi.keypadPress({
58
- keystroke: 'Left'
59
- });
60
- expect(calls.keystroke).toEqual(['moveToPreviousChar']);
61
- expect(calls.write).toEqual([]);
62
- });
63
- it('ignores keys with no action', function () {
64
- var _withInput4 = withInput(),
65
- mi = _withInput4.mi,
66
- calls = _withInput4.calls;
67
- mi.keypadPress({});
68
- mi.keypadPress({
69
- keystroke: 'Nonsense'
70
- });
71
- expect(calls.write).toEqual([]);
72
- expect(calls.keystroke).toEqual([]);
73
- });
74
- it('is a no-op before the field ref exists', function () {
75
- var mi = new _mathInput["default"]({});
76
- expect(function () {
77
- return mi.keypadPress({
78
- latex: '\\pi'
79
- });
80
- }).not.toThrow();
81
- });
82
- it('changeLatex only fires onChange when the value differs', function () {
83
- var onChange = jest.fn();
84
- var mi = new _mathInput["default"]({
85
- latex: 'x',
86
- onChange: onChange
87
- });
88
- mi.changeLatex('x');
89
- expect(onChange).not.toHaveBeenCalled();
90
- mi.changeLatex('y');
91
- expect(onChange).toHaveBeenCalledWith('y');
92
- });
93
- it('tracks focus state', function () {
94
- var mi = new _mathInput["default"]({});
95
- mi.setState = function (s) {
96
- return Object.assign(mi.state, s);
97
- };
98
- mi.inputFocus();
99
- expect(mi.state.focused).toBe(true);
100
- mi.inputBlur();
101
- expect(mi.state.focused).toBe(false);
102
- });
103
- });
104
- describe('HorizontalKeypad legacy {value,type} model', function () {
105
- var pressed = function pressed(key) {
106
- var onClick = jest.fn();
107
- var hk = new _horizontalKeypad["default"]({
108
- onClick: onClick
109
- });
110
- hk.keypadPress(key);
111
- return onClick.mock.calls[0][0];
112
- };
113
- it('maps a command to {value, type: command}', function () {
114
- expect(pressed({
115
- command: '\\frac'
116
- })).toEqual({
117
- value: '\\frac',
118
- type: 'command'
119
- });
120
- });
121
- it('maps a write to {value}', function () {
122
- expect(pressed({
123
- write: 'x'
124
- })).toEqual({
125
- value: 'x'
126
- });
127
- });
128
- it('maps a keystroke to {type: cursor}', function () {
129
- expect(pressed({
130
- keystroke: 'Left'
131
- })).toEqual({
132
- type: 'cursor',
133
- value: 'Left'
134
- });
135
- });
136
- it('renders without throwing', function () {
137
- expect(function () {
138
- return (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_horizontalKeypad["default"], {
139
- onClick: function onClick() {}
140
- }));
141
- }).not.toThrow();
142
- });
143
- });
144
- describe('Input: controlled-value sync and events', function () {
145
- var withField = function withField() {
146
- var input = new _input["default"]({});
147
- input.mathField = {
148
- value: '',
149
- getValue: function getValue() {
150
- return input.mathField.value;
151
- },
152
- insert: function insert(v) {
153
- input.mathField.value += v;
154
- },
155
- executeCommand: function executeCommand() {},
156
- focus: function focus() {},
157
- blur: function blur() {},
158
- addEventListener: function addEventListener() {},
159
- removeEventListener: function removeEventListener() {},
160
- remove: function remove() {}
161
- };
162
- return input;
163
- };
164
- it('emits stored-form latex on edit', function () {
165
- var onChange = jest.fn();
166
- var input = withField();
167
- input.props = {
168
- onChange: onChange
169
- };
170
- input.mathField.value = '\\longdiv{\\placeholder{}}';
171
- input.onInputEdit();
172
-
173
- // \placeholder is MathLive-internal and must not reach the model
174
- expect(onChange).toHaveBeenCalledWith('\\longdiv{}');
175
- expect(input.lastEmitted).toEqual('\\longdiv{}');
176
- });
177
- it('does not push a prop echo back into the field', function () {
178
- var input = withField();
179
- input.mathField.value = 'x';
180
- input.lastEmitted = 'x';
181
- input.props = {
182
- latex: 'x'
183
- };
184
- input.componentDidUpdate({
185
- latex: ''
186
- });
187
- expect(input.mathField.value).toEqual('x');
188
- });
189
- it('pushes a genuinely new latex prop into the field', function () {
190
- var input = withField();
191
- input.mathField.value = 'x';
192
- input.lastEmitted = 'x';
193
- input.props = {
194
- latex: 'y'
195
- };
196
- input.componentDidUpdate({
197
- latex: 'x'
198
- });
199
- expect(input.mathField.value).toEqual('y');
200
- });
201
- it('bridges stored latex on the way in', function () {
202
- var input = withField();
203
- input.props = {
204
- latex: '\\longdiv{5}'
205
- };
206
- input.componentDidUpdate({
207
- latex: ''
208
- });
209
-
210
- // must become native \enclose so the content stays editable
211
- expect(input.mathField.value).toEqual('\\enclose{longdiv}{5}');
212
- });
213
- it('toggles readOnly', function () {
214
- var input = withField();
215
- input.props = {
216
- readOnly: true
217
- };
218
- input.componentDidUpdate({
219
- readOnly: false
220
- });
221
- expect(input.mathField.readOnly).toBe(true);
222
- });
223
- it('clear() empties the field', function () {
224
- var input = withField();
225
- input.mathField.value = 'abc';
226
- expect(input.clear()).toEqual('');
227
- expect(input.mathField.value).toEqual('');
228
- });
229
- it('suppresses Enter so it cannot insert a line break', function () {
230
- var input = withField();
231
- var preventDefault = jest.fn();
232
- input.onKeyDown({
233
- key: 'Enter',
234
- preventDefault: preventDefault
235
- });
236
- expect(preventDefault).toHaveBeenCalled();
237
- preventDefault.mockClear();
238
- input.onKeyDown({
239
- key: 'a',
240
- preventDefault: preventDefault
241
- });
242
- expect(preventDefault).not.toHaveBeenCalled();
243
- });
244
- it('componentDidUpdate is safe before the field exists', function () {
245
- var input = new _input["default"]({
246
- latex: 'x'
247
- });
248
- expect(function () {
249
- return input.componentDidUpdate({
250
- latex: ''
251
- });
252
- }).not.toThrow();
253
- });
254
- });
255
- //# sourceMappingURL=composites.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"composites.test.js","names":["_react","_interopRequireDefault","require","_react2","_mathInput","_horizontalKeypad","_input","describe","withInput","mi","MathInput","calls","write","keystroke","input","v","push","it","_withInput","keypadPress","latex","expect","toEqual","_withInput2","command","_withInput3","_withInput4","not","toThrow","onChange","jest","fn","changeLatex","toHaveBeenCalled","toHaveBeenCalledWith","setState","s","Object","assign","state","inputFocus","focused","toBe","inputBlur","pressed","key","onClick","hk","HorizontalKeypad","mock","value","type","render","createElement","withField","Input","mathField","getValue","insert","executeCommand","focus","blur","addEventListener","removeEventListener","remove","props","onInputEdit","lastEmitted","componentDidUpdate","readOnly","clear","preventDefault","onKeyDown","mockClear"],"sources":["../../src/__tests__/composites.test.jsx"],"sourcesContent":["import React from 'react';\nimport { render } from '@testing-library/react';\n\nimport MathInput from '../math-input';\nimport HorizontalKeypad from '../horizontal-keypad';\nimport Input from '../mf/input';\n\n/**\n * The pieces that sit between the keypad and the field: how a pressed key is\n * routed into the field, and the legacy `{value,type}` shape math-toolbar\n * consumes.\n */\ndescribe('MathInput.keypadPress routing', () => {\n const withInput = () => {\n const mi = new MathInput({});\n const calls = { write: [], keystroke: [] };\n\n mi.input = {\n write: (v) => calls.write.push(v),\n keystroke: (v) => calls.keystroke.push(v),\n };\n\n return { mi, calls };\n };\n\n it('routes an insert action to write()', () => {\n const { mi, calls } = withInput();\n\n mi.keypadPress({ latex: '\\\\pi' });\n\n expect(calls.write).toEqual(['\\\\pi']);\n expect(calls.keystroke).toEqual([]);\n });\n\n it('expands an argument-taking command before writing', () => {\n const { mi, calls } = withInput();\n\n mi.keypadPress({ command: '\\\\frac' });\n\n expect(calls.write).toEqual(['\\\\frac{#?}{#?}']);\n });\n\n it('routes a cursor keystroke to keystroke()', () => {\n const { mi, calls } = withInput();\n\n mi.keypadPress({ keystroke: 'Left' });\n\n expect(calls.keystroke).toEqual(['moveToPreviousChar']);\n expect(calls.write).toEqual([]);\n });\n\n it('ignores keys with no action', () => {\n const { mi, calls } = withInput();\n\n mi.keypadPress({});\n mi.keypadPress({ keystroke: 'Nonsense' });\n\n expect(calls.write).toEqual([]);\n expect(calls.keystroke).toEqual([]);\n });\n\n it('is a no-op before the field ref exists', () => {\n const mi = new MathInput({});\n\n expect(() => mi.keypadPress({ latex: '\\\\pi' })).not.toThrow();\n });\n\n it('changeLatex only fires onChange when the value differs', () => {\n const onChange = jest.fn();\n const mi = new MathInput({ latex: 'x', onChange });\n\n mi.changeLatex('x');\n expect(onChange).not.toHaveBeenCalled();\n\n mi.changeLatex('y');\n expect(onChange).toHaveBeenCalledWith('y');\n });\n\n it('tracks focus state', () => {\n const mi = new MathInput({});\n\n mi.setState = (s) => Object.assign(mi.state, s);\n mi.inputFocus();\n expect(mi.state.focused).toBe(true);\n\n mi.inputBlur();\n expect(mi.state.focused).toBe(false);\n });\n});\n\ndescribe('HorizontalKeypad legacy {value,type} model', () => {\n const pressed = (key) => {\n const onClick = jest.fn();\n const hk = new HorizontalKeypad({ onClick });\n\n hk.keypadPress(key);\n\n return onClick.mock.calls[0][0];\n };\n\n it('maps a command to {value, type: command}', () => {\n expect(pressed({ command: '\\\\frac' })).toEqual({ value: '\\\\frac', type: 'command' });\n });\n\n it('maps a write to {value}', () => {\n expect(pressed({ write: 'x' })).toEqual({ value: 'x' });\n });\n\n it('maps a keystroke to {type: cursor}', () => {\n expect(pressed({ keystroke: 'Left' })).toEqual({ type: 'cursor', value: 'Left' });\n });\n\n it('renders without throwing', () => {\n expect(() => render(<HorizontalKeypad onClick={() => {}} />)).not.toThrow();\n });\n});\n\ndescribe('Input: controlled-value sync and events', () => {\n const withField = () => {\n const input = new Input({});\n\n input.mathField = {\n value: '',\n getValue: () => input.mathField.value,\n insert(v) {\n input.mathField.value += v;\n },\n executeCommand() {},\n focus() {},\n blur() {},\n addEventListener() {},\n removeEventListener() {},\n remove() {},\n };\n\n return input;\n };\n\n it('emits stored-form latex on edit', () => {\n const onChange = jest.fn();\n const input = withField();\n\n input.props = { onChange };\n input.mathField.value = '\\\\longdiv{\\\\placeholder{}}';\n input.onInputEdit();\n\n // \\placeholder is MathLive-internal and must not reach the model\n expect(onChange).toHaveBeenCalledWith('\\\\longdiv{}');\n expect(input.lastEmitted).toEqual('\\\\longdiv{}');\n });\n\n it('does not push a prop echo back into the field', () => {\n const input = withField();\n\n input.mathField.value = 'x';\n input.lastEmitted = 'x';\n input.props = { latex: 'x' };\n\n input.componentDidUpdate({ latex: '' });\n\n expect(input.mathField.value).toEqual('x');\n });\n\n it('pushes a genuinely new latex prop into the field', () => {\n const input = withField();\n\n input.mathField.value = 'x';\n input.lastEmitted = 'x';\n input.props = { latex: 'y' };\n\n input.componentDidUpdate({ latex: 'x' });\n\n expect(input.mathField.value).toEqual('y');\n });\n\n it('bridges stored latex on the way in', () => {\n const input = withField();\n\n input.props = { latex: '\\\\longdiv{5}' };\n input.componentDidUpdate({ latex: '' });\n\n // must become native \\enclose so the content stays editable\n expect(input.mathField.value).toEqual('\\\\enclose{longdiv}{5}');\n });\n\n it('toggles readOnly', () => {\n const input = withField();\n\n input.props = { readOnly: true };\n input.componentDidUpdate({ readOnly: false });\n\n expect(input.mathField.readOnly).toBe(true);\n });\n\n it('clear() empties the field', () => {\n const input = withField();\n\n input.mathField.value = 'abc';\n expect(input.clear()).toEqual('');\n expect(input.mathField.value).toEqual('');\n });\n\n it('suppresses Enter so it cannot insert a line break', () => {\n const input = withField();\n const preventDefault = jest.fn();\n\n input.onKeyDown({ key: 'Enter', preventDefault });\n expect(preventDefault).toHaveBeenCalled();\n\n preventDefault.mockClear();\n input.onKeyDown({ key: 'a', preventDefault });\n expect(preventDefault).not.toHaveBeenCalled();\n });\n\n it('componentDidUpdate is safe before the field exists', () => {\n const input = new Input({ latex: 'x' });\n\n expect(() => input.componentDidUpdate({ latex: '' })).not.toThrow();\n });\n});\n"],"mappings":"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","ignoreList":[]}