Core: Add "open in editor" feature#32452
Conversation
| if (!(storyId && isLocal && importPath)) { | ||
| return null; | ||
| } | ||
| const file = importPath; |
There was a problem hiding this comment.
style: redundant variable assignment - importPath can be used directly instead of assigning to file
| const file = importPath; | |
| return ( | |
| <IconButton | |
| key="open-in-editor" | |
| onClick={() => openInEditor(importPath)} | |
| title="Open in editor" | |
| aria-label="Open in editor" | |
| > | |
| <VSCodeIcon /> | |
| </IconButton> | |
| ); |
| }) => { | ||
| const buttonText = status === 'errored' ? 'Scroll to error' : 'Scroll to end'; | ||
| const theme = useTheme(); | ||
| const onOpenInEditor = async (file: string | undefined) => openInEditor(file); |
There was a problem hiding this comment.
style: The async function wrapper is unnecessary since openInEditor already returns a Promise and the result isn't awaited
| const onOpenInEditor = async (file: string | undefined) => openInEditor(file); | |
| const onOpenInEditor = (file: string | undefined) => openInEditor(file); |
|
View your CI Pipeline Execution ↗ for commit cc2ffba
☁️ Nx Cloud last updated this comment at |
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 43 | 43 | 0 |
| Self size | 30.04 MB | 30.13 MB | 🚨 +94 KB 🚨 |
| Dependency size | 17.30 MB | 17.30 MB | 0 B |
| Bundle Size Analyzer | Link | Link |
@storybook/cli
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 187 | 187 | 0 |
| Self size | 886 KB | 886 KB | 0 B |
| Dependency size | 79.64 MB | 79.73 MB | 🚨 +94 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/codemod
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 169 | 169 | 0 |
| Self size | 35 KB | 35 KB | 0 B |
| Dependency size | 76.06 MB | 76.16 MB | 🚨 +94 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
create-storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 44 | 44 | 0 |
| Self size | 1.55 MB | 1.55 MB | 🚨 +30 B 🚨 |
| Dependency size | 47.34 MB | 47.43 MB | 🚨 +94 KB 🚨 |
| Bundle Size Analyzer | node | node |
8a3af8f to
5ab5822
Compare
… open-in-editor functionality to Subnav and Preview components, allowing users to open files directly in their code editor. Updated package dependencies and added related types for improved type safety.
0933141 to
1853097
Compare
…xt menu, as well as shortcuts
bfb941e to
b665876
Compare
…onent. Updated package dependencies to include 'react-qr-code' and added related stories for testing. Enhanced sidebar context menu with new shortcut keys.
…omposed Storybooks
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
code/e2e-tests/manager.spec.ts (4)
11-15: Grant clipboard permissions before navigation to deflake clipboard assertionsWithout explicit permissions,
navigator.clipboard.readText()is flaky across browsers. Grant permissions for the Storybook origin beforepage.goto().Apply this diff:
test.beforeEach(async ({ page }) => { - await page.goto(storybookUrl); + await page.context().grantPermissions(['clipboard-read', 'clipboard-write'], { + origin: storybookUrl, + }); + await page.goto(storybookUrl); await new SbPage(page, expect).waitUntilLoaded(); });
58-101: Use polling for clipboard; keep context‑menu checks as-isGreat dev-only gating via
test.skip. Replace direct clipboard read withexpect.pollfor stability.Apply this diff:
- await expect(page.evaluate(() => navigator.clipboard.readText())).resolves.toContain( - 'Primary' - ); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toContain('Primary');
102-114: Dev share test: poll clipboard resultThe dev gating is correct. Swap to polling to avoid timing flakes after clicking “Copy story link”.
Apply this diff:
- await expect(page.evaluate(() => navigator.clipboard.readText())).resolves.toContain( - `${storybookUrl}/?path=/story/example-button--primary` - ); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toContain(`${storybookUrl}/?path=/story/example-button--primary`);
116-128: Build share test: poll clipboard resultSame clipboard flake here; use polling.
Apply this diff:
- await expect(page.evaluate(() => navigator.clipboard.readText())).resolves.toContain( - `${storybookUrl}/?path=/story/example-button--primary` - ); + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toContain(`${storybookUrl}/?path=/story/example-button--primary`);
🧹 Nitpick comments (1)
code/e2e-tests/manager.spec.ts (1)
8-8: Name the env flag more explicitlyConsider renaming
typetostorybookTypefor clarity and to avoid confusion with the TStypekeyword in declarations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
code/e2e-tests/manager.spec.ts(8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
code/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Files:
code/e2e-tests/manager.spec.ts
🧬 Code graph analysis (1)
code/e2e-tests/manager.spec.ts (1)
code/e2e-tests/util.ts (1)
SbPage(8-203)
🔇 Additional comments (6)
code/e2e-tests/manager.spec.ts (6)
20-35: Settings tooltip test reads wellThe visibility/hide flows (Escape and outside click) look solid.
49-49: UI trigger switch to Settings: LGTMSwitching the menu trigger to Settings aligns with the new UX.
150-150: Toolbar toggling via Settings: LGTMAlso applies to: 154-154
189-189: Panel toggling via Settings: LGTM
241-241: Fullscreen toggling via Settings: LGTM
269-269: Settings page navigation: LGTM
Closes #
What I did
This PR introduces an API in
storybook/manager-apito open a file in the editor:openInEditor(filePath, lineNumber?, columnNumber?).This new API is used in a few places to open a story in the editor:
Such buttons will not be shown in production builds, as this is a dev only feature.
Context Menu
Upon clicking on the three dots in stories only
Interactions panel
Presented in local stories only. In composed Storybooks or published Storybooks the text will just be a normal text and not a link

New share button
This new button groups the actions of copying the story URL, opening in isolation mode and introduces a QR Code for opening the network address from your phone

This PR also fixes an issue where the shortcut font was broken in a few places:

How it works
There is a very nice package called launch-editor which comes out of the box with Vite, and once it is present, you can fetch a specific route to open a file in the editor, it will figure out your existing editors and open the most active one, like magic. For Webpack projects, we need to include the launch-editor-middleware, but it works the same way.
Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
yarn storybook:uiDocumentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsMake sure this PR contains one of the labels below:
Available labels
bug: Internal changes that fixes incorrect behavior.maintenance: User-facing maintenance tasks.dependencies: Upgrading (sometimes downgrading) dependencies.build: Internal-facing build tooling & test updates. Will not show up in release changelog.cleanup: Minor cleanup style change. Will not show up in release changelog.documentation: Documentation only changes. Will not show up in release changelog.feature request: Introducing a new feature.BREAKING CHANGE: Changes that break compatibility in some way with current major version.other: Changes that don't fit in the above categories.🦋 Canary release
This pull request has been released as version
0.0.0-pr-32452-sha-d0653ada. Try it out in a new sandbox by runningnpx storybook@0.0.0-pr-32452-sha-d0653ada sandboxor in an existing project withnpx storybook@0.0.0-pr-32452-sha-d0653ada upgrade.More information
0.0.0-pr-32452-sha-d0653adayann/open-in-editor-featured0653ada1758124134)To request a new release of this pull request, mention the
@storybookjs/coreteam.core team members can create a new canary release here or locally with
gh workflow run --repo storybookjs/storybook canary-release-pr.yml --field pr=32452Greptile Summary
Updated On: 2025-09-15 08:36:29 UTC
This PR implements an "open in editor" feature that allows developers to quickly open story files directly in their code editor from the Storybook UI. The implementation introduces a new
openInEditor(filePath, lineNumber?, columnNumber?)API that leverages the launch-editor ecosystem to automatically detect and open files in the user's preferred editor.Core Implementation:
openInEditorfunction incode/core/src/manager-api/lib/open-in-editor.tsmakes POST requests to a/__open-in-editorendpoint with file path, line, and column informationUI Integration Points:
openInEditorToolto the default toolbar tools array, appearing in both story and docs view modesHow It Works:
The feature uses the launch-editor package ecosystem which automatically detects available editors and opens the most recently used one. For Vite projects, this works out of the box. For Webpack projects, the PR adds launch-editor-middleware to provide the same functionality. The implementation follows Storybook's established patterns for addon tools and maintains consistency across different UI contexts.
The change integrates seamlessly with Storybook's existing architecture, using the Consumer pattern to access story metadata and following established conventions for toolbar tools and API exports.
Confidence score: 3/5
Summary by CodeRabbit
New Features
Improvements
Documentation
Chores