From 600bf6acbbf76817e3bf7893f8f85188a538bd6a Mon Sep 17 00:00:00 2001 From: Tamara <60857422+Myranae@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:55:24 -0600 Subject: [PATCH] Create helper to build public widget options for categorizer (#2092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: This adds a function that takes categorizer's full widget options and filters out answer data. It also adds this function to the widget's widget export and adds a test confirming the function does what we expect. Issue: LEMS-2756 ## Test plan: - Confirm all checks pass - Confirm categorizer still works as expected Author: Myranae Reviewers: Myranae, handeyeco, jeremywiebe Required Reviewers: Approved By: jeremywiebe Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x) Pull Request URL: https://github.com/Khan/perseus/pull/2092 --- .changeset/moody-numbers-move.md | 5 +++ packages/perseus/src/types.ts | 13 ++++++++ packages/perseus/src/widgets.ts | 7 ++++ .../src/widgets/categorizer/categorizer.tsx | 2 ++ .../categorizer/categorizer.util.test.ts | 32 +++++++++++++++++++ .../widgets/categorizer/categorizer.util.ts | 29 +++++++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 .changeset/moody-numbers-move.md create mode 100644 packages/perseus/src/widgets/categorizer/categorizer.util.test.ts create mode 100644 packages/perseus/src/widgets/categorizer/categorizer.util.ts diff --git a/.changeset/moody-numbers-move.md b/.changeset/moody-numbers-move.md new file mode 100644 index 0000000000..dd785e6b52 --- /dev/null +++ b/.changeset/moody-numbers-move.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus": minor +--- + +Introduce a widget export function to filter out scoring data from widget options. Implement this function for the categorizer widget. \ No newline at end of file diff --git a/packages/perseus/src/types.ts b/packages/perseus/src/types.ts index 8d1a904d03..bcc52693c8 100644 --- a/packages/perseus/src/types.ts +++ b/packages/perseus/src/types.ts @@ -8,6 +8,7 @@ import type { UserInputMap, } from "./validation.types"; import type {WidgetPromptJSON} from "./widget-ai-utils/prompt-types"; +import type getCategorizerPublicWidgetOptions from "./widgets/categorizer/categorizer.util"; import type {KeypadAPI} from "@khanacademy/math-input"; import type { Hint, @@ -542,6 +543,12 @@ export type WidgetScorerFunction = ( locale?: string, ) => PerseusScore; +/** + * A union type of all the functions that provide public widget options. + */ +export type PublicWidgetOptionsFunction = + typeof getCategorizerPublicWidgetOptions; + export type WidgetExports< T extends React.ComponentType & Widget = React.ComponentType, > = Readonly<{ @@ -589,6 +596,12 @@ export type WidgetExports< */ scorer?: WidgetScorerFunction; + /** + * A function that provides a public version of the widget options that can + * be shared with the client. + */ + getPublicWidgetOptions?: PublicWidgetOptionsFunction; + getOneCorrectAnswerFromRubric?: ( rubric: Rubric, ) => string | null | undefined; diff --git a/packages/perseus/src/widgets.ts b/packages/perseus/src/widgets.ts index 74dccba384..958acfd31a 100644 --- a/packages/perseus/src/widgets.ts +++ b/packages/perseus/src/widgets.ts @@ -11,6 +11,7 @@ import type { WidgetExports, WidgetTransform, WidgetScorerFunction, + PublicWidgetOptionsFunction, } from "./types"; import type {PerseusWidget} from "@khanacademy/perseus-core"; import type * as React from "react"; @@ -141,6 +142,12 @@ export const getWidgetScorer = (name: string): WidgetScorerFunction | null => { return widgets[name]?.scorer ?? null; }; +export const getPublicWidgetOptionsFunction = ( + name: string, +): PublicWidgetOptionsFunction => { + return widgets[name]?.getPublicWidgetOptions ?? ((i) => i); +}; + export const getEditor = (name: string): Editor | null | undefined => { return _.has(editors, name) ? editors[name] : null; }; diff --git a/packages/perseus/src/widgets/categorizer/categorizer.tsx b/packages/perseus/src/widgets/categorizer/categorizer.tsx index 4d18004162..ab4c47e366 100644 --- a/packages/perseus/src/widgets/categorizer/categorizer.tsx +++ b/packages/perseus/src/widgets/categorizer/categorizer.tsx @@ -16,6 +16,7 @@ import sharedStyles from "../../styles/shared"; import Util from "../../util"; import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/categorizer/categorizer-ai-utils"; +import getCategorizerPublicWidgetOptions from "./categorizer.util"; import scoreCategorizer from "./score-categorizer"; import type {Widget, WidgetExports, WidgetProps} from "../../types"; @@ -328,4 +329,5 @@ export default { // TODO(LEMS-2656): remove TS suppression // @ts-expect-error: Type 'UserInput' is not assignable to type 'PerseusCSProgramUserInput'. scorer: scoreCategorizer, + getPublicWidgetOptions: getCategorizerPublicWidgetOptions, } satisfies WidgetExports; diff --git a/packages/perseus/src/widgets/categorizer/categorizer.util.test.ts b/packages/perseus/src/widgets/categorizer/categorizer.util.test.ts new file mode 100644 index 0000000000..3b95496540 --- /dev/null +++ b/packages/perseus/src/widgets/categorizer/categorizer.util.test.ts @@ -0,0 +1,32 @@ +import getCategorizerPublicWidgetOptions from "./categorizer.util"; + +import type {PerseusCategorizerWidgetOptions} from "@khanacademy/perseus-core"; + +describe("getCategorizerPublicWidgetOptions", () => { + it("returns an object without the answer data", () => { + const categorizerTestWidgetOptions: PerseusCategorizerWidgetOptions = { + values: [0, 1], + items: ["apples", "oranges"], + categories: ["citrus", "non-citrus"], + randomizeItems: true, + static: false, + highlightLint: false, + linterContext: { + contentType: "type", + paths: ["paths"], + stack: ["stack"], + }, + }; + + const publicWidgetOptions = getCategorizerPublicWidgetOptions( + categorizerTestWidgetOptions, + ); + + expect(publicWidgetOptions).toEqual({ + items: ["apples", "oranges"], + categories: ["citrus", "non-citrus"], + randomizeItems: true, + static: false, + }); + }); +}); diff --git a/packages/perseus/src/widgets/categorizer/categorizer.util.ts b/packages/perseus/src/widgets/categorizer/categorizer.util.ts new file mode 100644 index 0000000000..9e64059479 --- /dev/null +++ b/packages/perseus/src/widgets/categorizer/categorizer.util.ts @@ -0,0 +1,29 @@ +import type {PerseusCategorizerWidgetOptions} from "@khanacademy/perseus-core"; + +/** + * For details on the individual options, see the + * PerseusCategorizerWidgetOptions type + */ +type CategorizerPublicWidgetOptions = { + items: PerseusCategorizerWidgetOptions["items"]; + categories: PerseusCategorizerWidgetOptions["categories"]; + randomizeItems: PerseusCategorizerWidgetOptions["randomizeItems"]; + static: PerseusCategorizerWidgetOptions["static"]; +}; + +/** + * Given a PerseusCategorizerWidgetOptions object, return a new object with only + * the public options that should be exposed to the client. + */ +function getCategorizerPublicWidgetOptions( + options: PerseusCategorizerWidgetOptions, +): CategorizerPublicWidgetOptions { + return { + items: options.items, + categories: options.categories, + randomizeItems: options.randomizeItems, + static: options.static, + }; +} + +export default getCategorizerPublicWidgetOptions;