@khanacademy/math-input 4.1.1 → 4.3.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.
- package/CHANGELOG.md +17 -0
- package/dist/components/input/math-wrapper.d.ts +1 -1
- package/dist/components/input/math-wrapper.js.flow +1 -1
- package/dist/components/input/mathquill-instance.d.ts +14 -3
- package/dist/components/input/mathquill-instance.js.flow +18 -3
- package/dist/components/input/mathquill-types.d.ts +30 -6
- package/dist/components/input/mathquill-types.js.flow +33 -7
- package/dist/components/key-handlers/handle-arrow.d.ts +3 -0
- package/dist/components/{input/key-handlers → key-handlers}/handle-arrow.js.flow +2 -2
- package/dist/components/{input/key-handlers → key-handlers}/handle-backspace.d.ts +1 -1
- package/dist/components/{input/key-handlers → key-handlers}/handle-backspace.js.flow +1 -1
- package/dist/components/key-handlers/handle-exponent.d.ts +3 -0
- package/dist/components/{input/key-handlers → key-handlers}/handle-exponent.js.flow +2 -2
- package/dist/components/{input/key-handlers → key-handlers}/handle-jump-out.d.ts +2 -2
- package/dist/components/{input/key-handlers → key-handlers}/handle-jump-out.js.flow +2 -2
- package/dist/components/key-handlers/key-translator.d.ts +4 -0
- package/dist/components/{key-translator.js.flow → key-handlers/key-translator.js.flow} +3 -3
- package/dist/components/keypad/index.d.ts +8 -16
- package/dist/components/keypad/index.js.flow +10 -22
- package/dist/components/keypad/keypad-button.d.ts +12 -0
- package/dist/components/keypad/keypad-button.js.flow +18 -0
- package/dist/components/keypad/keypad-pages/extras-page.d.ts +9 -0
- package/dist/components/keypad/{operators-page/basic-relations-buttons.js.flow → keypad-pages/extras-page.js.flow} +3 -3
- package/dist/components/keypad/keypad-pages/geometry-page.d.ts +7 -0
- package/dist/components/keypad/{operators-page/logarithms-buttons.js.flow → keypad-pages/geometry-page.js.flow} +1 -3
- package/dist/components/keypad/keypad-pages/numbers-page.d.ts +7 -0
- package/dist/components/keypad/{operators-page/pre-algebra-buttons.js.flow → keypad-pages/numbers-page.js.flow} +1 -3
- package/dist/components/keypad/keypad-pages/operators-page.d.ts +11 -0
- package/dist/components/keypad/{geometry-page/index.js.flow → keypad-pages/operators-page.js.flow} +5 -4
- package/dist/components/keypad/shared-keys.d.ts +9 -0
- package/dist/components/keypad/{numbers-page/types.js.flow → shared-keys.js.flow} +4 -1
- package/dist/es/index.js +763 -878
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +740 -878
- package/dist/index.js.flow +6 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/src/components/input/__tests__/test-math-wrapper.ts +2 -5
- package/src/components/input/math-input.tsx +10 -14
- package/src/components/input/math-wrapper.ts +23 -49
- package/src/components/input/mathquill-helpers.ts +11 -11
- package/src/components/input/mathquill-instance.ts +57 -2
- package/src/components/input/mathquill-types.ts +43 -7
- package/src/components/{input/key-handlers → key-handlers}/handle-arrow.ts +6 -6
- package/src/components/{input/key-handlers → key-handlers}/handle-backspace.ts +19 -17
- package/src/components/{input/key-handlers → key-handlers}/handle-exponent.ts +8 -5
- package/src/components/{input/key-handlers → key-handlers}/handle-jump-out.ts +15 -10
- package/src/components/{key-translator.ts → key-handlers/key-translator.ts} +43 -28
- package/src/components/keypad/__tests__/{KeypadButton.test.tsx → keypad-button.test.tsx} +7 -5
- package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +231 -0
- package/src/components/keypad/__tests__/keypad-v2.cypress.ts +19 -6
- package/src/components/keypad/index.tsx +73 -49
- package/src/components/keypad/{button.stories.tsx → keypad-button.stories.tsx} +2 -1
- package/src/components/keypad/keypad-button.tsx +128 -0
- package/src/components/keypad/keypad-mathquill.stories.tsx +24 -26
- package/src/components/keypad/keypad-pages/extras-page.tsx +34 -0
- package/src/components/keypad/keypad-pages/geometry-page.tsx +33 -0
- package/src/components/keypad/keypad-pages/numbers-page.tsx +84 -0
- package/src/components/keypad/keypad-pages/operators-page.tsx +116 -0
- package/src/components/keypad/shared-keys.tsx +78 -0
- package/src/index.ts +6 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/input/key-handlers/handle-arrow.d.ts +0 -3
- package/dist/components/input/key-handlers/handle-exponent.d.ts +0 -3
- package/dist/components/key-translator.d.ts +0 -4
- package/dist/components/keypad/button.d.ts +0 -12
- package/dist/components/keypad/button.js.flow +0 -18
- package/dist/components/keypad/extras-page/index.d.ts +0 -10
- package/dist/components/keypad/extras-page/index.js.flow +0 -15
- package/dist/components/keypad/geometry-page/index.d.ts +0 -9
- package/dist/components/keypad/keypad-page-items.d.ts +0 -26
- package/dist/components/keypad/keypad-page-items.js.flow +0 -40
- package/dist/components/keypad/numbers-page/index.d.ts +0 -10
- package/dist/components/keypad/numbers-page/index.js.flow +0 -18
- package/dist/components/keypad/numbers-page/types.d.ts +0 -4
- package/dist/components/keypad/operators-page/advanced-relations-buttons.d.ts +0 -8
- package/dist/components/keypad/operators-page/advanced-relations-buttons.js.flow +0 -13
- package/dist/components/keypad/operators-page/basic-relations-buttons.d.ts +0 -8
- package/dist/components/keypad/operators-page/index.d.ts +0 -10
- package/dist/components/keypad/operators-page/index.js.flow +0 -18
- package/dist/components/keypad/operators-page/logarithms-buttons.d.ts +0 -8
- package/dist/components/keypad/operators-page/pre-algebra-buttons.d.ts +0 -8
- package/dist/components/keypad/operators-page/types.d.ts +0 -6
- package/dist/components/keypad/operators-page/types.js.flow +0 -12
- package/src/components/keypad/__tests__/Button.test.tsx +0 -51
- package/src/components/keypad/button.tsx +0 -108
- package/src/components/keypad/extras-page/index.tsx +0 -27
- package/src/components/keypad/geometry-page/index.tsx +0 -89
- package/src/components/keypad/keypad-page-items.tsx +0 -118
- package/src/components/keypad/numbers-page/index.tsx +0 -136
- package/src/components/keypad/numbers-page/types.ts +0 -4
- package/src/components/keypad/operators-page/advanced-relations-buttons.tsx +0 -33
- package/src/components/keypad/operators-page/basic-relations-buttons.tsx +0 -33
- package/src/components/keypad/operators-page/index.tsx +0 -94
- package/src/components/keypad/operators-page/logarithms-buttons.tsx +0 -33
- package/src/components/keypad/operators-page/pre-algebra-buttons.tsx +0 -37
- package/src/components/keypad/operators-page/types.ts +0 -6
- /package/src/components/keypad/{keypad-pages.stories.tsx → keypad-pages/keypad-pages.stories.tsx} +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import Key from "
|
|
2
|
-
import {DecimalSeparator} from "
|
|
3
|
-
import {decimalSeparator} from "
|
|
4
|
-
|
|
5
|
-
import MQ from "./input/mathquill-instance";
|
|
1
|
+
import Key from "../../data/keys";
|
|
2
|
+
import {DecimalSeparator} from "../../enums";
|
|
3
|
+
import {decimalSeparator} from "../../utils";
|
|
4
|
+
import {mathQuillInstance} from "../input/mathquill-instance";
|
|
6
5
|
import {
|
|
7
6
|
MathFieldInterface,
|
|
8
|
-
|
|
9
|
-
} from "
|
|
7
|
+
MathFieldUpdaterCallback,
|
|
8
|
+
} from "../input/mathquill-types";
|
|
9
|
+
|
|
10
|
+
import handleArrow from "./handle-arrow";
|
|
11
|
+
import handleExponent from "./handle-exponent";
|
|
12
|
+
import handleJumpOut from "./handle-jump-out";
|
|
10
13
|
|
|
11
14
|
enum ActionType {
|
|
12
15
|
WRITE = "write",
|
|
@@ -20,7 +23,7 @@ const decimalSymbol = decimalSeparator === DecimalSeparator.COMMA ? "," : ".";
|
|
|
20
23
|
function buildGenericCallback(
|
|
21
24
|
str: string,
|
|
22
25
|
type: ActionType = ActionType.WRITE,
|
|
23
|
-
):
|
|
26
|
+
): MathFieldUpdaterCallback {
|
|
24
27
|
return function (mathQuill: MathFieldInterface) {
|
|
25
28
|
switch (type) {
|
|
26
29
|
case ActionType.WRITE: {
|
|
@@ -39,20 +42,41 @@ function buildGenericCallback(
|
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
function buildNormalFunctionCallback(command: string) {
|
|
46
|
+
return function (mathField: MathFieldInterface) {
|
|
47
|
+
mathField.write(`\\${command}\\left(\\right)`);
|
|
48
|
+
mathField.keystroke("Left");
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const keyToMathquillMap: Record<Key, MathFieldUpdaterCallback> = {
|
|
53
|
+
EXP: handleExponent,
|
|
54
|
+
EXP_2: handleExponent,
|
|
55
|
+
EXP_3: handleExponent,
|
|
56
|
+
|
|
57
|
+
JUMP_OUT_PARENTHESES: handleJumpOut,
|
|
58
|
+
JUMP_OUT_EXPONENT: handleJumpOut,
|
|
59
|
+
JUMP_OUT_BASE: handleJumpOut,
|
|
60
|
+
JUMP_INTO_NUMERATOR: handleJumpOut,
|
|
61
|
+
JUMP_OUT_NUMERATOR: handleJumpOut,
|
|
62
|
+
JUMP_OUT_DENOMINATOR: handleJumpOut,
|
|
63
|
+
|
|
64
|
+
LEFT: handleArrow,
|
|
65
|
+
RIGHT: handleArrow,
|
|
66
|
+
|
|
67
|
+
LOG: buildNormalFunctionCallback("log"),
|
|
68
|
+
LN: buildNormalFunctionCallback("ln"),
|
|
69
|
+
SIN: buildNormalFunctionCallback("sin"),
|
|
70
|
+
COS: buildNormalFunctionCallback("cos"),
|
|
71
|
+
TAN: buildNormalFunctionCallback("tan"),
|
|
72
|
+
|
|
43
73
|
CDOT: buildGenericCallback("\\cdot"),
|
|
44
|
-
COS: buildGenericCallback("cos"),
|
|
45
74
|
DECIMAL: buildGenericCallback(decimalSymbol),
|
|
46
75
|
DIVIDE: buildGenericCallback("\\div"),
|
|
47
76
|
EQUAL: buildGenericCallback("="),
|
|
48
|
-
EXP: buildGenericCallback("^"),
|
|
49
|
-
EXP_2: buildGenericCallback("^2"),
|
|
50
|
-
EXP_3: buildGenericCallback("^3"),
|
|
51
77
|
GEQ: buildGenericCallback("\\geq"),
|
|
52
78
|
GT: buildGenericCallback(">"),
|
|
53
79
|
LEQ: buildGenericCallback("\\leq"),
|
|
54
|
-
LN: buildGenericCallback("\\ln"),
|
|
55
|
-
LOG: buildGenericCallback("\\log"),
|
|
56
80
|
LT: buildGenericCallback("<"),
|
|
57
81
|
MINUS: buildGenericCallback("-"),
|
|
58
82
|
NEGATIVE: buildGenericCallback("-"),
|
|
@@ -60,13 +84,12 @@ const keyToMathquillMap: Record<Key, MathQuillUpdaterCallback> = {
|
|
|
60
84
|
PERCENT: buildGenericCallback("%"),
|
|
61
85
|
PERIOD: buildGenericCallback("."),
|
|
62
86
|
PLUS: buildGenericCallback("+"),
|
|
63
|
-
SIN: buildGenericCallback("sin"),
|
|
64
|
-
TAN: buildGenericCallback("tan"),
|
|
65
87
|
TIMES: buildGenericCallback("\\times"),
|
|
66
88
|
|
|
67
89
|
// The `FRAC_EXCLUSIVE` variant is handled manually, since we may need to do
|
|
68
90
|
// some additional navigation depending on the cursor position.
|
|
69
91
|
FRAC_INCLUSIVE: buildGenericCallback("/", ActionType.CMD),
|
|
92
|
+
FRAC: buildGenericCallback("\\frac", ActionType.CMD),
|
|
70
93
|
LEFT_PAREN: buildGenericCallback("(", ActionType.CMD),
|
|
71
94
|
RIGHT_PAREN: buildGenericCallback(")", ActionType.CMD),
|
|
72
95
|
SQRT: buildGenericCallback("sqrt", ActionType.CMD),
|
|
@@ -75,6 +98,7 @@ const keyToMathquillMap: Record<Key, MathQuillUpdaterCallback> = {
|
|
|
75
98
|
THETA: buildGenericCallback("theta", ActionType.CMD),
|
|
76
99
|
RADICAL: buildGenericCallback("nthroot", ActionType.CMD),
|
|
77
100
|
|
|
101
|
+
BACKSPACE: buildGenericCallback("Backspace", ActionType.KEYSTROKE),
|
|
78
102
|
UP: buildGenericCallback("Up", ActionType.KEYSTROKE),
|
|
79
103
|
DOWN: buildGenericCallback("Down", ActionType.KEYSTROKE),
|
|
80
104
|
|
|
@@ -87,7 +111,8 @@ const keyToMathquillMap: Record<Key, MathQuillUpdaterCallback> = {
|
|
|
87
111
|
const cursor = mathQuill.__controller.cursor;
|
|
88
112
|
// If there's nothing to the left of the cursor, then we want to
|
|
89
113
|
// leave the cursor to the left of the fraction after creating it.
|
|
90
|
-
const shouldNavigateLeft =
|
|
114
|
+
const shouldNavigateLeft =
|
|
115
|
+
cursor[mathQuillInstance.L] === ActionType.MQ_END;
|
|
91
116
|
mathQuill.cmd("\\frac");
|
|
92
117
|
if (shouldNavigateLeft) {
|
|
93
118
|
mathQuill.keystroke("Left");
|
|
@@ -128,17 +153,7 @@ const keyToMathquillMap: Record<Key, MathQuillUpdaterCallback> = {
|
|
|
128
153
|
|
|
129
154
|
// These need to be overwritten by the consumer
|
|
130
155
|
// if they're going to be used
|
|
131
|
-
FRAC: () => {},
|
|
132
|
-
RIGHT: () => {},
|
|
133
|
-
LEFT: () => {},
|
|
134
|
-
BACKSPACE: () => {},
|
|
135
156
|
DISMISS: () => {},
|
|
136
|
-
JUMP_OUT_PARENTHESES: () => {},
|
|
137
|
-
JUMP_OUT_EXPONENT: () => {},
|
|
138
|
-
JUMP_OUT_BASE: () => {},
|
|
139
|
-
JUMP_INTO_NUMERATOR: () => {},
|
|
140
|
-
JUMP_OUT_NUMERATOR: () => {},
|
|
141
|
-
JUMP_OUT_DENOMINATOR: () => {},
|
|
142
157
|
NOOP: () => {},
|
|
143
158
|
MANY: () => {},
|
|
144
159
|
|
|
@@ -4,21 +4,22 @@ import * as React from "react";
|
|
|
4
4
|
import "@testing-library/jest-dom";
|
|
5
5
|
|
|
6
6
|
import Keys from "../../../data/key-configs";
|
|
7
|
-
import {KeypadButton} from "../keypad-
|
|
7
|
+
import {KeypadButton} from "../keypad-button";
|
|
8
8
|
|
|
9
9
|
describe("<KeypadButton />", () => {
|
|
10
10
|
it("uses the aria label from the key config", () => {
|
|
11
11
|
// Arrange
|
|
12
12
|
render(
|
|
13
13
|
<KeypadButton
|
|
14
|
-
onClickKey={(
|
|
14
|
+
onClickKey={() => {}}
|
|
15
15
|
keyConfig={Keys.LEFT_PAREN}
|
|
16
|
+
coord={[0, 0]}
|
|
16
17
|
/>,
|
|
17
18
|
);
|
|
18
19
|
|
|
19
20
|
// Assert
|
|
20
21
|
expect(
|
|
21
|
-
screen.getByRole("button", {name: "
|
|
22
|
+
screen.getByRole("button", {name: "Left parenthesis"}),
|
|
22
23
|
).toBeInTheDocument();
|
|
23
24
|
});
|
|
24
25
|
|
|
@@ -29,13 +30,14 @@ describe("<KeypadButton />", () => {
|
|
|
29
30
|
<KeypadButton
|
|
30
31
|
onClickKey={mockClickKeyCallback}
|
|
31
32
|
keyConfig={Keys.LEFT_PAREN}
|
|
33
|
+
coord={[0, 0]}
|
|
32
34
|
/>,
|
|
33
35
|
);
|
|
34
36
|
|
|
35
37
|
// Act
|
|
36
|
-
userEvent.click(screen.getByRole("button", {name: "
|
|
38
|
+
userEvent.click(screen.getByRole("button", {name: "Left parenthesis"}));
|
|
37
39
|
|
|
38
40
|
// Assert
|
|
39
|
-
expect(mockClickKeyCallback).
|
|
41
|
+
expect(mockClickKeyCallback).toHaveBeenCalledWith("LEFT_PAREN");
|
|
40
42
|
});
|
|
41
43
|
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
2
|
+
import {Popover} from "@khanacademy/wonder-blocks-popover";
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import "@testing-library/jest-dom";
|
|
8
|
+
|
|
9
|
+
import Key from "../../../data/keys";
|
|
10
|
+
import {createMathField} from "../../input/mathquill-instance";
|
|
11
|
+
import {MathFieldInterface} from "../../input/mathquill-types";
|
|
12
|
+
import keyTranslator from "../../key-handlers/key-translator";
|
|
13
|
+
import Keypad from "../index";
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
onChangeMathInput: (mathInputTex: string) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function V2KeypadWithMathquill(props: Props) {
|
|
20
|
+
const mathFieldWrapperRef = React.useRef<HTMLDivElement>(null);
|
|
21
|
+
const [mathField, setMathField] = React.useState<MathFieldInterface>();
|
|
22
|
+
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
if (!mathField && mathFieldWrapperRef.current) {
|
|
25
|
+
const mathFieldInstance = createMathField(
|
|
26
|
+
mathFieldWrapperRef.current,
|
|
27
|
+
(baseConfig) => {
|
|
28
|
+
return {
|
|
29
|
+
...baseConfig,
|
|
30
|
+
handlers: {
|
|
31
|
+
edit: (mathField) => {
|
|
32
|
+
props.onChangeMathInput(mathField.latex());
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
setMathField(mathFieldInstance);
|
|
39
|
+
}
|
|
40
|
+
}, [mathField, props]);
|
|
41
|
+
|
|
42
|
+
function handleClickKey(key: Key) {
|
|
43
|
+
if (!mathField) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const mathFieldCallback = keyTranslator[key];
|
|
48
|
+
if (mathFieldCallback) {
|
|
49
|
+
mathFieldCallback(mathField, key);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div style={{maxWidth: "400px", margin: "2em"}}>
|
|
55
|
+
<Popover
|
|
56
|
+
content={
|
|
57
|
+
<div>
|
|
58
|
+
<Keypad
|
|
59
|
+
extraKeys={["a", "b", "c"]}
|
|
60
|
+
onClickKey={handleClickKey}
|
|
61
|
+
advancedRelations
|
|
62
|
+
basicRelations
|
|
63
|
+
divisionKey
|
|
64
|
+
logarithms
|
|
65
|
+
multiplicationDot
|
|
66
|
+
preAlgebra
|
|
67
|
+
trigonometry
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
}
|
|
71
|
+
dismissEnabled
|
|
72
|
+
opened
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
style={{
|
|
76
|
+
width: "100%",
|
|
77
|
+
marginBottom: "1em",
|
|
78
|
+
border: `1px solid ${Color.offBlack16}`,
|
|
79
|
+
}}
|
|
80
|
+
ref={mathFieldWrapperRef}
|
|
81
|
+
/>
|
|
82
|
+
</Popover>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe("Keypad v2 with MathQuill", () => {
|
|
88
|
+
it("can write the Pythagorean theorem (simple)", () => {
|
|
89
|
+
// Arrange
|
|
90
|
+
const mockMathInputCallback = jest.fn();
|
|
91
|
+
render(
|
|
92
|
+
<V2KeypadWithMathquill onChangeMathInput={mockMathInputCallback} />,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Act
|
|
96
|
+
|
|
97
|
+
// a^2
|
|
98
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
99
|
+
userEvent.click(screen.getByRole("button", {name: "a"}));
|
|
100
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
101
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
102
|
+
|
|
103
|
+
// +
|
|
104
|
+
userEvent.click(screen.getByRole("button", {name: "Numbers"}));
|
|
105
|
+
userEvent.click(screen.getByRole("button", {name: "Plus"}));
|
|
106
|
+
|
|
107
|
+
// b^2 =
|
|
108
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
109
|
+
userEvent.click(screen.getByRole("button", {name: "b"}));
|
|
110
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
111
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
112
|
+
userEvent.click(screen.getByRole("button", {name: "Equals sign"}));
|
|
113
|
+
|
|
114
|
+
// c^2
|
|
115
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
116
|
+
userEvent.click(screen.getByRole("button", {name: "c"}));
|
|
117
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
118
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(mockMathInputCallback).toHaveBeenLastCalledWith("a^2+b^2=c^2");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("can write the Pythagorean theorem (complex)", () => {
|
|
125
|
+
// Arrange
|
|
126
|
+
const mockMathInputCallback = jest.fn();
|
|
127
|
+
render(
|
|
128
|
+
<V2KeypadWithMathquill onChangeMathInput={mockMathInputCallback} />,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Act
|
|
132
|
+
|
|
133
|
+
// c = /Square root
|
|
134
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
135
|
+
userEvent.click(screen.getByRole("button", {name: "c"}));
|
|
136
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
137
|
+
userEvent.click(screen.getByRole("button", {name: "Equals sign"}));
|
|
138
|
+
userEvent.click(screen.getByRole("button", {name: "Square root"}));
|
|
139
|
+
|
|
140
|
+
// a^2
|
|
141
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
142
|
+
userEvent.click(screen.getByRole("button", {name: "a"}));
|
|
143
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
144
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
145
|
+
|
|
146
|
+
// +
|
|
147
|
+
userEvent.click(screen.getByRole("button", {name: "Numbers"}));
|
|
148
|
+
userEvent.click(screen.getByRole("button", {name: "Plus"}));
|
|
149
|
+
|
|
150
|
+
// b^2
|
|
151
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
152
|
+
userEvent.click(screen.getByRole("button", {name: "b"}));
|
|
153
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
154
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
155
|
+
|
|
156
|
+
// Assert
|
|
157
|
+
expect(mockMathInputCallback).toHaveBeenLastCalledWith(
|
|
158
|
+
"c=\\sqrt{a^2+b^2}",
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("writes the Pythagorean theorem using typing/clicking together", () => {
|
|
163
|
+
// Arrange
|
|
164
|
+
const mockMathInputCallback = jest.fn();
|
|
165
|
+
render(
|
|
166
|
+
<V2KeypadWithMathquill onChangeMathInput={mockMathInputCallback} />,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Act
|
|
170
|
+
|
|
171
|
+
// Argument is empty because mathquill generates textarea w/o label
|
|
172
|
+
userEvent.type(screen.getByRole("textbox"), "a");
|
|
173
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
174
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
175
|
+
|
|
176
|
+
userEvent.type(screen.getByRole("textbox"), "+");
|
|
177
|
+
|
|
178
|
+
// b^2
|
|
179
|
+
userEvent.click(screen.getByRole("button", {name: "Extras"}));
|
|
180
|
+
userEvent.click(screen.getByRole("button", {name: "b"}));
|
|
181
|
+
userEvent.click(screen.getByRole("button", {name: "Operators"}));
|
|
182
|
+
userEvent.click(screen.getByRole("button", {name: "Square"}));
|
|
183
|
+
userEvent.type(screen.getByRole("textbox"), "=c^2");
|
|
184
|
+
|
|
185
|
+
// Assert
|
|
186
|
+
expect(mockMathInputCallback).toHaveBeenLastCalledWith("a^2+b^2=c^2");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("deletes from the input using the backspace button", () => {
|
|
190
|
+
// Arrange
|
|
191
|
+
const mockMathInputCallback = jest.fn();
|
|
192
|
+
render(
|
|
193
|
+
<V2KeypadWithMathquill onChangeMathInput={mockMathInputCallback} />,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Act
|
|
197
|
+
// Write `a^2+b^2=c^2` using the keypad
|
|
198
|
+
const buttonPressesForFormula = [
|
|
199
|
+
"Extras",
|
|
200
|
+
"a",
|
|
201
|
+
"Operators",
|
|
202
|
+
"Square",
|
|
203
|
+
"Numbers",
|
|
204
|
+
"Plus",
|
|
205
|
+
"Extras",
|
|
206
|
+
"b",
|
|
207
|
+
"Operators",
|
|
208
|
+
"Square",
|
|
209
|
+
"Equals sign",
|
|
210
|
+
"Extras",
|
|
211
|
+
"c",
|
|
212
|
+
"Operators",
|
|
213
|
+
"Square",
|
|
214
|
+
];
|
|
215
|
+
buttonPressesForFormula.forEach((button) => {
|
|
216
|
+
userEvent.click(screen.getByRole("button", {name: button}));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Assert
|
|
220
|
+
// make sure the formula was typed correctly
|
|
221
|
+
expect(mockMathInputCallback).toHaveBeenLastCalledWith("a^2+b^2=c^2");
|
|
222
|
+
|
|
223
|
+
userEvent.click(screen.getByRole("button", {name: "Numbers"}));
|
|
224
|
+
// delete: need 14 backspaces in MathQuill to delete `a^2+b^2=c^2`
|
|
225
|
+
for (let i = 0; i < 14; i++) {
|
|
226
|
+
userEvent.click(screen.getByRole("button", {name: "Delete"}));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
expect(mockMathInputCallback).toHaveBeenLastCalledWith("");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import renderSingleKeypad from "../../../../../../testing/render-keypad-with-cypress";
|
|
2
|
+
import KeyConfigs from "../../../data/key-configs";
|
|
2
3
|
|
|
3
4
|
const tabs = [
|
|
4
|
-
{
|
|
5
|
-
|
|
5
|
+
{
|
|
6
|
+
name: "Operators",
|
|
7
|
+
specialButton: "EXP_2",
|
|
8
|
+
label: KeyConfigs["EXP_2"].ariaLabel,
|
|
9
|
+
},
|
|
10
|
+
{name: "Extras", specialButton: "PI", label: KeyConfigs["PI"].ariaLabel},
|
|
6
11
|
|
|
7
|
-
{
|
|
8
|
-
|
|
12
|
+
{
|
|
13
|
+
name: "Geometry",
|
|
14
|
+
specialButton: "COS",
|
|
15
|
+
label: KeyConfigs["COS"].ariaLabel,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "Numbers",
|
|
19
|
+
specialButton: "NUM_7",
|
|
20
|
+
label: KeyConfigs["NUM_7"].ariaLabel,
|
|
21
|
+
},
|
|
9
22
|
];
|
|
10
23
|
|
|
11
24
|
describe("Keypad v2", () => {
|
|
@@ -16,14 +29,14 @@ describe("Keypad v2", () => {
|
|
|
16
29
|
// currently clicking on the bottom left due to button re-rendering
|
|
17
30
|
// after mousedown but before mouseup (only in Cypress)
|
|
18
31
|
cy.get('[aria-label="' + tab.name + '"]').click("bottomLeft");
|
|
19
|
-
cy.get('[aria-label="' + tab.
|
|
32
|
+
cy.get('[aria-label="' + tab.label + '"]').should("exist");
|
|
20
33
|
});
|
|
21
34
|
|
|
22
35
|
it(`calls ${tab.specialButton} key callback in ${tab.name} tab`, () => {
|
|
23
36
|
const onClickKeySpy = cy.spy().as("onClickKeySpy");
|
|
24
37
|
renderSingleKeypad(onClickKeySpy);
|
|
25
38
|
cy.get('[aria-label="' + tab.name + '"]').click();
|
|
26
|
-
cy.get('[aria-label="' + tab.
|
|
39
|
+
cy.get('[aria-label="' + tab.label + '"]').click();
|
|
27
40
|
cy.get("@onClickKeySpy").should(
|
|
28
41
|
"have.been.calledOnceWithExactly",
|
|
29
42
|
tab.specialButton,
|
|
@@ -8,35 +8,27 @@ import {ClickKeyCallback} from "../../types";
|
|
|
8
8
|
import Tabbar from "../tabbar/tabbar";
|
|
9
9
|
import {TabbarItemType} from "../tabbar/types";
|
|
10
10
|
|
|
11
|
-
import ExtrasPage from "./extras-page";
|
|
12
|
-
import GeometryPage from "./geometry-page";
|
|
13
|
-
import NumbersPage from "./numbers-page";
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import {OperatorsButtonSets} from "./operators-page/types";
|
|
11
|
+
import ExtrasPage from "./keypad-pages/extras-page";
|
|
12
|
+
import GeometryPage from "./keypad-pages/geometry-page";
|
|
13
|
+
import NumbersPage from "./keypad-pages/numbers-page";
|
|
14
|
+
import OperatorsPage from "./keypad-pages/operators-page";
|
|
15
|
+
import SharedKeys from "./shared-keys";
|
|
17
16
|
|
|
18
17
|
export type Props = {
|
|
19
18
|
onClickKey: ClickKeyCallback;
|
|
20
19
|
trigonometry?: boolean;
|
|
21
20
|
extraKeys: ReadonlyArray<Key>;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type DefaultProps = {
|
|
30
|
-
extraKeys: Props["extraKeys"];
|
|
21
|
+
multiplicationDot?: boolean;
|
|
22
|
+
divisionKey?: boolean;
|
|
23
|
+
preAlgebra?: boolean;
|
|
24
|
+
logarithms?: boolean;
|
|
25
|
+
basicRelations?: boolean;
|
|
26
|
+
advancedRelations?: boolean;
|
|
31
27
|
};
|
|
32
28
|
|
|
33
29
|
function allPages(props: Props): ReadonlyArray<TabbarItemType> {
|
|
34
30
|
const pages: Array<TabbarItemType> = ["Numbers"];
|
|
35
31
|
|
|
36
|
-
if (props.extraKeys?.length) {
|
|
37
|
-
pages.push("Extras");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
32
|
if (
|
|
41
33
|
// OperatorsButtonSets
|
|
42
34
|
props.preAlgebra ||
|
|
@@ -51,48 +43,80 @@ function allPages(props: Props): ReadonlyArray<TabbarItemType> {
|
|
|
51
43
|
pages.push("Geometry");
|
|
52
44
|
}
|
|
53
45
|
|
|
46
|
+
if (props.extraKeys?.length) {
|
|
47
|
+
pages.push("Extras");
|
|
48
|
+
}
|
|
49
|
+
|
|
54
50
|
return pages;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
export default
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{
|
|
53
|
+
export default function Keypad(props: Props) {
|
|
54
|
+
const [selectedPage, setSelectedPage] =
|
|
55
|
+
React.useState<TabbarItemType>("Numbers");
|
|
56
|
+
|
|
57
|
+
const availablePages = allPages(props);
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
onClickKey,
|
|
61
|
+
extraKeys = [],
|
|
62
|
+
multiplicationDot,
|
|
63
|
+
divisionKey,
|
|
64
|
+
preAlgebra,
|
|
65
|
+
logarithms,
|
|
66
|
+
basicRelations,
|
|
67
|
+
advancedRelations,
|
|
68
|
+
} = props;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<View>
|
|
72
|
+
<Tabbar
|
|
73
|
+
items={availablePages}
|
|
74
|
+
selectedItem={selectedPage}
|
|
75
|
+
onSelectItem={(tabbarItem: TabbarItemType) => {
|
|
76
|
+
setSelectedPage(tabbarItem);
|
|
77
|
+
}}
|
|
78
|
+
style={styles.tabbar}
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
<View style={styles.grid}>
|
|
82
|
+
{selectedPage === "Numbers" && (
|
|
83
|
+
<NumbersPage onClickKey={onClickKey} />
|
|
84
|
+
)}
|
|
85
|
+
{selectedPage === "Extras" && (
|
|
86
|
+
<ExtrasPage onClickKey={onClickKey} extraKeys={extraKeys} />
|
|
87
|
+
)}
|
|
83
88
|
{selectedPage === "Operators" && (
|
|
84
|
-
<OperatorsPage
|
|
89
|
+
<OperatorsPage
|
|
90
|
+
onClickKey={onClickKey}
|
|
91
|
+
preAlgebra={preAlgebra}
|
|
92
|
+
logarithms={logarithms}
|
|
93
|
+
basicRelations={basicRelations}
|
|
94
|
+
advancedRelations={advancedRelations}
|
|
95
|
+
/>
|
|
85
96
|
)}
|
|
86
97
|
{selectedPage === "Geometry" && (
|
|
87
|
-
<GeometryPage {
|
|
98
|
+
<GeometryPage onClickKey={onClickKey} />
|
|
88
99
|
)}
|
|
100
|
+
<SharedKeys
|
|
101
|
+
onClickKey={onClickKey}
|
|
102
|
+
multiplicationDot={multiplicationDot}
|
|
103
|
+
divisionKey={divisionKey}
|
|
104
|
+
/>
|
|
89
105
|
</View>
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
const styles = StyleSheet.create({
|
|
95
111
|
tabbar: {
|
|
96
112
|
background: Color.white,
|
|
97
113
|
},
|
|
114
|
+
grid: {
|
|
115
|
+
display: "grid",
|
|
116
|
+
gridTemplateColumns: "repeat(6, 1fr)",
|
|
117
|
+
gridTemplateRows: "repeat(4, 1fr)",
|
|
118
|
+
backgroundColor: "#DBDCDD",
|
|
119
|
+
maxHeight: 200,
|
|
120
|
+
maxWidth: 300,
|
|
121
|
+
},
|
|
98
122
|
});
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
|
|
5
5
|
import KeyConfigs from "../../data/key-configs";
|
|
6
6
|
|
|
7
|
-
import {KeypadButton, KeypadButtonProps} from "./keypad-
|
|
7
|
+
import {KeypadButton, KeypadButtonProps} from "./keypad-button";
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
10
|
title: "Keypad Button",
|
|
@@ -70,6 +70,7 @@ export const AllButtons: ComponentStory<typeof KeypadButton> = ({
|
|
|
70
70
|
<KeypadButton
|
|
71
71
|
keyConfig={KeyConfigs[key]}
|
|
72
72
|
onClickKey={action("pressed")}
|
|
73
|
+
coord={[0, 0]}
|
|
73
74
|
/>
|
|
74
75
|
</div>
|
|
75
76
|
))}
|