Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 52 additions & 2 deletions code/addons/mcp/src/preset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,60 @@ describe('experimental_devServer', () => {
expect(html).not.toMatch(/\{\{[A-Z_]+\}\}/);
});

it('names the missing manifest prerequisite instead of claiming the framework is unsupported', async () => {
// `mockOptions` has no manifests and `componentsManifest: false`, which is what an
// Angular-vite or Vue project that only forgot a feature flag looks like.
let getHandler: any;
mockApp.get = vi.fn((_path, handler) => {
getHandler = handler;
});

await (experimental_devServer as any)(mockApp, mockOptions);

const mockRes = { writeHead: vi.fn(), end: vi.fn() } as any;
await getHandler({ headers: { accept: 'text/html' } } as any, mockRes);

const html = mockRes.end.mock.calls[0][0] as string;
expect(html).toContain('This toolset requires a components manifest,');
expect(html).toContain('<code>componentsManifest</code>');
expect(html).toContain('<code>@storybook/angular-vite</code>');
expect(html).not.toContain('React-based setups');
expect(html).not.toMatch(/\{\{[A-Z_]+\}\}/);
});

it('blames the framework, not the config, when the feature is on and there is still no manifest', async () => {
const featureOnNoManifestOptions = {
...mockOptions,
presets: {
apply: vi.fn(async (key: string) => {
if (key === 'features') {
return { componentsManifest: true };
}
return undefined;
}),
},
} as unknown as Options;

let getHandler: any;
mockApp.get = vi.fn((_path, handler) => {
getHandler = handler;
});

await (experimental_devServer as any)(mockApp, featureOnNoManifestOptions);

const mockRes = { writeHead: vi.fn(), end: vi.fn() } as any;
await getHandler({ headers: { accept: 'text/html' } } as any, mockRes);

const html = mockRes.end.mock.calls[0][0] as string;
expect(html).toContain('which your framework does not generate');
expect(html).not.toContain('Enable the <code>componentsManifest</code> feature');
expect(html).not.toMatch(/\{\{[A-Z_]+\}\}/);
});

it('prompts to enable the manifest feature when manifests exist but the flag is off', async () => {
// Manifests are present on disk, but `componentsManifest` is not enabled — the docs
// toolset is unavailable for a different reason than "no manifests", so the page shows
// the "enable the feature" notice rather than the React-only one.
// the "enable the feature" notice rather than the missing-manifest one.
const manifestsNoFlagOptions = {
...mockOptions,
presets: {
Expand Down Expand Up @@ -431,7 +481,7 @@ describe('experimental_devServer', () => {

const html = mockRes.end.mock.calls[0][0] as string;
expect(html).toContain('This toolset requires enabling the component manifest feature.');
expect(html).not.toContain('only supported in React-based setups');
expect(html).not.toContain('This toolset requires a components manifest,');
expect(html).not.toMatch(/\{\{[A-Z_]+\}\}/);
});

Expand Down
14 changes: 11 additions & 3 deletions code/addons/mcp/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,16 @@ export const experimental_devServer: PresetPropertyFn<

let docsNotice = '';
if (!docsHasManifests) {
docsNotice = `<div class="toolset-notice">
This toolset is only supported in React-based setups.
// Manifests come from the React frameworks, angular-vite and vue3-vite. If the feature is
// already on and there is still no manifest, the framework is the reason, not the config.
docsNotice = docsFeatureEnabled
? `<div class="toolset-notice">
This toolset requires a components manifest, which your framework does not generate. On <code>@storybook/vue3-vite</code>, also enable <code>experimentalDocgenServer</code>.
<a target="_blank" href="https://storybook.js.org/docs/ai/mcp/overview#framework-support">See which frameworks are supported</a>
</div>`
: `<div class="toolset-notice">
This toolset requires a components manifest, which is generated by the React frameworks, <code>@storybook/angular-vite</code> and <code>@storybook/vue3-vite</code>. Enable the <code>componentsManifest</code> feature, plus <code>experimentalDocgenServer</code> on <code>@storybook/vue3-vite</code>.
<a target="_blank" href="https://storybook.js.org/docs/ai/mcp/overview#framework-support">Learn how to enable it</a>
</div>`;
} else if (!docsFeatureEnabled) {
docsNotice = `<div class="toolset-notice">
Expand Down Expand Up @@ -190,7 +198,7 @@ export const experimental_devServer: PresetPropertyFn<
? [`The <code>dev</code> toolset is disabled via addon options.`]
: [
!moduleGraphSupported &&
`<code>stories-find-by-component</code> requires a dev server with a builder that supports the module graph (e.g. Vite).`,
`<code>stories-find-by-component</code> requires a dev server with a builder that supports the module graph (Vite or Webpack 5).`,
!changeDetectionEnabled &&
`<code>stories-changed</code> requires enabling the <code>changeDetection</code> feature flag.`,
!reviewEnabled &&
Expand Down
1 change: 1 addition & 0 deletions code/core/src/cli/skills/content/framework-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const frameworkToRendererMap: Record<string, string> = {
'@nuxtjs/storybook': '@storybook/vue3',

'@storybook/angular': '@storybook/angular',
'@storybook/angular-vite': '@storybook/angular',

'@storybook/svelte-vite': '@storybook/svelte',
'@storybook/sveltekit': '@storybook/svelte',
Expand Down
9 changes: 4 additions & 5 deletions docs/_snippets/addon-mcp-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const config: StorybookConfig = {
export default config;
```

```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
```ts filename=".storybook/main.ts" renderer="common" language="ts" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, angular-vite, vue3-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
Expand All @@ -63,12 +63,11 @@ export default defineMain({

<!-- JS snippets still needed while providing both CSF 3 & Next -->

```js filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
```js filename=".storybook/main.js" renderer="common" language="js" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, angular-vite, vue3-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
Expand Down
Loading
Loading