diff --git a/.circleci/config.yml b/.circleci/config.yml index 2145f39bfaa7..2cc4eda78dc1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -950,7 +950,7 @@ jobs: mkdir features-1 cd features-1 npm set registry http://localhost:6001 - npx create-storybook --yes --package-manager npm --features docs test a11y + npx create-storybook --yes --package-manager npm --features docs test a11y --loglevel=debug npx vitest environment: IN_STORYBOOK_SANDBOX: true @@ -1172,7 +1172,7 @@ workflows: requires: - build - create-sandboxes: - parallelism: 34 + parallelism: 38 requires: - build - check-sandboxes: @@ -1180,7 +1180,7 @@ workflows: requires: - create-sandboxes - chromatic-sandboxes: - parallelism: 31 + parallelism: 35 requires: - create-sandboxes - e2e-production: @@ -1192,7 +1192,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 29 + parallelism: 33 requires: - create-sandboxes - vitest-integration: @@ -1286,11 +1286,11 @@ workflows: requires: - unit-tests - create-sandboxes: - parallelism: 19 + parallelism: 21 requires: - build - chromatic-sandboxes: - parallelism: 16 + parallelism: 18 requires: - create-sandboxes - e2e-production: @@ -1302,7 +1302,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 14 + parallelism: 16 requires: - create-sandboxes - vitest-integration: @@ -1381,11 +1381,11 @@ workflows: requires: - unit-tests - create-sandboxes: - parallelism: 13 + parallelism: 14 requires: - build - chromatic-sandboxes: - parallelism: 10 + parallelism: 11 requires: - create-sandboxes - e2e-production: @@ -1397,7 +1397,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 8 + parallelism: 9 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/daily.yml b/.circleci/src/workflows/daily.yml index 5af817bd33c9..7f02e5d6117e 100644 --- a/.circleci/src/workflows/daily.yml +++ b/.circleci/src/workflows/daily.yml @@ -31,7 +31,7 @@ jobs: requires: - build - create-sandboxes: - parallelism: 34 + parallelism: 38 requires: - build - check-sandboxes: @@ -42,7 +42,7 @@ jobs: # requires: # - create-sandboxes - chromatic-sandboxes: - parallelism: 31 + parallelism: 35 requires: - create-sandboxes - e2e-production: @@ -54,7 +54,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 29 + parallelism: 33 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/merged.yml b/.circleci/src/workflows/merged.yml index e0b5528a93bf..b0ae80bb63ea 100644 --- a/.circleci/src/workflows/merged.yml +++ b/.circleci/src/workflows/merged.yml @@ -34,11 +34,11 @@ jobs: requires: - unit-tests - create-sandboxes: - parallelism: 19 + parallelism: 21 requires: - build - chromatic-sandboxes: - parallelism: 16 + parallelism: 18 requires: - create-sandboxes - e2e-production: @@ -50,7 +50,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 14 + parallelism: 16 requires: - create-sandboxes - vitest-integration: diff --git a/.circleci/src/workflows/normal.yml b/.circleci/src/workflows/normal.yml index 1fe5bf46ed63..a3f4ed1de6f6 100644 --- a/.circleci/src/workflows/normal.yml +++ b/.circleci/src/workflows/normal.yml @@ -34,11 +34,11 @@ jobs: requires: - unit-tests - create-sandboxes: - parallelism: 13 + parallelism: 14 requires: - build - chromatic-sandboxes: - parallelism: 10 + parallelism: 11 requires: - create-sandboxes - e2e-production: @@ -50,7 +50,7 @@ jobs: requires: - create-sandboxes - test-runner-production: - parallelism: 8 + parallelism: 9 requires: - create-sandboxes - vitest-integration: diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 525bb81f14fb..0be2319465ae 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,9 @@ +## 10.1.0-alpha.14 + +- Angular: Add support for v21 - [#33098](https://github.com/storybookjs/storybook/pull/33098), thanks @valentinpalkovic! +- Build: Add Rsbuild-based sandboxes - [#33039](https://github.com/storybookjs/storybook/pull/33039), thanks @valentinpalkovic! +- CLI: Change yarn package manager value to yarn1 - [#33099](https://github.com/storybookjs/storybook/pull/33099), thanks @valentinpalkovic! + ## 10.1.0-alpha.13 - CLI: Modernize Storybook CLI with new init workflow, Clack UI, and Generator System - [#32717](https://github.com/storybookjs/storybook/pull/32717), thanks @valentinpalkovic! diff --git a/code/addons/a11y/vitest.config.ts b/code/addons/a11y/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/a11y/vitest.config.ts +++ b/code/addons/a11y/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/docs/vitest.config.ts b/code/addons/docs/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/docs/vitest.config.ts +++ b/code/addons/docs/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/links/vitest.config.ts b/code/addons/links/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/links/vitest.config.ts +++ b/code/addons/links/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/onboarding/vitest.config.ts b/code/addons/onboarding/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/onboarding/vitest.config.ts +++ b/code/addons/onboarding/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/pseudo-states/vitest.config.ts b/code/addons/pseudo-states/vitest.config.ts index 5347797a6664..0e10a6f31d49 100644 --- a/code/addons/pseudo-states/vitest.config.ts +++ b/code/addons/pseudo-states/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/themes/src/postinstall.ts b/code/addons/themes/src/postinstall.ts index 3d99b67f3d37..f5ad3aac352e 100644 --- a/code/addons/themes/src/postinstall.ts +++ b/code/addons/themes/src/postinstall.ts @@ -1,17 +1,19 @@ +import { PackageManagerName } from 'storybook/internal/common'; + import { spawnSync } from 'child_process'; const PACKAGE_MANAGER_TO_COMMAND = { - npm: 'npx', - pnpm: 'pnpm dlx', - yarn1: 'npx', - yarn2: 'yarn dlx', - bun: 'bunx', + [PackageManagerName.NPM]: 'npx', + [PackageManagerName.PNPM]: 'pnpm dlx', + [PackageManagerName.YARN1]: 'npx', + [PackageManagerName.YARN2]: 'yarn dlx', + [PackageManagerName.BUN]: 'bunx', }; const selectPackageManagerCommand = (packageManager: string) => PACKAGE_MANAGER_TO_COMMAND[packageManager as keyof typeof PACKAGE_MANAGER_TO_COMMAND]; -export default async function postinstall({ packageManager = 'npm' }) { +export default async function postinstall({ packageManager = PackageManagerName.NPM }) { const commandString = selectPackageManagerCommand(packageManager); const [command, ...commandArgs] = commandString.split(' '); diff --git a/code/addons/themes/vitest.config.ts b/code/addons/themes/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/themes/vitest.config.ts +++ b/code/addons/themes/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/addons/vitest/vitest.config.ts b/code/addons/vitest/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/addons/vitest/vitest.config.ts +++ b/code/addons/vitest/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/builders/builder-vite/vitest.config.ts b/code/builders/builder-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/builders/builder-vite/vitest.config.ts +++ b/code/builders/builder-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/builders/builder-webpack5/vitest.config.ts b/code/builders/builder-webpack5/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/builders/builder-webpack5/vitest.config.ts +++ b/code/builders/builder-webpack5/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index fdf735b29b75..f749c3283e4a 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -21,7 +21,7 @@ import type { InstallationMetadata } from './types'; export enum PackageManagerName { NPM = 'npm', - YARN1 = 'yarn', + YARN1 = 'yarn1', YARN2 = 'yarn2', PNPM = 'pnpm', BUN = 'bun', diff --git a/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts b/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts index ce7243d28027..dd1d8cd14d7d 100644 --- a/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts +++ b/code/core/src/common/js-package-manager/JsPackageManagerFactory.ts @@ -163,11 +163,11 @@ export class JsPackageManagerFactory { /** Look up map of package manager proxies by name */ private static PROXY_MAP: Record = { - npm: NPMProxy, - pnpm: PNPMProxy, - yarn: Yarn1Proxy, - yarn2: Yarn2Proxy, - bun: BUNProxy, + [PackageManagerName.NPM]: NPMProxy, + [PackageManagerName.PNPM]: PNPMProxy, + [PackageManagerName.YARN1]: Yarn1Proxy, + [PackageManagerName.YARN2]: Yarn2Proxy, + [PackageManagerName.BUN]: BUNProxy, }; /** diff --git a/code/core/src/csf-tools/ConfigFile.ts b/code/core/src/csf-tools/ConfigFile.ts index 91e2e3d5c402..c5f376f5096f 100644 --- a/code/core/src/csf-tools/ConfigFile.ts +++ b/code/core/src/csf-tools/ConfigFile.ts @@ -1179,7 +1179,7 @@ export const isCsfFactoryPreview = (previewConfig: ConfigFile) => { return !!program.body.find((node) => { return ( t.isImportDeclaration(node) && - node.source.value.includes('@storybook') && + node.source.value.includes('storybook') && node.specifiers.some((specifier) => { return ( t.isImportSpecifier(specifier) && diff --git a/code/core/src/telemetry/get-framework-info.test.ts b/code/core/src/telemetry/get-framework-info.test.ts index 8fc25c4b99ca..948e1bb43720 100644 --- a/code/core/src/telemetry/get-framework-info.test.ts +++ b/code/core/src/telemetry/get-framework-info.test.ts @@ -1,58 +1,47 @@ -import { sep } from 'node:path'; - -import { describe, expect, it, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { StorybookConfig } from 'storybook/internal/types'; import { getFrameworkInfo } from './get-framework-info'; -import { getActualPackageJson } from './package-json'; -vi.mock('./package-json', () => ({ - getActualPackageJson: vi.fn(), +vi.mock('storybook/internal/common', () => ({ + getStorybookInfo: vi.fn(), })); describe('getFrameworkInfo', () => { - it('should return an empty object if mainConfig.framework is undefined', async () => { - const result = await getFrameworkInfo({} as StorybookConfig); - expect(result).toEqual({}); + const defaultInfo = { + frameworkPackage: '@storybook/react', + rendererPackage: '@storybook/react', + builderPackage: '@storybook/builder-vite', + }; + + beforeEach(async () => { + const { getStorybookInfo } = await import('storybook/internal/common'); + vi.mocked(getStorybookInfo).mockResolvedValue(defaultInfo as any); }); - it('should return an empty object if mainConfig.framework name is undefined', async () => { - const result = await getFrameworkInfo({ framework: {} } as StorybookConfig); - expect(result).toEqual({}); + it('returns framework/builder/renderer with empty options when no framework provided', async () => { + const result = await getFrameworkInfo({} as StorybookConfig, '/tmp/.storybook'); + expect(result).toEqual({ + framework: { name: defaultInfo.frameworkPackage, options: {} }, + builder: defaultInfo.builderPackage, + renderer: defaultInfo.rendererPackage, + }); }); - it('should call getActualPackageJson with the correct package name', async () => { - const packageName = '@storybook/react'; - const framework = { name: packageName }; - await getFrameworkInfo({ framework } as StorybookConfig); - expect(getActualPackageJson).toHaveBeenCalledWith(packageName); + it('passes configDir to getStorybookInfo', async () => { + const configDir = '/my/project/.storybook'; + const { getStorybookInfo } = await import('storybook/internal/common'); + await getFrameworkInfo({} as StorybookConfig, configDir); + expect(getStorybookInfo).toHaveBeenCalledWith(configDir); }); - it('should resolve the framework package json correctly and strip project paths in the metadata', async () => { - const packageName = `${process.cwd()}/@storybook/react`.split('/').join(sep); - const framework = { name: packageName }; - const frameworkPackageJson = { - name: packageName, - dependencies: { - '@storybook/react': '7.0.0', - '@storybook/builder-vite': '7.0.0', - }, - }; - - vi.mocked(getActualPackageJson).mockResolvedValueOnce(frameworkPackageJson); - - const result = await getFrameworkInfo({ framework } as StorybookConfig); - - expect(getActualPackageJson).toHaveBeenCalledWith(packageName); - - expect(result).toEqual({ - framework: { - name: '$SNIP/@storybook/react'.split('/').join(sep), - options: undefined, - }, - builder: '@storybook/builder-vite', - renderer: '@storybook/react', - }); + it('returns provided framework options when object is passed', async () => { + const options = { foo: 'bar' } as any; + const result = await getFrameworkInfo( + { framework: { name: '@storybook/react', options } } as any, + '/tmp/.storybook' + ); + expect(result.framework.options).toEqual(options); }); }); diff --git a/code/core/src/telemetry/get-framework-info.ts b/code/core/src/telemetry/get-framework-info.ts index d114501481f0..51dd5feeb78e 100644 --- a/code/core/src/telemetry/get-framework-info.ts +++ b/code/core/src/telemetry/get-framework-info.ts @@ -1,80 +1,24 @@ -import { normalize } from 'node:path'; +import { getStorybookInfo } from 'storybook/internal/common'; +import type { StorybookConfig } from 'storybook/internal/types'; -import { frameworkPackages } from 'storybook/internal/common'; -import type { PackageJson, StorybookConfig } from 'storybook/internal/types'; - -import { getActualPackageJson } from './package-json'; import { cleanPaths } from './sanitize'; -const knownRenderers = [ - 'html', - 'react', - 'svelte', - 'vue3', - 'preact', - 'server', - 'vue', - 'web-components', - 'angular', - 'ember', -]; - -const knownBuilders = ['builder-webpack5', 'builder-vite']; - -function findMatchingPackage(packageJson: PackageJson, suffixes: string[]) { - const { name = '', version, dependencies, devDependencies, peerDependencies } = packageJson; - - const allDependencies = { - // We include the framework itself because it may be a renderer too (e.g. angular) - [name]: version, - ...dependencies, - ...devDependencies, - ...peerDependencies, - }; - - return suffixes.map((suffix) => `@storybook/${suffix}`).find((pkg) => allDependencies[pkg]); -} - -export const getFrameworkPackageName = (packageNameOrPath: string) => { - const normalizedPath = normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/'); - - const knownFramework = Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg)); - - return knownFramework || cleanPaths(packageNameOrPath).replace(/.*node_modules[\\/]/, ''); +const cleanAndSanitizePath = (path: string) => { + return cleanPaths(path).replace(/.*node_modules[\\/]/, ''); }; -export async function getFrameworkInfo(mainConfig: StorybookConfig) { - if (!mainConfig?.framework) { - return {}; - } - - const rawName = - typeof mainConfig.framework === 'string' ? mainConfig.framework : mainConfig.framework?.name; - if (!rawName) { - return {}; - } - - const frameworkPackageJson = await getActualPackageJson(rawName); - - if (!frameworkPackageJson) { - return {}; - } - - const builder = findMatchingPackage(frameworkPackageJson, knownBuilders); - const renderer = findMatchingPackage(frameworkPackageJson, knownRenderers); +export async function getFrameworkInfo(mainConfig: StorybookConfig, configDir: string) { + const { frameworkPackage, rendererPackage, builderPackage } = await getStorybookInfo(configDir); - // TODO: Evaluate if this is correct after removing pnp compatibility code in SB11 - // parse framework name and strip off pnp paths etc. - const sanitizedFrameworkName = getFrameworkPackageName(rawName); const frameworkOptions = typeof mainConfig.framework === 'object' ? mainConfig.framework.options : {}; return { framework: { - name: sanitizedFrameworkName, + name: frameworkPackage ? cleanAndSanitizePath(frameworkPackage) : undefined, options: frameworkOptions, }, - builder, - renderer, + builder: builderPackage ? cleanAndSanitizePath(builderPackage) : undefined, + renderer: rendererPackage ? cleanAndSanitizePath(rendererPackage) : undefined, }; } diff --git a/code/core/src/telemetry/storybook-metadata.test.ts b/code/core/src/telemetry/storybook-metadata.test.ts index 6bc4fe11002f..600b2e4e60e7 100644 --- a/code/core/src/telemetry/storybook-metadata.test.ts +++ b/code/core/src/telemetry/storybook-metadata.test.ts @@ -40,19 +40,25 @@ const mainJsMock: StorybookConfig = { stories: [], }; +const defaultInfo = { + framework: SupportedFramework.REACT_VITE, + renderer: SupportedRenderer.REACT, + builder: SupportedBuilder.VITE, + frameworkPackage: '@storybook/react-vite', + rendererPackage: '@storybook/react', + builderPackage: '@storybook/builder-vite', + addons: [], + mainConfig: { + stories: [], + }, + mainConfigPath: '', + previewConfigPath: '', + managerConfigPath: '', + version: 'x.x.x', +}; + beforeEach(() => { - vi.mocked(getStorybookInfo).mockImplementation(async () => ({ - framework: SupportedFramework.REACT_VITE, - renderer: SupportedRenderer.REACT, - builder: SupportedBuilder.VITE, - addons: [], - mainConfig: { - stories: [], - }, - mainConfigPath: '', - previewConfigPath: '', - managerConfigPath: '', - })); + vi.mocked(getStorybookInfo).mockImplementation(async () => defaultInfo); vi.mocked(detect).mockImplementation(async () => ({ name: 'yarn', @@ -187,6 +193,12 @@ describe('storybook-metadata', () => { }); it('should parse pnp paths for unknown frameworks', async () => { + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: + '/Users/foo/my-project/.yarn/__virtual__/@storybook-react-vite-virtual-769c990b9/0/cache/@storybook-react-rust-npm-7.1.0-alpha.38-512b-a23.zip/node_modules/storybook-react-rust' as any, + })); + const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -225,6 +237,11 @@ describe('storybook-metadata', () => { path.sep = '/'; cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('/Users/foo/my-projects'); + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: '/Users/foo/my-projects/.storybook/some-local-framework' as any, + })); + const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -240,10 +257,18 @@ describe('storybook-metadata', () => { expect(unixResult.framework).toEqual({ name: '$SNIP/.storybook/some-local-framework', }); + }); + it('should sanitize pnp paths for local frameworks on Windows', async () => { // @ts-expect-error the property is read only but we can change it for testing purposes path.sep = '\\'; cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('C:\\Users\\foo\\my-project'); + + vi.mocked(getStorybookInfo).mockImplementation(async () => ({ + ...defaultInfo, + frameworkPackage: 'C:\\Users\\foo\\my-project\\.storybook\\some-local-framework' as any, + })); + const windowsResult = await computeStorybookMetadata({ packageJson: packageJsonMock, packageJsonPath, @@ -262,49 +287,6 @@ describe('storybook-metadata', () => { }); }); - it('should return frameworkOptions from mainjs', async () => { - const reactResult = await computeStorybookMetadata({ - packageJson: packageJsonMock, - packageJsonPath, - configDir: '.storybook', - mainConfig: { - ...mainJsMock, - framework: { - name: '@storybook/react-vite', - options: { - strictMode: false, - }, - }, - }, - }); - - expect(reactResult.framework).toEqual({ - name: '@storybook/react-vite', - options: { strictMode: false }, - }); - - const angularResult = await computeStorybookMetadata({ - packageJson: packageJsonMock, - packageJsonPath, - configDir: '.storybook', - mainConfig: { - ...mainJsMock, - framework: { - name: '@storybook/angular', - options: { - enableIvy: true, - enableNgcc: true, - }, - }, - }, - }); - - expect(angularResult.framework).toEqual({ - name: '@storybook/angular', - options: { enableIvy: true, enableNgcc: true }, - }); - }); - it('should separate storybook packages and addons', async () => { const result = await computeStorybookMetadata({ packageJson: { diff --git a/code/core/src/telemetry/storybook-metadata.ts b/code/core/src/telemetry/storybook-metadata.ts index c6f5db760e31..56d4f7a357cb 100644 --- a/code/core/src/telemetry/storybook-metadata.ts +++ b/code/core/src/telemetry/storybook-metadata.ts @@ -143,7 +143,7 @@ export const computeStorybookMetadata = async ({ metadata.typescriptOptions = mainConfig.typescript; } - const frameworkInfo = await getFrameworkInfo(mainConfig); + const frameworkInfo = await getFrameworkInfo(mainConfig, configDir); if (typeof mainConfig.refs === 'object') { metadata.refCount = Object.keys(mainConfig.refs).length; diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index c6ea3cd99354..823837ab6d73 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -54,7 +54,7 @@ export type StorybookMetadata = { userSince?: number; language: 'typescript' | 'javascript'; framework?: { - name: string; + name?: string; options?: any; }; builder?: string; diff --git a/code/core/template/stories/module-mocking.stories.ts b/code/core/template/stories/moduleMocking.stories.ts similarity index 100% rename from code/core/template/stories/module-mocking.stories.ts rename to code/core/template/stories/moduleMocking.stories.ts diff --git a/code/core/vitest.config.ts b/code/core/vitest.config.ts index c2f7f17e8149..ea2ba22d0869 100644 --- a/code/core/vitest.config.ts +++ b/code/core/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../vitest.workspace'; +import { vitestCommonConfig } from '../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/e2e-tests/module-mocking.spec.ts b/code/e2e-tests/module-mocking.spec.ts index 6173744726fb..33420650c7b7 100644 --- a/code/e2e-tests/module-mocking.spec.ts +++ b/code/e2e-tests/module-mocking.spec.ts @@ -57,7 +57,7 @@ test.describe('module-mocking', () => { test('should assert that utils import is mocked', async ({ page }) => { const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory('core/module-mocking', 'basic'); + await sbPage.navigateToStory('core/moduleMocking', 'basic'); await sbPage.viewAddonPanel('Actions'); const logItem = sbPage.panelContent().filter({ diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 6460d98b1ca6..7d1d4ffb8dc1 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -90,18 +90,17 @@ "zone.js": "^0.15.0" }, "peerDependencies": { - "@angular-devkit/architect": ">=0.1800.0 < 0.2100.0", - "@angular-devkit/build-angular": ">=18.0.0 < 21.0.0", - "@angular-devkit/core": ">=18.0.0 < 21.0.0", - "@angular/animations": ">=18.0.0 < 21.0.0", - "@angular/cli": ">=18.0.0 < 21.0.0", - "@angular/common": ">=18.0.0 < 21.0.0", - "@angular/compiler": ">=18.0.0 < 21.0.0", - "@angular/compiler-cli": ">=18.0.0 < 21.0.0", - "@angular/core": ">=18.0.0 < 21.0.0", - "@angular/forms": ">=18.0.0 < 21.0.0", - "@angular/platform-browser": ">=18.0.0 < 21.0.0", - "@angular/platform-browser-dynamic": ">=18.0.0 < 21.0.0", + "@angular-devkit/architect": ">=0.1800.0 < 0.2200.0", + "@angular-devkit/build-angular": ">=18.0.0 < 22.0.0", + "@angular-devkit/core": ">=18.0.0 < 22.0.0", + "@angular/animations": ">=18.0.0 < 22.0.0", + "@angular/cli": ">=18.0.0 < 22.0.0", + "@angular/common": ">=18.0.0 < 22.0.0", + "@angular/compiler": ">=18.0.0 < 22.0.0", + "@angular/compiler-cli": ">=18.0.0 < 22.0.0", + "@angular/core": ">=18.0.0 < 22.0.0", + "@angular/platform-browser": ">=18.0.0 < 22.0.0", + "@angular/platform-browser-dynamic": ">=18.0.0 < 22.0.0", "rxjs": "^6.5.3 || ^7.4.0", "storybook": "workspace:^", "typescript": "^4.9.0 || ^5.0.0", diff --git a/code/frameworks/angular/vitest.config.ts b/code/frameworks/angular/vitest.config.ts index 4aad18113b65..4b2a516b6426 100644 --- a/code/frameworks/angular/vitest.config.ts +++ b/code/frameworks/angular/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/ember/vitest.config.ts b/code/frameworks/ember/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/ember/vitest.config.ts +++ b/code/frameworks/ember/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/nextjs-vite/vitest.config.ts b/code/frameworks/nextjs-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/nextjs-vite/vitest.config.ts +++ b/code/frameworks/nextjs-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/nextjs/vitest.config.ts b/code/frameworks/nextjs/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/nextjs/vitest.config.ts +++ b/code/frameworks/nextjs/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/preact-vite/vitest.config.ts b/code/frameworks/preact-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/preact-vite/vitest.config.ts +++ b/code/frameworks/preact-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/react-native-web-vite/vitest.config.ts b/code/frameworks/react-native-web-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/react-native-web-vite/vitest.config.ts +++ b/code/frameworks/react-native-web-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/react-vite/vitest.config.ts b/code/frameworks/react-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/react-vite/vitest.config.ts +++ b/code/frameworks/react-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/react-webpack5/vitest.config.ts b/code/frameworks/react-webpack5/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/react-webpack5/vitest.config.ts +++ b/code/frameworks/react-webpack5/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/server-webpack5/vitest.config.ts b/code/frameworks/server-webpack5/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/server-webpack5/vitest.config.ts +++ b/code/frameworks/server-webpack5/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/svelte-vite/vitest.config.ts b/code/frameworks/svelte-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/svelte-vite/vitest.config.ts +++ b/code/frameworks/svelte-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/sveltekit/vitest.config.ts b/code/frameworks/sveltekit/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/sveltekit/vitest.config.ts +++ b/code/frameworks/sveltekit/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/vue3-vite/vitest.config.ts b/code/frameworks/vue3-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/vue3-vite/vitest.config.ts +++ b/code/frameworks/vue3-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/frameworks/web-components-vite/vitest.config.ts b/code/frameworks/web-components-vite/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/frameworks/web-components-vite/vitest.config.ts +++ b/code/frameworks/web-components-vite/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/cli-sb/vitest.config.ts b/code/lib/cli-sb/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/cli-sb/vitest.config.ts +++ b/code/lib/cli-sb/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/cli-storybook/src/bin/run.ts b/code/lib/cli-storybook/src/bin/run.ts index 6a916e49e190..89abf3e55411 100644 --- a/code/lib/cli-storybook/src/bin/run.ts +++ b/code/lib/cli-storybook/src/bin/run.ts @@ -2,6 +2,7 @@ import { globalSettings } from 'storybook/internal/cli'; import { HandledError, JsPackageManagerFactory, + PackageManagerName, isCI, optionalEnvToBoolean, removeAddon as remove, @@ -11,7 +12,7 @@ import { withTelemetry } from 'storybook/internal/core-server'; import { CLI_COLORS, logTracker, logger } from 'storybook/internal/node-logger'; import { addToGlobalContext, telemetry } from 'storybook/internal/telemetry'; -import { program } from 'commander'; +import { Option, program } from 'commander'; import envinfo from 'envinfo'; import leven from 'leven'; import picocolors from 'picocolors'; @@ -88,7 +89,11 @@ command('init') .description('Initialize Storybook into your project') .option('-f --force', 'Force add Storybook') .option('-s --skip-install', 'Skip installing deps') - .option('--package-manager ', 'Force package manager for installing deps') + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) + ) // TODO: Remove in SB11 .option('--use-pnp', 'Enable PnP mode for Yarn 2+') .option('-p --parser ', 'jscodeshift parser') @@ -108,9 +113,10 @@ command('init') command('add ') .description('Add an addon to your Storybook') - .option( - '--package-manager ', - 'Force package manager for installing dependencies' + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) ) .option('-c, --config-dir ', 'Directory where to load Storybook configurations from') .option('--skip-install', 'Skip installing deps') @@ -132,9 +138,10 @@ command('add ') command('remove ') .description('Remove an addon from your Storybook') - .option( - '--package-manager ', - 'Force package manager for installing dependencies' + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) ) .option('-c, --config-dir ', 'Directory where to load Storybook configurations from') .option('-s --skip-install', 'Skip installing deps') @@ -159,9 +166,10 @@ command('remove ') command('upgrade') .description(`Upgrade your Storybook packages to v${versions.storybook}`) - .option( - '--package-manager ', - 'Force package manager for installing dependencies' + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) ) .option('-y --yes', 'Skip prompting the user') .option('-f --force', 'force the upgrade, skipping autoblockers') @@ -253,7 +261,11 @@ command('automigrate [fixId]') .description('Check storybook for incompatibilities or migrations and apply fixes') .option('-y --yes', 'Skip prompting the user') .option('-n --dry-run', 'Only check for fixes, do not actually run them') - .option('--package-manager ', 'Force package manager') + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) + ) .option('-l --list', 'List available migrations') .option('-c, --config-dir ', 'Directory of Storybook configurations to migrate') .option('-s --skip-install', 'Skip installing deps') @@ -272,7 +284,11 @@ command('automigrate [fixId]') command('doctor') .description('Check Storybook for known problems and provide suggestions or fixes') - .option('--package-manager ', 'Force package manager') + .addOption( + new Option('--package-manager ', 'Force package manager for installing deps').choices( + Object.values(PackageManagerName) + ) + ) .option('-c, --config-dir ', 'Directory of Storybook configuration') .action(async (options) => { withTelemetry('doctor', { cliOptions: options }, async () => { diff --git a/code/lib/cli-storybook/src/sandbox-templates.ts b/code/lib/cli-storybook/src/sandbox-templates.ts index d286fe14eae1..df497a8569b5 100644 --- a/code/lib/cli-storybook/src/sandbox-templates.ts +++ b/code/lib/cli-storybook/src/sandbox-templates.ts @@ -83,6 +83,7 @@ export type Template = { */ modifications?: { skipTemplateStories?: boolean; + skipMocking?: boolean; mainConfig?: LoosenedStorybookConfig | ((config: ConfigFile) => LoosenedStorybookConfig); testBuild?: boolean; disableDocs?: boolean; @@ -105,7 +106,7 @@ export type Template = { }; type BaseTemplates = Template & { - name: `${string} ${`v${number}` | 'Latest' | 'Prerelease'} (${'Webpack' | 'Vite'} | ${ + name: `${string} ${`v${number}` | 'Latest' | 'Prerelease'} (${'Webpack' | 'Vite' | 'RsBuild'} | ${ | 'JavaScript' | 'TypeScript'})`; }; @@ -481,6 +482,26 @@ export const baseTemplates = { }, skipTasks: ['e2e-tests', 'bench', 'vitest-integration'], }, + 'react-rsbuild/default-ts': { + name: 'React Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t react-ts --tools eslint', + expected: { + framework: 'storybook-react-rsbuild', + renderer: '@storybook/react', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['prop-types', 'storybook-react-rsbuild@^3.0.0-beta.1'], + useCsfFactory: true, + mainConfig: { + features: { + experimentalTestSyntax: true, + }, + }, + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'solid-vite/default-js': { name: 'SolidJS Latest (Vite | JavaScript)', script: 'npx degit solidjs/templates/js {{beforeDir}}', @@ -521,6 +542,25 @@ export const baseTemplates = { }, skipTasks: ['bench'], }, + 'vue3-rsbuild/default-ts': { + name: 'Vue Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t vue-ts --tools eslint', + expected: { + framework: 'storybook-vue3-rsbuild', + renderer: '@storybook/vue3', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-vue3-rsbuild@^3.0.0-beta.1'], + mainConfig: { + features: { + experimentalTestSyntax: true, + }, + }, + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, // 'nuxt-vite/default-ts': { // name: 'Nuxt v3 (Vite | TypeScript)', // script: 'npx nuxi init --packageManager yarn --gitInit false -M @nuxt/ui {{beforeDir}}', @@ -559,6 +599,20 @@ export const baseTemplates = { type: ProjectType.HTML, }, }, + 'html-rsbuild/default-ts': { + name: 'HTML Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t vanilla-ts --tools eslint', + expected: { + framework: 'storybook-html-rsbuild', + renderer: '@storybook/html', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-html-rsbuild@^3.0.0-beta.1'], + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'svelte-vite/default-js': { name: 'Svelte Latest (Vite | JavaScript)', script: 'npm create vite --yes {{beforeDir}} -- --template svelte', @@ -584,6 +638,10 @@ export const baseTemplates = { name: 'Angular CLI Prerelease (Webpack | TypeScript)', script: 'npx -p @angular/cli@next ng new angular-v16 --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn --ssr', + modifications: { + // Angular 21 has introduced a peer dependency requirement on standard-schema via @angular/forms` + extraDependencies: ['@standard-schema/spec@^1', '@angular/forms@next'], + }, expected: { framework: '@storybook/angular', renderer: '@storybook/angular', @@ -595,6 +653,9 @@ export const baseTemplates = { name: 'Angular CLI Latest (Webpack | TypeScript)', script: 'npx -p @angular/cli ng new angular-latest --directory {{beforeDir}} --routing=true --minimal=true --style=scss --strict --skip-git --skip-install --package-manager=yarn --ssr', + modifications: { + extraDependencies: ['@angular/forms@latest'], + }, expected: { framework: '@storybook/angular', renderer: '@storybook/angular', @@ -637,6 +698,20 @@ export const baseTemplates = { // Remove smoke-test from the list once https://github.com/storybookjs/storybook/issues/19351 is fixed. skipTasks: ['smoke-test', 'e2e-tests', 'bench', 'vitest-integration'], }, + 'lit-rsbuild/default-ts': { + name: 'Web Components Latest (RsBuild | TypeScript)', + script: 'yarn create rsbuild -d {{beforeDir}} -t lit-ts --tools eslint', + expected: { + framework: 'storybook-web-components-rsbuild', + renderer: '@storybook/web-components', + builder: 'storybook-builder-rsbuild', + }, + modifications: { + extraDependencies: ['storybook-web-components-rsbuild@^3.0.0-beta.1'], + skipMocking: true, + }, + skipTasks: ['e2e-tests', 'e2e-tests-dev', 'bench', 'vitest-integration'], + }, 'preact-vite/default-js': { name: 'Preact Latest (Vite | JavaScript)', script: 'npm create vite --yes {{beforeDir}} -- --template preact', @@ -824,6 +899,7 @@ const benchTemplates = { isInternal: true, modifications: { skipTemplateStories: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -841,6 +917,7 @@ const benchTemplates = { isInternal: true, modifications: { skipTemplateStories: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -858,6 +935,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, disableDocs: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -876,6 +954,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, testBuild: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -893,6 +972,7 @@ const benchTemplates = { modifications: { skipTemplateStories: true, testBuild: true, + skipMocking: true, }, skipTasks: [ 'e2e-tests', @@ -928,6 +1008,7 @@ export const normal: TemplateKey[] = [ 'bench/react-vite-default-ts-test-build', 'bench/react-webpack-18-ts-test-build', 'ember/default-js', + 'react-rsbuild/default-ts', ]; export const merged: TemplateKey[] = [ @@ -938,6 +1019,7 @@ export const merged: TemplateKey[] = [ 'nextjs-vite/15-ts', 'preact-vite/default-ts', 'html-vite/default-ts', + 'vue3-rsbuild/default-ts', ]; export const daily: TemplateKey[] = [ @@ -960,6 +1042,8 @@ export const daily: TemplateKey[] = [ 'internal/react16-webpack', 'internal/react18-webpack-babel', 'react-native-web-vite/expo-ts', + 'lit-rsbuild/default-ts', + 'html-rsbuild/default-ts', // 'react-native-web-vite/rn-cli-ts', ]; diff --git a/code/lib/cli-storybook/vitest.config.ts b/code/lib/cli-storybook/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/cli-storybook/vitest.config.ts +++ b/code/lib/cli-storybook/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/codemod/vitest.config.ts b/code/lib/codemod/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/codemod/vitest.config.ts +++ b/code/lib/codemod/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/core-webpack/vitest.config.ts b/code/lib/core-webpack/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/core-webpack/vitest.config.ts +++ b/code/lib/core-webpack/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/create-storybook/src/generators/ANGULAR/index.ts b/code/lib/create-storybook/src/generators/ANGULAR/index.ts index 784058453232..adeb5640031b 100644 --- a/code/lib/create-storybook/src/generators/ANGULAR/index.ts +++ b/code/lib/create-storybook/src/generators/ANGULAR/index.ts @@ -5,6 +5,7 @@ import { AngularJSON, ProjectType, copyTemplate } from 'storybook/internal/cli'; import { logger, prompt } from 'storybook/internal/node-logger'; import { SupportedBuilder, SupportedFramework, SupportedRenderer } from 'storybook/internal/types'; +import semver from 'semver'; import { dedent } from 'ts-dedent'; import { defineGeneratorModule } from '../modules/GeneratorModule'; @@ -85,11 +86,39 @@ export default defineGeneratorModule({ copyTemplate(templateDir, root || undefined); } + const toDevkitVersion = (ngRange?: string | null) => { + if (!ngRange) { + return undefined; + } + const min = semver.minVersion(ngRange); + + if (!min) { + return undefined; + } + const pre = min.prerelease && min.prerelease.length > 0 ? `-${min.prerelease.join('.')}` : ''; + // devkit follows 0.. + const devkitMinor = min.major * 100 + min.minor; + const versionCore = `0.${devkitMinor}.${min.patch}${pre}`; + const hasCaret = ngRange.trim().startsWith('^'); + return hasCaret ? `^${versionCore}` : versionCore; + }; + + const devkitVersion = toDevkitVersion(angularVersion); + + const extraAngularDeps = [ + angularVersion + ? `@angular-devkit/build-angular@${angularVersion}` + : '@angular-devkit/build-angular', + devkitVersion ? `@angular-devkit/architect@${devkitVersion}` : '@angular-devkit/architect', + angularVersion ? `@angular-devkit/core@${angularVersion}` : '@angular-devkit/core', + angularVersion + ? `@angular/platform-browser-dynamic@${angularVersion}` + : '@angular/platform-browser-dynamic', + ]; + return { extraPackages: [ - angularVersion - ? `@angular-devkit/build-angular@${angularVersion}` - : '@angular-devkit/build-angular', + ...extraAngularDeps, ...(useCompodoc ? ['@compodoc/compodoc', '@storybook/addon-docs'] : []), ], addScripts: false, // Handled above based on project count diff --git a/code/lib/create-storybook/vitest.config.ts b/code/lib/create-storybook/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/create-storybook/vitest.config.ts +++ b/code/lib/create-storybook/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/lib/csf-plugin/vitest.config.ts b/code/lib/csf-plugin/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/lib/csf-plugin/vitest.config.ts +++ b/code/lib/csf-plugin/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/package.json b/code/package.json index 9bb7e0836b5b..c29d2d5541bd 100644 --- a/code/package.json +++ b/code/package.json @@ -286,5 +286,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.1.0-alpha.14" } diff --git a/code/presets/server-webpack/vitest.config.ts b/code/presets/server-webpack/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/presets/server-webpack/vitest.config.ts +++ b/code/presets/server-webpack/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/html/vitest.config.ts b/code/renderers/html/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/renderers/html/vitest.config.ts +++ b/code/renderers/html/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/preact/vitest.config.ts b/code/renderers/preact/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/renderers/preact/vitest.config.ts +++ b/code/renderers/preact/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/react/vitest.config.ts b/code/renderers/react/vitest.config.ts index a5664ea547c3..22543b823778 100644 --- a/code/renderers/react/vitest.config.ts +++ b/code/renderers/react/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/server/vitest.config.ts b/code/renderers/server/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/renderers/server/vitest.config.ts +++ b/code/renderers/server/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/svelte/vitest.config.ts b/code/renderers/svelte/vitest.config.ts index f80ba91df63f..5b0263c4d8f8 100644 --- a/code/renderers/svelte/vitest.config.ts +++ b/code/renderers/svelte/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default defineConfig( mergeConfig(vitestCommonConfig, { diff --git a/code/renderers/vue3/vitest.config.ts b/code/renderers/vue3/vitest.config.ts index a38043181627..f3a883cf21fd 100644 --- a/code/renderers/vue3/vitest.config.ts +++ b/code/renderers/vue3/vitest.config.ts @@ -2,7 +2,7 @@ import { defineConfig, mergeConfig } from 'vitest/config'; import vue from '@vitejs/plugin-vue'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/renderers/web-components/vitest.config.ts b/code/renderers/web-components/vitest.config.ts index 7420176b2e46..16d2fd08535c 100644 --- a/code/renderers/web-components/vitest.config.ts +++ b/code/renderers/web-components/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig, mergeConfig } from 'vitest/config'; -import { vitestCommonConfig } from '../../vitest.workspace'; +import { vitestCommonConfig } from '../../vitest.shared'; export default mergeConfig( vitestCommonConfig, diff --git a/code/vitest-storybook.config.mts b/code/vitest-storybook.config.mts index 215b9a59e9e9..2ede258444db 100644 --- a/code/vitest-storybook.config.mts +++ b/code/vitest-storybook.config.mts @@ -4,7 +4,7 @@ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; import Inspect from 'vite-plugin-inspect'; -import { vitestCommonConfig } from './vitest.workspace'; +import { vitestCommonConfig } from './vitest.shared'; const extraPlugins: any[] = []; if (process.env.INSPECT === 'true') { diff --git a/code/vitest.config.ts b/code/vitest.config.ts index 7cf69b767e92..584e12d93b81 100644 --- a/code/vitest.config.ts +++ b/code/vitest.config.ts @@ -6,6 +6,17 @@ export default defineConfig({ NODE_ENV: 'test', }, + projects: [ + 'vitest-storybook.config.mts', + 'addons/*/vitest.config.ts', + 'frameworks/*/vitest.config.ts', + 'lib/*/vitest.config.ts', + 'core/vitest.config.ts', + 'builders/*/vitest.config.ts', + 'presets/*/vitest.config.ts', + 'renderers/*/vitest.config.ts', + ], + coverage: { all: false, provider: 'istanbul', diff --git a/code/vitest.workspace.ts b/code/vitest.shared.ts similarity index 74% rename from code/vitest.workspace.ts rename to code/vitest.shared.ts index 210fcedad8f2..c2709d10593b 100644 --- a/code/vitest.workspace.ts +++ b/code/vitest.shared.ts @@ -1,17 +1,6 @@ import { resolve } from 'node:path'; -import { defineConfig, defineWorkspace } from 'vitest/config'; - -export default defineWorkspace([ - 'vitest-storybook.config.mts', - 'addons/*/vitest.config.ts', - 'frameworks/*/vitest.config.ts', - 'lib/*/vitest.config.ts', - 'core/vitest.config.ts', - 'builders/*/vitest.config.ts', - 'presets/*/vitest.config.ts', - 'renderers/*/vitest.config.ts', -]); +import { defineConfig } from 'vitest/config'; /** * CircleCI reports the wrong number of threads to Node.js, so we need to set it manually. Unit diff --git a/code/yarn.lock b/code/yarn.lock index 8e9a3a02d44c..2ff72d53b356 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -7833,18 +7833,17 @@ __metadata: webpack: "npm:5" zone.js: "npm:^0.15.0" peerDependencies: - "@angular-devkit/architect": ">=0.1800.0 < 0.2100.0" - "@angular-devkit/build-angular": ">=18.0.0 < 21.0.0" - "@angular-devkit/core": ">=18.0.0 < 21.0.0" - "@angular/animations": ">=18.0.0 < 21.0.0" - "@angular/cli": ">=18.0.0 < 21.0.0" - "@angular/common": ">=18.0.0 < 21.0.0" - "@angular/compiler": ">=18.0.0 < 21.0.0" - "@angular/compiler-cli": ">=18.0.0 < 21.0.0" - "@angular/core": ">=18.0.0 < 21.0.0" - "@angular/forms": ">=18.0.0 < 21.0.0" - "@angular/platform-browser": ">=18.0.0 < 21.0.0" - "@angular/platform-browser-dynamic": ">=18.0.0 < 21.0.0" + "@angular-devkit/architect": ">=0.1800.0 < 0.2200.0" + "@angular-devkit/build-angular": ">=18.0.0 < 22.0.0" + "@angular-devkit/core": ">=18.0.0 < 22.0.0" + "@angular/animations": ">=18.0.0 < 22.0.0" + "@angular/cli": ">=18.0.0 < 22.0.0" + "@angular/common": ">=18.0.0 < 22.0.0" + "@angular/compiler": ">=18.0.0 < 22.0.0" + "@angular/compiler-cli": ">=18.0.0 < 22.0.0" + "@angular/core": ">=18.0.0 < 22.0.0" + "@angular/platform-browser": ">=18.0.0 < 22.0.0" + "@angular/platform-browser-dynamic": ">=18.0.0 < 22.0.0" rxjs: ^6.5.3 || ^7.4.0 storybook: "workspace:^" typescript: ^4.9.0 || ^5.0.0 diff --git a/docs/_snippets/nextjs-add-framework.md b/docs/_snippets/nextjs-add-framework.md index 1c87e051035a..011399eac75e 100644 --- a/docs/_snippets/nextjs-add-framework.md +++ b/docs/_snippets/nextjs-add-framework.md @@ -1,43 +1,44 @@ -```js filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF 3" +```diff filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF 3" export default { // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs', }; ``` -```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" -import type { StorybookConfig } from '@storybook/nextjs'; +```diff filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" +- import type { StorybookConfig } from '@storybook/your-previous-framework'; ++ import type { StorybookConfig } from '@storybook/nextjs'; const config: StorybookConfig = { // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs', }; 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) -import { defineMain } from '@storybook/your-framework/node'; +```diff filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF Next πŸ§ͺ" +- import { defineMain } from '@storybook/your-previous-framework/node'; ++ import { defineMain } from '@storybook/nextjs/node'; export default defineMain({ // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs', }); ``` -```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) -import { defineMain } from '@storybook/your-framework/node'; +```diff filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF Next πŸ§ͺ" +- import { defineMain } from '@storybook/your-previous-framework/node'; ++ import { defineMain } from '@storybook/nextjs/node'; export default defineMain({ // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs', }); ``` diff --git a/docs/_snippets/nextjs-app-directory-in-preview.md b/docs/_snippets/nextjs-app-directory-in-preview.md index 8ecf8d79e6e9..1b9b7ac6c05c 100644 --- a/docs/_snippets/nextjs-app-directory-in-preview.md +++ b/docs/_snippets/nextjs-app-directory-in-preview.md @@ -28,7 +28,7 @@ export default preview; ``` ```ts filename=".storybook/preview.ts" renderer="react" language="ts" tabTitle="CSF Next πŸ§ͺ" -// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite) +// Replace your-framework with nextjs or nextjs-vite import { definePreview } from '@storybook/your-framework'; export default definePreview({ @@ -45,7 +45,7 @@ export default definePreview({ ```js filename=".storybook/preview.js" renderer="react" language="js" tabTitle="CSF Next πŸ§ͺ" -// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite) +// Replace your-framework with nextjs or nextjs-vite import { definePreview } from '@storybook/your-framework'; export default definePreview({ diff --git a/docs/_snippets/nextjs-configure-svgr.md b/docs/_snippets/nextjs-configure-svgr.md index 332b6fc078aa..8b471f318893 100644 --- a/docs/_snippets/nextjs-configure-svgr.md +++ b/docs/_snippets/nextjs-configure-svgr.md @@ -53,7 +53,7 @@ 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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ @@ -83,7 +83,7 @@ export default defineMain({ ```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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ diff --git a/docs/_snippets/nextjs-image-static-dirs.md b/docs/_snippets/nextjs-image-static-dirs.md index af077340e6e3..8bb085e50fa6 100644 --- a/docs/_snippets/nextjs-image-static-dirs.md +++ b/docs/_snippets/nextjs-image-static-dirs.md @@ -28,7 +28,7 @@ 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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ @@ -45,7 +45,7 @@ export default defineMain({ ```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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ diff --git a/docs/_snippets/nextjs-remove-addons.md b/docs/_snippets/nextjs-remove-addons.md index 065a5d713d1f..fb08932d0173 100644 --- a/docs/_snippets/nextjs-remove-addons.md +++ b/docs/_snippets/nextjs-remove-addons.md @@ -11,7 +11,8 @@ export default { ``` ```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" -import type { StorybookConfig } from '@storybook/nextjs'; +// Replace your-framework with nextjs or nextjs-vite +import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { // ... @@ -27,7 +28,7 @@ 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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ @@ -44,7 +45,7 @@ export default defineMain({ ```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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ diff --git a/docs/_snippets/nextjs-vite-add-framework.md b/docs/_snippets/nextjs-vite-add-framework.md index 137479e97e3d..32b50f024ce7 100644 --- a/docs/_snippets/nextjs-vite-add-framework.md +++ b/docs/_snippets/nextjs-vite-add-framework.md @@ -1,43 +1,44 @@ -```js filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF 3" +```diff filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF 3" export default { // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs-vite', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs-vite', }; ``` -```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" -import type { StorybookConfig } from '@storybook/nextjs-vite'; +```diff filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" +- import type { StorybookConfig } from '@storybook/your-previous-framework'; ++ import type { StorybookConfig } from '@storybook/nextjs-vite'; const config: StorybookConfig = { // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs-vite', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs-vite', }; 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) -import { defineMain } from '@storybook/your-framework/node'; +```diff filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF Next πŸ§ͺ" +- import { defineMain } from '@storybook/your-previous-framework/node'; ++ import { defineMain } from '@storybook/nextjs-vite/node'; export default defineMain({ // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs-vite', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs-vite', }); ``` -```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) -import { defineMain } from '@storybook/your-framework/node'; +```diff filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF Next πŸ§ͺ" +- import { defineMain } from '@storybook/your-previous-framework/node'; ++ import { defineMain } from '@storybook/nextjs-vite/node'; export default defineMain({ // ... - // framework: '@storybook/react-webpack5', πŸ‘ˆ Remove this - framework: '@storybook/nextjs-vite', // πŸ‘ˆ Add this +- framework: '@storybook/react-webpack5', ++ framework: '@storybook/nextjs-vite', }); ``` diff --git a/docs/_snippets/nextjs-vite-remove-addons.md b/docs/_snippets/nextjs-vite-remove-addons.md deleted file mode 100644 index 0a28992d16bd..000000000000 --- a/docs/_snippets/nextjs-vite-remove-addons.md +++ /dev/null @@ -1,59 +0,0 @@ -```js filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF 3" -export default { - // ... - addons: [ - // ... - // πŸ‘‡ These can both be removed - // 'storybook-addon-next', - // 'storybook-addon-next-router', - ], -}; -``` - -```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF 3" -import type { StorybookConfig } from '@storybook/nextjs-vite'; - -const config: StorybookConfig = { - // ... - addons: [ - // ... - // πŸ‘‡ These can both be removed - // 'storybook-addon-next', - // 'storybook-addon-next-router', - ], -}; - -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) -import { defineMain } from '@storybook/your-framework/node'; - -export default defineMain({ - // ... - addons: [ - // ... - // πŸ‘‡ These can both be removed - // 'storybook-addon-next', - // 'storybook-addon-next-router', - ], -}); -``` - - - -```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) -import { defineMain } from '@storybook/your-framework/node'; - -export default defineMain({ - // ... - addons: [ - // ... - // πŸ‘‡ These can both be removed - // 'storybook-addon-next', - // 'storybook-addon-next-router', - ], -}); -``` diff --git a/docs/_snippets/rsc-feature-flag.md b/docs/_snippets/rsc-feature-flag.md index 1084b10054c7..438c6a6428d3 100644 --- a/docs/_snippets/rsc-feature-flag.md +++ b/docs/_snippets/rsc-feature-flag.md @@ -22,7 +22,7 @@ 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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ @@ -36,7 +36,7 @@ export default defineMain({ ```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) +// Replace your-framework with nextjs or nextjs-vite import { defineMain } from '@storybook/your-framework/node'; export default defineMain({ diff --git a/docs/api/cli-options.mdx b/docs/api/cli-options.mdx index 6890686804ce..f33b476e0f38 100644 --- a/docs/api/cli-options.mdx +++ b/docs/api/cli-options.mdx @@ -123,13 +123,15 @@ Options include: | `-s`, `--skip-install` | Skips the dependency installation step. Used only when you need to configure Storybook manually.
`storybook init --skip-install` | | `-t`, `--type` | Defines the [framework](../configure/integration/frameworks.mdx) to use for your Storybook instance.
`storybook init --type solid` | | `-y`, `--yes` | Skips interactive prompts and automatically installs Storybook per specified version, including all features.
`storybook init --yes` | -| `--features [...values]` | Use these features when installing, skipping the prompt. Supported values are `docs`, `test` and `a11y`, space separated.
`storybook init --features docs test a11y` | +| `--features [...values]` | Use these features when installing, skipping the prompt. Supported values are `docs`, `test`, and `a11y`, space separated.
`storybook init --features docs test a11y` | | `--package-manager` | Sets the package manager to use when installing Storybook.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook init --package-manager pnpm` | | `--use-pnp` | Enables [Plug'n'Play](https://yarnpkg.com/features/pnp) support for Yarn. This option is only available when using Yarn as your package manager.
`storybook init --use-pnp` | | `-p`, `--parser` | Sets the [jscodeshift parser](https://github.com/facebook/jscodeshift#parser).
Available parsers include `babel`, `babylon`, `flow`, `ts`, and `tsx`.
`storybook init --parser tsx` | | `--debug` | Outputs more logs in the CLI to assist debugging.
`storybook init --debug` | | `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.mdx#how-to-opt-out).
`storybook init --disable-telemetry` | | `--enable-crash-reports` | Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry.mdx#crash-reports-disabled-by-default).
`storybook init --enable-crash-reports` | +| `--loglevel ` | Controls level of logging during initialization.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook init --loglevel debug` | +| `--logfile [path]` | Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook init --logfile /tmp/debug-storybook.log` | | `--no-dev` | Complete the initialization of Storybook without running the Storybook dev server.
`storybook init --no-dev` | ### `add` @@ -149,6 +151,8 @@ Options include: | `--package-manager` | Sets the package manager to use when installing the addon.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook add [addon] --package-manager pnpm` | | `-s`, `--skip-postinstall` | Skips post-install configuration. Used only when you need to configure the addon yourself.
`storybook add [addon] --skip-postinstall` | | `--debug` | Outputs more logs in the CLI to assist debugging.
`storybook add --debug` | +| `--loglevel ` | Controls level of logging during addon installation.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook add [addon] --loglevel debug` | +| `--logfile [path]` | Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook add [addon] --logfile /tmp/debug-storybook.log` | ### `remove` @@ -366,11 +370,13 @@ Options include: | `-s`, `--skip-install` | Skips the dependency installation step. Used only when you need to configure Storybook manually.
`create storybook --skip-install` | | `-t`, `--type` | Defines the [framework](../configure/integration/frameworks.mdx) to use for your Storybook instance.
`create storybook --type solid` | | `-y`, `--yes` | Skips interactive prompts and automatically installs Storybook per specified version, including all features.
`create storybook --yes` | -| `--features [...values]` | Use these features when installing, skipping the prompt. Supported values are `docs`, `test` and `a11y`, space separated.
`create storybook --features docs test a11y` | +| `--features [...values]` | Use these features when installing, skipping the prompt. Supported values are `docs`, `test`, and `a11y`, space separated.
`create storybook --features docs test a11y` | | `--package-manager` | Sets the package manager to use when installing Storybook.
Available package managers include `npm`, `yarn`, and `pnpm`.
`create storybook --package-manager pnpm` | | `--use-pnp` | Enables [Plug'n'Play](https://yarnpkg.com/features/pnp) support for Yarn. This option is only available when using Yarn as your package manager.
`create storybook --use-pnp` | | `-p`, `--parser` | Sets the [jscodeshift parser](https://github.com/facebook/jscodeshift#parser).
Available parsers include `babel`, `babylon`, `flow`, `ts`, and `tsx`.
`create storybook --parser tsx` | | `--debug` | Outputs more logs in the CLI to assist debugging.
`create storybook --debug` | | `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.mdx#how-to-opt-out).
`create storybook --disable-telemetry` | | `--enable-crash-reports` | Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry.mdx#crash-reports-disabled-by-default).
`create storybook --enable-crash-reports` | +| `--loglevel ` | Controls level of logging during initialization.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook init --loglevel debug` | +| `--logfile [path]` | Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook init --logfile /tmp/debug-storybook.log` | | `--no-dev` | Complete the initialization of Storybook without running the Storybook dev server.
`create storybook --no-dev` | diff --git a/docs/get-started/frameworks/nextjs-vite.mdx b/docs/get-started/frameworks/nextjs-vite.mdx new file mode 100644 index 000000000000..12b66b6329b0 --- /dev/null +++ b/docs/get-started/frameworks/nextjs-vite.mdx @@ -0,0 +1,954 @@ +--- +title: Storybook for Next.js with Vite +hideRendererSelector: true +sidebar: + order: 2 + title: Next.js (Vite) +--- + +Storybook for Next.js (Vite) is the **recommended** [framework](../../contribute/framework.mdx) for developing and testing UI components in isolation for [Next.js](https://nextjs.org/) applications. It uses [Vite](https://vitejs.dev/) for faster builds and better performance. It includes: + +* πŸ”€ Routing +* πŸ–Ό Image optimization +* ‡️ Absolute imports +* 🎨 Styling +* ⚑ Vite-powered builds +* πŸ’« and more! + +This Vite-based framework offers several advantages over the Webpack-based [`@storybook/nextjs`](./nextjs.mdx) framework: + +* ⚑ **Faster builds** - Vite's build system is significantly faster than Webpack +* πŸ”§ **Modern tooling** - Uses the latest build tools and optimizations +* πŸ§ͺ **Better test support** - Full support for the [Vitest addon](../../writing-tests/integrations/vitest-addon.mdx) and other testing features +* πŸ“¦ **Simpler configuration** - No need for Babel or complex Webpack configurations +* 🎯 **Better development experience** - Faster HMR (Hot Module Replacement) and dev server startup + +## Requirements + +* Next.js β‰₯ 14.1 + +## Getting started + +### In a project without Storybook + +When you run `storybook init` in your Next.js project, Storybook will automatically detect your project and select the `@storybook/nextjs-vite` framework **unless** your project has custom Webpack or Babel configurations that may be incompatible with Vite. + +Follow the prompts after running this command in your Next.js project's root directory: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +[More on getting started with Storybook.](../install.mdx) + + + +If your project has a custom `webpack.config.js` or `.babelrc` file, `storybook init` will prompt you to choose between: + +- **`@storybook/nextjs-vite`** (recommended) - Faster, more modern, supports latest testing features +- **`@storybook/nextjs`** (Webpack 5) - Better compatibility with custom Webpack/Babel configurations + +Choose `nextjs-vite` if you're willing to migrate your custom configurations to Vite. Choose `nextjs` (Webpack 5) if you need to keep your existing Webpack/Babel setup. + + + +### In a project with Storybook + +This framework is designed to work with Storybook 10+. If you're not already using v10, upgrade with this command: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +#### Automatic migration + +When running the `upgrade` command above, you should get a prompt asking you to migrate to `@storybook/nextjs-vite`, which should handle everything for you. In case that auto-migration does not work for your project, refer to the manual migration below. + +You can also use the [`nextjs-to-nextjs-vite` automigration](#migrating-from-webpack) to migrate from the Webpack-based `@storybook/nextjs` framework to this Vite-based framework. + +#### Manual migration + +First, install the framework: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +Then, update your `.storybook/main.js|ts` to change the framework property: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +If your Storybook configuration contains custom Webpack operations in [`webpackFinal`](../../api/main-config/main-config-webpack-final.mdx), you will likely need to create equivalents in [`viteFinal`](../../api/main-config/main-config-vite-final.mdx). + +For more information, see the [Vite builder documentation](../../builders/vite.mdx#migrating-from-webpack). + + + +Finally, if you were using Storybook plugins to integrate with Next.js, those are no longer necessary when using this framework and can be removed: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +#### Migrating from Webpack + +Storybook provides a migration tool for migrating to this framework from the Webpack-based Next.js framework, `@storybook/nextjs`. To migrate, run this command: + +```bash +npx storybook automigrate nextjs-to-nextjs-vite +``` + +This automigration tool performs the following actions: + +1. Updates `package.json` files to replace `@storybook/nextjs` with `@storybook/nextjs-vite` +2. Updates `.storybook/main.js|ts` to change the framework property +3. Scans and updates import statements in your story files and configuration files + + + +If your project has custom Webpack configurations in `.storybook/main.js|ts` (via `webpackFinal`), you'll need to migrate those to Vite configuration (via `viteFinal`) after running this automigration. See the [Vite builder documentation](../builders/vite.mdx#migrating-from-webpack) for more information. + + + +## Run the Setup Wizard + +If all goes well, you should see a setup wizard that will help you get started with Storybook introducing you to the main concepts and features, including how the UI is organized, how to write your first story, and how to test your components' response to various inputs utilizing [controls](../../essentials/controls.mdx). + +![Storybook onboarding](../../_assets/get-started/example-onboarding-wizard.png) + +If you skipped the wizard, you can always run it again by adding the `?path=/onboarding` query parameter to the URL of your Storybook instance, provided that the example stories are still available. + +## Next.js's Image component + +This framework allows you to use Next.js's [next/image](https://nextjs.org/docs/pages/api-reference/components/image) with no configuration. + +### Local images + +[Local images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#local-images) are supported. + +```jsx title="index.jsx" +import Image from 'next/image'; +import profilePic from '../public/me.png'; + +function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ); +} +``` + +### Remote images + +[Remote images](https://nextjs.org/docs/pages/building-your-application/optimizing/images#remote-images) are also supported. + +```jsx title="index.jsx" +import Image from 'next/image'; + +export default function Home() { + return ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ); +} +``` + +## Next.js font optimization + +[next/font](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts) is partially supported in Storybook. The packages `next/font/google` and `next/font/local` are supported. + +### `next/font/google` + +You don't have to do anything. `next/font/google` is supported out of the box. + +### `next/font/local` + +For local fonts you have to define the [src](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts#local-fonts) property. +The path is relative to the directory where the font loader function is called. + +If the following component defines your localFont like this: + +```js title="src/components/MyComponent.js" +import localFont from 'next/font/local'; + +const localRubikStorm = localFont({ src: './fonts/RubikStorm-Regular.ttf' }); +``` + +The Vite-based framework automatically handles font path mapping, so you don't need to configure `staticDirs` for fonts like you would with the Webpack-based framework. + +### Not supported features of `next/font` + +The following features are not supported (yet). Support for these features might be planned for the future: + +* [Support font loaders configuration in next.config.js](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts#local-fonts) +* [fallback](https://nextjs.org/docs/pages/api-reference/components/font#fallback) option +* [adjustFontFallback](https://nextjs.org/docs/pages/api-reference/components/font#adjustfontfallback) option +* [preload](https://nextjs.org/docs/pages/api-reference/components/font#preload) option gets ignored. Storybook handles Font loading its own way. +* [display](https://nextjs.org/docs/pages/api-reference/components/font#display) option gets ignored. All fonts are loaded with display set to "block" to make Storybook load the font properly. + +### Mocking fonts during testing + +Occasionally fetching fonts from Google may fail as part of your Storybook build step. It is highly recommended to mock these requests, as those failures can cause your pipeline to fail as well. Next.js [supports mocking fonts](https://github.com/vercel/next.js/blob/725ddc7371f80cca273779d37f961c3e20356f95/packages/font/src/google/fetch-css-from-google-fonts.ts#L36) via a JavaScript module located where the env var `NEXT_FONT_GOOGLE_MOCKED_RESPONSES` references. + +For example, using [GitHub Actions](https://www.chromatic.com/docs/github-actions): + +```yaml title=".github/workflows/ci.yml" +- uses: chromaui/action@latest + env: + #πŸ‘‡ the location of mocked fonts to use + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: ${{ github.workspace }}/mocked-google-fonts.js + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} +``` + +Your mocked fonts will look something like this: + +```js title="mocked-google-fonts.js" +//πŸ‘‡ Mocked responses of google fonts with the URL as the key +module.exports = { + 'https://fonts.googleapis.com/css?family=Inter:wght@400;500;600;800&display=block': ` + /* cyrillic-ext */ + @font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiJ-Ek-_EeAmM.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; + } + /* more font declarations go here */ + /* latin */ + @font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + }`, +}; +``` + +## Next.js routing + +[Next.js's router](https://nextjs.org/docs/pages/building-your-application/routing) is automatically stubbed for you so that when the router is interacted with, all of its interactions are automatically logged to the [Actions panel](../../essentials/actions.mdx). + + + +You should only use `next/router` in the `pages` directory. In the `app` directory, it is necessary to use `next/navigation`. + + + +### Overriding defaults + +Per-story overrides can be done by adding a `nextjs.router` property onto the story [parameters](../../writing-stories/parameters.mdx). The framework will shallowly merge whatever you put here into the router. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +These overrides can also be applied to [all stories for a component](../../api/parameters.mdx#meta-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +### Default router + +The default values on the stubbed router are as follows (see [globals](../../essentials/toolbars-and-globals.mdx#globals) for more details on how globals work). + +```ts +// Default router +const defaultRouter = { + // The locale should be configured globally: https://storybook.js.org/docs/essentials/toolbars-and-globals#globals + locale: globals?.locale, + asPath: '/', + basePath: '/', + isFallback: false, + isLocaleDomain: false, + isReady: true, + isPreview: false, + route: '/', + pathname: '/', + query: {}, +}; +``` + +Additionally, the [`router` object](https://nextjs.org/docs/pages/api-reference/functions/use-router#router-object) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). + +To override these defaults, you can use [parameters](../../writing-stories/parameters.mdx) and [`beforeEach`](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#setting-up-and-cleaning-up): + +```ts title=".storybook/preview.js|ts" +import type { Preview } from '@storybook/nextjs-vite'; + +// πŸ‘‡ Must include the `.mock` portion of filename to have mocks typed correctly +import { getRouter } from "@storybook/nextjs-vite/router.mock"; + +const preview: Preview = { + parameters: { + nextjs: { + // πŸ‘‡ Override the default router properties + router: { + basePath: '/app/', + }, + }, + }, + async beforeEach() { + // πŸ‘‡ Manipulate the default router method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` + +## Next.js navigation + + + +Please note that [`next/navigation`](https://nextjs.org/docs/app/building-your-application/routing) can only be used in components/pages in the `app` directory. + + + +### Set `nextjs.appDirectory` to `true` + +If your story imports components that use `next/navigation`, you need to set the parameter `nextjs.appDirectory` to `true` in for that component's stories: + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +If your Next.js project uses the `app` directory for every page (in other words, it does not have a `pages` directory), you can set the parameter `nextjs.appDirectory` to `true` in the [`.storybook/preview.js|ts`](../../configure/index.mdx#configure-story-rendering) file to apply it to all stories. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +### Overriding defaults + +Per-story overrides can be done by adding a `nextjs.navigation` property onto the story [parameters](../../writing-stories/parameters.mdx). The framework will shallowly merge whatever you put here into the router. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + + + +These overrides can also be applied to [all stories for a component](../../api/parameters.mdx#meta-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +### `useSelectedLayoutSegment`, `useSelectedLayoutSegments`, and `useParams` hooks + +The `useSelectedLayoutSegment`, `useSelectedLayoutSegments`, and `useParams` hooks are supported in Storybook. You have to set the `nextjs.navigation.segments` parameter to return the segments or the params you want to use. + +{/* prettier-ignore-start */} + + + +With the above configuration, the component rendered in the stories would receive the following values from the hooks: + +```js title="NavigationBasedComponent.js" +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; + +export default function NavigationBasedComponent() { + const segment = useSelectedLayoutSegment(); // dashboard + const segments = useSelectedLayoutSegments(); // ["dashboard", "analytics"] + const params = useParams(); // {} + // ... +} +``` + +{/* prettier-ignore-end */} + +To use `useParams`, you have to use a segments array where each element is an array containing two strings. The first string is the param key and the second string is the param value. + +{/* prettier-ignore-start */} + + + +{/* prettier-ignore-end */} + +With the above configuration, the component rendered in the stories would receive the following values from the hooks: + +```js title="ParamsBasedComponent.js" +import { useSelectedLayoutSegment, useSelectedLayoutSegments, useParams } from 'next/navigation'; + +export default function ParamsBasedComponent() { + const segment = useSelectedLayoutSegment(); // hello + const segments = useSelectedLayoutSegments(); // ["hello", "nextjs"] + const params = useParams(); // { slug: "hello", framework: "nextjs" } + ... +} +``` + + + +These overrides can also be applied to [a single story](../../api/parameters.mdx#story-parameters) or [all stories in your project](../../api/parameters.mdx#project-parameters). Standard [parameter inheritance](../../api/parameters.mdx#parameter-inheritance) rules apply. + + + +The default value of `nextjs.navigation.segments` is `[]` if not set. + +### Default navigation context + +The default values on the stubbed navigation context are as follows: + +```ts +// Default navigation context +const defaultNavigationContext = { + pathname: '/', + query: {}, +}; +``` + +Additionally, the [`router` object](https://nextjs.org/docs/app/api-reference/functions/use-router#userouter) contains all of the original methods (such as `push()`, `replace()`, etc.) as mock functions that can be manipulated and asserted on using [regular mock APIs](https://vitest.dev/api/mock.html). + +To override these defaults, you can use [parameters](../../writing-stories/parameters.mdx) and [`beforeEach`](../../writing-stories/mocking-data-and-modules/mocking-modules.mdx#setting-up-and-cleaning-up): + +```ts title=".storybook/preview.js|ts" +import type { Preview } from '@storybook/nextjs-vite'; + +// πŸ‘‡ Must include the `.mock` portion of filename to have mocks typed correctly +import { getRouter } from '@storybook/nextjs-vite/navigation.mock'; + +const preview: Preview = { + parameters: { + nextjs: { + // πŸ‘‡ Override the default navigation properties + navigation: { + pathname: '/app/', + }, + }, + }, + async beforeEach() { + // πŸ‘‡ Manipulate the default navigation method mocks + getRouter().push.mockImplementation(() => { + /* ... */ + }); + }, +}; +``` + +## Next.js Head + +[`next/head`](https://nextjs.org/docs/pages/api-reference/components/head) is supported out of the box. You can use it in your stories like you would in your Next.js application. Please keep in mind, that the Head `children` are placed into the head element of the iframe that Storybook uses to render your stories. + +## Sass/Scss + +[Global Sass/Scss stylesheets](https://nextjs.org/docs/pages/building-your-application/styling/sass) are supported without any additional configuration as well. Just import them into [`.storybook/preview.js|ts`](../../configure/index.mdx#configure-story-rendering) + +```js title=".storybook/preview.js|ts" +import '../styles/globals.scss'; +``` + +This will automatically include any of your [custom Sass configurations](https://nextjs.org/docs/pages/building-your-application/styling/sass#customizing-sass-options) in your `next.config.js` file. + +```js title="next.config.js" +import * as path from 'path'; + +export default { + // Any options here are included in Sass compilation for your stories + sassOptions: { + includePaths: [path.join(process.cwd(), 'styles')], + }, +}; +``` + +## CSS/Sass/Scss Modules + +[CSS modules](https://nextjs.org/docs/pages/building-your-application/styling/css-modules) work as expected. + +```jsx title="src/components/Button.jsx" +// This import will work in Storybook +import styles from './Button.module.css'; +// Sass/Scss is also supported +// import styles from './Button.module.scss' +// import styles from './Button.module.sass' + +export function Button() { + return ( + + ); +} +``` + +## PostCSS + +Next.js lets you [customize PostCSS config](https://nextjs.org/docs/pages/building-your-application/configuring/post-css). Thus this framework will automatically handle your PostCSS config for you. + +This allows for cool things like zero-config Tailwind! (See [Next.js' example](https://github.com/vercel/next.js/tree/canary/packages/create-next-app/templates/default-tw)) + +## Absolute imports + +[Absolute imports](https://nextjs.org/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases#absolute-imports) from the root directory are supported. + +```jsx title="index.jsx|tsx" +// All good! +import Button from 'components/button'; +// Also good! +import styles from 'styles/HomePage.module.css'; + +export default function HomePage() { + return ( + <> +

Hello World

+