From 4cfad4143018e79ead0e84dedb4c115665f12af0 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Tue, 24 Sep 2024 10:02:00 -0500 Subject: [PATCH 1/2] remove onInputError --- .../src/iframe-content-renderer.tsx | 3 +- .../src/__tests__/renderer-api.test.tsx | 14 -------- packages/perseus/src/perseus-api.tsx | 4 --- packages/perseus/src/renderer.tsx | 9 +++-- packages/perseus/src/types.ts | 6 ---- .../expression/expression-validator.test.ts | 24 ++----------- .../expression/expression-validator.ts | 13 ++----- .../src/widgets/expression/expression.tsx | 34 +++++-------------- .../widgets/expression/expression.types.ts | 6 ---- .../input-number/input-number-validator.ts | 11 ++---- .../src/widgets/input-number/input-number.tsx | 18 ++-------- 11 files changed, 23 insertions(+), 119 deletions(-) diff --git a/packages/perseus-editor/src/iframe-content-renderer.tsx b/packages/perseus-editor/src/iframe-content-renderer.tsx index a80a93950f..8497224edf 100644 --- a/packages/perseus-editor/src/iframe-content-renderer.tsx +++ b/packages/perseus-editor/src/iframe-content-renderer.tsx @@ -170,8 +170,7 @@ class IframeContentRenderer extends React.Component { this._lastData = data; // We can't use JSON.stringify/parse for this because the apiOptions - // includes the functions GroupMetadataEditor, groupAnnotator, - // onFocusChange, and onInputError. + // includes the functions GroupMetadataEditor, groupAnnotator, and onFocusChange. // @ts-expect-error - TS2339 - Property 'iframeDataStore' does not exist on type 'Window & typeof globalThis'. window.iframeDataStore[this.iframeID] = data; frame.contentWindow.postMessage(this.iframeID, "*"); diff --git a/packages/perseus/src/__tests__/renderer-api.test.tsx b/packages/perseus/src/__tests__/renderer-api.test.tsx index 3c02aa207d..aabc359ac9 100644 --- a/packages/perseus/src/__tests__/renderer-api.test.tsx +++ b/packages/perseus/src/__tests__/renderer-api.test.tsx @@ -126,20 +126,6 @@ describe("Perseus API", function () { }); }); - describe("onInputError", function () { - it("should call a callback when grading an empty input-number", function () { - let wasCalled; - const {renderer} = renderQuestion(inputNumber1Item.question, { - onInputError: function (widgetId) { - wasCalled = true; - }, - }); - - expect(renderer).toHaveInvalidInput(); - expect(wasCalled).toBe(true); - }); - }); - describe("CSS ClassNames", function () { describe("perseus-focused", function () { it("should be on an input-number exactly when focused", async function () { diff --git a/packages/perseus/src/perseus-api.tsx b/packages/perseus/src/perseus-api.tsx index 8cb18a2428..222f186dad 100644 --- a/packages/perseus/src/perseus-api.tsx +++ b/packages/perseus/src/perseus-api.tsx @@ -8,8 +8,6 @@ * * nothing if it is purely a bug fix. * * Callbacks passed to Renderer/ItemRenderer: - * * onInputError: - * Called when there is an error grading a widget * * onFocusChange: (newFocusPath, oldFocusPath, keypadDOMNode) * Called when the user focus changes. The first two parameters are `path` * arrays uniquely identifying the respect inputs. The third parameter, @@ -40,7 +38,6 @@ export const ApiOptions = { propTypes: PropTypes.shape({ isArticle: PropTypes.bool.isRequired, - onInputError: PropTypes.func.isRequired, onFocusChange: PropTypes.func.isRequired, GroupMetadataEditor: PropTypes.func.isRequired, showAlignmentOptions: PropTypes.bool.isRequired, @@ -139,7 +136,6 @@ export const ApiOptions = { defaults: { isArticle: false, isMobile: false, - onInputError: function () {}, onFocusChange: function () {}, GroupMetadataEditor: StubTagEditor, showAlignmentOptions: false, diff --git a/packages/perseus/src/renderer.tsx b/packages/perseus/src/renderer.tsx index fd33e52d60..bc0a22f6ad 100644 --- a/packages/perseus/src/renderer.tsx +++ b/packages/perseus/src/renderer.tsx @@ -1809,7 +1809,6 @@ class Renderer extends React.Component { [widgetId: string]: PerseusScore; } = () => { const widgetProps = this.state.widgetInfo; - const onInputError = this.getApiOptions().onInputError; const gradedWidgetIds = _.filter(this.widgetIds, (id) => { const props = widgetProps[id]; @@ -1826,10 +1825,10 @@ class Renderer extends React.Component { const widget = this.getWidgetInstance(id); // widget can be undefined if it hasn't yet been rendered if (widget && widget.simpleValidate) { - widgetScores[id] = widget.simpleValidate( - {...props?.options, scoring: true}, - onInputError, - ); + widgetScores[id] = widget.simpleValidate({ + ...props?.options, + scoring: true, + }); } }); diff --git a/packages/perseus/src/types.ts b/packages/perseus/src/types.ts index 0ca8712620..9f940fa470 100644 --- a/packages/perseus/src/types.ts +++ b/packages/perseus/src/types.ts @@ -174,11 +174,6 @@ export const InteractiveGraphLockedFeaturesFlags = [ */ export type APIOptions = Readonly<{ isArticle?: boolean; - onInputError?: ( - widgetId: any, - value: string, - message?: string | null | undefined, - ) => unknown; onFocusChange?: ( newFocusPath: FocusPath, oldFocusPath: FocusPath, @@ -447,7 +442,6 @@ export type APIOptionsWithDefaults = Readonly< isArticle: NonNullable; isMobile: NonNullable; onFocusChange: NonNullable; - onInputError: NonNullable; readOnly: NonNullable; setDrawingAreaAvailable: NonNullable< APIOptions["setDrawingAreaAvailable"] diff --git a/packages/perseus/src/widgets/expression/expression-validator.test.ts b/packages/perseus/src/widgets/expression/expression-validator.test.ts index d9364d41eb..801c5eaa65 100644 --- a/packages/perseus/src/widgets/expression/expression-validator.test.ts +++ b/packages/perseus/src/widgets/expression/expression-validator.test.ts @@ -5,24 +5,12 @@ import {expressionItem3Options} from "./expression.testdata"; describe("expression-validator", () => { it("should handle defined ungraded answer case with no error callback", function () { - const err = validate( - "x+1", - expressionItem3Options, - undefined, - mockStrings, - "en", - ); + const err = validate("x+1", expressionItem3Options, mockStrings, "en"); expect(err).toHaveInvalidInput(); }); it("should handle invalid expression answer with no error callback", function () { - const err = validate( - "x+^1", - expressionItem3Options, - undefined, - mockStrings, - "en", - ); + const err = validate("x+^1", expressionItem3Options, mockStrings, "en"); expect(err).toHaveInvalidInput(); }); @@ -30,7 +18,6 @@ describe("expression-validator", () => { const result = validate( "y+1", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -41,7 +28,6 @@ describe("expression-validator", () => { const result = validate( "2+2", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -52,7 +38,6 @@ describe("expression-validator", () => { const result = validate( "z+1", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -64,7 +49,6 @@ describe("expression-validator", () => { const result1 = validate( "z+1", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -74,7 +58,6 @@ describe("expression-validator", () => { const result2 = validate( "a+1", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -85,7 +68,6 @@ describe("expression-validator", () => { const result = validate( "z+1.0", expressionItem3Options, - undefined, mockStrings, "en", ); @@ -96,7 +78,6 @@ describe("expression-validator", () => { const result = validate( "z+1,0", expressionItem3Options, - undefined, mockStrings, "fr", ); @@ -107,7 +88,6 @@ describe("expression-validator", () => { const result = validate( "z+1,0", expressionItem3Options, - undefined, mockStrings, "en", ); diff --git a/packages/perseus/src/widgets/expression/expression-validator.ts b/packages/perseus/src/widgets/expression/expression-validator.ts index 00de31e45f..d73d0995fa 100644 --- a/packages/perseus/src/widgets/expression/expression-validator.ts +++ b/packages/perseus/src/widgets/expression/expression-validator.ts @@ -7,7 +7,7 @@ import KhanAnswerTypes from "../../util/answer-types"; import getDecimalSeparator from "./get-decimal-separator"; -import type {Rubric, OnInputErrorFunctionType} from "./expression.types"; +import type {Rubric} from "./expression.types"; import type {PerseusExpressionAnswerForm} from "../../perseus-types"; import type {PerseusStrings} from "../../strings"; import type {PerseusScore} from "../../types"; @@ -34,8 +34,6 @@ import type {Score} from "../../util/answer-types"; function expressionValidator( userInput: string, rubric: Rubric, - // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'. - onInputError: OnInputErrorFunctionType = function () {}, strings: PerseusStrings, locale: string, ): PerseusScore { @@ -150,16 +148,9 @@ function expressionValidator( }; } if (matchingAnswerForm.considered === "ungraded") { - // We matched an ungraded answer form - return "invalid", which - // will let the user try again with no penalty - const apiResult = onInputError( - null, // Reserved for some widget identifier - userInput, - matchMessage, - ); return { type: "invalid", - message: apiResult === false ? null : matchMessage, + message: matchMessage, }; } // We matched a graded answer form, so we can now tell the user diff --git a/packages/perseus/src/widgets/expression/expression.tsx b/packages/perseus/src/widgets/expression/expression.tsx index d3c6bdd231..59d7d807a3 100644 --- a/packages/perseus/src/widgets/expression/expression.tsx +++ b/packages/perseus/src/widgets/expression/expression.tsx @@ -20,7 +20,7 @@ import a11y from "../../util/a11y"; import expressionValidator from "./expression-validator"; import getDecimalSeparator from "./get-decimal-separator"; -import type {Rubric, OnInputErrorFunctionType} from "./expression.types"; +import type {Rubric} from "./expression.types"; import type {DependenciesContext} from "../../dependencies"; import type {PerseusExpressionWidgetOptions} from "../../perseus-types"; import type {PerseusStrings} from "../../strings"; @@ -125,18 +125,10 @@ export class Expression extends React.Component { static validate( userInput: string, rubric: Rubric, - // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'. - onInputError: OnInputErrorFunctionType = function () {}, strings: PerseusStrings, locale: string, ): PerseusScore { - return expressionValidator( - userInput, - rubric, - onInputError, - strings, - locale, - ); + return expressionValidator(userInput, rubric, strings, locale); } static getUserInputFromProps(props: Props): string { @@ -209,16 +201,9 @@ export class Expression extends React.Component { showErrorStyle: false, }); if (!this.parse(this.props.value, this.props).parsed) { - const apiResult = this.props.apiOptions.onInputError( - null, // reserved for some widget identifier - this.props.value, - this.context.strings.ERROR_TITLE, - ); - if (apiResult !== false) { - this.setState({ - invalid: true, - }); - } + this.setState({ + invalid: true, + }); } } }; @@ -235,14 +220,13 @@ export class Expression extends React.Component { } }; - simpleValidate: ( - rubric: Rubric & {scoring?: boolean}, - onInputError: OnInputErrorFunctionType, - ) => PerseusScore = ({scoring, ...rubric}, onInputError) => { + simpleValidate: (rubric: Rubric & {scoring?: boolean}) => PerseusScore = ({ + scoring, + ...rubric + }) => { const score = expressionValidator( this.getUserInput(), rubric, - onInputError || function () {}, this.context.strings, this.context.locale, ); diff --git a/packages/perseus/src/widgets/expression/expression.types.ts b/packages/perseus/src/widgets/expression/expression.types.ts index 6f999e3ae5..0c3cf9c50f 100644 --- a/packages/perseus/src/widgets/expression/expression.types.ts +++ b/packages/perseus/src/widgets/expression/expression.types.ts @@ -1,9 +1,3 @@ import type {PerseusExpressionWidgetOptions} from "../../perseus-types"; export type Rubric = PerseusExpressionWidgetOptions; - -export type OnInputErrorFunctionType = ( - arg1?: any, - arg2?: any, - arg3?: any, -) => boolean | null | undefined; diff --git a/packages/perseus/src/widgets/input-number/input-number-validator.ts b/packages/perseus/src/widgets/input-number/input-number-validator.ts index 068ef5acf5..c9de5165e6 100644 --- a/packages/perseus/src/widgets/input-number/input-number-validator.ts +++ b/packages/perseus/src/widgets/input-number/input-number-validator.ts @@ -3,7 +3,7 @@ import KhanAnswerTypes from "../../util/answer-types"; import type {Rubric} from "./input-number.types"; import type {PerseusStrings} from "../../strings"; -import type {APIOptions, PerseusScore} from "@khanacademy/perseus"; +import type {PerseusScore} from "@khanacademy/perseus"; const ParseTex = TexWrangler.parseTex; @@ -48,7 +48,6 @@ function inputNumberValidator( }, rubric: Rubric, strings: PerseusStrings, - onInputError: APIOptions["onInputError"] = () => {}, ): PerseusScore { if (rubric.answerType == null) { rubric.answerType = "number"; @@ -78,15 +77,9 @@ function inputNumberValidator( // TODO(eater): Seems silly to translate result to this invalid/points // thing and immediately translate it back in ItemRenderer.scoreInput() if (result.empty) { - // TODO(FEI-3867): remove null-check once we have APIOptionsInternal - const apiResult = onInputError?.( - null, // reserved for some widget identifier - state.currentValue, - result.message, - ); return { type: "invalid", - message: apiResult === false ? null : result.message, + message: result.message, }; } return { diff --git a/packages/perseus/src/widgets/input-number/input-number.tsx b/packages/perseus/src/widgets/input-number/input-number.tsx index 043adcda19..b890db9c29 100644 --- a/packages/perseus/src/widgets/input-number/input-number.tsx +++ b/packages/perseus/src/widgets/input-number/input-number.tsx @@ -14,13 +14,7 @@ import inputNumberValidator, {answerTypes} from "./input-number-validator"; import type {Rubric} from "./input-number.types"; import type {PerseusInputNumberWidgetOptions} from "../../perseus-types"; import type {PerseusStrings} from "../../strings"; -import type { - APIOptions, - Path, - PerseusScore, - WidgetExports, - WidgetProps, -} from "../../types"; +import type {Path, PerseusScore, WidgetExports, WidgetProps} from "../../types"; const formExamples = { integer: function (options, strings: PerseusStrings) { @@ -104,9 +98,8 @@ class InputNumber extends React.Component { }, rubric: Rubric, strings: PerseusStrings, - onInputError: APIOptions["onInputError"] = () => {}, ): PerseusScore { - return inputNumberValidator(state, rubric, strings, onInputError); + return inputNumberValidator(state, rubric, strings); } static getUserInputFromProps(props: Props): { @@ -200,16 +193,11 @@ class InputNumber extends React.Component { return InputNumber.getUserInputFromProps(this.props); }; - simpleValidate: ( - rubric: Rubric, - onInputError?: APIOptions["onInputError"], - ) => PerseusScore = (rubric, onInputError) => { - onInputError = onInputError || function () {}; + simpleValidate: (rubric: Rubric) => PerseusScore = (rubric) => { return inputNumberValidator( this.getUserInput(), rubric, this.context.strings, - onInputError, ); }; From 81a4c9db67a3ac60fa180fa0fa71db6a2aafc2eb Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Tue, 24 Sep 2024 10:16:58 -0500 Subject: [PATCH 2/2] changeset --- .changeset/tough-snails-double.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/tough-snails-double.md diff --git a/.changeset/tough-snails-double.md b/.changeset/tough-snails-double.md new file mode 100644 index 0000000000..5e6fa48952 --- /dev/null +++ b/.changeset/tough-snails-double.md @@ -0,0 +1,6 @@ +--- +"@khanacademy/perseus": major +"@khanacademy/perseus-editor": patch +--- + +Remove unused onInputError from APIOptions