@khanacademy/math-input 4.3.1 → 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.
- package/CHANGELOG.md +19 -0
- package/dist/components/input/math-wrapper.d.ts +2 -2
- package/dist/components/input/math-wrapper.js.flow +2 -4
- package/dist/components/input/mathquill-helpers.d.ts +1 -1
- package/dist/components/input/mathquill-helpers.js.flow +2 -2
- package/dist/components/input/mathquill-types.d.ts +270 -10
- package/dist/components/input/mathquill-types.js.flow +312 -10
- package/dist/components/keypad/index.d.ts +11 -1
- package/dist/components/keypad/index.js.flow +14 -1
- package/dist/components/keypad/shared-keys.d.ts +4 -0
- package/dist/components/keypad/shared-keys.js.flow +4 -0
- package/dist/components/tabbar/tabbar.d.ts +1 -0
- package/dist/components/tabbar/tabbar.js.flow +1 -0
- package/dist/components/tabbar/types.d.ts +1 -1
- package/dist/components/tabbar/types.js.flow +6 -1
- package/dist/es/index.js +173 -48
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +173 -48
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/components/input/__tests__/mathquill-helpers.test.ts +105 -0
- package/src/components/input/math-input.tsx +1 -1
- package/src/components/input/math-wrapper.ts +6 -10
- package/src/components/input/mathquill-helpers.ts +8 -1
- package/src/components/input/mathquill-types.ts +308 -40
- package/src/components/key-handlers/__tests__/handle-jump-out.test.ts +94 -0
- package/src/components/key-handlers/handle-jump-out.ts +3 -2
- package/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx +42 -39
- package/src/components/keypad/__tests__/keypad.test.tsx +42 -0
- package/src/components/keypad/button-assets.tsx +34 -6
- package/src/components/keypad/index.tsx +25 -2
- package/src/components/keypad/keypad-mathquill.stories.tsx +19 -0
- package/src/components/keypad/keypad-pages/extras-page.tsx +1 -1
- package/src/components/keypad/keypad-pages/numbers-page.tsx +25 -16
- package/src/components/keypad/shared-keys.tsx +56 -8
- package/src/components/tabbar/__tests__/tabbar.test.tsx +54 -14
- package/src/components/tabbar/icons.tsx +34 -12
- package/src/components/tabbar/item.tsx +2 -0
- package/src/components/tabbar/tabbar.tsx +32 -12
- package/src/components/tabbar/types.ts +6 -1
- package/tsconfig-build.json +3 -1
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -17,9 +17,7 @@ describe("<Tabbar />", () => {
|
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
// Assert
|
|
20
|
-
expect(
|
|
21
|
-
screen.getByRole("button", {name: "Numbers"}),
|
|
22
|
-
).toBeInTheDocument();
|
|
20
|
+
expect(screen.getByRole("tab", {name: "Numbers"})).toBeInTheDocument();
|
|
23
21
|
});
|
|
24
22
|
|
|
25
23
|
it("renders many tabs", () => {
|
|
@@ -33,17 +31,11 @@ describe("<Tabbar />", () => {
|
|
|
33
31
|
);
|
|
34
32
|
|
|
35
33
|
// Assert
|
|
34
|
+
expect(screen.getByRole("tab", {name: "Numbers"})).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByRole("tab", {name: "Extras"})).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByRole("tab", {name: "Geometry"})).toBeInTheDocument();
|
|
36
37
|
expect(
|
|
37
|
-
screen.getByRole("
|
|
38
|
-
).toBeInTheDocument();
|
|
39
|
-
expect(
|
|
40
|
-
screen.getByRole("button", {name: "Extras"}),
|
|
41
|
-
).toBeInTheDocument();
|
|
42
|
-
expect(
|
|
43
|
-
screen.getByRole("button", {name: "Geometry"}),
|
|
44
|
-
).toBeInTheDocument();
|
|
45
|
-
expect(
|
|
46
|
-
screen.getByRole("button", {name: "Operators"}),
|
|
38
|
+
screen.getByRole("tab", {name: "Operators"}),
|
|
47
39
|
).toBeInTheDocument();
|
|
48
40
|
});
|
|
49
41
|
|
|
@@ -59,7 +51,55 @@ describe("<Tabbar />", () => {
|
|
|
59
51
|
);
|
|
60
52
|
|
|
61
53
|
// Assert
|
|
62
|
-
userEvent.click(screen.getByRole("
|
|
54
|
+
userEvent.click(screen.getByRole("tab", {name: "Geometry"}));
|
|
63
55
|
expect(mockSelectCallback).toHaveBeenCalledWith("Geometry");
|
|
64
56
|
});
|
|
57
|
+
|
|
58
|
+
it("shows dismiss button with a onClickClose callback", () => {
|
|
59
|
+
// Arrange
|
|
60
|
+
render(
|
|
61
|
+
<Tabbar
|
|
62
|
+
items={["Numbers"]}
|
|
63
|
+
selectedItem={"Numbers"}
|
|
64
|
+
onSelectItem={() => {}}
|
|
65
|
+
onClickClose={() => {}}
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Assert
|
|
70
|
+
expect(screen.getByRole("tab", {name: "Dismiss"})).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("does not show dismiss button without onClickClose callback", () => {
|
|
74
|
+
// Arrange
|
|
75
|
+
render(
|
|
76
|
+
<Tabbar
|
|
77
|
+
items={["Numbers"]}
|
|
78
|
+
selectedItem={"Numbers"}
|
|
79
|
+
onSelectItem={() => {}}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Assert
|
|
84
|
+
expect(
|
|
85
|
+
screen.queryByRole("tab", {name: "Dismiss"}),
|
|
86
|
+
).not.toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("handles onClickClose callback", () => {
|
|
90
|
+
// Arrange
|
|
91
|
+
const mockClickCloseCallback = jest.fn();
|
|
92
|
+
render(
|
|
93
|
+
<Tabbar
|
|
94
|
+
items={["Numbers", "Geometry"]}
|
|
95
|
+
selectedItem={"Numbers"}
|
|
96
|
+
onSelectItem={() => {}}
|
|
97
|
+
onClickClose={mockClickCloseCallback}
|
|
98
|
+
/>,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Assert
|
|
102
|
+
userEvent.click(screen.getByRole("tab", {name: "Dismiss"}));
|
|
103
|
+
expect(mockClickCloseCallback).toHaveBeenCalled();
|
|
104
|
+
});
|
|
65
105
|
});
|
|
@@ -64,32 +64,54 @@ const IconAsset = function ({tintColor, type}: Props): React.ReactElement {
|
|
|
64
64
|
case "Extras": {
|
|
65
65
|
return (
|
|
66
66
|
<svg
|
|
67
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
67
68
|
width="32"
|
|
68
69
|
height="32"
|
|
69
|
-
viewBox="0 0 32 32"
|
|
70
70
|
fill="none"
|
|
71
|
-
|
|
71
|
+
viewBox="0 0 32 32"
|
|
72
72
|
>
|
|
73
|
-
<g
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
<g clipPath="url(#a)">
|
|
74
|
+
<path
|
|
75
|
+
fill={tintColor}
|
|
76
|
+
fillRule="evenodd"
|
|
77
|
+
d="M6.127 10.454c.224-.032.403-.05.53-.05.591 0 1.137.153 1.63.459.398.239.713.529.94.872l.188-.248.005-.007.006-.006c.619-.703 1.347-1.07 2.178-1.07.691 0 1.269.204 1.71.626.454.403.686.91.686 1.51 0 .533-.156.994-.476 1.37-.312.383-.738.574-1.254.574-.345 0-.643-.101-.878-.317a1.1 1.1 0 0 1-.353-.843c0-.405.11-.76.342-1.051.153-.193.354-.352.596-.479a1.416 1.416 0 0 0-.492-.07c-.195 0-.316.016-.38.035-.53.226-.938.694-1.208 1.445l-.001.003c-.02.05-.085.289-.202.74-.115.443-.275 1.077-.481 1.901-.414 1.641-.633 2.547-.662 2.74l-.002.01a3.423 3.423 0 0 0-.067.673c0 .337.097.581.272.756.176.177.413.272.733.272.6 0 1.15-.247 1.657-.768.518-.549.864-1.146 1.044-1.791l.001-.004a1.2 1.2 0 0 1 .088-.224.368.368 0 0 1 .161-.164.564.564 0 0 1 .198-.056 2.19 2.19 0 0 1 .276-.014c.159 0 .305.016.42.064.059.025.12.063.167.122.05.063.073.137.073.213 0 .023-.004.048-.005.057a12.52 12.52 0 0 1-.046.245l-.004.015c-.281 1.026-.86 1.917-1.73 2.67l-.007.007c-.776.611-1.605.925-2.484.925-1.08 0-1.93-.45-2.53-1.33-.453.605-1.015 1.024-1.685 1.248l-.01.003-.011.002a3.23 3.23 0 0 1-.664.053c-.974 0-1.703-.35-2.13-1.078A2.05 2.05 0 0 1 2 19.437c0-.52.158-.975.478-1.349.326-.38.749-.572 1.252-.572.372 0 .69.091.913.31.224.218.318.531.318.898 0 .327-.078.621-.241.874a1.706 1.706 0 0 1-.707.597l-.018.009c.158.063.331.095.52.095.467 0 .902-.285 1.295-.966l.002-.005c.071-.115.185-.417.341-.938.154-.51.341-1.209.563-2.096v-.002c.095-.364.198-.767.31-1.21.11-.444.188-.78.235-1.014l.002-.013c.058-.216.098-.36.119-.425.077-.42.113-.709.113-.877 0-.342-.092-.588-.254-.762-.159-.171-.384-.267-.704-.267-.652 0-1.217.251-1.704.768l-.002.002A4.215 4.215 0 0 0 3.79 14.28a1.084 1.084 0 0 1-.065.207.41.41 0 0 1-.14.176l-.01.007-.012.006a.35.35 0 0 1-.104.03 1.16 1.16 0 0 1-.095.01 5.04 5.04 0 0 1-.275.006H2.67l-.061-.061c-.109-.11-.204-.247-.204-.41v-.015l.003-.015c.07-.472.335-1.05.768-1.723l.001-.002c.771-1.165 1.754-1.857 2.949-2.042h.002Z"
|
|
78
|
+
clipRule="evenodd"
|
|
79
|
+
/>
|
|
80
|
+
<path
|
|
81
|
+
fill={tintColor}
|
|
82
|
+
d="M21.084 10.284c.932-.008 2.301-.013 4.107-.013 1.325 0 2.327.003 3.007.007a75.812 75.812 0 0 1 .99.013c.025 0 .047.002.065.003h.002c.01 0 .04.003.067.01l.01.002.011.004c.201.07.37.183.488.347a.966.966 0 0 1 .169.574c0 .3-.078.568-.248.79-.168.221-.411.377-.708.479h-.002a1.01 1.01 0 0 1-.221.034 8.213 8.213 0 0 1-.35.016c-.29.008-.696.012-1.219.012h-1.39l-.038.223v.001c-.198 1.185-.295 2.156-.295 2.916 0 1.446.251 2.746.75 3.905l.004.007c.059.153.105.284.137.393.03.103.053.205.053.29 0 .359-.16.68-.44.961-.278.296-.63.445-1.041.445-.255 0-.492-.03-.654-.139l-.009-.006-.008-.006c-.126-.101-.236-.274-.338-.477l-.006-.012c-.331-.768-.49-1.722-.49-2.852 0-.595.007-1.002.025-1.212v-.005c.118-1.157.377-2.551.776-4.18v-.002c.024-.096.045-.18.061-.25h-1.948c-.008.038-.02.086-.034.143l-.002.007a35.14 35.14 0 0 0-.146.537c-.05.232-.1.448-.15.648v.001a230.673 230.673 0 0 1-1.312 4.936 41.285 41.285 0 0 1-.411 1.384c-.104.322-.19.557-.256.681-.115.262-.28.473-.5.617-.225.146-.49.212-.783.212-.449 0-.807-.173-1.006-.549l-.006-.011-.005-.012a1.37 1.37 0 0 1-.067-.486v-.326l.346-.745c1.24-2.61 2.136-4.858 2.695-6.747l.002-.008.094-.281h-.463c-.662 0-1.105.025-1.346.07-.198.04-.47.173-.824.43l-.007.005-.007.005c-.366.228-.69.542-.97.947-.044.069-.085.13-.125.18a.651.651 0 0 1-.141.136l-.027.017-.03.01a.8.8 0 0 1-.19.03c-.07.005-.156.008-.258.008-.17 0-.335-.021-.465-.09a.437.437 0 0 1-.216-.546c.014-.042.034-.086.057-.132.047-.093.113-.208.198-.343l.003-.005c1.147-1.745 2.311-2.774 3.508-2.96a2.345 2.345 0 0 1 .158-.015 60.295 60.295 0 0 1 1.369-.026Z"
|
|
83
|
+
/>
|
|
80
84
|
</g>
|
|
81
85
|
<defs>
|
|
82
|
-
<clipPath id="
|
|
86
|
+
<clipPath id="a">
|
|
83
87
|
<path
|
|
84
88
|
fill="#fff"
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
d="M0 0h28v11.457H0z"
|
|
90
|
+
transform="translate(2 10.271)"
|
|
87
91
|
/>
|
|
88
92
|
</clipPath>
|
|
89
93
|
</defs>
|
|
90
94
|
</svg>
|
|
91
95
|
);
|
|
92
96
|
}
|
|
97
|
+
case "Dismiss": {
|
|
98
|
+
return (
|
|
99
|
+
<svg
|
|
100
|
+
width="44"
|
|
101
|
+
height="44"
|
|
102
|
+
viewBox="0 0 44 44"
|
|
103
|
+
fill="none"
|
|
104
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
105
|
+
>
|
|
106
|
+
<path
|
|
107
|
+
fillRule="evenodd"
|
|
108
|
+
clipRule="evenodd"
|
|
109
|
+
d="M28.7071 15.2929C28.3166 14.9024 27.6834 14.9024 27.2929 15.2929L22 20.5858L16.7071 15.2929C16.3166 14.9024 15.6834 14.9024 15.2929 15.2929C14.9024 15.6834 14.9024 16.3166 15.2929 16.7071L20.5858 22L15.2929 27.2929C14.9024 27.6834 14.9024 28.3166 15.2929 28.7071C15.6834 29.0976 16.3166 29.0976 16.7071 28.7071L22 23.4142L27.2929 28.7071C27.6834 29.0976 28.3166 29.0976 28.7071 28.7071C29.0976 28.3166 29.0976 27.6834 28.7071 27.2929L23.4142 22L28.7071 16.7071C29.0976 16.3166 29.0976 15.6834 28.7071 15.2929Z"
|
|
110
|
+
fill={tintColor}
|
|
111
|
+
/>
|
|
112
|
+
</svg>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
93
115
|
default: {
|
|
94
116
|
// type as never;
|
|
95
117
|
throw new Error("Invalid icon type");
|
|
@@ -84,6 +84,8 @@ class TabbarItem extends React.Component<Props> {
|
|
|
84
84
|
onClick={onClick}
|
|
85
85
|
disabled={itemState === "disabled"}
|
|
86
86
|
aria-label={itemType}
|
|
87
|
+
aria-selected={itemState === "active"}
|
|
88
|
+
role="tab"
|
|
87
89
|
>
|
|
88
90
|
{({hovered, focused, pressed}) => {
|
|
89
91
|
const tintColor = imageTintColor(
|
|
@@ -9,33 +9,53 @@ const styles = StyleSheet.create({
|
|
|
9
9
|
tabbar: {
|
|
10
10
|
display: "flex",
|
|
11
11
|
flexDirection: "row",
|
|
12
|
+
justifyContent: "space-between",
|
|
12
13
|
paddingTop: 2,
|
|
13
14
|
paddingBottom: 2,
|
|
14
15
|
},
|
|
16
|
+
pages: {
|
|
17
|
+
display: "flex",
|
|
18
|
+
flexDirection: "row",
|
|
19
|
+
},
|
|
15
20
|
});
|
|
16
21
|
|
|
17
22
|
type Props = {
|
|
18
23
|
items: ReadonlyArray<TabbarItemType>;
|
|
19
24
|
selectedItem: TabbarItemType;
|
|
25
|
+
onClickClose?: () => void;
|
|
20
26
|
onSelectItem: (item: TabbarItemType) => void;
|
|
21
27
|
style?: StyleType;
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
function Tabbar(props: Props): React.ReactElement {
|
|
25
|
-
const {items, selectedItem, onSelectItem, style} = props;
|
|
31
|
+
const {items, onClickClose, selectedItem, onSelectItem, style} = props;
|
|
26
32
|
|
|
27
33
|
return (
|
|
28
|
-
<View style={[styles.tabbar, style]}>
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
<View style={[styles.tabbar, style]} role="tablist">
|
|
35
|
+
<View style={[styles.pages]}>
|
|
36
|
+
{items.map((item) => (
|
|
37
|
+
<TabbarItem
|
|
38
|
+
key={`tabbar-item-${item}`}
|
|
39
|
+
itemState={
|
|
40
|
+
item === selectedItem ? "active" : "inactive"
|
|
41
|
+
}
|
|
42
|
+
itemType={item}
|
|
43
|
+
onClick={() => {
|
|
44
|
+
onSelectItem(item);
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
))}
|
|
48
|
+
</View>
|
|
49
|
+
|
|
50
|
+
<View>
|
|
51
|
+
{onClickClose && (
|
|
52
|
+
<TabbarItem
|
|
53
|
+
itemState="inactive"
|
|
54
|
+
itemType="Dismiss"
|
|
55
|
+
onClick={onClickClose}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
</View>
|
|
39
59
|
</View>
|
|
40
60
|
);
|
|
41
61
|
}
|