Skip to content

Commit 909148c

Browse files
authored
[LEMS-2852] Answerless Expression (#2226)
## Summary: So the main thing with the Expression widget is that it was using answer data to determine which `extraKeys` to show in the `MathInput` keypad. For instance if a possible answer was `42i`, it would show `i` in the `Extra` tab on the keypad. This PR sets us up for removing answers by adding `extraKeys` to the data schema so it can be determined at publish time rather than read time. We simulate this by upgrading the widget to provide `extraKeys` when not present on the data. Issue: LEMS-2852 ## Test plan: - Go to an Expression widget that has a constant/variable in the answer - It should render the same, including having the constant/variable in the keypad - It should be answerable/scorable Author: handeyeco Reviewers: handeyeco, jeremywiebe Required Reviewers: Approved By: jeremywiebe Checks: ✅ 8 checks were successful Pull Request URL: #2226
1 parent a0aee41 commit 909148c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1055
-445
lines changed

.changeset/three-teachers-punch.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@khanacademy/math-input": major
3+
"@khanacademy/perseus-core": major
4+
"@khanacademy/perseus-editor": minor
5+
"@khanacademy/perseus": patch
6+
---
7+
8+
Answerless Expression: Expression can render and is interactive with answerless data

packages/math-input/src/components/__tests__/integration.test.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ import {userEvent as userEventLib} from "@testing-library/user-event";
1313
import MathQuill from "mathquill";
1414
import React, {useState} from "react";
1515

16-
import {KeypadType} from "../../enums";
1716
import MathInput from "../input/math-input";
1817
import {MobileKeypad} from "../keypad";
1918

20-
import type {KeypadConfiguration} from "../../types";
19+
import type {KeypadConfiguration} from "@khanacademy/perseus-core";
2120
import type {UserEvent} from "@testing-library/user-event";
2221

2322
const MQ = MathQuill.getInterface(3);
2423

2524
const defaultConfiguration: KeypadConfiguration = {
26-
keypadType: KeypadType.FRACTION,
25+
keypadType: "FRACTION",
2726
};
2827

2928
function InputWithContext({keypadConfiguration}) {
@@ -249,8 +248,8 @@ describe("math input integration", () => {
249248
});
250249

251250
it("handles fractions correctly in expression", async () => {
252-
const keypadConfiguration = {
253-
keypadType: KeypadType.EXPRESSION,
251+
const keypadConfiguration: KeypadConfiguration = {
252+
keypadType: "EXPRESSION",
254253
};
255254
render(
256255
<ConnectedMathInput keypadConfiguration={keypadConfiguration} />,

packages/math-input/src/components/input/math-wrapper.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import type {
2424
MathFieldInterface,
2525
MathFieldUpdaterCallback,
2626
} from "./mathquill-types";
27-
import type Key from "../../data/keys";
2827
import type {MathInputStrings} from "../../strings";
28+
import type {KeypadKey} from "@khanacademy/perseus-core";
2929

3030
/**
3131
* This file contains a wrapper around MathQuill so that we can provide a
@@ -35,7 +35,7 @@ import type {MathInputStrings} from "../../strings";
3535
class MathWrapper {
3636
mathField: MathFieldInterface; // MathQuill MathField input
3737
callbacks: any;
38-
mobileKeyTranslator: Record<Key, MathFieldUpdaterCallback>;
38+
mobileKeyTranslator: Record<KeypadKey, MathFieldUpdaterCallback>;
3939

4040
constructor(
4141
mathFieldMount,
@@ -97,7 +97,7 @@ class MathWrapper {
9797
* @param {Key} key - an enum representing the key that was pressed
9898
* @returns {object} a cursor object, consisting of a cursor context
9999
*/
100-
pressKey(key: Key) {
100+
pressKey(key: KeypadKey) {
101101
const cursor = this.getCursor();
102102
const translator = this.mobileKeyTranslator[key];
103103

packages/math-input/src/components/input/mathquill-types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type Key from "../../data/keys";
1+
import type {KeypadKey} from "@khanacademy/perseus-core";
22
import type MathQuill from "mathquill";
33

44
export type MathQuillInterface = MathQuill.v3.API;
@@ -17,7 +17,7 @@ export type MathFieldInterface = MathQuill.v3.EditableMathQuill & {
1717

1818
export type MathFieldUpdaterCallback = (
1919
mathField: MathFieldInterface,
20-
key: Key,
20+
key: KeypadKey,
2121
) => void;
2222

2323
/**

packages/math-input/src/components/key-handlers/handle-arrow.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
} from "../input/mathquill-helpers";
66
import {mathQuillInstance} from "../input/mathquill-instance";
77

8-
import type Key from "../../data/keys";
98
import type {MathFieldInterface} from "../input/mathquill-types";
9+
import type {KeypadKey} from "@khanacademy/perseus-core";
1010
import type MathQuill from "mathquill";
1111

1212
function handleLeftArrow(
@@ -57,7 +57,10 @@ function handleRightArrow(
5757
}
5858
}
5959

60-
export default function handleArrow(mathField: MathFieldInterface, key: Key) {
60+
export default function handleArrow(
61+
mathField: MathFieldInterface,
62+
key: KeypadKey,
63+
) {
6164
const cursor = mathField.cursor();
6265

6366
if (key === "LEFT") {

packages/math-input/src/components/key-handlers/handle-exponent.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import {MathFieldActionType} from "../../types";
22
import {mathQuillInstance} from "../input/mathquill-instance";
33

4-
import type Key from "../../data/keys";
54
import type {MathFieldInterface} from "../input/mathquill-types";
5+
import type {KeypadKey} from "@khanacademy/perseus-core";
66

77
const ArithmeticOperators = ["+", "-", "\\cdot", "\\times", "\\div"];
88
const EqualityOperators = ["=", "\\neq", "<", "\\leq", ">", "\\geq"];
99

1010
export default function handleExponent(
1111
mathField: MathFieldInterface,
12-
key: Key,
12+
key: KeypadKey,
1313
) {
1414
const cursor = mathField.cursor();
1515
// If there's an invalid operator preceding the cursor (anything that

packages/math-input/src/components/key-handlers/handle-jump-out.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
} from "../input/mathquill-helpers";
88
import {mathQuillInstance} from "../input/mathquill-instance";
99

10-
import type Key from "../../data/keys";
1110
import type {MathFieldInterface} from "../input/mathquill-types";
11+
import type {KeypadKey} from "@khanacademy/perseus-core";
1212

1313
const KeysForJumpContext = {
1414
[CursorContext.IN_PARENS]: "JUMP_OUT_PARENTHESES",
@@ -22,7 +22,7 @@ const KeysForJumpContext = {
2222
/**
2323
* Advances the cursor to the next logical position.
2424
*/
25-
function handleJumpOut(mathField: MathFieldInterface, key: Key): void {
25+
function handleJumpOut(mathField: MathFieldInterface, key: KeypadKey): void {
2626
const cursor = mathField.cursor();
2727
const context = getCursorContext(mathField);
2828

packages/math-input/src/components/key-handlers/key-translator.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import handleArrow from "./handle-arrow";
77
import handleExponent from "./handle-exponent";
88
import handleJumpOut from "./handle-jump-out";
99

10-
import type Key from "../../data/keys";
1110
import type {
1211
MathFieldInterface,
1312
MathFieldUpdaterCallback,
1413
} from "../input/mathquill-types";
14+
import type {KeypadKey} from "@khanacademy/perseus-core";
1515

1616
function buildGenericCallback(
1717
str: string,
@@ -75,7 +75,7 @@ type KeyTranslatorStrings = {
7575
export const getKeyTranslator = (
7676
locale: string,
7777
strings: KeyTranslatorStrings,
78-
): Record<Key, MathFieldUpdaterCallback> => ({
78+
): Record<KeypadKey, MathFieldUpdaterCallback> => ({
7979
EXP: handleExponent,
8080
EXP_2: handleExponent,
8181
EXP_3: handleExponent,

packages/math-input/src/components/keypad/__tests__/keypad-v2-mathquill.test.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import {createMathField} from "../../input/mathquill-instance";
99
import {getKeyTranslator} from "../../key-handlers/key-translator";
1010
import Keypad from "../index";
1111

12-
import type Key from "../../../data/keys";
1312
import type {MathFieldInterface} from "../../input/mathquill-types";
14-
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
13+
import type {
14+
AnalyticsEventHandlerFn,
15+
KeypadKey,
16+
} from "@khanacademy/perseus-core";
1517
import type {UserEvent} from "@testing-library/user-event";
1618

1719
type Props = {
@@ -59,7 +61,7 @@ function V2KeypadWithMathquill(props: Props) {
5961

6062
const keyTranslator = getKeyTranslator("en", strings);
6163

62-
function handleClickKey(key: Key) {
64+
function handleClickKey(key: KeypadKey) {
6365
if (!mathField) {
6466
return;
6567
}

packages/math-input/src/components/keypad/button-assets.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import * as React from "react";
1515

1616
import {useMathInputI18n} from "../i18n-context";
1717

18-
import type Key from "../../data/keys";
18+
import type {KeypadKey} from "@khanacademy/perseus-core";
1919

20-
type Props = {id: Key};
20+
type Props = {id: KeypadKey};
2121

2222
export default function ButtonAsset({id}: Props): React.ReactNode {
2323
const {locale, strings} = useMathInputI18n();

packages/math-input/src/components/keypad/keypad-mathquill.stories.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {getCursorContext} from "../input/mathquill-helpers";
88
import {createMathField} from "../input/mathquill-instance";
99
import {getKeyTranslator} from "../key-handlers/key-translator";
1010

11-
import type Key from "../../data/keys";
1211
import type {MathFieldInterface} from "../input/mathquill-types";
12+
import type {KeypadKey} from "@khanacademy/perseus-core";
1313

1414
import Keypad from "./index";
1515

@@ -52,7 +52,7 @@ export function V2KeypadWithMathquill() {
5252
tan: "tan",
5353
});
5454

55-
function handleClickKey(key: Key) {
55+
function handleClickKey(key: KeypadKey) {
5656
if (!mathField) {
5757
return;
5858
}

packages/math-input/src/components/keypad/keypad-pages/extras-page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import KeyConfigs from "../../../data/key-configs";
44
import {useMathInputI18n} from "../../i18n-context";
55
import {KeypadButton} from "../keypad-button";
66

7-
import type Key from "../../../data/keys";
87
import type {ClickKeyCallback} from "../../../types";
8+
import type {KeypadKey} from "@khanacademy/perseus-core";
99

1010
type Props = {
11-
extraKeys: ReadonlyArray<Key>;
11+
extraKeys: ReadonlyArray<KeypadKey>;
1212
onClickKey: ClickKeyCallback;
1313
};
1414

packages/math-input/src/components/keypad/keypad.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import NavigationPad from "./navigation-pad";
1515
import SharedKeys from "./shared-keys";
1616
import {expandedViewThreshold} from "./utils";
1717

18-
import type Key from "../../data/keys";
1918
import type {ClickKeyCallback, KeypadPageType} from "../../types";
2019
import type {CursorContext} from "../input/cursor-contexts";
21-
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
20+
import type {
21+
AnalyticsEventHandlerFn,
22+
KeypadKey,
23+
} from "@khanacademy/perseus-core";
2224

2325
type Props = {
24-
extraKeys?: ReadonlyArray<Key>;
26+
extraKeys?: ReadonlyArray<KeypadKey>;
2527
cursorContext?: (typeof CursorContext)[keyof typeof CursorContext];
2628
showDismiss?: boolean;
2729
expandedView?: boolean;

packages/math-input/src/components/keypad/mobile-keypad-internals.tsx

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ import AphroditeCssTransitionGroup from "../aphrodite-css-transition-group";
88
import Keypad from "./keypad";
99
import {expandedViewThreshold} from "./utils";
1010

11-
import type Key from "../../data/keys";
11+
import type {Cursor, KeyHandler, KeypadAPI} from "../../types";
1212
import type {
13-
Cursor,
13+
AnalyticsEventHandlerFn,
1414
KeypadConfiguration,
15-
KeyHandler,
16-
KeypadAPI,
17-
} from "../../types";
18-
import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core";
15+
KeypadKey,
16+
} from "@khanacademy/perseus-core";
1917
import type {StyleType} from "@khanacademy/wonder-blocks-core";
2018

2119
const AnimationDurationInMS = 200;
@@ -157,7 +155,7 @@ class MobileKeypadInternals
157155
return ReactDOM.findDOMNode(this);
158156
};
159157

160-
_handleClickKey(key: Key) {
158+
_handleClickKey(key: KeypadKey) {
161159
if (key === "DISMISS") {
162160
this.dismiss();
163161
return;

packages/math-input/src/components/keypad/navigation-button.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import * as React from "react";
66

77
import ButtonAsset from "./button-assets";
88

9-
import type Key from "../../data/keys";
109
import type {KeyConfig, ClickKeyCallback} from "../../types";
10+
import type {KeypadKey} from "@khanacademy/perseus-core";
1111

1212
type KeypadButtonProps = {
1313
// 0 indexed [x, y] position in keypad CSS grid
@@ -16,7 +16,7 @@ type KeypadButtonProps = {
1616
onClickKey: ClickKeyCallback;
1717
};
1818

19-
function getStyles(key: Key) {
19+
function getStyles(key: KeypadKey) {
2020
switch (key) {
2121
case "UP":
2222
return styles.up;

packages/math-input/src/data/key-configs.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
/**
22
* This file contains configuration settings for the buttons in the keypad.
33
*/
4-
import type Key from "./keys";
4+
55
import type {KeyType} from "../enums";
66
import type {MathInputStrings} from "../strings";
77
import type {KeyConfig} from "../types";
8+
import type {KeypadKey} from "@khanacademy/perseus-core";
89

910
type KeyConfigMapper = (args: {
10-
key: Key;
11+
key: KeypadKey;
1112
keyType?: KeyType;
1213
ariaLabel?: string;
1314
data?: string;
@@ -58,7 +59,7 @@ const getDefaultNumberFields: KeyConfigMapper = ({
5859
const KeyConfigs = (
5960
strings: MathInputStrings,
6061
): {
61-
[key in Key]: KeyConfig;
62+
[key in KeypadKey]: KeyConfig;
6263
} => ({
6364
// Basic math
6465
PLUS: {

0 commit comments

Comments
 (0)