diff --git a/.changeset/rare-books-eat.md b/.changeset/rare-books-eat.md new file mode 100644 index 0000000000..a94926a185 --- /dev/null +++ b/.changeset/rare-books-eat.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus-editor": minor +--- + +Revert defensive code in ExpressionEditor that caused a crash when an expression config's answer form was missing a `key` property. diff --git a/packages/perseus-editor/src/widgets/__tests__/expression-editor.test.tsx b/packages/perseus-editor/src/widgets/__tests__/expression-editor.test.tsx new file mode 100644 index 0000000000..0658bfc09b --- /dev/null +++ b/packages/perseus-editor/src/widgets/__tests__/expression-editor.test.tsx @@ -0,0 +1,56 @@ +import {Dependencies} from "@khanacademy/perseus"; +import {render, screen} from "@testing-library/react"; +import * as React from "react"; + +import "@testing-library/jest-dom"; + +import {testDependencies} from "../../../../../testing/test-dependencies"; +import ExpressionEditor from "../expression-editor"; + +import type {PropsFor} from "@khanacademy/wonder-blocks-core"; + +describe("expression-editor", () => { + beforeEach(() => { + jest.spyOn(Dependencies, "getDependencies").mockReturnValue( + testDependencies, + ); + }); + + it("should render", async () => { + render( undefined} />); + + expect(await screen.findByText(/Add new answer/)).toBeInTheDocument(); + }); + + it("should render answerForms missing a key", async () => { + const answerForms: PropsFor["answerForms"] = [ + { + value: "x\\cdot3=y", + form: true, + simplify: true, + considered: "correct", + }, + { + value: "x^2=y", + form: true, + simplify: true, + considered: "wrong", + }, + { + value: "x=y\\cdotπ", + form: true, + simplify: true, + considered: "wrong", + }, + ]; + + render( + undefined} + answerForms={answerForms} + />, + ); + + expect(await screen.findByText(/π/)).toBeInTheDocument(); + }); +}); diff --git a/packages/perseus-editor/src/widgets/expression-editor.tsx b/packages/perseus-editor/src/widgets/expression-editor.tsx index bb3a721385..c1075b9f31 100644 --- a/packages/perseus-editor/src/widgets/expression-editor.tsx +++ b/packages/perseus-editor/src/widgets/expression-editor.tsx @@ -18,7 +18,6 @@ import SortableArea from "../components/sortable"; import type {PerseusExpressionWidgetOptions} from "@khanacademy/perseus"; const {InfoTip, PropCheckBox, TexButtons} = components; -const {getDependencies} = Dependencies; type Props = { widgetId?: any; @@ -38,11 +37,12 @@ type DefaultProps = { }; const parseAnswerKey = ({key}: AnswerForm): number => { - const parsedKey = Number.parseInt(key ?? ""); - if (Number.isNaN(parsedKey)) { - throw new Error(`Invalid answer key: ${key}`); - } - return parsedKey; + // We don't throw here because there is data stored in some + // exercises/articles where the answer forms don't have a key. If we throw, + // it blocks content editors from loading the page at all. + // TODO(Jeremy): find a way to handle these answer forms that are missing + // keys more gracefully. + return Number.parseInt(key ?? ""); }; // Pick a key that isn't currently used by an answer in answerForms @@ -182,7 +182,7 @@ class ExpressionEditor extends React.Component { }, ); - const {TeX} = getDependencies(); // OldExpression only + const {TeX} = Dependencies.getDependencies(); // OldExpression only buttonSetChoices.splice( 1, diff --git a/tsconfig.json b/tsconfig.json index 82fb009fb7..ed6fa87cf4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,5 +17,10 @@ "@khanacademy/pure-markdown": ["./packages/pure-markdown/src"], "@khanacademy/simple-markdown": ["./packages/simple-markdown/src"], }, + + /* Preference */ + // If this is disabled, TS will sometimes just delete dead code, which + // can be confusing! + "allowUnreachableCode": true }, }