@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/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.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.1",
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
- props.onChangeMathInput(mathField.latex());
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, props]);
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: `1px solid ${Color.blue}`,
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
  >