Skip to content

[Feature] Preview Next Generation (NG)#3841

Draft
jeremywiebe wants to merge 8 commits into
mainfrom
jer/feature-preview-ng
Draft

[Feature] Preview Next Generation (NG)#3841
jeremywiebe wants to merge 8 commits into
mainfrom
jer/feature-preview-ng

Conversation

@jeremywiebe

Copy link
Copy Markdown
Collaborator

Summary:

This PR contains all of the already-landed PRs for the Preview NG work. It contains changes that move our preview system from an ad-hoc, untyped message passing system to a fully typed, hook-based communication.

I've created this PR mostly so I can get npm snapshots

Issue: LEMS-3741

Test plan:

## Summary:

*This PR is part of a series building a typed, hook-based preview system for the Perseus editor. The new system replaces the untyped `window.iframeDataStore` + raw `postMessage(string)` communication with structured, validated message passing via `usePreviewController` and `usePreviewPresenter` hooks. The new system is being built alongside the old one — no existing behavior changes until the final PR in the series flips the switch.*

Previous PRs in this series:
- #3464
- #3467
- #3474
- #3492
- #3495
- #3499
- #3578 

---

This is the Preview NG changeover PR! After this lands, Perseus will be exclusively using the new Preview communication system (no more legacy `iframeDataStore` + string-postMessage preview protocol). This means that all Storybook preview code now uses the new `usePreviewPresenter` hook, and all four editors (`EditorPage`, `ArticleEditor`, `HintEditor`, `ItemEditor`) switch from `IframeContentRenderer` to `PreviewWithIframe` (introduced in #3578).

The legacy `IframeContentRenderer` remains because some client's still use it directly. When this change is released, I will switch clients over as part of the Perseus release. The legacy component will be deleted in a later cleanup pass once that's done.


https://github.com/user-attachments/assets/a801bb4a-2cbd-4154-940f-09a2bd4037c5


NOTE: the issue of the keypad being clipped by the preview iframe is a longstanding issue and is unrelated to the changes in this PR. 


Issue: LEMS-3741

## Test plan:

- [x] `pnpm tsc` clean
- [x] `pnpm lint` clean for touched files
- [x] `pnpm test`
- [ ] Reviewer: open Storybook, navigate to the editor stories, and confirm each editor's preview renders content (question, hint, article single section, article-all). The behaviour should be identical to `main`.

Author: jeremywiebe

Reviewers: jeremywiebe, claude[bot], mark-fitzgerald

Required Reviewers:

Approved By: mark-fitzgerald

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful, ⌛ 1 check is pending

Pull Request URL: #3581
…iOptions (#3590)

## Summary:

*This PR is part of a series building a typed, hook-based preview system for the Perseus editor. The new system replaces the untyped `window.iframeDataStore` + raw `postMessage(string)` communication with structured, validated message passing via `usePreviewController` and `usePreviewPresenter` hooks. The new system is being built alongside the old one — no existing behavior changes until the final PR in the series flips the switch.*

Previous PRs in this series:
- #3464
- #3467
- #3474
- #3492
- #3495
- #3499
- #3578
- #3588
- #3581 
- #3590 

-- 

This PR is a message-field cleanup — it restructures the preview data shapes now that the editor migration is in place (#3581).

  * The data-shape changes: `ArticlePreviewData.json` is renamed to `article` (and is now a single `PerseusRenderer` for the one section being edited, not an array)
  * `QuestionPreviewData.initialHintsVisible` is dropped (it was always `0` in practice)
  * `article-all` previews now use a new `ArticleAllPreviewData` (`{article: ReadonlyArray<PerseusRenderer>, apiOptions}`) with a single shared `apiOptions` instead of the previous per-section `ArticlePreviewData[]`
  * The presenter hook's returned `data` is also now named `content`, so consumers read `content` instead of the awkward `message.content.data`.
  * The preview message types now type their `apiOptions` as a new `SerializableApiOptions` defined next to the stripping logic in `sanitize-api-options.ts`) rather than the full `APIOptions`. This makes the types honest about what actually survives the `postMessage` structured-clone boundary.

Issue: LEMS-3741

## Test plan:

- [x] `pnpm tsc`
- [x] `pnpm lint`
- [x] `pnpm test`
- [ ] Reviewer: open Storybook, navigate to the editor previews, and confirm:
  - Question preview still renders (no visible hints; behavior matches main)
  - Article single-section preview renders content
  - Article-all preview renders all sections concatenated with shared apiOptions
  - Hint preview unchanged

Author: jeremywiebe

Reviewers: claude[bot], jeremywiebe, mark-fitzgerald

Required Reviewers:

Approved By: mark-fitzgerald

Checks: ⏭️  1 check has been skipped, ✅ 11 checks were successful

Pull Request URL: #3590
…3762)

## Summary:

Adds an `"exercise"` variant to the typed preview protocol to support the full-exercise preview view.

Note that there is no preview rendering of this new type as that is (and will continue to be) handled by the consuming apps. This just plumbs the value through for use. 

Issue: LEMS-3741

## Test plan:

- `pnpm test`
- `pnpm tsc`

Author: jeremywiebe

Reviewers: mark-fitzgerald

Required Reviewers:

Approved By: mark-fitzgerald

Checks: ⏭️  1 check has been skipped, ✅ 11 checks were successful

Pull Request URL: #3762
…-editor (#3764)

## Summary

Exports two symbols from `@khanacademy/perseus-editor` that are needed by consumers: 

  * `PreviewWithIframe` — the component that hosts the preview iframe and the parent↔iframe message bridge.
  * `PreviewContent` — the discriminated-union type describing what a preview renders (question, hint, article-section, article-all, exercise).

Both already lived in the package; this just adds them to the public entry point (src/index.ts). No behavior change.

Issue: LEMS-3741 

## Test plan

  * `pnpm typecheck` passes — the new re-exports resolve.
  * No runtime change; nothing to exercise manually.

Author: jeremywiebe

Reviewers: anakaren-rojas

Required Reviewers:

Approved By: anakaren-rojas

Checks: ⏭️  1 check has been skipped, ✅ 11 checks were successful

Pull Request URL: #3764
## Summary:

A few small fixes found when testing preview in Storybook: 

  * **Fixes**: wires user-input into all previews so that widgets that rely on user-input wiring don't freeze, or worse, throw.

  * **Fixes**: The hint preview now uses the right renderer (`HintRenderer`) and respects the `pos` (hint position), which restores the hint labeling/semantics, brings its own `UserInputManager`, and forces `customKeypad: false` like production hint
rendering does.

  * **Fixes**: the `MobileKeypad` is only mounted when `isMobile: true` to avoid extra spacing/border at the bottom of the preview.

  * **Fixes**: accesses `isMobile`/`hasLintGutter` through `usePreviewPresenter` instead of reaching up to `window.frameElement`'s `data-*` attributes.

Issue: LEMS-3741

## Test plan:

- `pnpm typecheck`, `pnpm lint`, and `pnpm test`
- Open the EditorPage story in Storybook and add an input widget (e.g. numeric-input or expression) — the preview should render it and accept typed input instead of crashing/freezing
- Switch the preview to a phone/tablet device — focusing an expression widget should raise the mobile keypad inside the preview iframe
- Switch back to desktop — no hairline bar across the bottom of the preview, and expression widgets use the desktop keypad popover
- Check the hint preview row — hints render with hint styling and the   screen-reader position label

Author: jeremywiebe

Reviewers: nishasy, jeremywiebe

Required Reviewers:

Approved By: nishasy

Checks: ⏭️  1 check has been skipped, ✅ 11 checks were successful

Pull Request URL: #3766
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

npm Snapshot: Published

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

Example:

pnpm add @khanacademy/perseus@PR3841

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

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

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

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

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Size Change: +134 B (+0.03%)

Total Size: 518 kB

📦 View Changed
Filename Size Change
packages/perseus-editor/dist/es/index.js 104 kB +134 B (+0.13%)
ℹ️ 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.6 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

# Conflicts:
#	packages/perseus-editor/src/article-editor.tsx
#	packages/perseus-editor/src/testing/preview/exercise-preview-page.tsx
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.

1 participant