Skip to content

Don't set default apiOptions - they're provided always#3851

Merged
jeremywiebe merged 4 commits into
jer/feature-preview-ngfrom
jer/bugfix-editor-crash-adding-widget
Jul 10, 2026
Merged

Don't set default apiOptions - they're provided always#3851
jeremywiebe merged 4 commits into
jer/feature-preview-ngfrom
jer/bugfix-editor-crash-adding-widget

Conversation

@jeremywiebe

@jeremywiebe jeremywiebe commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary:

Adding a widget to the editor was crashing the preview with a DataCloneError because postMessage can't serialize functions. The crash traced back to three widget editors — ExpressionEditor, NumericInputEditor, and FreeResponseEditor — each including apiOptions: ApiOptions.defaults in their static defaultProps.

ApiOptions.defaults contains fields that cannot be passed through the structured-clone algorithm used by postMessage for sending data to the preview iframe.

The bug surfaced because Editor._addWidgetToContent uses widgetEditor.defaultProps as the initial options for a newly added widget. That put the unserializable values directly into the widget data, which then flowed through postMessage to the preview iframe.

The fix is to remove apiOptions from those three editors' defaultProps. Widget editors should never provide a default apiOptions — the WidgetEditor wrapper always supplies it as a prop, so having it in defaultProps was both redundant and harmful.

Widget editor tests have been also updated to handle apiOptions being required and not defaulted now.

Issue: LEMS-3741

Test plan:

  • Open the EditorPage demo in Storybook, add an expression widget to the content, and confirm the preview no longer crashes with a DataCloneError
  • Run pnpm test

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Size Change: -15 B (0%)

Total Size: 517 kB

📦 View Changed
Filename Size Change
packages/perseus-editor/dist/es/index.js 104 kB -15 B (-0.01%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.6 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 6.31 kB
packages/math-input/dist/es/index.js 98.5 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-core/dist/es/index.item-splitting.js 12.9 kB
packages/perseus-core/dist/es/index.js 27.7 kB
packages/perseus-linter/dist/es/index.js 9.79 kB
packages/perseus-score/dist/es/index.js 9.86 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/perseus/dist/es/index.js 204 kB
packages/perseus/dist/es/strings.js 12.4 kB
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.71 kB

compressed-size-action

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (c828848) and published it to npm. You
can install it using the tag PR3851.

Example:

pnpm add @khanacademy/perseus@PR3851

If you are working in Khan Academy's frontend, you can run the below command.

./dev/tools/bump_perseus_version.ts -t PR3851

If you are working in Khan Academy's webapp, you can run the below command.

./dev/tools/bump_perseus_version.js -t PR3851

import type {PropsFor} from "@khanacademy/wonder-blocks-core";
import type {UserEvent} from "@testing-library/user-event";

const HarnessedEditor = React.forwardRef<

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted to create a "harnessed editor" which has the onChange and (now required) apiOptions defaulted. This allows us to render the editor in each test without a bunch of duplication, but also allows providing onChange where needed.

This pattern is now duplicated across three editors: expression-editor, free-response-editor, and numeric-input-editor... so that could get deduped eventually.

});

it("should render", async () => {
render(<ExpressionEditor onChange={() => undefined} />);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I like that we don't provide the prop when it's irrelevant to the test.


static defaultProps = {
...expressionLogic.defaultWidgetOptions,
apiOptions: ApiOptions.defaults,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the core of the fix. Providing a default apiOptions causes them to get "smuggled" into the widget options that the preview system then tries to serialize across to the preview via postMessage().

@jeremywiebe jeremywiebe marked this pull request as ready for review June 30, 2026 20:11
@jeremywiebe jeremywiebe requested review from a team, catandthemachines and ivyolamit June 30, 2026 20:11
@jeremywiebe jeremywiebe changed the base branch from main to jer/feature-preview-ng June 30, 2026 21:07
@jeremywiebe jeremywiebe force-pushed the jer/bugfix-editor-crash-adding-widget branch from 8c6219e to c828848 Compare June 30, 2026 21:08

@catandthemachines catandthemachines left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks great. Thank you Jeremy!

…tions from defaultProps in ExpressionEditor, NumericInputEditor, and FreeResponseEditor. Its provided by the widget editor wrapper always.
@jeremywiebe jeremywiebe force-pushed the jer/bugfix-editor-crash-adding-widget branch from c828848 to 5912789 Compare July 10, 2026 17:34
@jeremywiebe jeremywiebe merged commit 8f74bfb into jer/feature-preview-ng Jul 10, 2026
10 checks passed
@jeremywiebe jeremywiebe deleted the jer/bugfix-editor-crash-adding-widget branch July 10, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants