From 8947da6f4fd9a26211426308c227a097a6e3b21a Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Fri, 13 Feb 2026 14:30:29 -0600 Subject: [PATCH 1/2] [LEMS-3560/autofill-definition] Autofill definition toggle prompt --- .../perseus-editor/src/editor-page.test.tsx | 58 +++++++++++++++++++ packages/perseus-editor/src/editor.tsx | 13 +++-- .../src/widgets/definition-editor.tsx | 12 ++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/packages/perseus-editor/src/editor-page.test.tsx b/packages/perseus-editor/src/editor-page.test.tsx index 358f037df59..823f0a6c286 100644 --- a/packages/perseus-editor/src/editor-page.test.tsx +++ b/packages/perseus-editor/src/editor-page.test.tsx @@ -278,4 +278,62 @@ describe("EditorPage", () => { screen.getByDisplayValue(/Updated content from parent/), ).toBeInTheDocument(); }); + + it("should call getStartWidgetOptions if available", async () => { + const onChangeMock = jest.fn(); + + const startRenderer: PerseusRenderer = { + content: "That's an onomatopoeia!", + widgets: {}, + images: {}, + }; + + render( + {}} + previewDevice="desktop" + previewURL="" + itemId="itemId" + developerMode={false} + jsonMode={false} + widgetsAreOpen={true} + />, + ); + + const textarea = screen.getByRole("textbox") as HTMLTextAreaElement; + expect(textarea).toBeInTheDocument(); + + textarea.setSelectionRange(10, 22); + + const select = screen.getByTestId("editor__widget-select"); + await userEvent.selectOptions(select, "Definition"); + + expect(onChangeMock).toHaveBeenCalledWith( + { + answerArea: {}, + question: { + content: "That's an [[☃ definition 1]]!", + images: {}, + widgets: { + "definition 1": { + options: { + definition: "", + togglePrompt: "onomatopoeia", + }, + type: "definition", + version: { + major: 0, + minor: 0, + }, + }, + }, + }, + }, + expect.any(Function), + undefined, + ); + }); }); diff --git a/packages/perseus-editor/src/editor.tsx b/packages/perseus-editor/src/editor.tsx index d326963ca15..e6e3942700d 100644 --- a/packages/perseus-editor/src/editor.tsx +++ b/packages/perseus-editor/src/editor.tsx @@ -659,6 +659,7 @@ class Editor extends React.Component { const isBlock = CoreWidgetRegistry.getDefaultAlignment(widgetType) === "block"; + const selectedText = oldContent.slice(cursorRange[0], cursorRange[1]); const prelude = oldContent.slice(0, cursorRange[0]); const postlude = oldContent.slice(cursorRange[1]); @@ -672,8 +673,12 @@ class Editor extends React.Component { const newContent = newPrelude + widgetContent + newPostlude; const newWidgets = {...this.props.widgets}; + const widgetEditor = Widgets.getEditor(widgetType); + const startWidgetOptions = + widgetEditor?.getStartWidgetOptions?.(selectedText); + const defaultProps = widgetEditor?.defaultProps; newWidgets[id] = { - options: Widgets.getEditor(widgetType)?.defaultProps, + options: startWidgetOptions || defaultProps, type: widgetType, // Track widget version on creation, so that a widget editor // without a valid version prop can only possibly refer to a @@ -860,7 +865,6 @@ class Editor extends React.Component { let pieces; let widgets; let underlayPieces; - let widgetsDropDown; let templatesDropDown; let widgetsAndTemplates; let wordCountDisplay; @@ -920,8 +924,7 @@ class Editor extends React.Component { } } - this.widgetIds = _.keys(widgets); - widgetsDropDown = ; + this.widgetIds = Object.keys(widgets); const insertTemplateString = "Insert template\u2026"; templatesDropDown = ( @@ -957,7 +960,7 @@ class Editor extends React.Component { widgetsAndTemplates = (
- {widgetsDropDown} + {templatesDropDown} {wordCountDisplay}
diff --git a/packages/perseus-editor/src/widgets/definition-editor.tsx b/packages/perseus-editor/src/widgets/definition-editor.tsx index d5e6797fd72..e1aa9b7dce0 100644 --- a/packages/perseus-editor/src/widgets/definition-editor.tsx +++ b/packages/perseus-editor/src/widgets/definition-editor.tsx @@ -31,6 +31,18 @@ class DefinitionEditor extends React.Component { static defaultProps: DefinitionDefaultWidgetOptions = definitionLogic.defaultWidgetOptions; + static getStartWidgetOptions(selectedText?: string) { + const defaultWidgetOptions = { + ...definitionLogic.defaultWidgetOptions, + }; + + if (selectedText) { + defaultWidgetOptions.togglePrompt = selectedText; + } + + return defaultWidgetOptions; + } + change: (arg1: any, arg2: any, arg3: any) => any = (...args) => { return Changeable.change.apply(this, args); }; From 58608da799b95e7b2028890545e1411493354114 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Fri, 13 Feb 2026 14:37:48 -0600 Subject: [PATCH 2/2] [LEMS-3560/autofill-definition] respond to Jeremy's feedback --- packages/perseus-editor/src/editor.tsx | 12 ++++++++++-- .../perseus-editor/src/widgets/definition-editor.tsx | 7 ++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/perseus-editor/src/editor.tsx b/packages/perseus-editor/src/editor.tsx index e6e3942700d..567a4ffd9a4 100644 --- a/packages/perseus-editor/src/editor.tsx +++ b/packages/perseus-editor/src/editor.tsx @@ -150,6 +150,10 @@ type State = { textAreaValue: string; }; +export type StartWidgetOptionsContext = { + selectedText: string; +}; + // eslint-disable-next-line react/no-unsafe class Editor extends React.Component { lastUserValue: string | null | undefined; @@ -674,8 +678,12 @@ class Editor extends React.Component { const newWidgets = {...this.props.widgets}; const widgetEditor = Widgets.getEditor(widgetType); - const startWidgetOptions = - widgetEditor?.getStartWidgetOptions?.(selectedText); + const startWidgetOptionsContext: StartWidgetOptionsContext = { + selectedText, + }; + const startWidgetOptions = widgetEditor?.getStartWidgetOptions?.( + startWidgetOptionsContext, + ); const defaultProps = widgetEditor?.defaultProps; newWidgets[id] = { options: startWidgetOptions || defaultProps, diff --git a/packages/perseus-editor/src/widgets/definition-editor.tsx b/packages/perseus-editor/src/widgets/definition-editor.tsx index e1aa9b7dce0..76eb6b909f1 100644 --- a/packages/perseus-editor/src/widgets/definition-editor.tsx +++ b/packages/perseus-editor/src/widgets/definition-editor.tsx @@ -7,6 +7,7 @@ import _ from "underscore"; import Editor from "../editor"; +import type {StartWidgetOptionsContext} from "../editor"; import type {DefinitionDefaultWidgetOptions} from "@khanacademy/perseus-core"; const {TextInput} = components; @@ -31,13 +32,13 @@ class DefinitionEditor extends React.Component { static defaultProps: DefinitionDefaultWidgetOptions = definitionLogic.defaultWidgetOptions; - static getStartWidgetOptions(selectedText?: string) { + static getStartWidgetOptions(context: StartWidgetOptionsContext) { const defaultWidgetOptions = { ...definitionLogic.defaultWidgetOptions, }; - if (selectedText) { - defaultWidgetOptions.togglePrompt = selectedText; + if (context.selectedText) { + defaultWidgetOptions.togglePrompt = context.selectedText; } return defaultWidgetOptions;