Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
1853097
Integrate launch-editor-middleware for enhanced editor support. Added…
yannbf Sep 15, 2025
b665876
Introduce open in editor and copy story name actions in sidebar conte…
yannbf Sep 15, 2025
534e35b
add open in editor action in interactions panel
yannbf Sep 15, 2025
e5bcac0
Add share tool functionality with QR code support in the preview comp…
yannbf Sep 15, 2025
90b7020
surface network address to Storybook's UI
yannbf Sep 15, 2025
c110aed
fix types and most tests
yannbf Sep 15, 2025
4c3209a
fix story
yannbf Sep 16, 2025
c83b79c
only hide tooltip on escape key
yannbf Sep 16, 2025
f16eb5d
update storybook icons and use new bug icon
yannbf Sep 16, 2025
f72aa77
fix exports
yannbf Sep 16, 2025
33f12f1
use share icon
yannbf Sep 16, 2025
983e479
in the interactions panel, show just story name as text on deployed/c…
yannbf Sep 16, 2025
7a74161
fix shortcuts fonts
yannbf Sep 16, 2025
22e796c
fix registering shortcuts with alt on macos
yannbf Sep 16, 2025
cceb627
fix tests
yannbf Sep 16, 2025
4ce4bfd
cleanup and add copyStoryLink shortcut
yannbf Sep 16, 2025
03ddcf9
temporarily disable copy story name shortcut
yannbf Sep 16, 2025
1af1dbb
small rename on measure addon shortcut
yannbf Sep 16, 2025
0036635
fix qr url
yannbf Sep 16, 2025
f252cdb
small refactor
yannbf Sep 16, 2025
383fe6f
fix types
yannbf Sep 16, 2025
f4bb5fa
fix types
yannbf Sep 16, 2025
09cdcc3
Merge branch 'next' into yann/open-in-editor-feature
yannbf Sep 16, 2025
6636c1d
make qrcode fallback to current url
yannbf Sep 16, 2025
809d220
add copy story link default shortcut
yannbf Sep 16, 2025
45daed4
skip vitest test
yannbf Sep 16, 2025
632561d
fix qr code url calculation
yannbf Sep 16, 2025
72d98b7
Merge branch 'next' into yann/open-in-editor-feature
yannbf Sep 17, 2025
5be73b1
update icons and use the new editor icon
yannbf Sep 17, 2025
c8e2ac3
Merge branch 'next' into yann/open-in-editor-feature
ndelangen Sep 17, 2025
6984ccd
Enhance exports: Add 'EditorIcon' to the list of exported icons in gl…
ndelangen Sep 17, 2025
e442493
Merge branch 'yann/open-in-editor-feature' of github.com:storybookjs/…
yannbf Sep 17, 2025
90817f1
rework the open in editor feature and add error handling
yannbf Sep 17, 2025
cf13e70
address PR feedback
yannbf Sep 17, 2025
244fac1
revamp context menu and support opening mdx stories
yannbf Sep 17, 2025
7fad5ab
Merge branch 'next' into yann/open-in-editor-feature
ndelangen Sep 17, 2025
cd1b6e5
replace qrcode library
yannbf Sep 17, 2025
43d966b
Merge branch 'yann/open-in-editor-feature' of github.com:storybookjs/…
yannbf Sep 17, 2025
a363ba8
more fixes
yannbf Sep 17, 2025
aeb30b3
Merge branch 'next' into yann/open-in-editor-feature
yannbf Sep 17, 2025
d0653ad
fix stories
yannbf Sep 17, 2025
8c61a23
Merge branch 'next' into yann/open-in-editor-feature
ndelangen Sep 17, 2025
1190cb5
fix: add missing newline in no-stories-of.md documentation
ndelangen Sep 17, 2025
efd5294
fix: update comments and formatting in test and CSS files
ndelangen Sep 17, 2025
7ebc251
Merge branch 'next' into yann/open-in-editor-feature
ndelangen Sep 17, 2025
e194a32
prebundle qrcode.react
yannbf Sep 18, 2025
c58e9e9
add "open in editor" action to component nodes
yannbf Sep 18, 2025
a744783
address PR feedback
yannbf Sep 18, 2025
bbfa9e9
write E2E tests
yannbf Sep 18, 2025
a6b51dc
revamp open in editor api and bring it into useStorybookApi
yannbf Sep 18, 2025
6c985c5
Merge branch 'next' into yann/open-in-editor-feature
yannbf Sep 18, 2025
21529d2
fix test
yannbf Sep 18, 2025
3b49d88
Merge branch 'yann/open-in-editor-feature' of github.com:storybookjs/…
yannbf Sep 18, 2025
af37fd7
fix tests
yannbf Sep 18, 2025
269c931
better structure E2E tests
yannbf Sep 18, 2025
cc2ffba
Merge branch 'next' into yann/open-in-editor-feature
yannbf Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/addons/a11y/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"devDependencies": {
"@radix-ui/react-tabs": "1.0.4",
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"@testing-library/react": "^14.0.0",
"execa": "^9.5.2",
"react": "^18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion code/addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"dependencies": {
"@mdx-js/react": "^3.0.0",
"@storybook/csf-plugin": "workspace:*",
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"@storybook/react-dom-shim": "workspace:*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
Expand Down
2 changes: 1 addition & 1 deletion code/addons/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"upath": "^2.0.1"
},
"devDependencies": {
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-resize-detector": "^7.1.2",
Expand Down
2 changes: 1 addition & 1 deletion code/addons/onboarding/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
},
"devDependencies": {
"@neoconfetti/react": "^1.0.0",
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-joyride": "^2.8.2",
Expand Down
2 changes: 1 addition & 1 deletion code/addons/pseudo-states/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"prep": "jiti ../../../scripts/build/build-package.ts"
},
"devDependencies": {
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.8.3"
Expand Down
2 changes: 1 addition & 1 deletion code/addons/themes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"ts-dedent": "^2.0.0"
},
"devDependencies": {
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.8.3"
Expand Down
2 changes: 1 addition & 1 deletion code/addons/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"prompts": "^2.4.0",
"ts-dedent": "^2.2.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ export const InSidebarContextMenu: Story = {
importPath: './path/to/story',
prepared: true,
parent: 'parent-id',
exportName: 'ExampleStory',
depth: 1,
},
},
Expand Down
1 change: 1 addition & 0 deletions code/builders/builder-webpack5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ const starter: StarterFunction = async function* starterGeneratorFn({
immutable: true,
})
);

router.use(compilation);
router.use(webpackHotMiddleware(compiler, { log: false }));

Expand Down
4 changes: 3 additions & 1 deletion code/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.4.0",
"@storybook/icons": "^1.6.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/user-event": "^14.6.1",
"@vitest/expect": "3.2.4",
Expand Down Expand Up @@ -317,6 +317,7 @@
"jiti": "^2.4.2",
"js-yaml": "^4.1.0",
"jsdoc-type-pratt-parser": "^4.0.0",
"launch-editor": "^2.11.1",
"lazy-universal-dotenv": "^4.0.0",
"leven": "^4.0.0",
"memfs": "^4.11.1",
Expand All @@ -334,6 +335,7 @@
"prettier": "^3.5.3",
"pretty-hrtime": "^1.0.3",
"prompts": "^2.4.0",
"qrcode.react": "^4.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
Expand Down
4 changes: 4 additions & 0 deletions code/core/src/builder-manager/utils/framework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,9 @@ export const buildFrameworkGlobalsFromOptions = async (options: Options) => {
globals.STORYBOOK_FRAMEWORK = framework;
}

if (options.networkAddress) {
globals.STORYBOOK_NETWORK_ADDRESS = options.networkAddress;
}

return globals;
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ const managerContext: any = {
api: {
getDocsUrl: fn().mockName('api::getDocsUrl'),
emit: fn().mockName('api::emit'),
getData: fn()
.mockName('api::getData')
.mockImplementation(() => ({
importPath: 'core/src/component-testing/components/InteractionsPanel.stories.tsx',
})),
},
};

Expand Down
10 changes: 10 additions & 0 deletions code/core/src/component-testing/components/InteractionsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';

import { transparentize } from 'polished';
import type { API } from 'storybook/manager-api';
import { styled } from 'storybook/theming';

import { type Call, type CallStates, type ControlStates } from '../../instrumenter/types';
Expand Down Expand Up @@ -44,6 +45,9 @@ interface InteractionsPanelProps {
onScrollToEnd?: () => void;
hasResultMismatch?: boolean;
browserTestStatus?: CallStates;
importPath?: string;
canOpenInEditor?: boolean;
api: API;
}

const Container = styled.div(({ theme }) => ({
Expand Down Expand Up @@ -104,6 +108,9 @@ export const InteractionsPanel: React.FC<InteractionsPanelProps> = React.memo(
endRef,
hasResultMismatch,
browserTestStatus,
importPath,
canOpenInEditor,
api,
}) {
const filter = useAnsiToHtmlFilter();
const hasRealInteractions = interactions.some((i) => i.id !== INTERNAL_RENDER_CALL_ID);
Expand All @@ -120,6 +127,9 @@ export const InteractionsPanel: React.FC<InteractionsPanelProps> = React.memo(
status={status}
storyFileName={fileName}
onScrollToEnd={onScrollToEnd}
importPath={importPath}
canOpenInEditor={canOpenInEditor}
api={api}
/>
<div aria-label="Interactions list">
{interactions.map((call) => (
Expand Down
11 changes: 11 additions & 0 deletions code/core/src/component-testing/components/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
useAddonState,
useChannel,
useParameter,
useStorybookApi,
useStorybookState,
} from 'storybook/manager-api';

import {
Expand Down Expand Up @@ -191,6 +193,12 @@ export const Panel = memo<{ refId?: string; storyId: string; storyUrl: string }>
});

// shared state
const state = useStorybookState();
const api = useStorybookApi();
const data = api.getData(state.storyId, state.refId);
const importPath = data?.importPath as string | undefined;
const canOpenInEditor = global.CONFIG_TYPE === 'DEVELOPMENT' && !state.refId;

const [panelState, set] = useAddonState<PanelState>(ADDON_ID, {
status: 'rendering' as PlayStatus,
controlStates: INITIAL_CONTROL_STATES,
Expand Down Expand Up @@ -406,6 +414,9 @@ export const Panel = memo<{ refId?: string; storyId: string; storyUrl: string }>
// @ts-expect-error TODO
endRef={endRef}
onScrollToEnd={scrollTarget && scrollToTarget}
importPath={importPath}
canOpenInEditor={canOpenInEditor}
api={api}
/>
</Fragment>
);
Expand Down
17 changes: 17 additions & 0 deletions code/core/src/component-testing/components/Subnav.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from 'react';

import { action } from 'storybook/actions';

import { Subnav } from './Subnav';
Expand Down Expand Up @@ -132,3 +134,18 @@ export const Detached = {
},
},
};

export const WithOpenInEditorLink = {
args: {
status: 'completed',
controlStates: {
detached: true,
start: false,
back: false,
goto: false,
next: false,
end: false,
},
canOpenInEditor: true,
},
};
36 changes: 32 additions & 4 deletions code/core/src/component-testing/components/Subnav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SyncIcon,
} from '@storybook/icons';

import { type API } from 'storybook/manager-api';
import { styled, useTheme } from 'storybook/theming';

import { type ControlStates } from '../../instrumenter/types';
Expand Down Expand Up @@ -47,6 +48,9 @@ interface SubnavProps {
status: PlayStatus;
storyFileName?: string;
onScrollToEnd?: () => void;
importPath?: string;
canOpenInEditor?: boolean;
api: API;
}

const StyledButton = styled(Button)(({ theme }) => ({
Expand All @@ -73,8 +77,10 @@ const StyledSeparator = styled(Separator)({
marginTop: 0,
});

const StyledLocation = styled(P)(({ theme }) => ({
color: theme.textMutedColor,
const StyledLocation = styled(P)<{ isText?: boolean }>(({ theme, isText }) => ({
color: isText ? theme.textMutedColor : theme.color.secondary,
cursor: isText ? 'default' : 'pointer',
fontWeight: isText ? theme.typography.weight.regular : theme.typography.weight.bold,
justifyContent: 'flex-end',
textAlign: 'right',
whiteSpace: 'nowrap',
Expand Down Expand Up @@ -119,6 +125,9 @@ export const Subnav: React.FC<SubnavProps> = ({
status,
storyFileName,
onScrollToEnd,
importPath,
canOpenInEditor,
api,
}) => {
const buttonText = status === 'errored' ? 'Scroll to error' : 'Scroll to end';
const theme = useTheme();
Expand Down Expand Up @@ -182,9 +191,28 @@ export const Subnav: React.FC<SubnavProps> = ({
</RerunButton>
</WithTooltip>
</Group>
{storyFileName && (
{(importPath || storyFileName) && (
<Group>
<StyledLocation>{storyFileName}</StyledLocation>
{canOpenInEditor ? (
<WithTooltip
trigger="hover"
hasChrome={false}
tooltip={<Note note="Open in editor" />}
>
<StyledLocation
aria-label="Open in editor"
onClick={() => {
api.openInEditor({
file: importPath as string,
});
}}
>
{storyFileName}
</StyledLocation>
</WithTooltip>
) : (
<StyledLocation isText={true}>{storyFileName}</StyledLocation>
)}
</Group>
)}
</StyledSubnav>
Expand Down
9 changes: 7 additions & 2 deletions code/core/src/components/components/tooltip/WithTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,12 @@ const WithToolTipState = ({

useEffect(() => {
const hide = () => onVisibilityChange(false);
document.addEventListener('keydown', hide, false);
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
hide();
}
};
document.addEventListener('keydown', handleKeyDown, false);

// Find all iframes on the screen and bind to clicks inside them (waiting until the iframe is ready)
const iframes: HTMLIFrameElement[] = Array.from(document.getElementsByTagName('iframe'));
Expand Down Expand Up @@ -211,7 +216,7 @@ const WithToolTipState = ({
});

return () => {
document.removeEventListener('keydown', hide);
document.removeEventListener('keydown', handleKeyDown);
unbinders.forEach((unbind) => {
unbind();
});
Expand Down
8 changes: 8 additions & 0 deletions code/core/src/core-events/data/open-in-editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type OpenInEditorRequestPayload = { file: string; line?: number; column?: number };

export type OpenInEditorResponsePayload = {
file: string;
line?: number;
column?: number;
error: string | null;
};
6 changes: 6 additions & 0 deletions code/core/src/core-events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ enum events {
ARGTYPES_INFO_RESPONSE = 'argtypesInfoResponse',
CREATE_NEW_STORYFILE_REQUEST = 'createNewStoryfileRequest',
CREATE_NEW_STORYFILE_RESPONSE = 'createNewStoryfileResponse',
// Open a file in the code editor
OPEN_IN_EDITOR_REQUEST = 'openInEditorRequest',
OPEN_IN_EDITOR_RESPONSE = 'openInEditorResponse',
}

// Enables: `import Events from ...`
Expand Down Expand Up @@ -151,6 +154,8 @@ export const {
SAVE_STORY_RESPONSE,
ARGTYPES_INFO_REQUEST,
ARGTYPES_INFO_RESPONSE,
OPEN_IN_EDITOR_REQUEST,
OPEN_IN_EDITOR_RESPONSE,
} = events;

export * from './data/create-new-story';
Expand All @@ -160,3 +165,4 @@ export * from './data/request-response';
export * from './data/save-story';
export * from './data/whats-new';
export * from './data/phases';
export * from './data/open-in-editor';
3 changes: 3 additions & 0 deletions code/core/src/core-server/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export async function storybookDevServer(options: Options) {
const proto = options.https ? 'https' : 'http';
const { address, networkAddress } = getServerAddresses(port, host, proto, initialPath);

// Expose addresses on options for the manager builder to surface in globals, important for QR code link sharing
options.networkAddress = networkAddress;

if (!core?.builder) {
throw new MissingBuilderError();
}
Expand Down
2 changes: 2 additions & 0 deletions code/core/src/core-server/presets/common-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { dedent } from 'ts-dedent';
import { resolvePackageDir } from '../../shared/utils/module';
import { initCreateNewStoryChannel } from '../server-channel/create-new-story-channel';
import { initFileSearchChannel } from '../server-channel/file-search-channel';
import { initOpenInEditorChannel } from '../server-channel/open-in-editor-channel';
import { defaultFavicon, defaultStaticDirs } from '../utils/constants';
import { initializeSaveStory } from '../utils/save-story/save-story';
import { parseStaticDir } from '../utils/server-statics';
Expand Down Expand Up @@ -256,6 +257,7 @@ export const experimental_serverChannel = async (

initFileSearchChannel(channel, options, coreOptions);
initCreateNewStoryChannel(channel, options, coreOptions);
initOpenInEditorChannel(channel, options, coreOptions);

return channel;
};
Expand Down
Loading
Loading