@khanacademy/math-input 6.0.0 → 6.0.2
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 +14 -0
- package/dist/es/index.js +38 -10
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +37 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +63 -4
- package/src/components/keypad/index.tsx +22 -0
- package/src/components/keypad/keypad-button.tsx +6 -14
- package/src/components/keypad/keypad-mathquill.stories.tsx +10 -1
- package/src/components/tabbar/item.tsx +6 -0
- 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": "6.0.
|
|
6
|
+
"version": "6.0.2",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"source": "src/index.ts",
|
|
20
20
|
"scripts": {},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@khanacademy/perseus-core": "0.0.
|
|
22
|
+
"@khanacademy/perseus-core": "0.0.2",
|
|
23
23
|
"mathquill": "git+https://git@github.com/Khan/mathquill.git#a9ae54e057c5c1acc8244a5627acbff29901d992",
|
|
24
24
|
"performance-now": "^0.2.0"
|
|
25
25
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {PerseusAnalyticsEvent} from "@khanacademy/perseus-core";
|
|
1
2
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
2
3
|
import {Popover} from "@khanacademy/wonder-blocks-popover";
|
|
3
4
|
import {render, screen} from "@testing-library/react";
|
|
@@ -14,11 +15,15 @@ import Keypad from "../index";
|
|
|
14
15
|
|
|
15
16
|
type Props = {
|
|
16
17
|
onChangeMathInput: (mathInputTex: string) => void;
|
|
18
|
+
keypadClosed?: boolean;
|
|
19
|
+
sendEvent?: (event: PerseusAnalyticsEvent) => Promise<void>;
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
function V2KeypadWithMathquill(props: Props) {
|
|
20
23
|
const mathFieldWrapperRef = React.useRef<HTMLDivElement>(null);
|
|
21
24
|
const [mathField, setMathField] = React.useState<MathFieldInterface>();
|
|
25
|
+
const {onChangeMathInput, keypadClosed, sendEvent} = props;
|
|
26
|
+
const [keypadOpen, setKeypadOpen] = React.useState<boolean>(!keypadClosed);
|
|
22
27
|
|
|
23
28
|
React.useEffect(() => {
|
|
24
29
|
if (!mathField && mathFieldWrapperRef.current) {
|
|
@@ -29,7 +34,7 @@ function V2KeypadWithMathquill(props: Props) {
|
|
|
29
34
|
...baseConfig,
|
|
30
35
|
handlers: {
|
|
31
36
|
edit: (mathField) => {
|
|
32
|
-
|
|
37
|
+
onChangeMathInput(mathField.latex());
|
|
33
38
|
},
|
|
34
39
|
},
|
|
35
40
|
};
|
|
@@ -37,13 +42,17 @@ function V2KeypadWithMathquill(props: Props) {
|
|
|
37
42
|
);
|
|
38
43
|
setMathField(mathFieldInstance);
|
|
39
44
|
}
|
|
40
|
-
}, [mathField,
|
|
45
|
+
}, [mathField, onChangeMathInput]);
|
|
41
46
|
|
|
42
47
|
function handleClickKey(key: Key) {
|
|
43
48
|
if (!mathField) {
|
|
44
49
|
return;
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
if (key === "DISMISS") {
|
|
53
|
+
setKeypadOpen(false);
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
const mathFieldCallback = keyTranslator[key];
|
|
48
57
|
if (mathFieldCallback) {
|
|
49
58
|
mathFieldCallback(mathField, key);
|
|
@@ -65,12 +74,13 @@ function V2KeypadWithMathquill(props: Props) {
|
|
|
65
74
|
multiplicationDot
|
|
66
75
|
preAlgebra
|
|
67
76
|
trigonometry
|
|
68
|
-
sendEvent={async () => {}}
|
|
77
|
+
sendEvent={sendEvent ? sendEvent : async () => {}}
|
|
78
|
+
showDismiss
|
|
69
79
|
/>
|
|
70
80
|
</div>
|
|
71
81
|
}
|
|
72
82
|
dismissEnabled
|
|
73
|
-
opened
|
|
83
|
+
opened={keypadOpen}
|
|
74
84
|
>
|
|
75
85
|
<div
|
|
76
86
|
style={{
|
|
@@ -81,6 +91,12 @@ function V2KeypadWithMathquill(props: Props) {
|
|
|
81
91
|
ref={mathFieldWrapperRef}
|
|
82
92
|
/>
|
|
83
93
|
</Popover>
|
|
94
|
+
<button
|
|
95
|
+
aria-label="Keypad toggle"
|
|
96
|
+
onClick={() => setKeypadOpen(!keypadOpen)}
|
|
97
|
+
>
|
|
98
|
+
{keypadOpen ? "close keypad" : "open keypad"}
|
|
99
|
+
</button>
|
|
84
100
|
</div>
|
|
85
101
|
);
|
|
86
102
|
}
|
|
@@ -231,4 +247,47 @@ describe("Keypad v2 with MathQuill", () => {
|
|
|
231
247
|
|
|
232
248
|
expect(mockMathInputCallback).toHaveBeenLastCalledWith("");
|
|
233
249
|
});
|
|
250
|
+
|
|
251
|
+
// Keypad event tests
|
|
252
|
+
it("fires the keypad open event on open", () => {
|
|
253
|
+
// Arrange
|
|
254
|
+
const mockSendEvent = jest.fn();
|
|
255
|
+
render(
|
|
256
|
+
<V2KeypadWithMathquill
|
|
257
|
+
onChangeMathInput={() => {}}
|
|
258
|
+
keypadClosed={true}
|
|
259
|
+
sendEvent={mockSendEvent}
|
|
260
|
+
/>,
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
userEvent.click(screen.getByRole("button", {name: "Keypad toggle"}));
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
expect(mockSendEvent).toHaveBeenLastCalledWith({
|
|
268
|
+
type: "math-input:keypad-opened",
|
|
269
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Keypad event tests
|
|
274
|
+
it("fires the keypad open event on close", () => {
|
|
275
|
+
// Arrange
|
|
276
|
+
const mockSendEvent = jest.fn();
|
|
277
|
+
render(
|
|
278
|
+
<V2KeypadWithMathquill
|
|
279
|
+
onChangeMathInput={() => {}}
|
|
280
|
+
sendEvent={mockSendEvent}
|
|
281
|
+
/>,
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// Act
|
|
285
|
+
userEvent.click(screen.getByRole("button", {name: "Keypad toggle"}));
|
|
286
|
+
|
|
287
|
+
// Assert
|
|
288
|
+
expect(mockSendEvent).toHaveBeenLastCalledWith({
|
|
289
|
+
type: "math-input:keypad-closed",
|
|
290
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
291
|
+
});
|
|
292
|
+
});
|
|
234
293
|
});
|
|
@@ -2,6 +2,7 @@ import Color from "@khanacademy/wonder-blocks-color";
|
|
|
2
2
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import {StyleSheet} from "aphrodite";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import {useEffect} from "react";
|
|
5
6
|
|
|
6
7
|
import Key from "../../data/keys";
|
|
7
8
|
import {ClickKeyCallback} from "../../types";
|
|
@@ -68,6 +69,7 @@ function allPages(props: Props): ReadonlyArray<TabbarItemType> {
|
|
|
68
69
|
export default function Keypad(props: Props) {
|
|
69
70
|
const [selectedPage, setSelectedPage] =
|
|
70
71
|
React.useState<TabbarItemType>("Numbers");
|
|
72
|
+
const [isMounted, setIsMounted] = React.useState<boolean>(false);
|
|
71
73
|
|
|
72
74
|
const availablePages = allPages(props);
|
|
73
75
|
|
|
@@ -82,8 +84,28 @@ export default function Keypad(props: Props) {
|
|
|
82
84
|
basicRelations,
|
|
83
85
|
advancedRelations,
|
|
84
86
|
showDismiss,
|
|
87
|
+
sendEvent,
|
|
85
88
|
} = props;
|
|
86
89
|
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isMounted) {
|
|
92
|
+
sendEvent({
|
|
93
|
+
type: "math-input:keypad-opened",
|
|
94
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
95
|
+
});
|
|
96
|
+
setIsMounted(true);
|
|
97
|
+
}
|
|
98
|
+
return () => {
|
|
99
|
+
if (isMounted) {
|
|
100
|
+
sendEvent({
|
|
101
|
+
type: "math-input:keypad-closed",
|
|
102
|
+
payload: {virtualKeypadVersion: "MATH_INPUT_KEYPAD_V2"},
|
|
103
|
+
});
|
|
104
|
+
setIsMounted(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}, [sendEvent, isMounted]);
|
|
108
|
+
|
|
87
109
|
return (
|
|
88
110
|
<View>
|
|
89
111
|
<Tabbar
|
|
@@ -47,13 +47,7 @@ export const KeypadButton = ({
|
|
|
47
47
|
>
|
|
48
48
|
{({hovered, focused, pressed}) => {
|
|
49
49
|
return (
|
|
50
|
-
<View
|
|
51
|
-
style={[
|
|
52
|
-
styles.outerBoxBase,
|
|
53
|
-
hovered && styles.outerBoxHover,
|
|
54
|
-
pressed && styles.outerBoxPressed,
|
|
55
|
-
]}
|
|
56
|
-
>
|
|
50
|
+
<View style={[styles.outerBoxBase]}>
|
|
57
51
|
<View
|
|
58
52
|
style={[
|
|
59
53
|
styles.base,
|
|
@@ -91,7 +85,7 @@ const styles = StyleSheet.create({
|
|
|
91
85
|
padding: 1,
|
|
92
86
|
},
|
|
93
87
|
hovered: {
|
|
94
|
-
border: `
|
|
88
|
+
border: `2px solid ${Color.blue}`,
|
|
95
89
|
padding: 1,
|
|
96
90
|
boxShadow: "none",
|
|
97
91
|
},
|
|
@@ -114,15 +108,13 @@ const styles = StyleSheet.create({
|
|
|
114
108
|
borderRadius: 7,
|
|
115
109
|
border: "2px solid transparent",
|
|
116
110
|
},
|
|
117
|
-
outerBoxHover: {
|
|
118
|
-
border: `2px solid ${Color.blue}`,
|
|
119
|
-
},
|
|
120
|
-
outerBoxPressed: {
|
|
121
|
-
border: "2px solid #1B50B3",
|
|
122
|
-
},
|
|
123
111
|
clickable: {
|
|
124
112
|
width: "100%",
|
|
125
113
|
height: "100%",
|
|
126
114
|
boxSizing: "border-box",
|
|
115
|
+
|
|
116
|
+
":focus": {
|
|
117
|
+
outline: `none`,
|
|
118
|
+
},
|
|
127
119
|
},
|
|
128
120
|
});
|
|
@@ -18,6 +18,7 @@ export default {
|
|
|
18
18
|
export function V2KeypadWithMathquill() {
|
|
19
19
|
const mathFieldWrapperRef = React.useRef<HTMLDivElement>(null);
|
|
20
20
|
const [mathField, setMathField] = React.useState<MathFieldInterface>();
|
|
21
|
+
const [keypadOpen, setKeypadOpen] = React.useState<boolean>(true);
|
|
21
22
|
const [cursorContext, setCursorContext] = React.useState<
|
|
22
23
|
typeof CursorContext[keyof typeof CursorContext]
|
|
23
24
|
>(CursorContext.NONE);
|
|
@@ -44,6 +45,10 @@ export function V2KeypadWithMathquill() {
|
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
if (key === "DISMISS") {
|
|
49
|
+
setKeypadOpen(false);
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
const mathFieldCallback = keyTranslator[key];
|
|
48
53
|
if (mathFieldCallback) {
|
|
49
54
|
mathFieldCallback(mathField, key);
|
|
@@ -79,11 +84,12 @@ export function V2KeypadWithMathquill() {
|
|
|
79
84
|
// eslint-disable-next-line no-console
|
|
80
85
|
console.log("Send Event:", event);
|
|
81
86
|
}}
|
|
87
|
+
showDismiss
|
|
82
88
|
/>
|
|
83
89
|
</PopoverContentCore>
|
|
84
90
|
}
|
|
85
91
|
dismissEnabled
|
|
86
|
-
opened
|
|
92
|
+
opened={keypadOpen}
|
|
87
93
|
>
|
|
88
94
|
<div
|
|
89
95
|
style={{
|
|
@@ -94,6 +100,9 @@ export function V2KeypadWithMathquill() {
|
|
|
94
100
|
ref={mathFieldWrapperRef}
|
|
95
101
|
/>
|
|
96
102
|
</Popover>
|
|
103
|
+
<button onClick={() => setKeypadOpen(!keypadOpen)}>
|
|
104
|
+
{keypadOpen ? "close keypad" : "open keypad"}
|
|
105
|
+
</button>
|
|
97
106
|
</div>
|
|
98
107
|
);
|
|
99
108
|
}
|
|
@@ -50,6 +50,11 @@ const styles = StyleSheet.create({
|
|
|
50
50
|
height: 3,
|
|
51
51
|
marginLeft: 3,
|
|
52
52
|
},
|
|
53
|
+
clickable: {
|
|
54
|
+
":focus": {
|
|
55
|
+
outline: `none`,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
53
58
|
});
|
|
54
59
|
|
|
55
60
|
function imageTintColor(
|
|
@@ -84,6 +89,7 @@ class TabbarItem extends React.Component<Props> {
|
|
|
84
89
|
onClick={onClick}
|
|
85
90
|
disabled={itemState === "disabled"}
|
|
86
91
|
aria-label={itemType}
|
|
92
|
+
style={styles.clickable}
|
|
87
93
|
aria-selected={itemState === "active"}
|
|
88
94
|
role="tab"
|
|
89
95
|
>
|