feat(storybook): add @fluentui/react-storybook-addon-playground with in-browser TSX playground - #36728
Draft
Dmytro Kirpa (dmytrokirpa) wants to merge 19 commits into
Conversation
…s button from export-to-sandbox lookup
…s and satisfy lint rules
…nner and docs decorator
…-allowlisted modules are reported
…or allowlisted dependencies and Prettier formatting
…Document shortcut
- branded header with primary Run action, compact icon-only toolbar on narrow screens - pane headers with file name, IntelliSense and run status badges - resizable editor/preview split (pointer + keyboard), stacked layout on narrow viewports - Monaco editor theme derived from the active Fluent theme - preview loading / error placeholders, error bar hint about the last successful render - Ctrl/Cmd+Enter runs the code from the editor
…playground - gradient canvas with floating card panes, glass header, gradient brand mark and Run button - custom status pills (pulsing while compiling), file tab, dotted design-canvas preview with fade-in per run - custom error panel and empty state - curated GitHub-inspired syntax palettes for the light and dark editor themes, editor typography tweaks
…sign - flat neutral canvas, bordered surfaces with a single 8px radius, no gradients / glass / shadows / animations - standard Fluent primary button for Run, plain toolbar and dividers - dot + label status indicators (color only for ready / error / warning), spinner while busy - plain preview surface, flat error panel with a top border
Copilot started reviewing on behalf of
Dmytro Kirpa (dmytrokirpa)
September 11, 2026 15:14
View session
Contributor
There was a problem hiding this comment.
🟡 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 on lines
+123
to
+126
| // Evaluating user code is the whole point of the playground; imports are restricted to the allowlist above. | ||
| // eslint-disable-next-line no-new-func | ||
| const fn = new Function('require', 'exports', 'module', code); | ||
| fn(require, module.exports, module); |
Comment on lines
+52
to
+53
| /** {@link file://./../packages/react-components/react-storybook-addon-playground/package.json} */ | ||
| loadWorkspaceAddon('@fluentui/react-storybook-addon-playground', { tsConfigPath }), |
Comment on lines
+27
to
+44
| rootLight: { | ||
| '--pg-canvas': '#f4f5f7', | ||
| '--pg-surface': tokens.colorNeutralBackground1, | ||
| '--pg-border': '#e3e5e9', | ||
| '--pg-muted': '#6b7280', | ||
| '--pg-success': '#1a7f37', | ||
| '--pg-danger': '#cf222e', | ||
| '--pg-warning': '#9a6700', | ||
| }, | ||
| rootDark: { | ||
| '--pg-canvas': '#1b1b1c', | ||
| '--pg-surface': tokens.colorNeutralBackground1, | ||
| '--pg-border': 'rgba(255, 255, 255, 0.1)', | ||
| '--pg-muted': '#9ca3af', | ||
| '--pg-success': '#3fb950', | ||
| '--pg-danger': '#f85149', | ||
| '--pg-warning': '#d29922', | ||
| }, |
Comment on lines
+305
to
+308
| const url = `${targetWindow.location.origin}${targetWindow.location.pathname}${createCodeHash(code)}`; | ||
| await targetWindow.navigator.clipboard.writeText(url); | ||
|
|
||
| notify('Link copied to clipboard', 'success'); |
Comment on lines
+31
to
+35
| // Curated palettes (GitHub-inspired) that read well on the Fluent light / dark surfaces | ||
| const LIGHT_PALETTE: SyntaxPalette = { | ||
| foreground: '#1f2328', | ||
| comment: '#6e7781', | ||
| keyword: '#cf222e', |
Comment on lines
+26
to
+30
| ## Installation | ||
|
|
||
| ```sh | ||
| yarn add @fluentui/react-storybook-addon-playground | ||
| ``` |
|
Pull request demo site: URL |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 private workspace package
@fluentui/react-storybook-addon-playground(packages/react-components/react-storybook-addon-playground):preset.js)previewAnnotations: registers thewithOpenInPlaygroundButtondecorator, which adds an Open in Playground button next to "Show code" in Docs mode. It takes the story source fromparameters.fullSource(injected by@fluentui/babel-preset-storybook-full-sourcevia 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, soplayground.htmlis part of the Storybook static output (works in bothstorybook devandstorybook build).src/playground, separate webpack bundle built by thebuild-playgroundnx target, whichbuilddepends on)jsx: react-jsx,importsNotUsedAsValues: preserveso importing a non-allowlisted module is reported right away).tools/collect-typings.jswalks the transitive.d.tsclosure ofreact,react-dom,@fluentui/react-components(/unstable)and@fluentui/react-iconsfrom the built workspace packages (handlestypes/typings, sub path packages andtypesVersionsfor Monaco's TS 4.5); a webpack loader emits it as one lazily fetched JSON asset (~300 files, ~530 KB gzipped) that is registered viaaddExtraLibunder virtualfile:///node_modules/**paths. Semantic validation is switched on once the types are loaded.prettier/standalone+ TypeScript parser (lazy chunk) registered as Monaco's document formatting provider, using the repository Prettier settings. Available via the Format toolbar button and Monaco's "Format Document" shortcut (Shift+Alt+F, Ctrl+Shift+I on Linux; the tooltip shows the platform specific one). Syntax errors surface as a toast.require()specifiers, validates them against the allowlist (lazily loaded as separate chunks) and evaluates the module. Component pick order:default->Default-> first component-like export, so story files work as-is.FluentProvider+ error boundary; compile / import / runtime / export errors are shown in an error section below the preview.Ctrl+Enter/Cmd+Enter, also a Monaco command palette action), Format, Reset, Copy link and a themeDropdown(Web/Teams light/dark); icon-only toolbar on narrow screens. Pane headers show the IntelliSense and run state. Resizable editor/preview split (role="separator", pointer + keyboard, double-click reset) that stacks below 800px. Editor theme is derived from the active Fluent theme with GitHub-inspired syntax palettes. Code is synced to the URL hash so links are shareable and survive reloads.Repo wiring:
.storybook/main.jsloads the addon vialoadWorkspaceAddon;.storybook/preview.jsimports its styles.public-docsite-v9gets the dev dependency,build-storybookdependency and story-glob exclusion.tsconfig.base(.all).jsonpaths, CODEOWNERS.react-storybook-addon-export-to-sandbox: the "Show code" lookup now also ignores.with-open-in-playground-button(change file included).Verification:
yarn nx run-many -t lint,type-check,test -p react-storybook-addon-playground,react-storybook-addon-export-to-sandboxpasses (44 + 50 tests);yarn nx run react-storybook-addon-playground:buildsucceeds;react-buttonStorybook in--docsmode injects the button on each story and opens the story in the playground;storybook build --docsoutput containsplayground.htmlandplayground/*.storybook_playground_feature_demo.mp4
Related Issue(s)