@khanacademy/perseus 11.4.0 → 11.5.1
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 +24 -0
- package/dist/es/index.js +6 -7
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/widgets/expression.d.ts +2 -6
- package/package.json +3 -2
- package/src/widgets/__stories__/expression.stories.tsx +5 -1
- package/src/widgets/__tests__/expression-mobile.test.tsx +346 -0
- package/src/widgets/expression.tsx +11 -9
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { KeypadType } from "@khanacademy/math-input";
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import { useDependencies } from "../dependencies";
|
|
4
3
|
import type { PerseusExpressionWidgetOptions } from "../perseus-types";
|
|
5
4
|
import type { PerseusScore, WidgetProps } from "../types";
|
|
6
|
-
import type { Keys as Key } from "@khanacademy/math-input";
|
|
5
|
+
import type { Keys as Key, KeypadConfiguration } from "@khanacademy/math-input";
|
|
7
6
|
type InputPath = ReadonlyArray<string>;
|
|
8
7
|
type Rubric = PerseusExpressionWidgetOptions;
|
|
9
8
|
type RenderProps = {
|
|
@@ -79,10 +78,7 @@ export declare class Expression extends React.Component<Props, ExpressionState>
|
|
|
79
78
|
* to be included as keys on the keypad. These are scraped from the answer
|
|
80
79
|
* forms.
|
|
81
80
|
*/
|
|
82
|
-
declare const keypadConfigurationForProps: (widgetOptions: PerseusExpressionWidgetOptions) =>
|
|
83
|
-
keypadType: KeypadType;
|
|
84
|
-
extraKeys: string[];
|
|
85
|
-
};
|
|
81
|
+
declare const keypadConfigurationForProps: (widgetOptions: PerseusExpressionWidgetOptions) => KeypadConfiguration;
|
|
86
82
|
declare const _default: Readonly<{
|
|
87
83
|
name: string;
|
|
88
84
|
displayName: string;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "11.
|
|
6
|
+
"version": "11.5.1",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.1",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.2",
|
|
27
|
-
"@khanacademy/math-input": "^
|
|
27
|
+
"@khanacademy/math-input": "^13.1.0",
|
|
28
28
|
"@khanacademy/perseus-core": "1.1.0",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.4",
|
|
30
30
|
"@khanacademy/pure-markdown": "^0.2.6",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"jquery": "^2.1.1",
|
|
58
58
|
"katex": "^0.11.1",
|
|
59
59
|
"lodash.debounce": "^4.0.8",
|
|
60
|
+
"mathquill": "git+https://git@github.com/Khan/mathquill.git#32d9f351aaa68537170b3120a52e99b8def3a2c3",
|
|
60
61
|
"perseus-build-settings": "^0.2.0",
|
|
61
62
|
"prop-types": "^15.6.1",
|
|
62
63
|
"react": "16.14.0",
|
|
@@ -11,6 +11,7 @@ import expressionExport from "../expression";
|
|
|
11
11
|
import TestKeypadContextWrapper from "./test-keypad-context-wrapper";
|
|
12
12
|
|
|
13
13
|
import type {PerseusItem} from "../../perseus-types";
|
|
14
|
+
import type {Keys as Key} from "@khanacademy/math-input";
|
|
14
15
|
|
|
15
16
|
type StoryArgs = {
|
|
16
17
|
customKeypad: boolean;
|
|
@@ -34,8 +35,11 @@ const WrappedKeypadContext = (props: WrappedKeypadContextProps) => {
|
|
|
34
35
|
return (
|
|
35
36
|
<ItemRendererWithDebugUI
|
|
36
37
|
item={props.item}
|
|
38
|
+
// Hardcoding the V2 Keypad to true as the Storybook Args
|
|
39
|
+
// were not working.
|
|
37
40
|
apiOptions={{
|
|
38
41
|
customKeypad: props.customKeypad,
|
|
42
|
+
useV2Keypad: true,
|
|
39
43
|
}}
|
|
40
44
|
/>
|
|
41
45
|
);
|
|
@@ -63,7 +67,7 @@ export const DesktopKitchenSink = (args: StoryArgs): React.ReactElement => {
|
|
|
63
67
|
|
|
64
68
|
const keypadConfiguration = {
|
|
65
69
|
keypadType: KeypadType.EXPRESSION,
|
|
66
|
-
extraKeys: ["x", "y", "z"],
|
|
70
|
+
extraKeys: ["x", "y", "z"] as Key[],
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
return (
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {KeypadContext, MobileKeypad} from "@khanacademy/math-input";
|
|
3
|
+
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
4
|
+
import {
|
|
5
|
+
fireEvent,
|
|
6
|
+
render,
|
|
7
|
+
screen,
|
|
8
|
+
waitFor,
|
|
9
|
+
within,
|
|
10
|
+
} from "@testing-library/react";
|
|
11
|
+
import userEvent from "@testing-library/user-event";
|
|
12
|
+
import MathQuill from "mathquill";
|
|
13
|
+
import * as React from "react";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
testDependencies,
|
|
17
|
+
testDependenciesV2,
|
|
18
|
+
} from "../../../../../testing/test-dependencies";
|
|
19
|
+
import * as Dependencies from "../../dependencies";
|
|
20
|
+
import WrappedServerItemRenderer from "../../server-item-renderer";
|
|
21
|
+
import {registerWidget} from "../../widgets";
|
|
22
|
+
import {expressionItem2} from "../__testdata__/expression.testdata";
|
|
23
|
+
import ExpressionExport from "../expression";
|
|
24
|
+
|
|
25
|
+
import type {KeypadAPI} from "@khanacademy/math-input";
|
|
26
|
+
|
|
27
|
+
const MQ = MathQuill.getInterface(2);
|
|
28
|
+
|
|
29
|
+
function RendererWithContext({item}) {
|
|
30
|
+
return (
|
|
31
|
+
<KeypadContext.Consumer>
|
|
32
|
+
{({keypadElement}) => {
|
|
33
|
+
return (
|
|
34
|
+
<WrappedServerItemRenderer
|
|
35
|
+
apiOptions={{
|
|
36
|
+
isMobile: true,
|
|
37
|
+
customKeypad: true,
|
|
38
|
+
useV2Keypad: true,
|
|
39
|
+
}}
|
|
40
|
+
item={item}
|
|
41
|
+
problemNum={0}
|
|
42
|
+
reviewMode={false}
|
|
43
|
+
dependencies={testDependenciesV2}
|
|
44
|
+
keypadElement={keypadElement}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}}
|
|
48
|
+
</KeypadContext.Consumer>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function KeypadWithContext() {
|
|
53
|
+
return (
|
|
54
|
+
<KeypadContext.Consumer>
|
|
55
|
+
{({setKeypadElement}) => {
|
|
56
|
+
return (
|
|
57
|
+
<MobileKeypad
|
|
58
|
+
onElementMounted={setKeypadElement}
|
|
59
|
+
onDismiss={() => {}}
|
|
60
|
+
onAnalyticsEvent={async () => {}}
|
|
61
|
+
useV2Keypad
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}}
|
|
65
|
+
</KeypadContext.Consumer>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function ConnectedRenderer({item = expressionItem2}) {
|
|
70
|
+
const [keypadElement, setKeypadElement] =
|
|
71
|
+
React.useState<KeypadAPI | null>();
|
|
72
|
+
const [renderer, setRenderer] = React.useState<any>(null);
|
|
73
|
+
const [scrollableElement, setScrollableElement] =
|
|
74
|
+
React.useState<HTMLElement | null>();
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<RenderStateRoot>
|
|
78
|
+
<KeypadContext.Provider
|
|
79
|
+
value={{
|
|
80
|
+
setKeypadElement,
|
|
81
|
+
keypadElement,
|
|
82
|
+
setRenderer,
|
|
83
|
+
renderer,
|
|
84
|
+
setScrollableElement,
|
|
85
|
+
scrollableElement,
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<RendererWithContext item={item} />
|
|
89
|
+
<KeypadWithContext />
|
|
90
|
+
</KeypadContext.Provider>
|
|
91
|
+
</RenderStateRoot>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
describe("expression mobile", () => {
|
|
96
|
+
beforeAll(() => {
|
|
97
|
+
registerWidget("expression", ExpressionExport);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
102
|
+
testDependencies,
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
// The Renderer uses a timer to wait for widgets to complete rendering.
|
|
108
|
+
// If we don't spin the timers here, then the timer fires in the test
|
|
109
|
+
// _after_ and breaks it because we do setState() in the callback,
|
|
110
|
+
// and by that point the component has been unmounted.
|
|
111
|
+
jest.runOnlyPendingTimers();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("renders input", () => {
|
|
115
|
+
render(<ConnectedRenderer />);
|
|
116
|
+
|
|
117
|
+
expect(
|
|
118
|
+
screen.getByLabelText(
|
|
119
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
120
|
+
),
|
|
121
|
+
).toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("shows the keypad after input interaction", async () => {
|
|
125
|
+
render(<ConnectedRenderer />);
|
|
126
|
+
|
|
127
|
+
const input = screen.getByLabelText(
|
|
128
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
fireEvent.touchStart(input);
|
|
132
|
+
|
|
133
|
+
// waiting because `visibility` is animated
|
|
134
|
+
await waitFor(() =>
|
|
135
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("is possible to use the keypad", async () => {
|
|
140
|
+
render(<ConnectedRenderer />);
|
|
141
|
+
|
|
142
|
+
const input = screen.getByLabelText(
|
|
143
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
fireEvent.touchStart(input);
|
|
147
|
+
|
|
148
|
+
// waiting because `visibility` is animated
|
|
149
|
+
await waitFor(() =>
|
|
150
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
userEvent.click(screen.getByRole("button", {name: "1"}));
|
|
154
|
+
|
|
155
|
+
// MathQuill is problematic,
|
|
156
|
+
// this is the only way I know how to test the "input"
|
|
157
|
+
const mathquillInput =
|
|
158
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
159
|
+
document.getElementsByClassName("mq-root-block")[0] as HTMLElement;
|
|
160
|
+
const span1 = within(mathquillInput).getByText("1");
|
|
161
|
+
|
|
162
|
+
expect(span1).toBeVisible();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("is possible to type many numbers", async () => {
|
|
166
|
+
render(<ConnectedRenderer />);
|
|
167
|
+
|
|
168
|
+
const input = screen.getByLabelText(
|
|
169
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
fireEvent.touchStart(input);
|
|
173
|
+
|
|
174
|
+
// waiting because `visibility` is animated
|
|
175
|
+
await waitFor(() =>
|
|
176
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const testNumbers = [8, 6, 7, 5, 3, 0, 9];
|
|
180
|
+
testNumbers.forEach((num) => {
|
|
181
|
+
userEvent.click(screen.getByRole("button", {name: `${num}`}));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// MathQuill is problematic,
|
|
185
|
+
// this is the only way I know how to test the "input"
|
|
186
|
+
const mathquillInput =
|
|
187
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
188
|
+
document.getElementsByClassName("mq-root-block")[0] as HTMLElement;
|
|
189
|
+
|
|
190
|
+
testNumbers.forEach((num) => {
|
|
191
|
+
const span = within(mathquillInput).getByText(`${num}`);
|
|
192
|
+
|
|
193
|
+
expect(span).toBeVisible();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("can handle symbols", async () => {
|
|
198
|
+
render(<ConnectedRenderer />);
|
|
199
|
+
|
|
200
|
+
const input = screen.getByLabelText(
|
|
201
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
fireEvent.touchStart(input);
|
|
205
|
+
|
|
206
|
+
// waiting because `visibility` is animated
|
|
207
|
+
await waitFor(() =>
|
|
208
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
userEvent.click(screen.getByRole("button", {name: "4"}));
|
|
212
|
+
userEvent.click(screen.getByRole("button", {name: "2"}));
|
|
213
|
+
userEvent.click(screen.getByRole("button", {name: "Percent"}));
|
|
214
|
+
|
|
215
|
+
// MathQuill is problematic,
|
|
216
|
+
// this is the only way I know how to test the "input"
|
|
217
|
+
const mathquillInput =
|
|
218
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
219
|
+
document.getElementsByClassName("mq-root-block")[0] as HTMLElement;
|
|
220
|
+
const span4 = within(mathquillInput).getByText("4");
|
|
221
|
+
const span2 = within(mathquillInput).getByText("2");
|
|
222
|
+
const spanPercent = within(mathquillInput).getByText("%");
|
|
223
|
+
|
|
224
|
+
expect(span4).toBeVisible();
|
|
225
|
+
expect(span2).toBeVisible();
|
|
226
|
+
expect(spanPercent).toBeVisible();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("can do arithmetic", async () => {
|
|
230
|
+
render(<ConnectedRenderer />);
|
|
231
|
+
|
|
232
|
+
const input = screen.getByLabelText(
|
|
233
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
fireEvent.touchStart(input);
|
|
237
|
+
|
|
238
|
+
// waiting because `visibility` is animated
|
|
239
|
+
await waitFor(() =>
|
|
240
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
userEvent.click(screen.getByRole("button", {name: "4"}));
|
|
244
|
+
userEvent.click(screen.getByRole("button", {name: "Plus"}));
|
|
245
|
+
userEvent.click(screen.getByRole("button", {name: "2"}));
|
|
246
|
+
|
|
247
|
+
// MathQuill is problematic,
|
|
248
|
+
// this is the only way I know how to test the "input"
|
|
249
|
+
const mathquillInput =
|
|
250
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
251
|
+
document.getElementsByClassName("mq-root-block")[0] as HTMLElement;
|
|
252
|
+
const span4 = within(mathquillInput).getByText("4");
|
|
253
|
+
const spanPlus = within(mathquillInput).getByText("+");
|
|
254
|
+
const span2 = within(mathquillInput).getByText("2");
|
|
255
|
+
|
|
256
|
+
expect(span4).toBeVisible();
|
|
257
|
+
expect(spanPlus).toBeVisible();
|
|
258
|
+
expect(span2).toBeVisible();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("can navigate to operators page", async () => {
|
|
262
|
+
render(<ConnectedRenderer />);
|
|
263
|
+
|
|
264
|
+
const input = screen.getByLabelText(
|
|
265
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
fireEvent.touchStart(input);
|
|
269
|
+
|
|
270
|
+
// waiting because `visibility` is animated
|
|
271
|
+
await waitFor(() =>
|
|
272
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
userEvent.click(screen.getByRole("tab", {name: "Operators"}));
|
|
276
|
+
expect(screen.getByRole("button", {name: "Equals sign"})).toBeVisible();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("can navigate to geometry page", async () => {
|
|
280
|
+
render(<ConnectedRenderer />);
|
|
281
|
+
|
|
282
|
+
const input = screen.getByLabelText(
|
|
283
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
fireEvent.touchStart(input);
|
|
287
|
+
|
|
288
|
+
// waiting because `visibility` is animated
|
|
289
|
+
await waitFor(() =>
|
|
290
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
userEvent.click(screen.getByRole("tab", {name: "Geometry"}));
|
|
294
|
+
expect(screen.getByRole("button", {name: "Sine"})).toBeVisible();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("can navigate to extras page", async () => {
|
|
298
|
+
render(<ConnectedRenderer />);
|
|
299
|
+
|
|
300
|
+
const input = screen.getByLabelText(
|
|
301
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
fireEvent.touchStart(input);
|
|
305
|
+
|
|
306
|
+
// waiting because `visibility` is animated
|
|
307
|
+
await waitFor(() =>
|
|
308
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
userEvent.click(screen.getByRole("tab", {name: "Extras"}));
|
|
312
|
+
expect(screen.getByRole("button", {name: "x"})).toBeVisible();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("can write a full equation", async () => {
|
|
316
|
+
render(<ConnectedRenderer />);
|
|
317
|
+
|
|
318
|
+
const input = screen.getByLabelText(
|
|
319
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
fireEvent.touchStart(input);
|
|
323
|
+
|
|
324
|
+
// waiting because `visibility` is animated
|
|
325
|
+
await waitFor(() =>
|
|
326
|
+
expect(screen.getByRole("button", {name: "1"})).toBeVisible(),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
userEvent.click(screen.getByRole("tab", {name: "Extras"}));
|
|
330
|
+
userEvent.click(screen.getByRole("button", {name: "x"}));
|
|
331
|
+
userEvent.click(screen.getByRole("tab", {name: "Operators"}));
|
|
332
|
+
userEvent.click(screen.getByRole("button", {name: "Equals sign"}));
|
|
333
|
+
userEvent.click(screen.getByRole("tab", {name: "Geometry"}));
|
|
334
|
+
userEvent.click(screen.getByRole("button", {name: "Sine"}));
|
|
335
|
+
userEvent.click(screen.getByRole("tab", {name: "Numbers"}));
|
|
336
|
+
userEvent.click(screen.getByRole("button", {name: "9"}));
|
|
337
|
+
|
|
338
|
+
// MathQuill is problematic,
|
|
339
|
+
// this is how to get the value of the input directly from MQ
|
|
340
|
+
const mathquillInstance =
|
|
341
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
342
|
+
MQ(document.getElementsByClassName("mq-editable-field")[0]);
|
|
343
|
+
|
|
344
|
+
expect(mathquillInstance.latex()).toBe("x=\\sin\\left(9\\right)");
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -29,7 +29,7 @@ import type {
|
|
|
29
29
|
WidgetExports,
|
|
30
30
|
WidgetProps,
|
|
31
31
|
} from "../types";
|
|
32
|
-
import type {Keys as Key} from "@khanacademy/math-input";
|
|
32
|
+
import type {Keys as Key, KeypadConfiguration} from "@khanacademy/math-input";
|
|
33
33
|
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
|
|
34
34
|
|
|
35
35
|
type InputPath = ReadonlyArray<string>;
|
|
@@ -610,7 +610,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
610
610
|
*/
|
|
611
611
|
const keypadConfigurationForProps = (
|
|
612
612
|
widgetOptions: PerseusExpressionWidgetOptions,
|
|
613
|
-
) => {
|
|
613
|
+
): KeypadConfiguration => {
|
|
614
614
|
// Always use the Expression keypad, regardless of the button sets that have
|
|
615
615
|
// been enabled.
|
|
616
616
|
const keypadType = KeypadType.EXPRESSION;
|
|
@@ -642,20 +642,22 @@ const keypadConfigurationForProps = (
|
|
|
642
642
|
|
|
643
643
|
// TODO(charlie): Alert the keypad as to which of these symbols should be
|
|
644
644
|
// treated as functions.
|
|
645
|
-
const extraVariables = Object.keys(
|
|
646
|
-
|
|
645
|
+
const extraVariables = Object.keys(
|
|
646
|
+
uniqueExtraVariables,
|
|
647
|
+
).sort() as ReadonlyArray<Key>;
|
|
647
648
|
|
|
648
|
-
const extraConstants = Object.keys(
|
|
649
|
-
|
|
649
|
+
const extraConstants = Object.keys(
|
|
650
|
+
uniqueExtraConstants,
|
|
651
|
+
).sort() as ReadonlyArray<Key>;
|
|
650
652
|
|
|
651
|
-
|
|
653
|
+
let extraKeys = [...extraVariables, ...extraConstants];
|
|
652
654
|
if (!extraKeys.length) {
|
|
653
655
|
// If there are no extra symbols available, we include Pi anyway, so
|
|
654
656
|
// that the "extra symbols" button doesn't appear empty.
|
|
655
|
-
extraKeys
|
|
657
|
+
extraKeys = ["PI"];
|
|
656
658
|
}
|
|
657
659
|
|
|
658
|
-
return {keypadType, extraKeys};
|
|
660
|
+
return {keypadType, extraKeys, times: widgetOptions.times};
|
|
659
661
|
};
|
|
660
662
|
|
|
661
663
|
const propUpgrades = {
|