@khanacademy/math-input 4.3.0 → 5.0.0

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 (43) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/components/input/math-wrapper.d.ts +2 -2
  3. package/dist/components/input/math-wrapper.js.flow +2 -4
  4. package/dist/components/input/mathquill-helpers.d.ts +1 -1
  5. package/dist/components/input/mathquill-helpers.js.flow +2 -2
  6. package/dist/components/input/mathquill-types.d.ts +270 -10
  7. package/dist/components/input/mathquill-types.js.flow +312 -10
  8. package/dist/components/keypad/index.d.ts +11 -1
  9. package/dist/components/keypad/index.js.flow +14 -1
  10. package/dist/components/keypad/shared-keys.d.ts +4 -0
  11. package/dist/components/keypad/shared-keys.js.flow +4 -0
  12. package/dist/components/tabbar/tabbar.d.ts +1 -0
  13. package/dist/components/tabbar/tabbar.js.flow +1 -0
  14. package/dist/components/tabbar/types.d.ts +1 -1
  15. package/dist/components/tabbar/types.js.flow +6 -1
  16. package/dist/es/index.js +607 -409
  17. package/dist/es/index.js.map +1 -1
  18. package/dist/index.js +607 -409
  19. package/dist/index.js.map +1 -1
  20. package/package.json +2 -1
  21. package/src/components/input/__tests__/mathquill-helpers.test.ts +105 -0
  22. package/src/components/input/math-input.tsx +1 -1
  23. package/src/components/input/math-wrapper.ts +6 -10
  24. package/src/components/input/mathquill-helpers.ts +8 -1
  25. package/src/components/input/mathquill-types.ts +308 -40
  26. package/src/components/key-handlers/__tests__/handle-jump-out.test.ts +94 -0
  27. package/src/components/key-handlers/handle-jump-out.ts +3 -2
  28. package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +42 -39
  29. package/src/components/keypad/__tests__/keypad.test.tsx +42 -0
  30. package/src/components/keypad/button-assets.tsx +540 -316
  31. package/src/components/keypad/index.tsx +25 -2
  32. package/src/components/keypad/keypad-mathquill.stories.tsx +20 -1
  33. package/src/components/keypad/keypad-pages/extras-page.tsx +1 -1
  34. package/src/components/keypad/keypad-pages/numbers-page.tsx +25 -16
  35. package/src/components/keypad/keypad.stories.tsx +11 -0
  36. package/src/components/keypad/shared-keys.tsx +56 -8
  37. package/src/components/tabbar/__tests__/tabbar.test.tsx +54 -14
  38. package/src/components/tabbar/icons.tsx +48 -8
  39. package/src/components/tabbar/item.tsx +2 -0
  40. package/src/components/tabbar/tabbar.tsx +32 -12
  41. package/src/components/tabbar/types.ts +6 -1
  42. package/tsconfig-build.json +3 -1
  43. package/tsconfig-build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Khan Academy's new expression editor for the mobile web.",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "4.3.0",
6
+ "version": "5.0.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -19,6 +19,7 @@
19
19
  "source": "src/index.ts",
20
20
  "scripts": {},
21
21
  "dependencies": {
22
+ "@khanacademy/perseus-core": "0.0.1",
22
23
  "mathquill": "git+https://git@github.com/Khan/mathquill.git#a9ae54e057c5c1acc8244a5627acbff29901d992",
23
24
  "performance-now": "^0.2.0"
24
25
  },
@@ -0,0 +1,105 @@
1
+ import {CursorContext} from "../cursor-contexts";
2
+ import {getCursorContext} from "../mathquill-helpers";
3
+ import {createMathField} from "../mathquill-instance";
4
+
5
+ describe("MathQuill Helpers", () => {
6
+ describe("getCursorContext", () => {
7
+ it("returns NONE for empty MathField", () => {
8
+ // Arrange
9
+ const mount = document.createElement("div");
10
+ const mathField = createMathField(mount);
11
+
12
+ // Act
13
+ const context = getCursorContext(mathField);
14
+
15
+ // Assert
16
+ expect(context).toBe(CursorContext.NONE);
17
+ });
18
+
19
+ it("returns BEFORE_FRACTION when before a fraction", () => {
20
+ // Arrange
21
+ const mount = document.createElement("div");
22
+ const mathField = createMathField(mount);
23
+ // create fraction and move cursor before it
24
+ mathField.cmd("\\frac");
25
+ mathField.keystroke("Left");
26
+
27
+ // Act
28
+ const context = getCursorContext(mathField);
29
+
30
+ // Assert
31
+ expect(context).toBe(CursorContext.BEFORE_FRACTION);
32
+ });
33
+
34
+ it("returns IN_PARENS when in parenthesis", () => {
35
+ // Arrange
36
+ const mount = document.createElement("div");
37
+ const mathField = createMathField(mount);
38
+ // MQ puts you inside parens when you start a paren
39
+ mathField.typedText("(");
40
+
41
+ // Act
42
+ const context = getCursorContext(mathField);
43
+
44
+ // Assert
45
+ expect(context).toBe(CursorContext.IN_PARENS);
46
+ });
47
+
48
+ it("returns IN_NUMERATOR when in numerator", () => {
49
+ // Arrange
50
+ const mount = document.createElement("div");
51
+ const mathField = createMathField(mount);
52
+ // MQ puts you in the numerator when making a fraction
53
+ mathField.cmd("\\frac");
54
+
55
+ // Act
56
+ const context = getCursorContext(mathField);
57
+
58
+ // Assert
59
+ expect(context).toBe(CursorContext.IN_NUMERATOR);
60
+ });
61
+
62
+ it("returns IN_DENOMINATOR when in denominator", () => {
63
+ // Arrange
64
+ const mount = document.createElement("div");
65
+ const mathField = createMathField(mount);
66
+ // create fraction and move cursor to denominator
67
+ mathField.cmd("\\frac");
68
+ mathField.keystroke("Down");
69
+
70
+ // Act
71
+ const context = getCursorContext(mathField);
72
+
73
+ // Assert
74
+ expect(context).toBe(CursorContext.IN_DENOMINATOR);
75
+ });
76
+
77
+ it("returns IN_SUB_SCRIPT when in subscript", () => {
78
+ // Arrange
79
+ const mount = document.createElement("div");
80
+ const mathField = createMathField(mount);
81
+ // "_" triggers a subscript
82
+ mathField.typedText("6_");
83
+
84
+ // Act
85
+ const context = getCursorContext(mathField);
86
+
87
+ // Assert
88
+ expect(context).toBe(CursorContext.IN_SUB_SCRIPT);
89
+ });
90
+
91
+ it("returns IN_SUPER_SCRIPT when in superscript", () => {
92
+ // Arrange
93
+ const mount = document.createElement("div");
94
+ const mathField = createMathField(mount);
95
+ // "^" triggers a superscript
96
+ mathField.typedText("6^");
97
+
98
+ // Act
99
+ const context = getCursorContext(mathField);
100
+
101
+ // Assert
102
+ expect(context).toBe(CursorContext.IN_SUPER_SCRIPT);
103
+ });
104
+ });
105
+ });
@@ -595,7 +595,7 @@ class MathInput extends React.Component<Props, State> {
595
595
  // In that event, we need to update the cursor context ourselves.
596
596
  this.props.keypadElement &&
597
597
  this.props.keypadElement.setCursor({
598
- context: this.mathField.contextForCursor(cursor),
598
+ context: this.mathField.contextForCursor(),
599
599
  });
600
600
  };
601
601
 
@@ -23,15 +23,11 @@ import keyTranslator from "../key-handlers/key-translator";
23
23
 
24
24
  import {
25
25
  getCursor,
26
- contextForCursor,
26
+ getCursorContext,
27
27
  maybeFindCommand,
28
28
  } from "./mathquill-helpers";
29
29
  import {createMathField, mathQuillInstance} from "./mathquill-instance";
30
- import {
31
- MathFieldInterface,
32
- MathFieldCursor,
33
- MathFieldUpdaterCallback,
34
- } from "./mathquill-types";
30
+ import {MathFieldInterface, MathFieldUpdaterCallback} from "./mathquill-types";
35
31
 
36
32
  const mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback> = {
37
33
  ...keyTranslator,
@@ -112,7 +108,7 @@ class MathWrapper {
112
108
  // on the MathField, as that handler isn't triggered on navigation
113
109
  // events.
114
110
  return {
115
- context: this.contextForCursor(cursor),
111
+ context: this.contextForCursor(),
116
112
  };
117
113
  }
118
114
 
@@ -157,7 +153,7 @@ class MathWrapper {
157
153
 
158
154
  if (this.callbacks.onCursorMove) {
159
155
  this.callbacks.onCursorMove({
160
- context: this.contextForCursor(cursor),
156
+ context: this.contextForCursor(),
161
157
  });
162
158
  }
163
159
  }
@@ -171,8 +167,8 @@ class MathWrapper {
171
167
 
172
168
  // note(Matthew): extracted this logic to keep this file focused,
173
169
  // but it's part of the public MathWrapper API
174
- contextForCursor(cursor: MathFieldCursor) {
175
- return contextForCursor(cursor);
170
+ contextForCursor() {
171
+ return getCursorContext(this.mathField);
176
172
  }
177
173
 
178
174
  getSelection() {
@@ -238,8 +238,15 @@ export function maybeFindCommandBeforeParens(leftParenNode) {
238
238
  return maybeFindCommand(leftParenNode[mathQuillInstance.L]);
239
239
  }
240
240
 
241
- export function contextForCursor(cursor: MathFieldCursor): CursorContext {
241
+ export function getCursorContext(
242
+ mathField?: MathFieldInterface,
243
+ ): CursorContext {
244
+ if (!mathField) {
245
+ return CursorContext.NONE;
246
+ }
247
+
242
248
  // First, try to find any fraction to the right, unimpeded.
249
+ const cursor = getCursor(mathField);
243
250
  let visitor = cursor;
244
251
  while (visitor[mathQuillInstance.R] !== MathFieldActionType.MQ_END) {
245
252
  if (isFraction(visitor[mathQuillInstance.R])) {
@@ -3,73 +3,339 @@ import Key from "../../data/keys";
3
3
  export interface MathQuillInterface {
4
4
  L: "L";
5
5
  R: "R";
6
+
7
+ /**
8
+ * Creates an editable MathQuill initialized with the contents of the HTML
9
+ * element and returns a MathField object.
10
+ *
11
+ * If the given element is already an editable math field, this will return
12
+ * a new editable MathField object with the same `.id`. If the element is a
13
+ * different type of MathQuill, this will return `null`.
14
+ */
6
15
  MathField: (
7
16
  mount: HTMLDivElement | HTMLSpanElement,
8
17
  config: MathFieldConfig,
9
18
  ) => MathFieldInterface;
10
19
  }
11
20
 
12
- type MathQuillDir = "L" | "R";
21
+ type MathQuillDirection = MathQuillInterface["L"] | MathQuillInterface["R"];
13
22
 
14
23
  export type MathFieldConfig = {
24
+ /**
25
+ * If spaceBehavesLikeTab is true the keystrokes {Shift-,}Spacebar will
26
+ * behave like {Shift-,}Tab escaping from the current block (as opposed
27
+ * to the default behavior of inserting a Space character).
28
+ */
15
29
  spaceBehavesLikeTab?: boolean;
16
- leftRightIntoCmdGoes?: string;
30
+
31
+ /**
32
+ * This allows you to change the way the left and right keys move the
33
+ * cursor when there are items of different height, like fractions.
34
+ *
35
+ * By default, the Left and Right keys move the cursor through all
36
+ * possible cursor positions in a particular order: right into a
37
+ * fraction puts the cursor at the left end of the numerator, right out
38
+ * of the numerator puts the cursor at the left end of the denominator,
39
+ * and right out of the denominator puts the cursor to the right of the
40
+ * fraction. Symmetrically, left into a fraction puts the cursor at the
41
+ * right end of the denominator, etc.
42
+ *
43
+ * If instead you want right to always visually go right, and left to always go
44
+ * visually left, you can set leftRightIntoCmdGoes to 'up' or 'down' so that
45
+ * left and right go up or down (respectively) into commands. For example, 'up'
46
+ * means that left into a fraction goes up into the numerator and right out of
47
+ * the numerator skips the denominator and puts the cursor to the right of the
48
+ * fraction. This behavior can be seen in the Desmos calculator. If this
49
+ * property is set to 'down' instead, the numerator is harder to navigate to,
50
+ * like in the Mac OS X built-in app Grapher.
51
+ */
52
+ leftRightIntoCmdGoes?: "up" | "down";
53
+
54
+ /**
55
+ * If restrictMismatchedBrackets is true then you can type [a,b) and
56
+ * (a,b], but if you try typing [x} or \langle x|, you'll get [{x}] or
57
+ * \langle|x|\rangle instead. This lets you type (|x|+1) normally;
58
+ * otherwise, you'd get \left( \right| x \left| + 1 \right).
59
+ */
17
60
  restrictMismatchedBrackets?: boolean;
61
+
62
+ /**
63
+ * If sumStartsWithNEquals is true then when you type \sum, \prod, or
64
+ * \coprod, the lower limit starts out with n=, e.g. you get the LaTeX
65
+ * \sum_{n=}^{ }, rather than empty by default.
66
+ */
18
67
  sumStartsWithNEquals?: boolean;
68
+
69
+ /**
70
+ * supSubsRequireOperand disables typing of superscripts and subscripts
71
+ * when there's nothing to the left of the cursor to be exponentiated
72
+ * or subscripted. Prevents the especially confusing typo x^^2, which
73
+ * looks much like x^2.
74
+ */
19
75
  supSubsRequireOperand?: boolean;
76
+
77
+ /**
78
+ * charsThatBreakOutOfSupSub takes a string of the chars that when
79
+ * typed, "break out" of superscripts and subscripts.
80
+ *
81
+ * Normally, to get out of a superscript or subscript, a user has to
82
+ * navigate out of it with the directional keys, a mouse click, tab, or
83
+ * Space if spaceBehavesLikeTab is true. For example, typing x^2n+y
84
+ * normally results in the LaTeX x^{2n+y}. If you wanted to get the
85
+ * LaTeX x^{2n}+y, the user would have to manually move the cursor out
86
+ * of the exponent.
87
+ *
88
+ * If this option was set to '+-', + and - would "break out" of the
89
+ * exponent. This doesn't apply to the first character in a superscript
90
+ * or subscript, so typing x^-6 still results in x^{-6}. The downside
91
+ * to setting this option is that in order to type x^{n+m}, a
92
+ * workaround like typing x^(n+m and then deleting the ( is required.
93
+ */
20
94
  charsThatBreakOutOfSupSub?: string;
95
+
96
+ /**
97
+ * :shrug: Undocumented except for being shown in an example for
98
+ * configuration options.
99
+ */
21
100
  autoSubscriptNumerals?: boolean;
101
+
102
+ /**
103
+ * autoCommands defines the set of commands automatically rendered by
104
+ * just typing the letters without typing a backslash first.
105
+ *
106
+ * This takes a string formatted as a space-delimited list of LaTeX
107
+ * commands. Each LaTeX command must be at least letters only with a
108
+ * minimum length of 2 characters.
109
+ *
110
+ * For example, with autoCommands set to 'pi theta', the word 'pi'
111
+ * automatically converts to the pi symbol and the word 'theta'
112
+ * automatically converts to the theta symbol.
113
+ */
22
114
  autoCommands?: string;
115
+
116
+ /**
117
+ * autoOperatorNames overrides the set of operator names that
118
+ * automatically become non-italicized when typing the letters without
119
+ * typing a backslash first, like sin, log, etc.
120
+ *
121
+ * This defaults to the LaTeX built-in operator names (Section 3.17 of
122
+ * the Short Math Guide) with additional trig operators like sech,
123
+ * arcsec, arsinh, etc. If you want some of these italicized after
124
+ * setting this property, you will have to add them to the list.
125
+ *
126
+ * Just like autoCommands above, this takes a string formatted as a
127
+ * space-delimited list of LaTeX commands.
128
+ */
23
129
  autoOperatorNames?: string;
130
+
131
+ /**
132
+ * maxDepth specifies the maximum number of nested MathBlocks. When
133
+ * maxDepth is set to 1, the user can type simple math symbols directly
134
+ * into the editor but not into nested MathBlocks, e.g. the numerator
135
+ * and denominator of a fraction.
136
+ *
137
+ * Nested content in latex rendered during initialization or pasted
138
+ * into mathquill is truncated to avoid violating maxDepth. When
139
+ * maxDepth is not set, no depth limit is applied by default.
140
+ */
24
141
  maxDepth?: number;
25
- substituteTextarea?: () => HTMLElement;
142
+
143
+ /**
144
+ * substituteTextarea is a function that creates a focusable DOM
145
+ * element that is called when setting up a math field. Overwriting
146
+ * this may be useful for hacks like suppressing built-in virtual
147
+ * keyboards. It defaults to <textarea autocorrect=off .../>.
148
+ *
149
+ * For example, Desmos substitutes <span tabindex=0></span> on iOS to
150
+ * suppress the built-in virtual keyboard in favor of a custom math
151
+ * keypad that calls the MathQuill API. Unfortunately there's no
152
+ * universal check for a virtual keyboard or way to detect a
153
+ * touchscreen, and even if you could, a touchscreen ≠ virtual keyboard
154
+ * (Windows 8 and ChromeOS devices have both physical keyboards and
155
+ * touchscreens and iOS and Android devices can have Bluetooth
156
+ * keyboards). Desmos currently sniffs the user agent for iOS, so
157
+ * Bluetooth keyboards just don't work in Desmos on iOS. The tradeoffs
158
+ * are up to you.
159
+ */
160
+ substituteTextarea?: () => HTMLElement | undefined;
161
+
162
+ /**
163
+ * Handlers are called after a specified event. They are called
164
+ * directly on the handlers object passed in, preserving the this value
165
+ */
26
166
  handlers?: {
27
167
  edit?: (mathField: MathFieldInterface) => void;
168
+ enter?: (mathField: MathFieldInterface) => void;
169
+
170
+ moveOutOf?: (mathField: MathFieldInterface) => void;
28
171
  upOutOf?: (mathField: MathFieldInterface) => void;
29
- moveOutOf?: (dir: MathQuillDir, mathField: MathFieldInterface) => void;
172
+ downOutOf?: (mathField: MathFieldInterface) => void;
173
+
174
+ deleteOutOf?: (
175
+ direction: MathQuillDirection,
176
+ mathField: MathFieldInterface,
177
+ ) => void;
178
+ selectOutOf?: (
179
+ direction: MathQuillDirection,
180
+ mathField: MathFieldInterface,
181
+ ) => void;
30
182
  };
31
183
  };
32
184
 
185
+ /**
186
+ * Editable math fields have all of the above methods in addition to
187
+ * the ones listed here.
188
+ * https://docs.mathquill.com/en/latest/Api_Methods/
189
+ */
33
190
  export interface MathFieldInterface {
34
- // Puts the focus on the editable field.
35
- // http://docs.mathquill.com/en/latest/Api_Methods/#focus
191
+ /**
192
+ * Any element that has been turned into a MathQuill instance can be
193
+ * reverted.
194
+ */
195
+ revert: () => void;
196
+
197
+ /**
198
+ * MathQuill uses computed dimensions, so if they change (because an
199
+ * element was mathquill-ified before it was in the visible HTML DOM, or
200
+ * the font size changed), then you'll need to tell MathQuill to recomput
201
+ */
202
+ reflow: () => void;
203
+
204
+ /** Returns the root HTML element. */
205
+ el: () => HTMLElement;
206
+
207
+ /**
208
+ * Puts the focus on the editable field.
209
+ * http://docs.mathquill.com/en/latest/Api_Methods/#focus
210
+ */
36
211
  focus: () => MathFieldInterface;
37
- // Removes focus from the editable field.
38
- // http://docs.mathquill.com/en/latest/Api_Methods/#blur
212
+
213
+ /**
214
+ * Removes focus from the editable field.
215
+ * http://docs.mathquill.com/en/latest/Api_Methods/#blur
216
+ */
39
217
  blur: () => MathFieldInterface;
40
- // Write LaTeX
41
- // https://docs.mathquill.com/en/latest/Api_Methods/#writelatex_string
42
- write: (input: string) => MathFieldInterface;
43
- // Enter a LaTeX command
44
- // https://docs.mathquill.com/en/latest/Api_Methods/#cmdlatex_string
45
- cmd: (input: string) => MathFieldInterface;
46
- // Simulates keystrokes given a string like "Ctrl-Home Del"
47
- // https://docs.mathquill.com/en/latest/Api_Methods/#keystrokekeys
48
- keystroke: (input: string) => MathFieldInterface;
49
- // Simulates typing text, one character at a time
50
- // https://docs.mathquill.com/en/latest/Api_Methods/#typedtexttext
51
- typedText: (input: string) => MathFieldInterface;
52
- // () => {}: Gets the contents as LaTeX
53
- // (string) => {}: Sets the contents as LaTeX
54
- // https://docs.mathquill.com/en/latest/Api_Methods/#latex
55
- latex: (input?: string) => string;
56
- // Moves the cursor to the end of the mathfield in the direction specified
57
- // https://docs.mathquill.com/en/latest/Api_Methods/#movetodirenddirection
58
- moveToDirEnd: (direction: "L" | "R") => void;
59
- // Selects the contents
60
- // https://docs.mathquill.com/en/latest/Api_Methods/#select
218
+
219
+ /**
220
+ * Write the given LaTeX at the current cursor position. If the
221
+ * cursor does not have focus, writes to last position the cursor
222
+ * occupied in the editable field.
223
+ *
224
+ * ```
225
+ * // writes ' - 1' to mathField at the cursor position
226
+ * mathField.write(' - 1');
227
+ * ```
228
+ *
229
+ * https://docs.mathquill.com/en/latest/Api_Methods/#writelatex_string
230
+ */
231
+ write: (latex_string: string) => MathFieldInterface;
232
+
233
+ /**
234
+ * Enter a LaTeX command at the current cursor position or with the
235
+ * current selection. If the cursor does not have focus, it writes it
236
+ * to last position the cursor occupied in the editable field.
237
+ *
238
+ * ```
239
+ * // writes a square root command at the cursor position
240
+ * mathField.cmd('\\sqrt');
241
+ * ```
242
+ *
243
+ * https://docs.mathquill.com/en/latest/Api_Methods/#cmdlatex_string
244
+ */
245
+ cmd: (latex_string: string) => MathFieldInterface;
246
+
247
+ /**
248
+ * Selects the contents (just like on textareas and on inputs).
249
+ *
250
+ * https://docs.mathquill.com/en/latest/Api_Methods/#select
251
+ */
61
252
  select: () => void;
62
- // Clears the selection
63
- // https://docs.mathquill.com/en/latest/Api_Methods/#clearselection
253
+
254
+ /**
255
+ * Clears the selection.
256
+ *
257
+ * https://docs.mathquill.com/en/latest/Api_Methods/#clearselection
258
+ */
64
259
  clearSelection: () => void;
65
- // Custom helper in our MathQuill fork
66
- // check to see if cursor is on right end
260
+
261
+ /**
262
+ * Move the cursor to the left end of the editable field. horthand
263
+ * for .moveToDirEnd(L)
264
+ */
265
+ moveToLeftEnd: () => void;
266
+
267
+ /**
268
+ * Move the cursor to the right end of the editable field. horthand
269
+ * for .moveToDirEnd(R)
270
+ */
271
+ moveToRightEnd: () => void;
272
+
273
+ /**
274
+ * Moves the cursor to the end of the mathfield in the direction
275
+ * specified. The direction can be one of MQ.L or MQ.R. These are
276
+ * constants, where MQ.L === -MQ.R and vice versa. This function
277
+ * may be easier to use than moveToLeftEnd or moveToRightEnd if
278
+ * used in the moveOutOf handler.
279
+ *
280
+ * https://docs.mathquill.com/en/latest/Api_Methods/#movetodirenddirection
281
+ */
282
+ moveToDirEnd: (direction: MathQuillDirection) => void;
283
+
284
+ /**
285
+ * Simulates keystrokes given a string like "Ctrl-Home Del", a
286
+ * whitespace-delimited list of key inputs with optional prefixes.
287
+ *
288
+ * ```
289
+ * mathField.keystroke('Shift-Left'); // Selects character before the current cursor position
290
+ * ```
291
+ *
292
+ * https://docs.mathquill.com/en/latest/Api_Methods/#keystrokekeys
293
+ */
294
+ keystroke: (keys: string) => MathFieldInterface;
295
+
296
+ /**
297
+ * Simulates typing text, one character at a time from where the
298
+ * cursor currently is. This is supposed to be identical to what
299
+ * would happen if a user were typing the text in.
300
+ *
301
+ * Types part of the demo from mathquill.com without delays between keystrokes
302
+ * ```
303
+ * mathField.typedText('x=-b\\pm \\sqrt b^2 -4ac');
304
+ * ```
305
+ *
306
+ * https://docs.mathquill.com/en/latest/Api_Methods/#typedtexttext
307
+ */
308
+ typedText: (text: string) => MathFieldInterface;
309
+
310
+ /**
311
+ * When called withot any pramaters, gets the contents as LaTeX
312
+ * When passed a string, sets the contents as LaTeX
313
+ *
314
+ * https://docs.mathquill.com/en/latest/Api_Methods/#latex
315
+ */
316
+ latex: (input?: string) => string;
317
+
318
+ /** Changes the configuration of just this math field. */
319
+ config: (new_config: MathFieldConfig) => void;
320
+
321
+ /**
322
+ * KA Custom helper in our MathQuill fork check to see if cursor is on
323
+ * right end
324
+ */
67
325
  cursorAtRightEnd: () => boolean;
68
- // Custom helper in our MathQuill fork
69
- // check to see if cursor is on left end
326
+
327
+ /**
328
+ * KA Custom helper in our MathQuill fork check to see if cursor is on left
329
+ * end
330
+ */
70
331
  cursorAtLeftEnd: () => boolean;
71
- // This isn't part of the MathQuill public API
72
- // I don't know what it is and it feels wrong using it
332
+
333
+ /**
334
+ * This isn't part of the MathQuill public API
335
+ * I don't know what it is and it feels wrong using it
336
+ *
337
+ * @deprecated This is internal and shouldn't be used.
338
+ */
73
339
  __controller: any;
74
340
  }
75
341
 
@@ -80,9 +346,11 @@ export enum MathFieldActionType {
80
346
  MQ_END = 0,
81
347
  }
82
348
 
83
- // The MathQuill MathField Cursor
84
- // it's not part of the public API for MathQuill,
85
- // we reach into the internals to get it
349
+ /**
350
+ * The MathQuill MathField Cursor
351
+ * it's not part of the public API for MathQuill,
352
+ * we reach into the internals to get it
353
+ */
86
354
  export type MathFieldCursor = any;
87
355
 
88
356
  export type MathFieldUpdaterCallback = (
@@ -0,0 +1,94 @@
1
+ import {createMathField} from "../../input/mathquill-instance";
2
+ import handleJumpOut from "../handle-jump-out";
3
+
4
+ describe("handleJumpOut", () => {
5
+ it("jumps out of parenthesis", () => {
6
+ // Arrange
7
+ const mount = document.createElement("div");
8
+ const mathField = createMathField(mount);
9
+
10
+ // Act
11
+ mathField.typedText("(4");
12
+ handleJumpOut(mathField, "JUMP_OUT_PARENTHESES");
13
+ mathField.typedText("6");
14
+
15
+ // Assert
16
+ expect(mathField.latex()).toBe("\\left(4\\right)6");
17
+ });
18
+
19
+ it("jumps into numerator", () => {
20
+ // Arrange
21
+ const mount = document.createElement("div");
22
+ const mathField = createMathField(mount);
23
+
24
+ // Act
25
+ mathField.typedText("4");
26
+ mathField.cmd("frac");
27
+ mathField.keystroke("Left");
28
+ handleJumpOut(mathField, "JUMP_INTO_NUMERATOR");
29
+ mathField.typedText("6");
30
+
31
+ // Assert
32
+ expect(mathField.latex()).toBe("4\\frac{6}{ }");
33
+ });
34
+
35
+ it("jumps out of numerator", () => {
36
+ // Arrange
37
+ const mount = document.createElement("div");
38
+ const mathField = createMathField(mount);
39
+
40
+ // Act
41
+ mathField.cmd("frac");
42
+ mathField.typedText("4");
43
+ handleJumpOut(mathField, "JUMP_OUT_NUMERATOR");
44
+ mathField.typedText("6");
45
+
46
+ // Assert
47
+ expect(mathField.latex()).toBe("\\frac{4}{6}");
48
+ });
49
+
50
+ it("jumps out of denominator", () => {
51
+ // Arrange
52
+ const mount = document.createElement("div");
53
+ const mathField = createMathField(mount);
54
+
55
+ // Act
56
+ mathField.cmd("frac");
57
+ mathField.typedText("4");
58
+ handleJumpOut(mathField, "JUMP_OUT_NUMERATOR");
59
+ mathField.typedText("6");
60
+ handleJumpOut(mathField, "JUMP_OUT_DENOMINATOR");
61
+ mathField.typedText("2");
62
+
63
+ // Assert
64
+ expect(mathField.latex()).toBe("\\frac{4}{6}2");
65
+ });
66
+
67
+ it("jumps out of superscript", () => {
68
+ // Arrange
69
+ const mount = document.createElement("div");
70
+ const mathField = createMathField(mount);
71
+
72
+ // Act
73
+ mathField.typedText("4^22");
74
+ handleJumpOut(mathField, "JUMP_OUT_EXPONENT");
75
+ mathField.typedText("6");
76
+
77
+ // Assert
78
+ expect(mathField.latex()).toBe("4^{22}6");
79
+ });
80
+
81
+ it("jumps out of subscript", () => {
82
+ // Arrange
83
+ const mount = document.createElement("div");
84
+ const mathField = createMathField(mount);
85
+
86
+ // Act
87
+ mathField.typedText("4_22");
88
+ handleJumpOut(mathField, "JUMP_OUT_BASE");
89
+ mathField.typedText("6");
90
+
91
+ // Assert
92
+ expect(mathField.latex()).toBe("4_{22}6");
93
+ });
94
+ });