Skip to content

feat(storybook): add @fluentui/react-storybook-addon-playground with in-browser TSX playground - #36728

Draft
Dmytro Kirpa (dmytrokirpa) wants to merge 6 commits into
microsoft:experimental/storybook-playgroundfrom
dmytrokirpa:cursor/storybook-playground-addon-9007
Draft

Dmytro Kirpa (dmytrokirpa) wants to merge 6 commits into
microsoft:experimental/storybook-playgroundfrom
dmytrokirpa:cursor/storybook-playground-addon-9007

Conversation

@dmytrokirpa

@dmytrokirpa Dmytro Kirpa (dmytrokirpa) commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Split from this PR: the published no-op package scaffold now lives in #36741 (targets master, releases as 0.0.1 / latest). This PR keeps the playground implementation and should land on experimental/storybook-playground after #36741 is merged and that experimental branch is updated from master.

Previous Behavior

Storybook Docs pages only offer "Show code" and the export-to-sandbox buttons (StackBlitz / CodeSandbox). There is no way to edit and run a story's TSX directly in the browser against Fluent UI React v9.

New Behavior

New workspace package @fluentui/react-storybook-addon-playground (packages/react-components/react-storybook-addon-playground):

  • Storybook addon preset (preset.js)
    • previewAnnotations: registers the withOpenInPlaygroundButton decorator, which adds an Open in Playground button next to "Show code" in Docs mode. It takes the story source from parameters.fullSource (injected by @fluentui/babel-preset-storybook-full-source via the export-to-sandbox addon) and opens ./playground.html#code=<lz-string> in a new tab.
    • staticDirs: serves the pre-built playground app (dist/playground) from the Storybook root, so playground.html is part of the Storybook static output (works in both storybook dev and storybook build).
  • Playground app (src/playground, separate webpack bundle built by the build-playground nx target, which build depends on)
    • Monaco editor with TSX; configurable module allowlist and setup via PresetConfig / definePlaygroundSetup.
    • TypeScript IntelliSense for configured dependencies; Prettier formatting; shareable URL hash (including CSS modules).
    • User code runs in a sandbox="allow-scripts" iframe with a postMessage protocol.
  • Jest specs for URL helpers, runner, docs decorator, typings collector, formatter, keyboard shortcut labels and the split-pane hook.

Repo wiring:

  • Headless stories and public-docsite-v9-headless load the addon via loadWorkspaceAddon.
  • react-storybook-addon-export-to-sandbox: the "Show code" lookup now also ignores .with-open-in-playground-button.

Related Issue(s)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Same-origin execution of URL-supplied code is unsafe, and shared Storybook targets can omit the required playground assets.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a private Storybook addon providing an in-browser Monaco-based TSX playground for Fluent UI v9 stories.

Changes:

  • Implements editing, compilation, formatting, IntelliSense, preview, themes, and shareable links.
  • Adds the Docs-mode launcher and Storybook static-asset integration.
  • Wires the addon into the public docsite and updates sandbox-addon interoperability.

Merge confidence: 40/100. URL-supplied code currently executes unsandboxed on the Storybook origin, and clean Storybook builds do not consistently build the playground assets.

File summaries
File Description
yarn.lock Registers the workspace package and dependencies.
tsconfig.base.json Adds the package path alias.
tsconfig.base.all.json Adds the complete-workspace alias.
webpack.playground.config.js Bundles the standalone playground.
tsconfig.spec.json Configures test type-checking.
tsconfig.playground.json Configures playground sources.
tsconfig.lib.json Configures addon library output.
tsconfig.json Defines TypeScript project references.
tools/typings-loader.js Emits collected typings as JSON.
tools/playground-typings.json Provides the loader stub asset.
tools/collect-typings.spec.js Tests declaration collection.
tools/collect-typings.js Collects transitive declarations.
src/url.ts Implements shareable code hashes.
src/url.spec.ts Tests URL helpers.
src/types.ts Extends Storybook context types.
src/styles.css Styles the Docs launcher button.
src/public-types.ts Defines addon parameters.
src/preset/preview.ts Registers the decorator.
src/playground/useSplitPane.ts Implements pane resizing.
src/playground/useSplitPane.spec.ts Tests keyboard resizing.
src/playground/useMediaQuery.ts Tracks responsive queries.
src/playground/typings.ts Registers Monaco declaration files.
src/playground/themes.ts Defines selectable Fluent themes.
src/playground/shortcuts.ts Produces platform shortcut labels.
src/playground/shortcuts.spec.ts Tests shortcut labels.
src/playground/runner.ts Evaluates compiled playground code.
src/playground/runner.spec.ts Tests evaluation and allowlisting.
src/playground/Preview.tsx Renders the component preview.
src/playground/Preview.styles.ts Styles the preview surface.
src/playground/Playground.tsx Implements the application shell.
src/playground/Playground.styles.ts Defines shell and responsive styles.
src/playground/playground.html Provides the application document.
src/playground/playground-typings.d.ts Types the generated typings asset.
src/playground/monaco.ts Configures Monaco and workers.
src/playground/modules.ts Defines runtime module loaders.
src/playground/main.tsx Bootstraps the React application.
src/playground/formatter.ts Integrates Prettier with Monaco.
src/playground/formatter.spec.ts Tests TSX formatting.
src/playground/ErrorBoundary.tsx Handles preview render failures.
src/playground/editorTheme.ts Defines Monaco themes.
src/playground/Editor.tsx Manages the Monaco editor.
src/playground/Editor.styles.ts Sizes the editor container.
src/playground/defaultCode.ts Supplies the initial example.
src/playground/compiler.ts Compiles TSX in Monaco’s worker.
src/index.ts Exports the addon API.
src/decorators/withOpenInPlaygroundButton.ts Injects the Docs launcher.
src/decorators/withOpenInPlaygroundButton.spec.ts Tests launcher behavior.
README.md Documents usage and development.
project.json Adds build targets.
preset.js Registers annotations and static assets.
package.json Defines the private package.
LICENSE Adds package licensing.
jest.config.js Configures Jest.
etc/react-storybook-addon-playground.api.md Records the public API.
eslint.config.js Configures package linting.
config/tests.js Adds Jest setup.
config/api-extractor.json Configures API Extractor.
.swcrc Configures SWC compilation.
.babelrc.json Adds Babel annotations.
sandbox-utils.ts Excludes the playground button.
change/...json Adds the sandbox-addon patch entry.
apps/public-docsite-v9/project.json Builds the playground dependency.
apps/public-docsite-v9/package.json Adds the workspace dependency.
apps/public-docsite-v9/.storybook/main.js Excludes addon sources from stories.
.storybook/preview.js Loads launcher styles.
.storybook/main.js Registers the addon globally.
.github/CODEOWNERS Assigns package ownership.
Review details
  • Files reviewed: 65/67 changed files
  • Comments generated: 6
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .storybook/main.js Outdated
Comment on lines +26 to +30
## Installation

```sh
yarn add @fluentui/react-storybook-addon-playground
```
@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

@Hotell Martin Hochel (Hotell) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

posting initial comments

Comment thread packages/react-components/react-storybook-addon-playground/package.json Outdated
Co-authored-by: Atishay Jain <atisjai@microsoft.com>
@Hotell

Martin Hochel (Hotell) commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Dmytro Kirpa (@dmytrokirpa) wanted to provide comprehensive "text" but I rather decided to prototype, here is the suggestion to make this more secure and configurable. the complexity is higher ofc but we can improve it.

#36732

in general we should ship this to our docs !

thoughts ?

@dmytrokirpa

Copy link
Copy Markdown
Contributor Author

Dmytro Kirpa (Dmytro Kirpa (@dmytrokirpa)) wanted to provide comprehensive "text" but I rather decided to prototype, here is the suggestion to make this more secure and configurable. the complexity is higher ofc but we can improve it.

#36732

in general we should ship this to our docs !

thoughts ?

Using configuration instead of hardcoded parameters makes perfect sense. I had planned to follow up on that, but we can do it in the intial PR. Replacing the new Function with an iframe also makes sense. However, I don’t think we should move Playground to a separate package at this point. I’d rather keep it in the SB addon while making it configurable and replaceable.

@Hotell

Copy link
Copy Markdown
Contributor

Dmytro Kirpa (Dmytro Kirpa (Dmytro Kirpa (@dmytrokirpa))) wanted to provide comprehensive "text" but I rather decided to prototype, here is the suggestion to make this more secure and configurable. the complexity is higher ofc but we can improve it.
#36732

in general we should ship this to our docs !

thoughts ?

Using configuration instead of hardcoded parameters makes perfect sense. I had planned to follow up on that, but we can do it in the intial PR. Replacing the new Function with an iframe also makes sense. However, I don’t think we should move Playground to a separate package at this point. I’d rather keep it in the SB addon while making it configurable and replaceable.

discussed offline.

  • one package for now
  • we will target experimental branch process first to iterate

Dmytro Kirpa (@dmytrokirpa) we should ideally merge package scaffold with some noop and release as 0.0.1, then move to experimental. otherwise the latest tag will point to first experimental which is confusing

…caffold

Ship 0.0.1 on latest first so later experimental playground releases do not take the latest npm tag.

Co-authored-by: Cursor <cursoragent@cursor.com>
Implements the configurable Monaco playground with a sandboxed preview, Docs launcher, and Storybook wiring on top of the published package scaffold.

Co-authored-by: Cursor <cursoragent@cursor.com>
@dmytrokirpa
Dmytro Kirpa (dmytrokirpa) force-pushed the cursor/storybook-playground-addon-9007 branch from ab45332 to 6b95616 Compare September 15, 2026 20:17
@dmytrokirpa

Copy link
Copy Markdown
Contributor Author

Split per #36728 (comment):

  1. Package scaffold (merge first, to master): feat(react-storybook-addon-playground): add published no-op package scaffold #36741 — published no-op addon so beachball can release 0.0.1 on latest.
  2. This PR (then to experimental/storybook-playground): playground implementation rebased onto that scaffold.

Suggested merge order:

  1. Land feat(react-storybook-addon-playground): add published no-op package scaffold #36741 on master and let the next stable release publish @fluentui/react-storybook-addon-playground@0.0.1.
  2. Update experimental/storybook-playground from master (it is currently a few commits behind).
  3. Rebase this branch if needed, then merge here and iterate / publish with the experimental pipeline.

Until step 2, GitHub will also show the scaffold commit (and any master commits not yet on the experimental branch) in this diff.

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.

6 participants