diff --git a/.github/workflows/tests-unit.yml b/.github/workflows/tests-unit.yml index d50dc7efdf27..c0e166bea164 100644 --- a/.github/workflows/tests-unit.yml +++ b/.github/workflows/tests-unit.yml @@ -17,6 +17,8 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 2 - name: Set node version uses: actions/setup-node@v4 with: diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 1389f9f4a4de..217d74b01133 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,63 @@ +## 8.5.0-beta.2 + +- Addon Test: Clear coverage data when starting or watching - [#30072](https://github.com/storybookjs/storybook/pull/30072), thanks @ghengeveld! +- Addon Test: Improve error message on missing coverage package - [#30088](https://github.com/storybookjs/storybook/pull/30088), thanks @JReinhold! +- UI: Fix test provider event handling on startup - [#30083](https://github.com/storybookjs/storybook/pull/30083), thanks @ghengeveld! +- UI: Keep failing stories in the sidebar, disregarding filters - [#30086](https://github.com/storybookjs/storybook/pull/30086), thanks @JReinhold! + +## 8.5.0-beta.1 + +- Addon A11y: Add conditional rendering for a11y violation number in Testing Module - [#30073](https://github.com/storybookjs/storybook/pull/30073), thanks @valentinpalkovic! +- Addon A11y: Remove warnings API - [#30049](https://github.com/storybookjs/storybook/pull/30049), thanks @kasperpeulen! +- Addon A11y: Show errors of axe properly - [#30050](https://github.com/storybookjs/storybook/pull/30050), thanks @kasperpeulen! +- Addon Test: Fix printing null% for coverage - [#30061](https://github.com/storybookjs/storybook/pull/30061), thanks @ghengeveld! +- Telemetry: Add metadata distinguishing "apps" from "design systems" - [#30070](https://github.com/storybookjs/storybook/pull/30070), thanks @tmeasday! + +## 8.5.0-beta.0 + +- Automigration: Improve setup file transformation and version range handling for a11y migration - [#30060](https://github.com/storybookjs/storybook/pull/30060), thanks @valentinpalkovic! +- Next.js: Support v15.1.1 - [#30068](https://github.com/storybookjs/storybook/pull/30068), thanks @valentinpalkovic! + +## 8.5.0-alpha.22 + +- Addon Docs: Dynamically import rehype - [#29544](https://github.com/storybookjs/storybook/pull/29544), thanks @valentinpalkovic! +- Addon Test: Fix duplicate `test.include` patterns - [#30029](https://github.com/storybookjs/storybook/pull/30029), thanks @JReinhold! +- Addon Test: Fix environment variable for Vitest Storybook integration - [#30054](https://github.com/storybookjs/storybook/pull/30054), thanks @valentinpalkovic! +- Addon Test: Use local storybook binary instead - [#30021](https://github.com/storybookjs/storybook/pull/30021), thanks @kasperpeulen! +- Addon Test: Wait for 2 seconds before showing result mismatch warning - [#30002](https://github.com/storybookjs/storybook/pull/30002), thanks @ghengeveld! +- Angular: Support statsJson in angular schemas - [#29233](https://github.com/storybookjs/storybook/pull/29233), thanks @yannbf! +- Core: Fix `scrollIntoView` behavior and reimplement testing module time rendering - [#30044](https://github.com/storybookjs/storybook/pull/30044), thanks @ghengeveld! +- Docs: Add code snippet to addons panel - [#29253](https://github.com/storybookjs/storybook/pull/29253), thanks @larsrickert! +- Next.js: Fix webpack fsCache not working - [#29654](https://github.com/storybookjs/storybook/pull/29654), thanks @sentience! +- Nextjs-Vite: Add TS docgen support - [#29824](https://github.com/storybookjs/storybook/pull/29824), thanks @yannbf! +- Nextjs-Vite: Fix docgen types in main config - [#30042](https://github.com/storybookjs/storybook/pull/30042), thanks @yannbf! +- React: Fix RSC compatibility with addon-themes and hooks - [#26243](https://github.com/storybookjs/storybook/pull/26243), thanks @shilman! +- UI: Fix controls and parameters on tag-filtered stories - [#30038](https://github.com/storybookjs/storybook/pull/30038), thanks @shilman! + +## 8.5.0-alpha.21 + +- Addon A11y: Add typesVersions support for TypeScript definitions in a11y package - [#30005](https://github.com/storybookjs/storybook/pull/30005), thanks @valentinpalkovic! +- Addon A11y: Refactor environment variable handling for Vitest integration - [#30022](https://github.com/storybookjs/storybook/pull/30022), thanks @valentinpalkovic! +- Addon A11y: Run the a11y automigration on postInstall - [#30004](https://github.com/storybookjs/storybook/pull/30004), thanks @kasperpeulen! +- Addon A11y: Update accessibility status handling in TestProviderRender - [#30027](https://github.com/storybookjs/storybook/pull/30027), thanks @valentinpalkovic! +- Addon Onboarding: Prebundle react-confetti - [#29996](https://github.com/storybookjs/storybook/pull/29996), thanks @yannbf! +- Addon Test: Correctly stop Storybook when Vitest closes - [#30012](https://github.com/storybookjs/storybook/pull/30012), thanks @JReinhold! +- Addon Test: Show sub test provider toggle state in main testing module - [#30019](https://github.com/storybookjs/storybook/pull/30019), thanks @ghengeveld! +- Addon Test: Wrap sub-paths exported with `require.resolve` - [#30026](https://github.com/storybookjs/storybook/pull/30026), thanks @ndelangen! +- Core: Fix bundling of React - [#30003](https://github.com/storybookjs/storybook/pull/30003), thanks @yannbf! + +## 8.5.0-alpha.20 + +- Addon Test: Add `@vitest/coverage-v8` during postinstall if no coverage reporter is installed - [#29993](https://github.com/storybookjs/storybook/pull/29993), thanks @ghengeveld! +- Addon Test: Add support for previewHead - [#29808](https://github.com/storybookjs/storybook/pull/29808), thanks @ndelangen! +- Addon Test: Always run Vitest in watch mode internally - [#29749](https://github.com/storybookjs/storybook/pull/29749), thanks @JReinhold! +- Addon Test: Filter out falsy test results in TestProviderRender - [#30001](https://github.com/storybookjs/storybook/pull/30001), thanks @valentinpalkovic! +- Addon Test: Handle undefined storyId - [#29998](https://github.com/storybookjs/storybook/pull/29998), thanks @ghengeveld! +- Addon Test: Make component tests status row link to the story's tests panel - [#29992](https://github.com/storybookjs/storybook/pull/29992), thanks @ghengeveld! +- Addon Test: Merge viteFinal config into vitest config - [#29806](https://github.com/storybookjs/storybook/pull/29806), thanks @ndelangen! +- Addon Test: Prompt switch to `experimental-nextjs-vite` - [#29814](https://github.com/storybookjs/storybook/pull/29814), thanks @ndelangen! +- Addon Test: Use ProgressSpinner for stop button in Testing Module - [#29997](https://github.com/storybookjs/storybook/pull/29997), thanks @ghengeveld! + ## 8.5.0-alpha.19 - Addon A11y: Create a11y test provider and revamp a11y addon - [#29643](https://github.com/storybookjs/storybook/pull/29643), thanks @valentinpalkovic! diff --git a/MIGRATION.md b/MIGRATION.md index fd3ce9e10be5..28b4bc5d6ddc 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,7 @@

Migration

- [From version 8.4.x to 8.5.x](#from-version-84x-to-85x) + - [Added source code panel to docs](#added-source-code-panel-to-docs) - [Addon-a11y: Component test integration](#addon-a11y-component-test-integration) - [Addon-a11y: Deprecated `parameters.a11y.manual`](#addon-a11y-deprecated-parametersa11ymanual) - [Indexing behavior of @storybook/experimental-addon-test is changed](#indexing-behavior-of-storybookexperimental-addon-test-is-changed) @@ -425,14 +426,32 @@ ## From version 8.4.x to 8.5.x +### Added source code panel to docs + +Starting in 8.5, Storybook Docs (`@storybook/addon-docs`) automatically adds a new addon panel to stories that displays a source snippet beneath each story. This works similarly to the existing [source snippet doc block](https://storybook.js.org/docs/writing-docs/doc-blocks#source), but in the story view. It is intended to replace the [Storysource addon](https://storybook.js.org/addons/@storybook/addon-storysource). + +If you wish to disable this panel globally, add the following line to your `.storybook/preview.js` project configuration. You can also selectively disable/enable at the story level. + +```js +export default { + parameters: { + docs: { + codePanel: false, + }, + }, +}; +``` + ### Addon-a11y: Component test integration -In Storybook 8.4, we introduced a new addon called [addon test](https://storybook.js.org/docs/writing-tests/test-addon). Powered by Vitest under the hood, this addon lets you watch, run, and debug your component tests directly in Storybook. +In Storybook 8.4, we introduced the [Test addon](https://storybook.js.org/docs/writing-tests/test-addon) (`@storybook/experimental-addon-test`). Powered by Vitest under the hood, this addon lets you watch, run, and debug your component tests directly in Storybook. + +In Storybook 8.5, we revamped the [Accessibility addon](https://storybook.js.org/docs/writing-tests/accessibility-testing) (`@storybook/addon-a11y`) to integrate it with the component tests feature. This means you can now extend your component tests to include accessibility tests. -In Storybook 8.5, we revamped the Accessibility addon (`@storybook/addon-a11y`) to integrate it with the component tests feature. This means you can now extend your component tests to include accessibility tests. If you upgrade to Storybook 8.5 via `npx storybook@latest upgrade`, the Accessibility addon will be automatically configured to work with the component tests. However, if you're upgrading manually and you have the [addon test](https://storybook.js.org/docs/writing-tests/test-addon) installed, adjust your configuration as follows: +If you upgrade to Storybook 8.5 via `npx storybook@latest upgrade`, the Accessibility addon will be automatically configured to work with the component tests. However, if you're upgrading manually and you have the Test addon installed, adjust your configuration as follows: ```diff -// .storybook/vitest.config.ts +// .storybook/vitest.setup.ts ... +import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview'; diff --git a/SECURITY.md b/SECURITY.md index 123b5d5bbeac..ac75ea2100ff 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,6 +8,6 @@ In the event of a high-risk vulnerability, we may backport the security fixes to ## Reporting a Vulnerability -To report a vulnerability, you can reach out to the maintainers directly on Twitter: https://twitter.com/storybookjs +To report a vulnerability, you can reach out to the maintainers directly on Twitter: https://twitter.com/storybookjs or Bluesky: https://bsky.app/profile/storybook.js.org When we fix a security issue, we will post a security advisory on Github/NPM, describe the change in the [release notes](https://github.com/storybookjs/storybook/releases), and also announce notify the community on [our Discord](https://discord.gg/storybook). diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx index 017419318b0a..23f95a0c5d5e 100644 --- a/code/.storybook/preview.tsx +++ b/code/.storybook/preview.tsx @@ -358,9 +358,6 @@ export const parameters = { opacity: 0.4, }, }, - a11y: { - warnings: ['minor', 'moderate', 'serious', 'critical'], - }, }; -export const tags = ['test', 'vitest']; +export const tags = ['test', 'vitest', '!a11ytest']; diff --git a/code/addons/a11y/package.json b/code/addons/a11y/package.json index 08d64b7d4f40..ca38d1848b6c 100644 --- a/code/addons/a11y/package.json +++ b/code/addons/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-a11y", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Test component compliance with web accessibility standards", "keywords": [ "a11y", @@ -38,11 +38,22 @@ }, "./manager": "./dist/manager.js", "./register": "./dist/manager.js", - "./package.json": "./package.json" + "./package.json": "./package.json", + "./postinstall": "./dist/postinstall.js" }, "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "dist/index.d.ts" + ], + "preview": [ + "dist/preview.d.ts" + ] + } + }, "files": [ "dist/**/*", "README.md", @@ -64,6 +75,7 @@ "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.12", "@testing-library/react": "^14.0.0", + "execa": "^9.5.2", "picocolors": "^1.1.0", "pretty-format": "^29.7.0", "react": "^18.2.0", @@ -87,6 +99,9 @@ ], "previewEntries": [ "./src/preview.tsx" + ], + "nodeEntries": [ + "./src/postinstall.ts" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae16", diff --git a/code/addons/a11y/src/components/A11YPanel.tsx b/code/addons/a11y/src/components/A11YPanel.tsx index dc048d97b941..61ea9a663af4 100644 --- a/code/addons/a11y/src/components/A11YPanel.tsx +++ b/code/addons/a11y/src/components/A11YPanel.tsx @@ -133,7 +133,11 @@ export const A11YPanel: React.FC = () => { <> The accessibility scan encountered an error.
- {typeof error === 'string' ? error : JSON.stringify(error)} + {typeof error === 'string' + ? error + : error instanceof Error + ? error.toString() + : JSON.stringify(error)} )} diff --git a/code/addons/a11y/src/params.ts b/code/addons/a11y/src/params.ts index dd4357687340..e66a0813a42c 100644 --- a/code/addons/a11y/src/params.ts +++ b/code/addons/a11y/src/params.ts @@ -6,8 +6,6 @@ export interface Setup { options: RunOptions; } -type Impact = NonNullable; - export interface A11yParameters { element?: ElementContext; config?: Spec; @@ -15,5 +13,4 @@ export interface A11yParameters { /** @deprecated Use globals.a11y.manual instead */ manual?: boolean; disable?: boolean; - warnings?: Impact[]; } diff --git a/code/addons/a11y/src/postinstall.ts b/code/addons/a11y/src/postinstall.ts new file mode 100644 index 000000000000..9a64796f5e1b --- /dev/null +++ b/code/addons/a11y/src/postinstall.ts @@ -0,0 +1,15 @@ +import type { PostinstallOptions } from '@storybook/cli/src/add'; + +// eslint-disable-next-line depend/ban-dependencies +import { execa } from 'execa'; + +const $ = execa({ + preferLocal: true, + stdio: 'inherit', + // we stream the stderr to the console + reject: false, +}); + +export default async function postinstall(options: PostinstallOptions) { + await $`storybook automigrate addonA11yAddonTest ${options.yes ? '--yes' : ''}`; +} diff --git a/code/addons/a11y/src/preview.test.tsx b/code/addons/a11y/src/preview.test.tsx index 334f7f924aeb..d09ba89462ed 100644 --- a/code/addons/a11y/src/preview.test.tsx +++ b/code/addons/a11y/src/preview.test.tsx @@ -156,60 +156,6 @@ describe('afterEach', () => { }); }); - it('should report warning status when there are only warnings', async () => { - const context = createContext({ - parameters: { - a11y: { - warnings: ['minor'], - }, - }, - }); - const result = { - violations: [ - { impact: 'minor', nodes: [] }, - { impact: 'critical', nodes: [] }, - ], - }; - mockedRun.mockResolvedValue(result as any); - - await expect(async () => experimental_afterEach(context)).rejects.toThrow(); - - expect(mockedRun).toHaveBeenCalledWith(context.parameters.a11y); - expect(context.reporting.addReport).toHaveBeenCalledWith({ - type: 'a11y', - version: 1, - result, - status: 'failed', - }); - }); - - it('should report error status when there are warnings and errors', async () => { - const context = createContext({ - parameters: { - a11y: { - warnings: ['minor'], - }, - }, - }); - const result = { - violations: [ - { impact: 'minor', nodes: [] }, - { impact: 'critical', nodes: [] }, - ], - }; - mockedRun.mockResolvedValue(result as any); - - await expect(async () => experimental_afterEach(context)).rejects.toThrow(); - - expect(mockedRun).toHaveBeenCalledWith(context.parameters.a11y); - expect(context.reporting.addReport).toHaveBeenCalledWith({ - type: 'a11y', - version: 1, - result, - status: 'failed', - }); - }); - it('should run accessibility checks if "a11ytest" flag is not available and is not running in Vitest', async () => { const context = createContext({ tags: [], diff --git a/code/addons/a11y/src/preview.tsx b/code/addons/a11y/src/preview.tsx index f7d2f9aa43ff..e496894cb113 100644 --- a/code/addons/a11y/src/preview.tsx +++ b/code/addons/a11y/src/preview.tsx @@ -21,7 +21,6 @@ export const experimental_afterEach: AfterEach = async ({ }) => { const a11yParameter: A11yParameters | undefined = parameters.a11y; const a11yGlobals = globals.a11y; - const warnings = a11yParameter?.warnings ?? []; const shouldRunEnvironmentIndependent = a11yParameter?.manual !== true && @@ -38,15 +37,11 @@ export const experimental_afterEach: AfterEach = async ({ if (result) { const hasViolations = (result?.violations.length ?? 0) > 0; - const hasErrors = result?.violations.some( - (violation) => !warnings.includes(violation.impact!) - ); - reporting.addReport({ type: 'a11y', version: 1, result: result, - status: hasErrors ? 'failed' : hasViolations ? 'warning' : 'passed', + status: hasViolations ? 'failed' : 'passed', }); /** @@ -58,7 +53,7 @@ export const experimental_afterEach: AfterEach = async ({ * implement proper try catch handling. */ if (getIsVitestStandaloneRun()) { - if (hasErrors) { + if (hasViolations) { // @ts-expect-error - todo - fix type extension of expect from @storybook/test expect(result).toHaveNoViolations(); } diff --git a/code/addons/a11y/src/utils.ts b/code/addons/a11y/src/utils.ts index 0864a2e3b2f7..f0612d5dfc73 100644 --- a/code/addons/a11y/src/utils.ts +++ b/code/addons/a11y/src/utils.ts @@ -1,25 +1,17 @@ export function getIsVitestStandaloneRun() { try { - return process.env.VITEST_STORYBOOK === 'false'; - } catch { - try { - // @ts-expect-error Suppress TypeScript warning about wrong setting. Doesn't matter, because we don't use tsc for bundling. - return import.meta.env.VITEST_STORYBOOK === 'false'; - } catch (e) { - return false; - } + // @ts-expect-error Suppress TypeScript warning about wrong setting. Doesn't matter, because we don't use tsc for bundling. + return import.meta.env.VITEST_STORYBOOK === 'false'; + } catch (e) { + return false; } } export function getIsVitestRunning() { try { - return process?.env.MODE === 'test'; - } catch { - try { - // @ts-expect-error Suppress TypeScript warning about wrong setting. Doesn't matter, because we don't use tsc for bundling. - return import.meta.env.MODE === 'test'; - } catch (e) { - return false; - } + // @ts-expect-error Suppress TypeScript warning about wrong setting. Doesn't matter, because we don't use tsc for bundling. + return import.meta.env.MODE === 'test'; + } catch (e) { + return false; } } diff --git a/code/addons/actions/package.json b/code/addons/actions/package.json index fe4159ba7501..0fd1b8a2f8c5 100644 --- a/code/addons/actions/package.json +++ b/code/addons/actions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-actions", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Get UI feedback when an action is performed on an interactive element", "keywords": [ "storybook", diff --git a/code/addons/backgrounds/package.json b/code/addons/backgrounds/package.json index e16e07c7a3b9..8b0a68e80997 100644 --- a/code/addons/backgrounds/package.json +++ b/code/addons/backgrounds/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-backgrounds", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Switch backgrounds to view components in different settings", "keywords": [ "addon", diff --git a/code/addons/controls/package.json b/code/addons/controls/package.json index 0eedc655d161..6076ae2b0695 100644 --- a/code/addons/controls/package.json +++ b/code/addons/controls/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-controls", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Interact with component inputs dynamically in the Storybook UI", "keywords": [ "addon", diff --git a/code/addons/docs/package.json b/code/addons/docs/package.json index daf9acd64148..c6edb3f14308 100644 --- a/code/addons/docs/package.json +++ b/code/addons/docs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-docs", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Document component usage and properties in Markdown", "keywords": [ "addon", @@ -71,7 +71,12 @@ "./angular": "./angular/index.js", "./angular/index.js": "./angular/index.js", "./web-components/index.js": "./web-components/index.js", - "./package.json": "./package.json" + "./package.json": "./package.json", + "./manager": { + "types": "./dist/manager.d.ts", + "import": "./dist/manager.mjs", + "require": "./dist/manager.js" + } }, "main": "dist/index.js", "module": "dist/index.mjs", @@ -129,7 +134,11 @@ "./src/preview.ts", "./src/blocks.ts", "./src/shims/mdx-react-shim.ts", - "./src/mdx-loader.ts" + "./src/mdx-loader.ts", + "./src/manager.tsx" + ], + "managerEntries": [ + "./src/manager.tsx" ] }, "gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae16", diff --git a/code/addons/docs/src/manager.tsx b/code/addons/docs/src/manager.tsx new file mode 100644 index 000000000000..0b84cd6e36f8 --- /dev/null +++ b/code/addons/docs/src/manager.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { AddonPanel, type SyntaxHighlighterFormatTypes } from 'storybook/internal/components'; +import { ADDON_ID, PANEL_ID, PARAM_KEY, SNIPPET_RENDERED } from 'storybook/internal/docs-tools'; +import { addons, types, useAddonState, useChannel } from 'storybook/internal/manager-api'; + +import { Source } from '@storybook/blocks'; + +addons.register(ADDON_ID, (api) => { + addons.add(PANEL_ID, { + title: 'Code', + type: types.PANEL, + paramKey: PARAM_KEY, + /** + * This code panel can be disabled by the user by adding this parameter: + * + * @example + * + * ```ts + * parameters: { + * docs: { + * codePanel: false, + * }, + * }, + * ``` + */ + disabled: (parameters) => { + return ( + !!parameters && + typeof parameters[PARAM_KEY] === 'object' && + parameters[PARAM_KEY].codePanel === false + ); + }, + match: ({ viewMode }) => viewMode === 'story', + render: ({ active }) => { + const [codeSnippet, setSourceCode] = useAddonState<{ + source: string; + format: SyntaxHighlighterFormatTypes; + }>(ADDON_ID, { + source: '', + format: 'html', + }); + + useChannel({ + [SNIPPET_RENDERED]: ({ source, format }) => { + setSourceCode({ source, format }); + }, + }); + + return ( + + + + ); + }, + }); +}); diff --git a/code/addons/docs/src/plugins/mdx-plugin.ts b/code/addons/docs/src/plugins/mdx-plugin.ts index 40d8e88c3f3a..36fe3b691e97 100644 --- a/code/addons/docs/src/plugins/mdx-plugin.ts +++ b/code/addons/docs/src/plugins/mdx-plugin.ts @@ -3,8 +3,6 @@ import { dirname, join } from 'node:path'; import type { Options } from 'storybook/internal/types'; import { createFilter } from '@rollup/pluginutils'; -import rehypeExternalLinks from 'rehype-external-links'; -import rehypeSlug from 'rehype-slug'; import type { Plugin } from 'vite'; import type { CompileOptions } from '../compiler'; @@ -24,6 +22,9 @@ export async function mdxPlugin(options: Options): Promise { const presetOptions = await presets.apply>('options', {}); const mdxPluginOptions = presetOptions?.mdxPluginOptions as CompileOptions; + const rehypeSlug = (await import('rehype-slug')).default; + const rehypeExternalLinks = (await import('rehype-external-links')).default; + return { name: 'storybook:mdx-plugin', enforce: 'pre', diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index e418b5e2e8aa..9cff707e8472 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -5,9 +5,6 @@ import type { DocsOptions, Options, PresetProperty } from 'storybook/internal/ty import type { CsfPluginOptions } from '@storybook/csf-plugin'; -import rehypeExternalLinks from 'rehype-external-links'; -import rehypeSlug from 'rehype-slug'; - import type { CompileOptions } from './compiler'; /** @@ -42,6 +39,9 @@ async function webpack( const { csfPluginOptions = {}, mdxPluginOptions = {} } = options; + const rehypeSlug = (await import('rehype-slug')).default; + const rehypeExternalLinks = (await import('rehype-external-links')).default; + const mdxLoaderOptions: CompileOptions = await options.presets.apply('mdxLoaderOptions', { ...mdxPluginOptions, mdxCompileOptions: { @@ -175,6 +175,9 @@ export const viteFinal = async (config: any, options: Options) => { const { plugins = [] } = config; const { mdxPlugin } = await import('./plugins/mdx-plugin'); + const rehypeSlug = (await import('rehype-slug')).default; + const rehypeExternalLinks = (await import('rehype-external-links')).default; + // Use the resolvedReact preset to alias react and react-dom to either the users version or the version shipped with addon-docs const { react, reactDom, mdx } = await getResolvedReact(options); diff --git a/code/addons/docs/template/stories/sourcePanel/index.stories.tsx b/code/addons/docs/template/stories/sourcePanel/index.stories.tsx new file mode 100644 index 000000000000..9958096cb815 --- /dev/null +++ b/code/addons/docs/template/stories/sourcePanel/index.stories.tsx @@ -0,0 +1,23 @@ +export default { + component: globalThis.Components.Button, + tags: ['autodocs'], + parameters: { + chromatic: { disable: true }, + docs: { + codePanel: false, + }, + }, +}; + +export const One = { args: { label: 'One' } }; + +export const Two = { args: { label: 'Two' } }; + +export const WithSource = { + args: { label: 'Three' }, + parameters: { + docs: { + codePanel: true, + }, + }, +}; diff --git a/code/addons/essentials/package.json b/code/addons/essentials/package.json index 23bad764705f..7bb91e8bb960 100644 --- a/code/addons/essentials/package.json +++ b/code/addons/essentials/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-essentials", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Curated addons to bring out the best of Storybook", "keywords": [ "addon", @@ -40,6 +40,7 @@ }, "./backgrounds/manager": "./dist/backgrounds/manager.js", "./controls/manager": "./dist/controls/manager.js", + "./docs/manager": "./dist/docs/manager.js", "./docs/preview": { "types": "./dist/docs/preview.d.ts", "import": "./dist/docs/preview.mjs", @@ -114,10 +115,14 @@ "./src/docs/preset.ts", "./src/docs/mdx-react-shim.ts" ], + "entries": [ + "./src/docs/manager.ts" + ], "managerEntries": [ "./src/actions/manager.ts", "./src/backgrounds/manager.ts", "./src/controls/manager.ts", + "./src/docs/manager.ts", "./src/measure/manager.ts", "./src/outline/manager.ts", "./src/toolbars/manager.ts", diff --git a/code/addons/essentials/src/docs/manager.ts b/code/addons/essentials/src/docs/manager.ts new file mode 100644 index 000000000000..6101f7d79261 --- /dev/null +++ b/code/addons/essentials/src/docs/manager.ts @@ -0,0 +1,2 @@ +// @ts-expect-error (no types needed for this) +export * from '@storybook/addon-docs/manager'; diff --git a/code/addons/essentials/src/index.ts b/code/addons/essentials/src/index.ts index 5809420bc1b8..a72554227ba2 100644 --- a/code/addons/essentials/src/index.ts +++ b/code/addons/essentials/src/index.ts @@ -88,9 +88,9 @@ export function addons(options: PresetOptions) { // NOTE: The order of these addons is important. return [ - 'docs', 'controls', 'actions', + 'docs', 'backgrounds', 'viewport', 'toolbars', diff --git a/code/addons/gfm/package.json b/code/addons/gfm/package.json index 8641c5a7f4ef..79438d40133c 100644 --- a/code/addons/gfm/package.json +++ b/code/addons/gfm/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-mdx-gfm", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "GitHub Flavored Markdown in Storybook", "keywords": [ "addon", diff --git a/code/addons/highlight/package.json b/code/addons/highlight/package.json index f78228db1657..4f9a269b034c 100644 --- a/code/addons/highlight/package.json +++ b/code/addons/highlight/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-highlight", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Highlight DOM nodes within your stories", "keywords": [ "storybook-addons", diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json index f8123e69278a..86cf84e46183 100644 --- a/code/addons/interactions/package.json +++ b/code/addons/interactions/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-interactions", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Automate, test and debug user interactions", "keywords": [ "storybook-addons", diff --git a/code/addons/jest/package.json b/code/addons/jest/package.json index 9794ab8acede..d80862cf4d59 100644 --- a/code/addons/jest/package.json +++ b/code/addons/jest/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-jest", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "React storybook addon that show component jest report", "keywords": [ "addon", diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 18de0fcea3ab..cdb3a91bb48e 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-links", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Link stories together to build demos and prototypes with your UI components", "keywords": [ "storybook-addons", diff --git a/code/addons/measure/package.json b/code/addons/measure/package.json index 181306c74e48..4e6cfa9efb7a 100644 --- a/code/addons/measure/package.json +++ b/code/addons/measure/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-measure", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Inspect layouts by visualizing the box model", "keywords": [ "storybook-addons", diff --git a/code/addons/onboarding/package.json b/code/addons/onboarding/package.json index ce9dbd9ee074..0a92fbc89396 100644 --- a/code/addons/onboarding/package.json +++ b/code/addons/onboarding/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-onboarding", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Addon Onboarding - Introduces a new onboarding experience", "keywords": [ "storybook-addons", diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json index 42eb08c6be81..6ea09d403517 100644 --- a/code/addons/outline/package.json +++ b/code/addons/outline/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-outline", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Outline all elements with CSS to help with layout placement and alignment", "keywords": [ "storybook-addons", diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json index e8a04d1edff9..6f016e3e02a5 100644 --- a/code/addons/storysource/package.json +++ b/code/addons/storysource/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storysource", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "View a story’s source code to see how it works and paste into your app", "keywords": [ "addon", diff --git a/code/addons/test/package.json b/code/addons/test/package.json index 1652327a9801..94246c0704de 100644 --- a/code/addons/test/package.json +++ b/code/addons/test/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/experimental-addon-test", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Integrate Vitest with Storybook", "keywords": [ "storybook-addons", @@ -85,7 +85,6 @@ "@storybook/icons": "^1.2.12", "@storybook/instrumenter": "workspace:*", "@storybook/test": "workspace:*", - "@storybook/theming": "workspace:*", "polished": "^4.2.2", "prompts": "^2.4.0", "ts-dedent": "^2.2.0" @@ -113,6 +112,7 @@ "slash": "^5.0.0", "strip-ansi": "^7.1.0", "tinyglobby": "^0.2.10", + "tree-kill": "^1.2.2", "ts-dedent": "^2.2.0", "typescript": "^5.3.2", "vitest": "^2.1.3" diff --git a/code/addons/test/src/components/ContextMenuItem.tsx b/code/addons/test/src/components/ContextMenuItem.tsx index 52c3b98061f9..229a396b5388 100644 --- a/code/addons/test/src/components/ContextMenuItem.tsx +++ b/code/addons/test/src/components/ContextMenuItem.tsx @@ -67,7 +67,7 @@ export const ContextMenuItem: FC<{ padding="small" disabled={state.crashed || isDisabled} > - + } /> diff --git a/code/addons/test/src/components/Description.tsx b/code/addons/test/src/components/Description.tsx index a94b775b5c95..58a80dbfdccc 100644 --- a/code/addons/test/src/components/Description.tsx +++ b/code/addons/test/src/components/Description.tsx @@ -12,7 +12,7 @@ export const Wrapper = styled.div(({ theme }) => ({ whiteSpace: 'nowrap', textOverflow: 'ellipsis', fontSize: theme.typography.size.s1, - color: theme.barTextColor, + color: theme.textMutedColor, })); const PositiveText = styled.span(({ theme }) => ({ @@ -60,10 +60,10 @@ export function Description({ state, ...props }: DescriptionProps) { ); } else if (state.progress?.finishedAt) { description = ( - + <> + Ran {state.progress.numTotalTests} {state.progress.numTotalTests === 1 ? 'test' : 'tests'}{' '} + + ); } else if (state.watching) { description = 'Watching for file changes'; diff --git a/code/addons/test/src/components/InteractionsPanel.stories.tsx b/code/addons/test/src/components/InteractionsPanel.stories.tsx index 24eefed2028c..f8cabbe24d53 100644 --- a/code/addons/test/src/components/InteractionsPanel.stories.tsx +++ b/code/addons/test/src/components/InteractionsPanel.stories.tsx @@ -58,7 +58,6 @@ const meta = { endRef: null, // prop for the AddonPanel used as wrapper of Panel active: true, - storyId: 'story-id', }, } as Meta; diff --git a/code/addons/test/src/components/InteractionsPanel.tsx b/code/addons/test/src/components/InteractionsPanel.tsx index 164e28b782fb..896350b926c7 100644 --- a/code/addons/test/src/components/InteractionsPanel.tsx +++ b/code/addons/test/src/components/InteractionsPanel.tsx @@ -44,8 +44,6 @@ interface InteractionsPanelProps { onScrollToEnd?: () => void; hasResultMismatch?: boolean; browserTestStatus?: CallStates; - storyId: StoryId; - testRunId: string; } const Container = styled.div(({ theme }) => ({ @@ -105,20 +103,12 @@ export const InteractionsPanel: React.FC = React.memo( endRef, hasResultMismatch, browserTestStatus, - storyId, - testRunId, }) { const filter = useAnsiToHtmlFilter(); return ( - {hasResultMismatch && ( - - )} + {hasResultMismatch && } {(interactions.length > 0 || hasException) && ( (function PanelMemoized({ storyId // local state const [scrollTarget, setScrollTarget] = useState(undefined); const [collapsed, setCollapsed] = useState>(new Set()); + const [hasResultMismatch, setResultMismatch] = useState(false); const { controlStates = INITIAL_CONTROL_STATES, @@ -226,7 +227,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId interactionsCount: list.filter(({ method }) => method !== 'step').length, }; }); - }, [collapsed]); + }, [set, collapsed]); const controls = useMemo( () => ({ @@ -239,7 +240,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId emit(FORCE_REMOUNT, { storyId }); }, }), - [storyId] + [emit, storyId] ); const storyFilePath = useParameter('fileName', ''); @@ -252,22 +253,48 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId interactions.some((v) => v.status === CallStates.ERROR); const storyStatus = storyStatuses[storyId]?.[TEST_PROVIDER_ID]; + const storyTestStatus = storyStatus?.status; - const browserTestStatus = React.useMemo(() => { + const browserTestStatus = useMemo(() => { if (!isPlaying && (interactions.length > 0 || hasException)) { return hasException ? CallStates.ERROR : CallStates.DONE; } return isPlaying ? CallStates.ACTIVE : null; }, [isPlaying, interactions, hasException]); - const hasResultMismatch = React.useMemo(() => { - return ( - browserTestStatus !== null && - browserTestStatus !== CallStates.ACTIVE && - storyStatus?.status !== undefined && - statusMap[browserTestStatus] !== storyStatus.status - ); - }, [browserTestStatus, storyStatus]); + const { testRunId } = storyStatus?.data || {}; + + useEffect(() => { + const isMismatch = + browserTestStatus && + storyTestStatus && + storyTestStatus !== 'pending' && + storyTestStatus !== statusMap[browserTestStatus]; + + if (isMismatch) { + const timeout = setTimeout( + () => + setResultMismatch((currentValue) => { + if (!currentValue) { + emit(STORYBOOK_ADDON_TEST_CHANNEL, { + type: 'test-discrepancy', + payload: { + browserStatus: browserTestStatus === CallStates.DONE ? 'PASS' : 'FAIL', + cliStatus: browserTestStatus === CallStates.DONE ? 'FAIL' : 'PASS', + storyId, + testRunId, + }, + }); + } + return true; + }), + 2000 + ); + return () => clearTimeout(timeout); + } else { + setResultMismatch(false); + } + }, [emit, browserTestStatus, storyTestStatus, storyId, testRunId]); if (isErrored) { return ; @@ -290,8 +317,6 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId pausedAt={pausedAt} endRef={endRef} onScrollToEnd={scrollTarget && scrollToTarget} - storyId={storyId} - testRunId={storyStatus?.data?.testRunId} /> ); diff --git a/code/addons/test/src/components/RelativeTime.stories.tsx b/code/addons/test/src/components/RelativeTime.stories.tsx new file mode 100644 index 000000000000..4d3c6af0f6d8 --- /dev/null +++ b/code/addons/test/src/components/RelativeTime.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { RelativeTime } from './RelativeTime'; + +const meta = { + component: RelativeTime, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const JustNow: Story = { + args: { + timestamp: Date.now() - 1000 * 10, + }, +}; + +export const AMinuteAgo: Story = { + args: { + timestamp: Date.now() - 1000 * 60, + }, +}; + +export const MinutesAgo: Story = { + args: { + timestamp: Date.now() - 1000 * 60 * 2, + }, +}; + +export const HoursAgo: Story = { + args: { + timestamp: Date.now() - 1000 * 60 * 60 * 3, + }, +}; + +export const Yesterday: Story = { + args: { + timestamp: Date.now() - 1000 * 60 * 60 * 24, + }, +}; + +export const DaysAgo: Story = { + args: { + timestamp: Date.now() - 1000 * 60 * 60 * 24 * 3, + }, +}; diff --git a/code/addons/test/src/components/RelativeTime.tsx b/code/addons/test/src/components/RelativeTime.tsx index fa9e7cf6d549..9cb1df1b1b66 100644 --- a/code/addons/test/src/components/RelativeTime.tsx +++ b/code/addons/test/src/components/RelativeTime.tsx @@ -1,41 +1,35 @@ import { useEffect, useState } from 'react'; -export function getRelativeTimeString(date: Date): string { - const delta = Math.round((date.getTime() - Date.now()) / 1000); - const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity]; - const units: Intl.RelativeTimeFormatUnit[] = [ - 'second', - 'minute', - 'hour', - 'day', - 'week', - 'month', - 'year', - ]; - - const unitIndex = cutoffs.findIndex((cutoff) => cutoff > Math.abs(delta)); - const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1; - const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' }); - return rtf.format(Math.floor(delta / divisor), units[unitIndex]); -} - -export const RelativeTime = ({ timestamp, testCount }: { timestamp: Date; testCount: number }) => { - const [relativeTimeString, setRelativeTimeString] = useState(null); +export const RelativeTime = ({ timestamp }: { timestamp?: number }) => { + const [timeAgo, setTimeAgo] = useState(null); useEffect(() => { if (timestamp) { - setRelativeTimeString(getRelativeTimeString(timestamp).replace(/^now$/, 'just now')); - - const interval = setInterval(() => { - setRelativeTimeString(getRelativeTimeString(timestamp).replace(/^now$/, 'just now')); - }, 10000); - + setTimeAgo(Date.now() - timestamp); + const interval = setInterval(() => setTimeAgo(Date.now() - timestamp), 10000); return () => clearInterval(interval); } }, [timestamp]); - return ( - relativeTimeString && - `Ran ${testCount} ${testCount === 1 ? 'test' : 'tests'} ${relativeTimeString}` - ); + if (timeAgo === null) { + return null; + } + + const seconds = Math.round(timeAgo / 1000); + if (seconds < 60) { + return `just now`; + } + + const minutes = Math.floor(seconds / 60); + if (minutes < 60) { + return minutes === 1 ? `a minute ago` : `${minutes} minutes ago`; + } + + const hours = Math.floor(minutes / 60); + if (hours < 24) { + return hours === 1 ? `an hour ago` : `${hours} hours ago`; + } + + const days = Math.floor(hours / 24); + return days === 1 ? `yesterday` : `${days} days ago`; }; diff --git a/code/addons/test/src/components/TestDiscrepancyMessage.stories.tsx b/code/addons/test/src/components/TestDiscrepancyMessage.stories.tsx index 840c08cdf3d3..81553cdc5a32 100644 --- a/code/addons/test/src/components/TestDiscrepancyMessage.stories.tsx +++ b/code/addons/test/src/components/TestDiscrepancyMessage.stories.tsx @@ -23,9 +23,6 @@ export default { parameters: { layout: 'fullscreen', }, - args: { - storyId: 'story-id', - }, decorators: [ (storyFn) => ( {storyFn()} diff --git a/code/addons/test/src/components/TestDiscrepancyMessage.tsx b/code/addons/test/src/components/TestDiscrepancyMessage.tsx index bbdf74e36a65..b23af4a7be6a 100644 --- a/code/addons/test/src/components/TestDiscrepancyMessage.tsx +++ b/code/addons/test/src/components/TestDiscrepancyMessage.tsx @@ -33,39 +33,23 @@ const Wrapper = styled.div(({ theme: { color, typography, background } }) => ({ interface TestDiscrepancyMessageProps { browserTestStatus: CallStates; - storyId: StoryId; - testRunId: string; } -export const TestDiscrepancyMessage = ({ - browserTestStatus, - storyId, - testRunId, -}: TestDiscrepancyMessageProps) => { + +export const TestDiscrepancyMessage = ({ browserTestStatus }: TestDiscrepancyMessageProps) => { const api = useStorybookApi(); const docsUrl = api.getDocsUrl({ subpath: DOCUMENTATION_DISCREPANCY_LINK, versioned: true, renderer: true, }); - const message = `This component test passed in ${browserTestStatus === CallStates.DONE ? 'this browser' : 'CLI'}, but the tests failed in ${browserTestStatus === CallStates.ERROR ? 'this browser' : 'CLI'}.`; - - useEffect( - () => - api.emit(STORYBOOK_ADDON_TEST_CHANNEL, { - type: 'test-discrepancy', - payload: { - browserStatus: browserTestStatus === CallStates.DONE ? 'PASS' : 'FAIL', - cliStatus: browserTestStatus === CallStates.DONE ? 'FAIL' : 'PASS', - storyId, - testRunId, - }, - }), - [api, browserTestStatus, storyId, testRunId] - ); + const [passed, failed] = + browserTestStatus === CallStates.ERROR + ? ['the CLI', 'this browser'] + : ['this browser', 'the CLI']; return ( - {message}{' '} + This component test passed in {passed}, but the tests failed in {failed}.{' '} Learn what could cause this diff --git a/code/addons/test/src/components/TestProviderRender.tsx b/code/addons/test/src/components/TestProviderRender.tsx index d9d68aef04d1..e04772447c43 100644 --- a/code/addons/test/src/components/TestProviderRender.tsx +++ b/code/addons/test/src/components/TestProviderRender.tsx @@ -76,6 +76,16 @@ const StopIcon = styled(StopAltIcon)({ width: 10, }); +const ItemTitle = styled.span<{ enabled?: boolean }>( + ({ enabled, theme }) => + !enabled && { + color: theme.textMutedColor, + '&:after': { + content: '" (disabled)"', + }, + } +); + const statusOrder: TestStatus[] = ['failed', 'warning', 'pending', 'passed', 'skipped']; const statusMap: Record['status']> = { failed: 'negative', @@ -104,6 +114,8 @@ export const TestProviderRender: FC< state.config || { a11y: false, coverage: false } ); + const isStoryEntry = entryId?.includes('--') ?? false; + const a11yResults = useMemo(() => { if (!isA11yAddon) { return []; @@ -111,12 +123,17 @@ export const TestProviderRender: FC< return state.details?.testResults?.flatMap((result) => result.results + .filter(Boolean) .filter((r) => !entryId || r.storyId === entryId || r.storyId?.startsWith(`${entryId}-`)) .map((r) => r.reports.find((report) => report.type === 'a11y')) ); }, [isA11yAddon, state.details?.testResults, entryId]); const a11yStatus = useMemo<'positive' | 'warning' | 'negative' | 'unknown'>(() => { + if (state.running) { + return 'unknown'; + } + if (!isA11yAddon || config.a11y === false) { return 'unknown'; } @@ -135,13 +152,13 @@ export const TestProviderRender: FC< } return 'positive'; - }, [a11yResults, isA11yAddon, config.a11y]); + }, [state.running, isA11yAddon, config.a11y, a11yResults]); const a11yNotPassedAmount = a11yResults?.filter( (result) => result?.status === 'failed' || result?.status === 'warning' ).length; - const storyId = entryId?.includes('--') ? entryId : undefined; + const storyId = isStoryEntry ? entryId : undefined; const results = (state.details?.testResults || []) .flatMap((test) => { if (!entryId) { @@ -153,7 +170,11 @@ export const TestProviderRender: FC< }) .sort((a, b) => statusOrder.indexOf(a.status) - statusOrder.indexOf(b.status)); - const status = (state.failed ? 'failed' : results[0]?.status) || 'unknown'; + const status = state.running + ? 'unknown' + : state.failed + ? 'failed' + : (results[0]?.status ?? 'unknown'); const openPanel = (id: string, panelId: string) => { api.selectStory(id); @@ -232,7 +253,7 @@ export const TestProviderRender: FC< /> Coverage} icon={} right={ Accessibility} icon={} right={ {coverageSummary ? ( Coverage} href={'/coverage/index.html'} // @ts-expect-error ListItem doesn't include all anchor attributes in types, but it is an achor element target="_blank" + aria-label="Open coverage report" icon={ } - right={`${coverageSummary.percentage}%`} + right={ + coverageSummary.percentage ? ( + + {coverageSummary.percentage} % + + ) : null + } /> ) : ( Coverage} icon={} /> )} {isA11yAddon && ( Accessibility} onClick={ (a11yStatus === 'negative' || a11yStatus === 'warning') && a11yResults.length ? () => { @@ -321,7 +349,7 @@ export const TestProviderRender: FC< : null } icon={} - right={a11yNotPassedAmount || null} + right={isStoryEntry ? null : a11yNotPassedAmount || null} /> )} diff --git a/code/addons/test/src/manager.tsx b/code/addons/test/src/manager.tsx index 4db0c5defcbd..26a8c8f42cd7 100644 --- a/code/addons/test/src/manager.tsx +++ b/code/addons/test/src/manager.tsx @@ -73,64 +73,75 @@ addons.register(ADDON_ID, (api) => { }, stateUpdater: (state, update) => { - if (!update.details?.testResults) { - return; + const updated = { + ...state, + ...update, + details: { ...state.details, ...update.details }, + }; + + if ((!state.running && update.running) || (!state.watching && update.watching)) { + // Clear coverage data when starting test run or enabling watch mode + delete updated.details.coverageSummary; } - (async () => { - await api.experimental_updateStatus( - TEST_PROVIDER_ID, - Object.fromEntries( - update.details.testResults.flatMap((testResult) => - testResult.results - .filter(({ storyId }) => storyId) - .map(({ storyId, status, testRunId, ...rest }) => [ - storyId, - { - title: 'Component tests', - status: statusMap[status], - description: - 'failureMessages' in rest && rest.failureMessages - ? rest.failureMessages.join('\n') - : '', - data: { testRunId }, - onClick: openTestsPanel, - sidebarContextMenu: false, - } satisfies API_StatusObject, - ]) + if (update.details?.testResults) { + (async () => { + await api.experimental_updateStatus( + TEST_PROVIDER_ID, + Object.fromEntries( + update.details.testResults.flatMap((testResult) => + testResult.results + .filter(({ storyId }) => storyId) + .map(({ storyId, status, testRunId, ...rest }) => [ + storyId, + { + title: 'Component tests', + status: statusMap[status], + description: + 'failureMessages' in rest && rest.failureMessages + ? rest.failureMessages.join('\n') + : '', + data: { testRunId }, + onClick: openTestsPanel, + sidebarContextMenu: false, + } satisfies API_StatusObject, + ]) + ) ) - ) - ); + ); - await api.experimental_updateStatus( - 'storybook/addon-a11y/test-provider', - Object.fromEntries( - update.details.testResults.flatMap((testResult) => - testResult.results - .filter(({ storyId }) => storyId) - .map(({ storyId, testRunId, reports }) => { - const a11yReport = reports.find((r: any) => r.type === 'a11y'); - return [ - storyId, - a11yReport - ? ({ - title: 'Accessibility tests', - description: '', - status: statusMap[a11yReport.status], - data: { testRunId }, - onClick: () => { - api.setSelectedPanel('storybook/a11y/panel'); - api.togglePanel(true); - }, - sidebarContextMenu: false, - } satisfies API_StatusObject) - : null, - ]; - }) + await api.experimental_updateStatus( + 'storybook/addon-a11y/test-provider', + Object.fromEntries( + update.details.testResults.flatMap((testResult) => + testResult.results + .filter(({ storyId }) => storyId) + .map(({ storyId, testRunId, reports }) => { + const a11yReport = reports.find((r: any) => r.type === 'a11y'); + return [ + storyId, + a11yReport + ? ({ + title: 'Accessibility tests', + description: '', + status: statusMap[a11yReport.status], + data: { testRunId }, + onClick: () => { + api.setSelectedPanel('storybook/a11y/panel'); + api.togglePanel(true); + }, + sidebarContextMenu: false, + } satisfies API_StatusObject) + : null, + ]; + }) + ) ) - ) - ); - })(); + ); + })(); + } + + return updated; }, } as Addon_TestProviderType); } diff --git a/code/addons/test/src/node/coverage-reporter.ts b/code/addons/test/src/node/coverage-reporter.ts index 452643cd9d60..cc1dd548000f 100644 --- a/code/addons/test/src/node/coverage-reporter.ts +++ b/code/addons/test/src/node/coverage-reporter.ts @@ -8,7 +8,7 @@ import type { TestManager } from './test-manager'; export type StorybookCoverageReporterOptions = { testManager: TestManager; - coverageOptions: ResolvedCoverageOptions<'v8'>; + coverageOptions: ResolvedCoverageOptions<'v8'> | undefined; }; export default class StorybookCoverageReporter extends ReportBase implements Partial { @@ -32,7 +32,7 @@ export default class StorybookCoverageReporter extends ReportBase implements Par // Fallback to Vitest's default watermarks https://vitest.dev/config/#coverage-watermarks const [lowWatermark = 50, highWatermark = 80] = - this.#coverageOptions.watermarks?.statements ?? []; + this.#coverageOptions?.watermarks?.statements ?? []; const coverageDetails: Details['coverageSummary'] = { percentage, diff --git a/code/addons/test/src/node/test-manager.ts b/code/addons/test/src/node/test-manager.ts index 4770f2b5a174..1595c2da3fa9 100644 --- a/code/addons/test/src/node/test-manager.ts +++ b/code/addons/test/src/node/test-manager.ts @@ -53,14 +53,7 @@ export class TestManager { coverage: this.coverage, }); } catch (e) { - const isV8 = e.message?.includes('@vitest/coverage-v8'); - const isIstanbul = e.message?.includes('@vitest/coverage-istanbul'); - - if (e.message?.includes('Error: Failed to load url') && (isIstanbul || isV8)) { - const coveragePackage = isIstanbul ? 'coverage-istanbul' : 'coverage-v8'; - e.message = `Please install the @vitest/${coveragePackage} package to run with coverage`; - } - this.reportFatalError('Failed to change coverage mode', e); + this.reportFatalError('Failed to change coverage configuration', e); } } } diff --git a/code/addons/test/src/node/vitest-manager.ts b/code/addons/test/src/node/vitest-manager.ts index 7267f3dcea38..26dbe16ab526 100644 --- a/code/addons/test/src/node/vitest-manager.ts +++ b/code/addons/test/src/node/vitest-manager.ts @@ -14,7 +14,7 @@ import type { TestingModuleRunRequestPayload } from 'storybook/internal/core-eve import type { DocsIndexEntry, StoryIndex, StoryIndexEntry } from '@storybook/types'; -import path, { normalize } from 'pathe'; +import path, { dirname, join, normalize } from 'pathe'; import slash from 'slash'; import { COVERAGE_DIRECTORY, type Config } from '../constants'; @@ -29,6 +29,11 @@ type TagsFilter = { skip: string[]; }; +const packageDir = dirname(require.resolve('@storybook/experimental-addon-test/package.json')); + +// We have to tell Vitest that it runs as part of Storybook +process.env.VITEST_STORYBOOK = 'true'; + export class VitestManager { vitest: Vitest | null = null; @@ -44,10 +49,10 @@ export class VitestManager { const { createVitest } = await import('vitest/node'); const storybookCoverageReporter: [string, StorybookCoverageReporterOptions] = [ - '@storybook/experimental-addon-test/internal/coverage-reporter', + join(packageDir, 'dist/node/coverage-reporter.js'), { testManager: this.testManager, - coverageOptions: this.vitest?.config?.coverage as ResolvedCoverageOptions<'v8'>, + coverageOptions: this.vitest?.config?.coverage as ResolvedCoverageOptions<'v8'> | undefined, }, ]; const coverageOptions = ( @@ -63,31 +68,17 @@ export class VitestManager { : { enabled: false } ) as CoverageOptions; - this.vitest = await createVitest( - 'test', - { - watch: true, - passWithNoTests: false, - // TODO: - // Do we want to enable Vite's default reporter? - // The output in the terminal might be too spamy and it might be better to - // find a way to just show errors and warnings for example - // Otherwise it might be hard for the user to discover Storybook related logs - reporters: ['default', new StorybookReporter(this.testManager)], - coverage: coverageOptions, - }, - { - define: { - // polyfilling process.env.VITEST_STORYBOOK to 'true' in the browser - 'process.env.VITEST_STORYBOOK': 'true', - }, - } - ); - - this.vitest.configOverride.env = { - // We signal to the test runner that we are running it via Storybook - VITEST_STORYBOOK: 'true', - }; + this.vitest = await createVitest('test', { + watch: true, + passWithNoTests: false, + // TODO: + // Do we want to enable Vite's default reporter? + // The output in the terminal might be too spamy and it might be better to + // find a way to just show errors and warnings for example + // Otherwise it might be hard for the user to discover Storybook related logs + reporters: ['default', new StorybookReporter(this.testManager)], + coverage: coverageOptions, + }); if (this.vitest) { this.vitest.onCancel(() => { @@ -98,15 +89,21 @@ export class VitestManager { try { await this.vitest.init(); } catch (e) { + let message = 'Failed to initialize Vitest'; const isV8 = e.message?.includes('@vitest/coverage-v8'); const isIstanbul = e.message?.includes('@vitest/coverage-istanbul'); - if (e.message?.includes('Error: Failed to load url') && (isIstanbul || isV8)) { + if ( + (e.message?.includes('Failed to load url') && (isIstanbul || isV8)) || + // Vitest will sometimes not throw the correct missing-package-detection error, so we have to check for this as well + (e instanceof TypeError && + e?.message === "Cannot read properties of undefined (reading 'name')") + ) { const coveragePackage = isIstanbul ? 'coverage-istanbul' : 'coverage-v8'; - e.message = `Please install the @vitest/${coveragePackage} package to run with coverage`; + message += `\n\nPlease install the @vitest/${coveragePackage} package to collect coverage\n`; } - - this.testManager.reportFatalError('Failed to init Vitest', e); + this.testManager.reportFatalError(message, e); + return; } await this.setupWatchers(); diff --git a/code/addons/test/src/postinstall.ts b/code/addons/test/src/postinstall.ts index fc7fc1e8d862..bb22c7517127 100644 --- a/code/addons/test/src/postinstall.ts +++ b/code/addons/test/src/postinstall.ts @@ -2,17 +2,21 @@ import { existsSync } from 'node:fs'; import * as fs from 'node:fs/promises'; import { writeFile } from 'node:fs/promises'; +import { traverse } from 'storybook/internal/babel'; import { JsPackageManagerFactory, extractProperFrameworkName, loadAllPresets, loadMainConfig, + serverResolve, validateFrameworkName, + versions, } from 'storybook/internal/common'; +import { readConfig, writeConfig } from 'storybook/internal/csf-tools'; import { colors, logger } from 'storybook/internal/node-logger'; // eslint-disable-next-line depend/ban-dependencies -import { execa } from 'execa'; +import { $ } from 'execa'; import { findUp } from 'find-up'; import { dirname, extname, join, relative, resolve } from 'pathe'; import picocolors from 'picocolors'; @@ -57,6 +61,51 @@ export default async function postInstall(options: PostinstallOptions) { // if Vitest is installed, we use the same version to keep consistency across Vitest packages const vitestVersionToInstall = vitestVersionSpecifier ?? 'latest'; + const mainJsPath = serverResolve(resolve(options.configDir, 'main')) as string; + const config = await readConfig(mainJsPath); + + const hasCustomWebpackConfig = !!config.getFieldNode(['webpackFinal']); + + if (info.frameworkPackageName === '@storybook/nextjs' && !hasCustomWebpackConfig) { + const out = options.yes + ? { + migrateToExperimentalNextjsVite: true, + } + : await prompts({ + type: 'confirm', + name: 'migrateToExperimentalNextjsVite', + message: dedent` + The addon requires the use of @storybook/experimental-nextjs-vite to work with Next.js. + https://storybook.js.org/docs/writing-tests/test-addon#install-and-set-up + + Do you want to migrate? + `, + initial: true, + }); + + if (out.migrateToExperimentalNextjsVite) { + await packageManager.addDependencies({ installAsDevDependencies: true }, [ + `@storybook/experimental-nextjs-vite@${versions['@storybook/experimental-nextjs-vite']}`, + ]); + + await packageManager.removeDependencies({}, ['@storybook/nextjs']); + + // eslint-disable-next-line no-underscore-dangle + traverse(config._ast, { + StringLiteral(path) { + if (path.node.value === '@storybook/nextjs') { + path.node.value = '@storybook/experimental-nextjs-vite'; + } + }, + }); + + await writeConfig(config, mainJsPath); + + info.frameworkPackageName = '@storybook/experimental-nextjs-vite'; + info.builderPackageName = '@storybook/builder-vite'; + } + } + const annotationsImport = [ '@storybook/nextjs', '@storybook/experimental-nextjs-vite', @@ -71,11 +120,16 @@ export default async function postInstall(options: PostinstallOptions) { ) ? info.rendererPackageName : null; + const isRendererSupported = !!annotationsImport; const prerequisiteCheck = async () => { const reasons = []; + if (hasCustomWebpackConfig) { + reasons.push('• The addon can not be used with a custom Webpack configuration.'); + } + if ( info.frameworkPackageName !== '@storybook/nextjs' && info.builderPackageName !== '@storybook/builder-vite' @@ -149,9 +203,7 @@ export default async function postInstall(options: PostinstallOptions) { return; } - const interactionsAddon = info.addons.find((addon) => addon.includes(addonInteractionsName)); - - if (!!interactionsAddon) { + if (info.hasAddonInteractions) { let shouldUninstall = options.yes; if (!options.yes) { printInfo( @@ -175,23 +227,9 @@ export default async function postInstall(options: PostinstallOptions) { } if (shouldUninstall) { - await execa( - packageManager.getRemoteRunCommand(), - [ - 'storybook', - 'remove', - addonInteractionsName, - '--package-manager', - options.packageManager, - '--config-dir', - options.configDir, - ], - { - shell: true, - } - ); - } else { - return; + await $({ + stdio: 'inherit', + })`storybook remove ${addonInteractionsName} --package-manager ${options.packageManager} --config-dir ${options.configDir}`; } } @@ -282,7 +320,7 @@ export default async function postInstall(options: PostinstallOptions) { logger.plain(colors.gray(` ${vitestSetupFile}`)); const previewExists = EXTENSIONS.map((ext) => resolve(options.configDir, `preview${ext}`)).some( - (config) => existsSync(config) + existsSync ); const a11yAddon = info.addons.find((addon) => addon.includes(addonA11yName)); @@ -489,7 +527,7 @@ const getVitestPluginInfo = (framework: string) => { frameworkPluginCall = 'storybookReactNativeWeb()'; } - // spaces for file identation + // spaces for file indentation frameworkPluginImport = `\n${frameworkPluginImport}`; frameworkPluginDocs = frameworkPluginDocs ? `\n ${frameworkPluginDocs}` : ''; frameworkPluginCall = frameworkPluginCall ? `\n ${frameworkPluginCall},` : ''; @@ -507,7 +545,6 @@ async function getStorybookInfo({ configDir, packageManager: pkgMgr }: Postinsta const frameworkName = typeof framework === 'string' ? framework : framework?.name; validateFrameworkName(frameworkName); const frameworkPackageName = extractProperFrameworkName(frameworkName); - const addons = getAddonNames(config); const presets = await loadAllPresets({ corePresets: [join(frameworkName, 'preset')], @@ -519,6 +556,8 @@ async function getStorybookInfo({ configDir, packageManager: pkgMgr }: Postinsta isCritical: true, }); + const hasAddonInteractions = !!(await presets.apply('ADDON_INTERACTIONS_IN_USE', false)); + const core = await presets.apply('core', {}); const { builder, renderer } = core; @@ -546,6 +585,7 @@ async function getStorybookInfo({ configDir, packageManager: pkgMgr }: Postinsta frameworkPackageName, builderPackageName, rendererPackageName, - addons, + hasAddonInteractions, + addons: getAddonNames(config), }; } diff --git a/code/addons/test/src/utils.ts b/code/addons/test/src/utils.ts index 066f09af15f1..053f4eec4ffe 100644 --- a/code/addons/test/src/utils.ts +++ b/code/addons/test/src/utils.ts @@ -1,6 +1,5 @@ import { type StorybookTheme, useTheme } from 'storybook/internal/theming'; - -import type { StorybookConfig } from '@storybook/types'; +import type { StorybookConfig } from 'storybook/internal/types'; import Filter from 'ansi-to-html'; import stripAnsi from 'strip-ansi'; diff --git a/code/addons/test/src/vitest-plugin/global-setup.ts b/code/addons/test/src/vitest-plugin/global-setup.ts index bff48208229a..ca287c105e2b 100644 --- a/code/addons/test/src/vitest-plugin/global-setup.ts +++ b/code/addons/test/src/vitest-plugin/global-setup.ts @@ -5,6 +5,8 @@ import type { GlobalSetupContext } from 'vitest/node'; import { logger } from 'storybook/internal/node-logger'; +import treeKill from 'tree-kill'; + let storybookProcess: ChildProcess | null = null; const getIsVitestStandaloneRun = () => { @@ -59,14 +61,6 @@ const startStorybookIfNotRunning = async () => { } }; -const killProcess = (process: ChildProcess) => { - return new Promise((resolve, reject) => { - process.on('close', resolve); - process.on('error', reject); - process.kill(); - }); -}; - export const setup = async ({ config }: GlobalSetupContext) => { if (config.watch && isVitestStandaloneRun) { await startStorybookIfNotRunning(); @@ -74,8 +68,19 @@ export const setup = async ({ config }: GlobalSetupContext) => { }; export const teardown = async () => { - if (storybookProcess) { - logger.verbose('Stopping Storybook process'); - await killProcess(storybookProcess); + if (!storybookProcess) { + return; } + logger.verbose('Stopping Storybook process'); + await new Promise((resolve, reject) => { + // Storybook starts multiple child processes, so we need to kill the whole tree + treeKill(storybookProcess.pid, 'SIGTERM', (error) => { + if (error) { + logger.error('Failed to stop Storybook process:'); + reject(error); + return; + } + resolve(); + }); + }); }; diff --git a/code/addons/test/src/vitest-plugin/index.ts b/code/addons/test/src/vitest-plugin/index.ts index eadadb4440e5..e73549ded6d9 100644 --- a/code/addons/test/src/vitest-plugin/index.ts +++ b/code/addons/test/src/vitest-plugin/index.ts @@ -1,4 +1,6 @@ /* eslint-disable no-underscore-dangle */ +import { dirname } from 'node:path'; + import type { Plugin } from 'vitest/config'; import { mergeConfig } from 'vitest/config'; import type { ViteUserConfig } from 'vitest/config'; @@ -23,7 +25,6 @@ import sirv from 'sirv'; import { convertPathToPattern } from 'tinyglobby'; import { dedent } from 'ts-dedent'; -import { TestManager } from '../node/test-manager'; import type { InternalOptions, UserOptions } from './types'; const WORKING_DIR = process.cwd(); @@ -63,6 +64,8 @@ const getStoryGlobsAndFiles = async ( }; }; +const packageDir = dirname(require.resolve('@storybook/experimental-addon-test/package.json')); + export const storybookTest = async (options?: UserOptions): Promise => { const finalOptions = { ...defaultOptions, @@ -124,7 +127,7 @@ export const storybookTest = async (options?: UserOptions): Promise => { .replace('', `${headHtmlSnippet ?? ''}`) .replace('', `${bodyHtmlSnippet ?? ''}`); }, - async config(inputConfig_DoNotMutate) { + async config(inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED) { // ! We're not mutating the input config, instead we're returning a new partial config // ! see https://vite.dev/guide/api-plugin.html#config try { @@ -143,18 +146,23 @@ export const storybookTest = async (options?: UserOptions): Promise => { // plugin.name?.startsWith('vitest:browser') // ) + // We signal the test runner that we are not running it via Storybook + // We are overriding the environment variable to 'true' if vitest runs via @storybook/addon-test's backend + const vitestStorybook = process.env.VITEST_STORYBOOK ?? 'false'; + const baseConfig: Omit = { test: { setupFiles: [ - '@storybook/experimental-addon-test/internal/setup-file', + join(packageDir, 'dist/vitest-plugin/setup-file.mjs'), // if the existing setupFiles is a string, we have to include it otherwise we're overwriting it - typeof inputConfig_DoNotMutate.test?.setupFiles === 'string' && - inputConfig_DoNotMutate.test?.setupFiles, - ].filter(Boolean), + typeof inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test + ?.setupFiles === 'string' && + inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test?.setupFiles, + ].filter(Boolean) as string[], ...(finalOptions.storybookScript ? { - globalSetup: ['@storybook/experimental-addon-test/internal/global-setup'], + globalSetup: [join(packageDir, 'dist/vitest-plugin/global-setup.mjs')], } : {}), @@ -162,9 +170,8 @@ export const storybookTest = async (options?: UserOptions): Promise => { ...storybookEnv, // To be accessed by the setup file __STORYBOOK_URL__: finalOptions.storybookUrl, - // We signal the test runner that we are not running it via Storybook - // We are overriding the environment variable to 'true' if vitest runs via @storybook/addon-test's backend - VITEST_STORYBOOK: 'false', + + VITEST_STORYBOOK: vitestStorybook, __VITEST_INCLUDE_TAGS__: finalOptions.tags.include.join(','), __VITEST_EXCLUDE_TAGS__: finalOptions.tags.exclude.join(','), __VITEST_SKIP_TAGS__: finalOptions.tags.skip.join(','), @@ -175,7 +182,8 @@ export const storybookTest = async (options?: UserOptions): Promise => { .map((path) => convertPathToPattern(path)), // if the existing deps.inline is true, we keep it as-is, because it will inline everything - ...(inputConfig_DoNotMutate.test?.server?.deps?.inline !== true + ...(inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test?.server?.deps + ?.inline !== true ? { server: { deps: { @@ -186,7 +194,7 @@ export const storybookTest = async (options?: UserOptions): Promise => { : {}), browser: { - ...inputConfig_DoNotMutate.test?.browser, + ...inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test?.browser, commands: { getInitialGlobals: () => { const envConfig = JSON.parse(process.env.VITEST_STORYBOOK_CONFIG ?? '{}'); @@ -203,8 +211,9 @@ export const storybookTest = async (options?: UserOptions): Promise => { }, }, // if there is a test.browser config AND test.browser.screenshotFailures is not explicitly set, we set it to false - ...(inputConfig_DoNotMutate.test?.browser && - inputConfig_DoNotMutate.test.browser.screenshotFailures === undefined + ...(inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test?.browser && + inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test.browser + .screenshotFailures === undefined ? { screenshotFailures: false, } @@ -213,7 +222,11 @@ export const storybookTest = async (options?: UserOptions): Promise => { }, envPrefix: Array.from( - new Set([...(inputConfig_DoNotMutate.envPrefix || []), 'STORYBOOK_', 'VITE_']) + new Set([ + ...(inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.envPrefix || []), + 'STORYBOOK_', + 'VITE_', + ]) ), resolve: { @@ -239,8 +252,6 @@ export const storybookTest = async (options?: UserOptions): Promise => { }, define: { - // polyfilling process.env.VITEST_STORYBOOK to 'false' in the browser - 'process.env.VITEST_STORYBOOK': JSON.stringify('false'), ...(frameworkName?.includes('vue3') ? { __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false' } : {}), @@ -254,8 +265,12 @@ export const storybookTest = async (options?: UserOptions): Promise => { ); // alert the user of problems - if (inputConfig_DoNotMutate.test.include?.length > 0) { - console.warn( + if ( + inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test.include?.length > 0 + ) { + // remove the user's existing include, because we're replacing it with our own heuristic based on main.ts#stories + inputConfig_ONLY_MUTATE_WHEN_STRICTLY_NEEDED_OR_YOU_WILL_BE_FIRED.test.include = []; + console.log( picocolors.yellow(dedent` Warning: Starting in Storybook 8.5.0-alpha.18, the "test.include" option in Vitest is discouraged in favor of just using the "stories" field in your Storybook configuration. diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json index 57114fdc2b4a..203ff96d62f6 100644 --- a/code/addons/themes/package.json +++ b/code/addons/themes/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-themes", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Switch between multiple themes for you components in Storybook", "keywords": [ "css", diff --git a/code/addons/themes/src/decorators/class-name.decorator.tsx b/code/addons/themes/src/decorators/class-name.decorator.tsx index 4bc56202f038..7e44306b9173 100644 --- a/code/addons/themes/src/decorators/class-name.decorator.tsx +++ b/code/addons/themes/src/decorators/class-name.decorator.tsx @@ -22,7 +22,7 @@ export const withThemeByClassName = ({ initializeThemeState(Object.keys(themes), defaultTheme); return (storyFn, context) => { - const { themeOverride } = useThemeParameters(); + const { themeOverride } = useThemeParameters(context); const selected = pluckThemeFromContext(context); useEffect(() => { diff --git a/code/addons/themes/src/decorators/data-attribute.decorator.tsx b/code/addons/themes/src/decorators/data-attribute.decorator.tsx index 6d4b1cfe26f9..ec5e0d909538 100644 --- a/code/addons/themes/src/decorators/data-attribute.decorator.tsx +++ b/code/addons/themes/src/decorators/data-attribute.decorator.tsx @@ -22,7 +22,7 @@ export const withThemeByDataAttribute = ({ }: DataAttributeStrategyConfiguration): DecoratorFunction => { initializeThemeState(Object.keys(themes), defaultTheme); return (storyFn, context) => { - const { themeOverride } = useThemeParameters(); + const { themeOverride } = useThemeParameters(context); const selected = pluckThemeFromContext(context); useEffect(() => { diff --git a/code/addons/themes/src/decorators/helpers.ts b/code/addons/themes/src/decorators/helpers.ts index 237b0943568c..0dd8c007ec7f 100644 --- a/code/addons/themes/src/decorators/helpers.ts +++ b/code/addons/themes/src/decorators/helpers.ts @@ -1,4 +1,4 @@ -import { addons, useParameter } from 'storybook/internal/preview-api'; +import { addons } from 'storybook/internal/preview-api'; import type { StoryContext } from 'storybook/internal/types'; import type { ThemeParameters } from '../constants'; @@ -12,8 +12,8 @@ export function pluckThemeFromContext({ globals }: StoryContext): string { return globals[GLOBAL_KEY] || ''; } -export function useThemeParameters(): ThemeParameters { - return useParameter(PARAM_KEY, DEFAULT_THEME_PARAMETERS) as ThemeParameters; +export function useThemeParameters(context: StoryContext): ThemeParameters { + return context.parameters[PARAM_KEY] || DEFAULT_THEME_PARAMETERS; } export function initializeThemeState(themeNames: string[], defaultTheme: string) { diff --git a/code/addons/themes/src/decorators/provider.decorator.tsx b/code/addons/themes/src/decorators/provider.decorator.tsx index 8985a9773fad..f1214f69f992 100644 --- a/code/addons/themes/src/decorators/provider.decorator.tsx +++ b/code/addons/themes/src/decorators/provider.decorator.tsx @@ -32,7 +32,7 @@ export const withThemeFromJSXProvider = ({ // eslint-disable-next-line react/display-name return (storyFn, context) => { - const { themeOverride } = useThemeParameters(); + const { themeOverride } = useThemeParameters(context); const selected = pluckThemeFromContext(context); const theme = useMemo(() => { diff --git a/code/addons/toolbars/package.json b/code/addons/toolbars/package.json index 67afb0929acc..bddd8a73c217 100644 --- a/code/addons/toolbars/package.json +++ b/code/addons/toolbars/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-toolbars", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Create your own toolbar items that control story rendering", "keywords": [ "addon", diff --git a/code/addons/viewport/package.json b/code/addons/viewport/package.json index 7ce39426636e..7aeb1ea7d640 100644 --- a/code/addons/viewport/package.json +++ b/code/addons/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-viewport", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Build responsive components by adjusting Storybook’s viewport size and orientation", "keywords": [ "addon", diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index 75ad6ae45767..e5d76e019f82 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "A plugin to run and build Storybooks with Vite", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite/#readme", "bugs": { diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index cacc5276d9da..3db13f0231cb 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/core/assets/server/addon.tsconfig.json b/code/core/assets/server/addon.tsconfig.json index 9161d5ff8254..38452bcdfa20 100644 --- a/code/core/assets/server/addon.tsconfig.json +++ b/code/core/assets/server/addon.tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "jsx": "react-jsx", + "jsx": "react", "jsxImportSource": "react" } } diff --git a/code/core/package.json b/code/core/package.json index d3f69bfe4d89..1744d188499f 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/core/src/__mocks__/page.ts b/code/core/src/__mocks__/page.ts new file mode 100644 index 000000000000..fb87bbd306b8 --- /dev/null +++ b/code/core/src/__mocks__/page.ts @@ -0,0 +1 @@ +// empty file only matched on path diff --git a/code/core/src/__mocks__/path/to/Screens/index.jsx b/code/core/src/__mocks__/path/to/Screens/index.jsx new file mode 100644 index 000000000000..fb87bbd306b8 --- /dev/null +++ b/code/core/src/__mocks__/path/to/Screens/index.jsx @@ -0,0 +1 @@ +// empty file only matched on path diff --git a/code/core/src/common/versions.ts b/code/core/src/common/versions.ts index ea14e9cf35be..cdb250c9cd8d 100644 --- a/code/core/src/common/versions.ts +++ b/code/core/src/common/versions.ts @@ -1,88 +1,88 @@ // auto generated file, do not edit export default { - '@storybook/addon-a11y': '8.5.0-alpha.19', - '@storybook/addon-actions': '8.5.0-alpha.19', - '@storybook/addon-backgrounds': '8.5.0-alpha.19', - '@storybook/addon-controls': '8.5.0-alpha.19', - '@storybook/addon-docs': '8.5.0-alpha.19', - '@storybook/addon-essentials': '8.5.0-alpha.19', - '@storybook/addon-mdx-gfm': '8.5.0-alpha.19', - '@storybook/addon-highlight': '8.5.0-alpha.19', - '@storybook/addon-interactions': '8.5.0-alpha.19', - '@storybook/addon-jest': '8.5.0-alpha.19', - '@storybook/addon-links': '8.5.0-alpha.19', - '@storybook/addon-measure': '8.5.0-alpha.19', - '@storybook/addon-onboarding': '8.5.0-alpha.19', - '@storybook/addon-outline': '8.5.0-alpha.19', - '@storybook/addon-storysource': '8.5.0-alpha.19', - '@storybook/experimental-addon-test': '8.5.0-alpha.19', - '@storybook/addon-themes': '8.5.0-alpha.19', - '@storybook/addon-toolbars': '8.5.0-alpha.19', - '@storybook/addon-viewport': '8.5.0-alpha.19', - '@storybook/builder-vite': '8.5.0-alpha.19', - '@storybook/builder-webpack5': '8.5.0-alpha.19', - '@storybook/core': '8.5.0-alpha.19', - '@storybook/builder-manager': '8.5.0-alpha.19', - '@storybook/channels': '8.5.0-alpha.19', - '@storybook/client-logger': '8.5.0-alpha.19', - '@storybook/components': '8.5.0-alpha.19', - '@storybook/core-common': '8.5.0-alpha.19', - '@storybook/core-events': '8.5.0-alpha.19', - '@storybook/core-server': '8.5.0-alpha.19', - '@storybook/csf-tools': '8.5.0-alpha.19', - '@storybook/docs-tools': '8.5.0-alpha.19', - '@storybook/manager': '8.5.0-alpha.19', - '@storybook/manager-api': '8.5.0-alpha.19', - '@storybook/node-logger': '8.5.0-alpha.19', - '@storybook/preview': '8.5.0-alpha.19', - '@storybook/preview-api': '8.5.0-alpha.19', - '@storybook/router': '8.5.0-alpha.19', - '@storybook/telemetry': '8.5.0-alpha.19', - '@storybook/theming': '8.5.0-alpha.19', - '@storybook/types': '8.5.0-alpha.19', - '@storybook/angular': '8.5.0-alpha.19', - '@storybook/ember': '8.5.0-alpha.19', - '@storybook/experimental-nextjs-vite': '8.5.0-alpha.19', - '@storybook/html-vite': '8.5.0-alpha.19', - '@storybook/html-webpack5': '8.5.0-alpha.19', - '@storybook/nextjs': '8.5.0-alpha.19', - '@storybook/preact-vite': '8.5.0-alpha.19', - '@storybook/preact-webpack5': '8.5.0-alpha.19', - '@storybook/react-native-web-vite': '8.5.0-alpha.19', - '@storybook/react-vite': '8.5.0-alpha.19', - '@storybook/react-webpack5': '8.5.0-alpha.19', - '@storybook/server-webpack5': '8.5.0-alpha.19', - '@storybook/svelte-vite': '8.5.0-alpha.19', - '@storybook/svelte-webpack5': '8.5.0-alpha.19', - '@storybook/sveltekit': '8.5.0-alpha.19', - '@storybook/vue3-vite': '8.5.0-alpha.19', - '@storybook/vue3-webpack5': '8.5.0-alpha.19', - '@storybook/web-components-vite': '8.5.0-alpha.19', - '@storybook/web-components-webpack5': '8.5.0-alpha.19', - '@storybook/blocks': '8.5.0-alpha.19', - storybook: '8.5.0-alpha.19', - sb: '8.5.0-alpha.19', - '@storybook/cli': '8.5.0-alpha.19', - '@storybook/codemod': '8.5.0-alpha.19', - '@storybook/core-webpack': '8.5.0-alpha.19', - 'create-storybook': '8.5.0-alpha.19', - '@storybook/csf-plugin': '8.5.0-alpha.19', - '@storybook/instrumenter': '8.5.0-alpha.19', - '@storybook/react-dom-shim': '8.5.0-alpha.19', - '@storybook/source-loader': '8.5.0-alpha.19', - '@storybook/test': '8.5.0-alpha.19', - '@storybook/preset-create-react-app': '8.5.0-alpha.19', - '@storybook/preset-html-webpack': '8.5.0-alpha.19', - '@storybook/preset-preact-webpack': '8.5.0-alpha.19', - '@storybook/preset-react-webpack': '8.5.0-alpha.19', - '@storybook/preset-server-webpack': '8.5.0-alpha.19', - '@storybook/preset-svelte-webpack': '8.5.0-alpha.19', - '@storybook/preset-vue3-webpack': '8.5.0-alpha.19', - '@storybook/html': '8.5.0-alpha.19', - '@storybook/preact': '8.5.0-alpha.19', - '@storybook/react': '8.5.0-alpha.19', - '@storybook/server': '8.5.0-alpha.19', - '@storybook/svelte': '8.5.0-alpha.19', - '@storybook/vue3': '8.5.0-alpha.19', - '@storybook/web-components': '8.5.0-alpha.19', + '@storybook/addon-a11y': '8.5.0-beta.2', + '@storybook/addon-actions': '8.5.0-beta.2', + '@storybook/addon-backgrounds': '8.5.0-beta.2', + '@storybook/addon-controls': '8.5.0-beta.2', + '@storybook/addon-docs': '8.5.0-beta.2', + '@storybook/addon-essentials': '8.5.0-beta.2', + '@storybook/addon-mdx-gfm': '8.5.0-beta.2', + '@storybook/addon-highlight': '8.5.0-beta.2', + '@storybook/addon-interactions': '8.5.0-beta.2', + '@storybook/addon-jest': '8.5.0-beta.2', + '@storybook/addon-links': '8.5.0-beta.2', + '@storybook/addon-measure': '8.5.0-beta.2', + '@storybook/addon-onboarding': '8.5.0-beta.2', + '@storybook/addon-outline': '8.5.0-beta.2', + '@storybook/addon-storysource': '8.5.0-beta.2', + '@storybook/experimental-addon-test': '8.5.0-beta.2', + '@storybook/addon-themes': '8.5.0-beta.2', + '@storybook/addon-toolbars': '8.5.0-beta.2', + '@storybook/addon-viewport': '8.5.0-beta.2', + '@storybook/builder-vite': '8.5.0-beta.2', + '@storybook/builder-webpack5': '8.5.0-beta.2', + '@storybook/core': '8.5.0-beta.2', + '@storybook/builder-manager': '8.5.0-beta.2', + '@storybook/channels': '8.5.0-beta.2', + '@storybook/client-logger': '8.5.0-beta.2', + '@storybook/components': '8.5.0-beta.2', + '@storybook/core-common': '8.5.0-beta.2', + '@storybook/core-events': '8.5.0-beta.2', + '@storybook/core-server': '8.5.0-beta.2', + '@storybook/csf-tools': '8.5.0-beta.2', + '@storybook/docs-tools': '8.5.0-beta.2', + '@storybook/manager': '8.5.0-beta.2', + '@storybook/manager-api': '8.5.0-beta.2', + '@storybook/node-logger': '8.5.0-beta.2', + '@storybook/preview': '8.5.0-beta.2', + '@storybook/preview-api': '8.5.0-beta.2', + '@storybook/router': '8.5.0-beta.2', + '@storybook/telemetry': '8.5.0-beta.2', + '@storybook/theming': '8.5.0-beta.2', + '@storybook/types': '8.5.0-beta.2', + '@storybook/angular': '8.5.0-beta.2', + '@storybook/ember': '8.5.0-beta.2', + '@storybook/experimental-nextjs-vite': '8.5.0-beta.2', + '@storybook/html-vite': '8.5.0-beta.2', + '@storybook/html-webpack5': '8.5.0-beta.2', + '@storybook/nextjs': '8.5.0-beta.2', + '@storybook/preact-vite': '8.5.0-beta.2', + '@storybook/preact-webpack5': '8.5.0-beta.2', + '@storybook/react-native-web-vite': '8.5.0-beta.2', + '@storybook/react-vite': '8.5.0-beta.2', + '@storybook/react-webpack5': '8.5.0-beta.2', + '@storybook/server-webpack5': '8.5.0-beta.2', + '@storybook/svelte-vite': '8.5.0-beta.2', + '@storybook/svelte-webpack5': '8.5.0-beta.2', + '@storybook/sveltekit': '8.5.0-beta.2', + '@storybook/vue3-vite': '8.5.0-beta.2', + '@storybook/vue3-webpack5': '8.5.0-beta.2', + '@storybook/web-components-vite': '8.5.0-beta.2', + '@storybook/web-components-webpack5': '8.5.0-beta.2', + '@storybook/blocks': '8.5.0-beta.2', + storybook: '8.5.0-beta.2', + sb: '8.5.0-beta.2', + '@storybook/cli': '8.5.0-beta.2', + '@storybook/codemod': '8.5.0-beta.2', + '@storybook/core-webpack': '8.5.0-beta.2', + 'create-storybook': '8.5.0-beta.2', + '@storybook/csf-plugin': '8.5.0-beta.2', + '@storybook/instrumenter': '8.5.0-beta.2', + '@storybook/react-dom-shim': '8.5.0-beta.2', + '@storybook/source-loader': '8.5.0-beta.2', + '@storybook/test': '8.5.0-beta.2', + '@storybook/preset-create-react-app': '8.5.0-beta.2', + '@storybook/preset-html-webpack': '8.5.0-beta.2', + '@storybook/preset-preact-webpack': '8.5.0-beta.2', + '@storybook/preset-react-webpack': '8.5.0-beta.2', + '@storybook/preset-server-webpack': '8.5.0-beta.2', + '@storybook/preset-svelte-webpack': '8.5.0-beta.2', + '@storybook/preset-vue3-webpack': '8.5.0-beta.2', + '@storybook/html': '8.5.0-beta.2', + '@storybook/preact': '8.5.0-beta.2', + '@storybook/react': '8.5.0-beta.2', + '@storybook/server': '8.5.0-beta.2', + '@storybook/svelte': '8.5.0-beta.2', + '@storybook/vue3': '8.5.0-beta.2', + '@storybook/web-components': '8.5.0-beta.2', }; diff --git a/code/core/src/components/components/Loader/Loader.tsx b/code/core/src/components/components/Loader/Loader.tsx index 26d01ad3f05d..dace42a9b351 100644 --- a/code/core/src/components/components/Loader/Loader.tsx +++ b/code/core/src/components/components/Loader/Loader.tsx @@ -63,7 +63,7 @@ const ProgressBar = styled.div(({ theme }) => ({ const ProgressMessage = styled.div(({ theme }) => ({ minHeight: '2em', fontSize: `${theme.typography.size.s1}px`, - color: theme.barTextColor, + color: theme.textMutedColor, })); const ErrorIcon = styled(LightningOffIcon)(({ theme }) => ({ diff --git a/code/core/src/components/components/tooltip/ListItem.tsx b/code/core/src/components/components/tooltip/ListItem.tsx index 2a93287af503..10e513cc1b6b 100644 --- a/code/core/src/components/components/tooltip/ListItem.tsx +++ b/code/core/src/components/components/tooltip/ListItem.tsx @@ -40,7 +40,7 @@ const Title = styled(({ active, loading, disabled, ...rest }: TitleProps) => disabled ? { - color: transparentize(0.7, theme.color.defaultText), + color: theme.textMutedColor, } : {} ); diff --git a/code/core/src/manager-api/lib/stories.test.ts b/code/core/src/manager-api/lib/stories.test.ts index 76aaadd7f99d..3f0cbe63b5be 100644 --- a/code/core/src/manager-api/lib/stories.test.ts +++ b/code/core/src/manager-api/lib/stories.test.ts @@ -2,8 +2,10 @@ import { describe, expect, it } from 'vitest'; import type { API_PreparedStoryIndex, StoryIndexV2, StoryIndexV3 } from '@storybook/core/types'; +import type { State } from '../root'; import { mockEntries } from '../tests/mockStoriesEntries'; import { + transformStoryIndexToStoriesHash, transformStoryIndexV2toV3, transformStoryIndexV3toV4, transformStoryIndexV4toV5, @@ -216,3 +218,60 @@ describe('transformStoryIndexV4toV5', () => { `); }); }); + +describe('transformStoryIndexToStoriesHash', () => { + it('does not apply filters to failing stories', () => { + // Arrange - set up an index with two stories, one of which has a failing status + const indexV5: API_PreparedStoryIndex = { + v: 5, + entries: { + '1': { + id: '1', + type: 'story', + title: 'Story 1', + name: 'Story 1', + importPath: './path/to/story-1.ts', + parameters: {}, + tags: [], + }, + '2': { + id: '2', + type: 'story', + title: 'Story 2', + name: 'Story 2', + importPath: './path/to/story-2.ts', + parameters: {}, + tags: [], + }, + }, + }; + + const filters: State['filters'] = { + someFilter: () => false, + }; + + const status: State['status'] = { + '1': { someStatus: { status: 'error', title: 'broken', description: 'very bad' } }, + '2': { someStatus: { status: 'success', title: 'perfect', description: 'nice' } }, + }; + + const options = { + provider: { + getConfig: () => ({ sidebar: {} }), + } as any, + docsOptions: { docsMode: false }, + filters, + status, + }; + + // Act - transform the index to hashes + const result = transformStoryIndexToStoriesHash(indexV5, options); + + // Assert - the failing story is still present in the result, even though the filters remove all stories + expect(Object.keys(result)).toHaveLength(2); + expect(result['story-1']).toBeTruthy(); + expect(result['1']).toBeTruthy(); + expect(result['story-2']).toBeUndefined(); + expect(result['2']).toBeUndefined(); + }); +}); diff --git a/code/core/src/manager-api/lib/stories.ts b/code/core/src/manager-api/lib/stories.ts index 59b59f070a10..b38d30000531 100644 --- a/code/core/src/manager-api/lib/stories.ts +++ b/code/core/src/manager-api/lib/stories.ts @@ -192,11 +192,17 @@ export const transformStoryIndexToStoriesHash = ( const entryValues = Object.values(index.entries).filter((entry: any) => { let result = true; + // All stories with a failing status should always show up, regardless of the applied filters + const storyStatus = status[entry.id]; + if (Object.values(storyStatus ?? {}).some(({ status: s }) => s === 'error')) { + return result; + } + Object.values(filters).forEach((filter: any) => { if (result === false) { return; } - result = filter({ ...entry, status: status[entry.id] }); + result = filter({ ...entry, status: storyStatus }); }); return result; @@ -342,6 +348,7 @@ export const transformStoryIndexToStoriesHash = ( .reduce(addItem, orphanHash); }; +/** Now we need to patch in the existing prepared stories */ export const addPreparedStories = (newHash: API_IndexHash, oldHash?: API_IndexHash) => { if (!oldHash) { return newHash; diff --git a/code/core/src/manager-api/modules/layout.ts b/code/core/src/manager-api/modules/layout.ts index 468d51af1b8a..6687c36b1137 100644 --- a/code/core/src/manager-api/modules/layout.ts +++ b/code/core/src/manager-api/modules/layout.ts @@ -100,7 +100,7 @@ export const defaultLayoutState: SubState = { panelPosition: 'bottom', showTabs: true, }, - selectedPanel: undefined, + selectedPanel: 'chromaui/addon-visual-tests/panel', theme: create(), }; diff --git a/code/core/src/manager-api/modules/refs.ts b/code/core/src/manager-api/modules/refs.ts index 166c04786adc..cb9b8916ce29 100644 --- a/code/core/src/manager-api/modules/refs.ts +++ b/code/core/src/manager-api/modules/refs.ts @@ -179,7 +179,15 @@ export const init: ModuleFn = ( }, changeRefVersion: async (id, url) => { const { versions, title } = api.getRefs()[id]; - const ref: API_SetRefData = { id, url, versions, title, index: {}, expanded: true }; + const ref: API_SetRefData = { + id, + url, + versions, + title, + index: {}, + filteredIndex: {}, + expanded: true, + }; await api.setRef(id, { ...ref, type: 'unknown' }, false); await api.checkRef(ref); @@ -292,6 +300,7 @@ export const init: ModuleFn = ( // eslint-disable-next-line @typescript-eslint/naming-convention let internal_index: StoryIndex | undefined; let index: API_IndexHash | undefined; + let filteredIndex: API_IndexHash | undefined; const { filters } = store.getState(); const { storyMapper = defaultStoryMapper } = provider.getConfig(); const ref = api.getRefs()[id]; @@ -304,19 +313,28 @@ export const init: ModuleFn = ( : storyIndex; // @ts-expect-error (could be undefined) - index = transformStoryIndexToStoriesHash(storyIndex, { + filteredIndex = transformStoryIndexToStoriesHash(storyIndex, { provider, docsOptions, filters, status: {}, }); + // @ts-expect-error (could be undefined) + index = transformStoryIndexToStoriesHash(storyIndex, { + provider, + docsOptions, + filters: {}, + status: {}, + }); } if (index) { index = addRefIds(index, ref); } - - await api.updateRef(id, { ...ref, ...rest, index, internal_index }); + if (filteredIndex) { + filteredIndex = addRefIds(filteredIndex, ref); + } + await api.updateRef(id, { ...ref, ...rest, index, filteredIndex, internal_index }); }, updateRef: async (id, data) => { diff --git a/code/core/src/manager-api/modules/stories.ts b/code/core/src/manager-api/modules/stories.ts index 3c5aac769c6a..fa295bc20567 100644 --- a/code/core/src/manager-api/modules/stories.ts +++ b/code/core/src/manager-api/modules/stories.ts @@ -568,41 +568,61 @@ export const init: ModuleFn = ({ // The story index we receive on fetchStoryIndex is not, but all the prepared fields are optional // so we can cast one to the other easily enough setIndex: async (input) => { - const { index: oldHash, status, filters } = store.getState(); - const newHash = transformStoryIndexToStoriesHash(input, { + const { filteredIndex: oldFilteredHash, index: oldHash, status, filters } = store.getState(); + const newFilteredHash = transformStoryIndexToStoriesHash(input, { provider, docsOptions, status, filters, }); + const newHash = transformStoryIndexToStoriesHash(input, { + provider, + docsOptions, + status, + filters: {}, + }); - // Now we need to patch in the existing prepared stories - const output = addPreparedStories(newHash, oldHash); - - await store.setState({ internal_index: input, index: output, indexError: undefined }); + await store.setState({ + internal_index: input, + filteredIndex: addPreparedStories(newFilteredHash, oldFilteredHash), + index: addPreparedStories(newHash, oldHash), + indexError: undefined, + }); }, + // FIXME: is there a bug where filtered stories get added back in on updateStory??? updateStory: async ( storyId: StoryId, update: StoryUpdate, ref?: API_ComposedRef ): Promise => { if (!ref) { - const { index } = store.getState(); - if (!index) { - return; + const { index, filteredIndex } = store.getState(); + if (index) { + index[storyId] = { + ...index[storyId], + ...update, + } as API_StoryEntry; + } + if (filteredIndex) { + filteredIndex[storyId] = { + ...filteredIndex[storyId], + ...update, + } as API_StoryEntry; + } + if (index || filteredIndex) { + await store.setState({ index, filteredIndex }); } + } else { + const { id: refId, index, filteredIndex }: any = ref; index[storyId] = { ...index[storyId], ...update, } as API_StoryEntry; - await store.setState({ index }); - } else { - const { id: refId, index }: any = ref; - index[storyId] = { - ...index[storyId], + filteredIndex[storyId] = { + ...filteredIndex[storyId], ...update, } as API_StoryEntry; - await fullAPI.updateRef(refId, { index }); + await fullAPI.updateRef(refId, { index, filteredIndex }); } }, updateDocs: async ( @@ -611,22 +631,33 @@ export const init: ModuleFn = ({ ref?: API_ComposedRef ): Promise => { if (!ref) { - const { index } = store.getState(); - if (!index) { - return; + const { index, filteredIndex } = store.getState(); + if (index) { + index[docsId] = { + ...index[docsId], + ...update, + } as API_DocsEntry; } + if (filteredIndex) { + filteredIndex[docsId] = { + ...filteredIndex[docsId], + ...update, + } as API_DocsEntry; + } + if (index || filteredIndex) { + await store.setState({ index, filteredIndex }); + } + } else { + const { id: refId, index, filteredIndex }: any = ref; index[docsId] = { ...index[docsId], ...update, } as API_DocsEntry; - await store.setState({ index }); - } else { - const { id: refId, index }: any = ref; - index[docsId] = { - ...index[docsId], + filteredIndex[docsId] = { + ...filteredIndex[docsId], ...update, } as API_DocsEntry; - await fullAPI.updateRef(refId, { index }); + await fullAPI.updateRef(refId, { index, filteredIndex }); } }, setPreviewInitialized: async (ref) => { diff --git a/code/core/src/manager-api/tests/refs.test.ts b/code/core/src/manager-api/tests/refs.test.ts index c8d65baafc57..b9d39ee6a88b 100644 --- a/code/core/src/manager-api/tests/refs.test.ts +++ b/code/core/src/manager-api/tests/refs.test.ts @@ -291,6 +291,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": undefined, "id": "fake", "index": undefined, "indexError": { @@ -360,6 +361,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": undefined, "id": "fake", "index": undefined, "indexError": { @@ -504,6 +506,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -522,6 +525,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -601,6 +605,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -682,6 +687,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -763,6 +769,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": undefined, "id": "fake", "index": undefined, "internal_index": undefined, @@ -905,6 +912,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": undefined, "id": "fake", "index": undefined, "internal_index": undefined, @@ -987,6 +995,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -1068,6 +1077,7 @@ describe('Refs API', () => { { "refs": { "fake": { + "filteredIndex": {}, "id": "fake", "index": {}, "internal_index": { @@ -1227,18 +1237,20 @@ describe('Refs API', () => { }, }; + const transformOptions = { + provider: provider as any, + docsOptions: {}, + filters: {}, + status: {}, + }; const initialState: Partial = { refs: { fake: { id: 'fake', url: 'https://example.com', previewInitialized: true, - index: transformStoryIndexToStoriesHash(index, { - provider: provider as any, - docsOptions: {}, - filters: {}, - status: {}, - }), + index: transformStoryIndexToStoriesHash(index, transformOptions), + filteredIndex: transformStoryIndexToStoriesHash(index, transformOptions), internal_index: index, }, }, @@ -1261,10 +1273,10 @@ describe('Refs API', () => { await api.setRef('fake', { storyIndex: index }); - await expect(api.getRefs().fake.index).toEqual( + await expect(api.getRefs().fake.filteredIndex).toEqual( expect.objectContaining({ 'a--1': expect.anything() }) ); - await expect(api.getRefs().fake.index).not.toEqual( + await expect(api.getRefs().fake.filteredIndex).not.toEqual( expect.objectContaining({ 'a--2': expect.anything() }) ); }); diff --git a/code/core/src/manager-api/tests/stories.test.ts b/code/core/src/manager-api/tests/stories.test.ts index b652c87cd7a1..6f480061d9ec 100644 --- a/code/core/src/manager-api/tests/stories.test.ts +++ b/code/core/src/manager-api/tests/stories.test.ts @@ -765,10 +765,15 @@ describe('stories API', () => { source: '', sourceLocation: '', type: '', - ref: { id: 'refId', index: { 'a--1': { args: { a: 'b' } } } } as any, + ref: { + id: 'refId', + index: { 'a--1': { args: { a: 'b' } } }, + filteredIndex: { 'a--1': { args: { a: 'b' } } }, + } as any, }); provider.channel.emit(STORY_ARGS_UPDATED, { storyId: 'a--1', args: { foo: 'bar' } }); expect(fullAPI.updateRef).toHaveBeenCalledWith('refId', { + filteredIndex: { 'a--1': { args: { foo: 'bar' } } }, index: { 'a--1': { args: { foo: 'bar' } } }, }); }); @@ -1539,6 +1544,7 @@ describe('stories API', () => { }) ); }); + it('updates state', async () => { const moduleArgs = createMockModuleArgs({}); const { api } = initStories(moduleArgs as unknown as ModuleArgs); @@ -1565,9 +1571,9 @@ describe('stories API', () => { await api.setIndex({ v: 5, entries: navigationEntries }); await api.experimental_setFilter('myCustomFilter', (item: any) => item.id.startsWith('a')); - const { index } = store.getState(); + const { filteredIndex } = store.getState(); - expect(index).toMatchInlineSnapshot(` + expect(filteredIndex).toMatchInlineSnapshot(` { "a": { "children": [ @@ -1624,7 +1630,7 @@ describe('stories API', () => { ); // empty, because there are no stories with status - expect(store.getState().index).toMatchInlineSnapshot('{}'); + expect(store.getState().filteredIndex).toMatchInlineSnapshot('{}'); // setting status should update the index await api.experimental_updateStatus('a-addon-id', { @@ -1636,7 +1642,7 @@ describe('stories API', () => { 'a--2': { status: 'success', title: 'a addon title', description: '' }, }); - expect(store.getState().index).toMatchInlineSnapshot(` + expect(store.getState().filteredIndex).toMatchInlineSnapshot(` { "a": { "children": [ @@ -1676,9 +1682,9 @@ describe('stories API', () => { await api.setIndex({ v: 5, entries: navigationEntries }); - const { index } = store.getState(); + const { filteredIndex } = store.getState(); - expect(index).toMatchInlineSnapshot(` + expect(filteredIndex).toMatchInlineSnapshot(` { "a": { "children": [ diff --git a/code/core/src/manager-api/version.ts b/code/core/src/manager-api/version.ts index 93047b78f0e7..c9d852d16745 100644 --- a/code/core/src/manager-api/version.ts +++ b/code/core/src/manager-api/version.ts @@ -1 +1 @@ -export const version = '8.5.0-alpha.19'; +export const version = '8.5.0-beta.2'; diff --git a/code/core/src/manager/components/panel/Panel.tsx b/code/core/src/manager/components/panel/Panel.tsx index 701cd94925e3..dc80c20949d2 100644 --- a/code/core/src/manager/components/panel/Panel.tsx +++ b/code/core/src/manager/components/panel/Panel.tsx @@ -60,7 +60,7 @@ export const AddonPanel = React.memo<{ return ( ({ const isActive = rp.path.includes(`tab=${tab.id}`); return ( { rp.applyQueryParams({ tab: tabIdToApply }); @@ -146,7 +146,7 @@ export const ToolbarComp = React.memo(function ToolbarComp({ {tabs.map((tab, index) => { return ( { api.applyQueryParams({ tab: tab.id === 'canvas' ? undefined : tab.id }); diff --git a/code/core/src/manager/components/sidebar/Explorer.stories.tsx b/code/core/src/manager/components/sidebar/Explorer.stories.tsx index 908ead2be9f3..34adad0a32a0 100644 --- a/code/core/src/manager/components/sidebar/Explorer.stories.tsx +++ b/code/core/src/manager/components/sidebar/Explorer.stories.tsx @@ -34,7 +34,7 @@ const simple: Record = { url: 'iframe.html', previewInitialized: true, // @ts-expect-error (invalid input) - index: mockDataset.withRoot, + filteredIndex: mockDataset.withRoot, }, }; @@ -47,7 +47,7 @@ const withRefs: Record = { previewInitialized: true, type: 'auto-inject', // @ts-expect-error (invalid input) - index: mockDataset.noRoot, + filteredIndex: mockDataset.noRoot, }, injected: { id: 'injected', @@ -56,7 +56,7 @@ const withRefs: Record = { previewInitialized: false, type: 'auto-inject', // @ts-expect-error (invalid input) - index: mockDataset.noRoot, + filteredIndex: mockDataset.noRoot, }, unknown: { id: 'unknown', @@ -65,7 +65,7 @@ const withRefs: Record = { previewInitialized: true, type: 'unknown', // @ts-expect-error (invalid input) - index: mockDataset.noRoot, + filteredIndex: mockDataset.noRoot, }, lazy: { id: 'lazy', @@ -74,7 +74,7 @@ const withRefs: Record = { previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) - index: mockDataset.withRoot, + filteredIndex: mockDataset.withRoot, }, }; diff --git a/code/core/src/manager/components/sidebar/LegacyRender.tsx b/code/core/src/manager/components/sidebar/LegacyRender.tsx index 82bc83bd1b34..f8afa4317f7f 100644 --- a/code/core/src/manager/components/sidebar/LegacyRender.tsx +++ b/code/core/src/manager/components/sidebar/LegacyRender.tsx @@ -32,7 +32,7 @@ const TitleWrapper = styled.div<{ crashed?: boolean }>(({ crashed, theme }) => ( const DescriptionWrapper = styled.div(({ theme }) => ({ fontSize: theme.typography.size.s1, - color: theme.barTextColor, + color: theme.textMutedColor, })); const Progress = styled(ProgressSpinner)({ diff --git a/code/core/src/manager/components/sidebar/Refs.stories.tsx b/code/core/src/manager/components/sidebar/Refs.stories.tsx index a042970beadc..3b6e37efce50 100644 --- a/code/core/src/manager/components/sidebar/Refs.stories.tsx +++ b/code/core/src/manager/components/sidebar/Refs.stories.tsx @@ -37,11 +37,11 @@ export default { }; const { menu } = standardHeaderData; -const index = mockDataset.withRoot; +const filteredIndex = mockDataset.withRoot; const storyId = '1-12-121'; -export const simpleData = { menu, index, storyId }; -export const loadingData = { menu, index: {} }; +export const simpleData = { menu, filteredIndex, storyId }; +export const loadingData = { menu, filteredIndex: {} }; // @ts-expect-error (non strict) const indexError: Error = (() => { @@ -60,14 +60,14 @@ const refs: Record = { previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) - index, + filteredIndex, }, empty: { id: 'empty', title: 'It is empty because no stories were loaded', url: 'https://example.com', type: 'lazy', - index: {}, + filteredIndex: {}, previewInitialized: false, }, startInjected_unknown: { @@ -77,7 +77,7 @@ const refs: Record = { type: 'unknown', previewInitialized: false, // @ts-expect-error (invalid input) - index, + filteredIndex, }, startInjected_loading: { id: 'startInjected_loading', @@ -86,7 +86,7 @@ const refs: Record = { type: 'auto-inject', previewInitialized: false, // @ts-expect-error (invalid input) - index, + filteredIndex, }, startInjected_ready: { id: 'startInjected_ready', @@ -95,7 +95,7 @@ const refs: Record = { type: 'auto-inject', previewInitialized: true, // @ts-expect-error (invalid input) - index, + filteredIndex, }, versions: { id: 'versions', @@ -103,7 +103,7 @@ const refs: Record = { url: 'https://example.com', type: 'lazy', // @ts-expect-error (invalid input) - index, + filteredIndex, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' }, previewInitialized: true, }, @@ -113,7 +113,7 @@ const refs: Record = { url: 'https://example.com', type: 'lazy', // @ts-expect-error (invalid input) - index, + filteredIndex, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com/v2' }, previewInitialized: true, }, @@ -138,7 +138,7 @@ const refs: Record = { title: 'This storybook has a very very long name for some reason', url: 'https://example.com', // @ts-expect-error (invalid input) - index, + filteredIndex, type: 'lazy', versions: { '111.111.888-new': 'https://example.com/new', @@ -154,7 +154,7 @@ const refs: Record = { previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) - index, + filteredIndex, }, }; diff --git a/code/core/src/manager/components/sidebar/Refs.tsx b/code/core/src/manager/components/sidebar/Refs.tsx index 7412a627e889..b4d2a79ca351 100644 --- a/code/core/src/manager/components/sidebar/Refs.tsx +++ b/code/core/src/manager/components/sidebar/Refs.tsx @@ -81,7 +81,7 @@ export const Ref: FC = React. const { docsOptions } = useStorybookState(); const api = useStorybookApi(); const { - index, + filteredIndex: index, id: refId, title = refId, isLoading: isLoadingMain, diff --git a/code/core/src/manager/components/sidebar/SearchResults.tsx b/code/core/src/manager/components/sidebar/SearchResults.tsx index 7573befe34a1..b7689b7d64d0 100644 --- a/code/core/src/manager/components/sidebar/SearchResults.tsx +++ b/code/core/src/manager/components/sidebar/SearchResults.tsx @@ -70,7 +70,7 @@ const NoResults = styled.div(({ theme }) => ({ lineHeight: `18px`, color: theme.color.defaultText, small: { - color: theme.barTextColor, + color: theme.textMutedColor, fontSize: `${theme.typography.size.s1}px`, }, })); diff --git a/code/core/src/manager/components/sidebar/Sidebar.stories.tsx b/code/core/src/manager/components/sidebar/Sidebar.stories.tsx index 53f22957f17d..0e2df01f6aed 100644 --- a/code/core/src/manager/components/sidebar/Sidebar.stories.tsx +++ b/code/core/src/manager/components/sidebar/Sidebar.stories.tsx @@ -111,7 +111,7 @@ const refs: Record = { title: 'This is a ref', url: 'https://example.com', type: 'lazy', - index, + filteredIndex: index, previewInitialized: true, }, }; @@ -123,7 +123,7 @@ const refsError = { optimized: { ...refs.optimized, // @ts-expect-error (non strict) - index: undefined as IndexHash, + filteredIndex: undefined as IndexHash, indexError, }, }; @@ -132,7 +132,7 @@ const refsEmpty = { optimized: { ...refs.optimized, // type: 'auto-inject', - index: {} as IndexHash, + filteredIndex: {} as IndexHash, }, }; diff --git a/code/core/src/manager/components/sidebar/Sidebar.tsx b/code/core/src/manager/components/sidebar/Sidebar.tsx index d4784cc8c562..dfec7fba7826 100644 --- a/code/core/src/manager/components/sidebar/Sidebar.tsx +++ b/code/core/src/manager/components/sidebar/Sidebar.tsx @@ -93,6 +93,7 @@ const useCombination = ( () => ({ [DEFAULT_REF_ID]: { index, + filteredIndex: index, indexError, previewInitialized, status, diff --git a/code/core/src/manager/components/sidebar/SidebarBottom.tsx b/code/core/src/manager/components/sidebar/SidebarBottom.tsx index 37a872810a6a..38efdae4f682 100644 --- a/code/core/src/manager/components/sidebar/SidebarBottom.tsx +++ b/code/core/src/manager/components/sidebar/SidebarBottom.tsx @@ -1,7 +1,7 @@ -import React, { Fragment, useEffect, useRef, useState } from 'react'; +import React, { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { styled } from '@storybook/core/theming'; -import { type API_FilterFunction, type API_StatusValue } from '@storybook/core/types'; +import { type API_FilterFunction } from '@storybook/core/types'; import { TESTING_MODULE_CRASH_REPORT, @@ -119,7 +119,8 @@ export const SidebarBottomBase = ({ api.experimental_setFilter('sidebar-bottom-filter', filter); }, [api, hasWarnings, hasErrors, warningsActive, errorsActive]); - useEffect(() => { + // Register listeners before the first render + useLayoutEffect(() => { const onCrashReport = ({ providerId, ...details }: TestingModuleCrashReportPayload) => { api.updateTestProviderState(providerId, { error: { name: 'Crashed!', message: details.error.message }, diff --git a/code/core/src/manager/components/sidebar/useHighlighted.ts b/code/core/src/manager/components/sidebar/useHighlighted.ts index d0f06fc87aa8..37701c7b1eec 100644 --- a/code/core/src/manager/components/sidebar/useHighlighted.ts +++ b/code/core/src/manager/components/sidebar/useHighlighted.ts @@ -22,6 +22,25 @@ export interface HighlightedProps { const fromSelection = (selection: Selection): Highlight => selection ? { itemId: selection.storyId, refId: selection.refId } : null; +const scrollToSelector = ( + selector: string, + options: { + containerRef?: RefObject; + center?: boolean; + attempts?: number; + delay?: number; + } = {}, + _attempt = 1 +) => { + const { containerRef, center = false, attempts = 3, delay = 500 } = options; + const element = (containerRef ? containerRef.current : document)?.querySelector(selector); + if (element) { + scrollIntoView(element, center); + } else if (_attempt <= attempts) { + setTimeout(scrollToSelector, delay, selector, options, _attempt + 1); + } +}; + export const useHighlighted = ({ containerRef, isLoading, @@ -65,14 +84,10 @@ export const useHighlighted = ({ const highlight = fromSelection(selected); updateHighlighted(highlight); if (highlight) { - const { itemId, refId } = highlight; - setTimeout(() => { - scrollIntoView( - // @ts-expect-error (non strict) - containerRef.current?.querySelector(`[data-item-id="${itemId}"][data-ref-id="${refId}"]`), - true // make sure it's clearly visible by centering it - ); - }, 0); + scrollToSelector(`[data-item-id="${highlight.itemId}"][data-ref-id="${highlight.refId}"]`, { + containerRef, + center: true, + }); } }, [containerRef, selected, updateHighlighted]); diff --git a/code/core/src/manager/container/Panel.tsx b/code/core/src/manager/container/Panel.tsx index c81e489d8f68..f8cc2877cef0 100644 --- a/code/core/src/manager/container/Panel.tsx +++ b/code/core/src/manager/container/Panel.tsx @@ -32,6 +32,12 @@ const getPanels = (api: API) => { if (paramKey && parameters && parameters[paramKey] && parameters[paramKey].disable) { return; } + if ( + panel.disabled === true || + (typeof panel.disabled === 'function' && panel.disabled(parameters)) + ) { + return; + } filteredPanels[id] = panel; }); diff --git a/code/core/src/manager/container/Sidebar.tsx b/code/core/src/manager/container/Sidebar.tsx index bc05d1713b59..723d9989ac10 100755 --- a/code/core/src/manager/container/Sidebar.tsx +++ b/code/core/src/manager/container/Sidebar.tsx @@ -27,7 +27,7 @@ const Sidebar = React.memo(function Sideber({ onMenuClick }: SidebarProps) { // is actually the stories hash. We should fix this up and make it consistent. // eslint-disable-next-line @typescript-eslint/naming-convention internal_index, - index, + filteredIndex: index, status, indexError, previewInitialized, diff --git a/code/core/src/manager/utils/tree.ts b/code/core/src/manager/utils/tree.ts index 3002eb97a77f..dfe6dfb4cf24 100644 --- a/code/core/src/manager/utils/tree.ts +++ b/code/core/src/manager/utils/tree.ts @@ -85,10 +85,14 @@ export const scrollIntoView = (element: Element, center = false) => { return; } const { top, bottom } = element.getBoundingClientRect(); - const isInView = - top >= 0 && bottom <= (globalWindow.innerHeight || document.documentElement.clientHeight); - - if (!isInView) { + if (!top || !bottom) { + return; + } + const bottomOffset = + document?.querySelector('#sidebar-bottom-wrapper')?.getBoundingClientRect().top || + globalWindow.innerHeight || + document.documentElement.clientHeight; + if (bottom > bottomOffset) { element.scrollIntoView({ block: center ? 'center' : 'nearest' }); } }; diff --git a/code/core/src/telemetry/exec-command-count-lines.test.ts b/code/core/src/telemetry/exec-command-count-lines.test.ts new file mode 100644 index 000000000000..eacfe9f72952 --- /dev/null +++ b/code/core/src/telemetry/exec-command-count-lines.test.ts @@ -0,0 +1,71 @@ +import type { Transform } from 'node:stream'; +import { PassThrough } from 'node:stream'; + +import { beforeEach, describe, expect, it, vitest } from 'vitest'; + +// eslint-disable-next-line depend/ban-dependencies +import { execaCommand as rawExecaCommand } from 'execa'; + +import { execCommandCountLines } from './exec-command-count-lines'; + +vitest.mock('execa'); + +const execaCommand = vitest.mocked(rawExecaCommand); +beforeEach(() => { + execaCommand.mockReset(); +}); + +type ExecaStreamer = typeof Promise & { + stdout: Transform; + kill: () => void; +}; + +function createExecaStreamer() { + let resolver: () => void; + const promiseLike: ExecaStreamer = new Promise((aResolver, aRejecter) => { + resolver = aResolver; + }) as any; + + promiseLike.stdout = new PassThrough(); + // @ts-expect-error technically it is invalid to use resolver "before" it is assigned (but not really) + promiseLike.kill = resolver; + return promiseLike; +} + +describe('execCommandCountLines', () => { + it('counts lines, many', async () => { + const streamer = createExecaStreamer(); + execaCommand.mockReturnValue(streamer as any); + + const promise = execCommandCountLines('some command'); + + streamer.stdout.write('First line\n'); + streamer.stdout.write('Second line\n'); + streamer.kill(); + + expect(await promise).toEqual(2); + }); + + it('counts lines, one', async () => { + const streamer = createExecaStreamer(); + execaCommand.mockReturnValue(streamer as any); + + const promise = execCommandCountLines('some command'); + + streamer.stdout.write('First line\n'); + streamer.kill(); + + expect(await promise).toEqual(1); + }); + + it('counts lines, none', async () => { + const streamer = createExecaStreamer(); + execaCommand.mockReturnValue(streamer as any); + + const promise = execCommandCountLines('some command'); + + streamer.kill(); + + expect(await promise).toEqual(0); + }); +}); diff --git a/code/core/src/telemetry/exec-command-count-lines.ts b/code/core/src/telemetry/exec-command-count-lines.ts new file mode 100644 index 000000000000..fdc4547ce464 --- /dev/null +++ b/code/core/src/telemetry/exec-command-count-lines.ts @@ -0,0 +1,35 @@ +import { createInterface } from 'node:readline'; + +// eslint-disable-next-line depend/ban-dependencies +import { execaCommand } from 'execa'; + +/** + * Execute a command in the local terminal and count the lines in the result + * + * @param command The command to execute. + * @param options Execa options + * @returns The number of lines the command returned + */ +export async function execCommandCountLines( + command: string, + options?: Parameters[1] +) { + const process = execaCommand(command, { shell: true, buffer: false, ...options }); + if (!process.stdout) { + // eslint-disable-next-line local-rules/no-uncategorized-errors + throw new Error('Unexpected missing stdout'); + } + + let lineCount = 0; + const rl = createInterface(process.stdout); + rl.on('line', () => { + lineCount += 1; + }); + + // If the process errors, this will throw + await process; + + rl.close(); + + return lineCount; +} diff --git a/code/core/src/telemetry/get-application-file-count.test.ts b/code/core/src/telemetry/get-application-file-count.test.ts new file mode 100644 index 000000000000..7fc570689147 --- /dev/null +++ b/code/core/src/telemetry/get-application-file-count.test.ts @@ -0,0 +1,14 @@ +import { join } from 'node:path'; + +import { describe, expect, it } from 'vitest'; + +import { getApplicationFilesCountUncached } from './get-application-file-count'; + +const mocksDir = join(__dirname, '..', '__mocks__'); + +describe('getApplicationFilesCount', () => { + it('should find files with correct names', async () => { + const files = await getApplicationFilesCountUncached(mocksDir); + expect(files).toMatchInlineSnapshot(`2`); + }); +}); diff --git a/code/core/src/telemetry/get-application-file-count.ts b/code/core/src/telemetry/get-application-file-count.ts new file mode 100644 index 000000000000..4f4807ddff00 --- /dev/null +++ b/code/core/src/telemetry/get-application-file-count.ts @@ -0,0 +1,32 @@ +import { sep } from 'node:path'; + +import { execCommandCountLines } from './exec-command-count-lines'; +import { runTelemetryOperation } from './run-telemetry-operation'; + +// We are looking for files with the word "page" or "screen" somewhere in them with these exts +const nameMatches = ['page', 'screen']; +const extensions = ['js', 'jsx', 'ts', 'tsx']; + +export const getApplicationFilesCountUncached = async (basePath: string) => { + const bothCasesNameMatches = nameMatches.flatMap((match) => [ + match, + [match[0].toUpperCase(), ...match.slice(1)].join(''), + ]); + + const globs = bothCasesNameMatches.flatMap((match) => + extensions.map((extension) => `"${basePath}${sep}*${match}*.${extension}"`) + ); + + try { + const command = `git ls-files -- ${globs.join(' ')}`; + return await execCommandCountLines(command); + } catch { + return undefined; + } +}; + +export const getApplicationFileCount = async (path: string) => { + return runTelemetryOperation('applicationFiles', async () => + getApplicationFilesCountUncached(path) + ); +}; diff --git a/code/core/src/telemetry/get-has-router-package.test.ts b/code/core/src/telemetry/get-has-router-package.test.ts new file mode 100644 index 000000000000..8504a5bc4d84 --- /dev/null +++ b/code/core/src/telemetry/get-has-router-package.test.ts @@ -0,0 +1,29 @@ +import { expect, it } from 'vitest'; + +import { getHasRouterPackage } from './get-has-router-package'; + +it('returns true if there is a routing package in package.json', () => { + expect( + getHasRouterPackage({ + dependencies: { + react: '^18', + 'react-dom': '^18', + 'react-router': '^6', + }, + }) + ).toBe(true); +}); + +it('returns false if there is a routing package in package.json dependencies', () => { + expect( + getHasRouterPackage({ + dependencies: { + react: '^18', + 'react-dom': '^18', + }, + devDependencies: { + 'react-router': '^6', + }, + }) + ).toBe(false); +}); diff --git a/code/core/src/telemetry/get-has-router-package.ts b/code/core/src/telemetry/get-has-router-package.ts new file mode 100644 index 000000000000..5873c3832d25 --- /dev/null +++ b/code/core/src/telemetry/get-has-router-package.ts @@ -0,0 +1,37 @@ +import type { PackageJson } from '../types'; + +const routerPackages = new Set([ + 'react-router', + 'react-router-dom', + 'remix', + '@tanstack/react-router', + 'expo-router', + '@reach/router', + 'react-easy-router', + '@remix-run/router', + 'wouter', + 'wouter-preact', + 'preact-router', + 'vue-router', + 'unplugin-vue-router', + '@angular/router', + '@solidjs/router', + + // metaframeworks that imply routing + 'next', + 'react-scripts', + 'gatsby', + 'nuxt', + '@sveltejs/kit', +]); + +/** + * @param packageJson The package JSON of the project + * @returns Boolean Does this project use a routing package? + */ +export function getHasRouterPackage(packageJson: PackageJson) { + // NOTE: we just check real dependencies; if it is in dev dependencies, it may just be an example + return Object.keys(packageJson?.dependencies ?? {}).some((depName) => + routerPackages.has(depName) + ); +} diff --git a/code/core/src/telemetry/get-portable-stories-usage.ts b/code/core/src/telemetry/get-portable-stories-usage.ts index cd9da7f4f589..0831b484ab69 100644 --- a/code/core/src/telemetry/get-portable-stories-usage.ts +++ b/code/core/src/telemetry/get-portable-stories-usage.ts @@ -1,37 +1,18 @@ -// eslint-disable-next-line depend/ban-dependencies -import { execaCommand } from 'execa'; - -import { createFileSystemCache, resolvePathInStorybookCache } from '../common'; - -const cache = createFileSystemCache({ - basePath: resolvePathInStorybookCache('portable-stories'), - ns: 'storybook', - ttl: 24 * 60 * 60 * 1000, // 24h -}); +import { execCommandCountLines } from './exec-command-count-lines'; +import { runTelemetryOperation } from './run-telemetry-operation'; export const getPortableStoriesFileCountUncached = async (path?: string) => { - const command = `git grep -l composeStor` + (path ? ` -- ${path}` : ''); - const { stdout } = await execaCommand(command, { - cwd: process.cwd(), - shell: true, - }); - - return stdout.split('\n').filter(Boolean).length; + try { + const command = `git grep -l composeStor` + (path ? ` -- ${path}` : ''); + return await execCommandCountLines(command); + } catch (err: any) { + // exit code 1 if no matches are found + return err.exitCode === 1 ? 0 : undefined; + } }; -const CACHE_KEY = 'portableStories'; export const getPortableStoriesFileCount = async (path?: string) => { - let cached = await cache.get(CACHE_KEY); - if (!cached) { - try { - const count = await getPortableStoriesFileCountUncached(); - cached = { count }; - await cache.set(CACHE_KEY, cached); - } catch (err: any) { - // exit code 1 if no matches are found - const count = err.exitCode === 1 ? 0 : null; - cached = { count }; - } - } - return cached.count; + return runTelemetryOperation('portableStories', async () => + getPortableStoriesFileCountUncached(path) + ); }; diff --git a/code/core/src/telemetry/run-telemetry-operation.ts b/code/core/src/telemetry/run-telemetry-operation.ts new file mode 100644 index 000000000000..29d2aee6c721 --- /dev/null +++ b/code/core/src/telemetry/run-telemetry-operation.ts @@ -0,0 +1,25 @@ +import { createFileSystemCache, resolvePathInStorybookCache } from '../common'; + +const cache = createFileSystemCache({ + basePath: resolvePathInStorybookCache('telemetry'), + ns: 'storybook', + ttl: 24 * 60 * 60 * 1000, // 24h +}); + +/** + * Run an (expensive) operation, caching the result in a FS cache for 24 hours. + * + * NOTE: if the operation returns `undefined` the value will not be cached. Use this to indicate + * that the operation failed. + */ +export const runTelemetryOperation = async (cacheKey: string, operation: () => Promise) => { + let cached = await cache.get(cacheKey); + if (cached === undefined) { + cached = await operation(); + // Undefined indicates an error, setting isn't really valuable. + if (cached !== undefined) { + await cache.set(cacheKey, cached); + } + } + return cached; +}; diff --git a/code/core/src/telemetry/storybook-metadata.test.ts b/code/core/src/telemetry/storybook-metadata.test.ts index 06bf355c0688..8d73ff7b437d 100644 --- a/code/core/src/telemetry/storybook-metadata.test.ts +++ b/code/core/src/telemetry/storybook-metadata.test.ts @@ -12,6 +12,8 @@ const packageJsonMock: PackageJson = { version: 'x.x.x', }; +const packageJsonPath = process.cwd(); + const mainJsMock: StorybookConfig = { stories: [], }; @@ -126,6 +128,7 @@ describe('storybook-metadata', () => { it('should parse pnp paths for known frameworks', async () => { const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -144,6 +147,7 @@ describe('storybook-metadata', () => { const windowsResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -164,6 +168,7 @@ describe('storybook-metadata', () => { it('should parse pnp paths for unknown frameworks', async () => { const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -178,6 +183,7 @@ describe('storybook-metadata', () => { const windowsResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -198,6 +204,7 @@ describe('storybook-metadata', () => { const unixResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -215,6 +222,7 @@ describe('storybook-metadata', () => { cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue('C:\\Users\\foo\\my-project'); const windowsResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -232,6 +240,7 @@ describe('storybook-metadata', () => { it('should return frameworkOptions from mainjs', async () => { const reactResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -250,6 +259,7 @@ describe('storybook-metadata', () => { const angularResult = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: { @@ -279,6 +289,7 @@ describe('storybook-metadata', () => { 'storybook-addon-deprecated': 'x.x.z', }, } as PackageJson, + packageJsonPath, mainConfig: { ...mainJsMock, addons: [ @@ -319,6 +330,7 @@ describe('storybook-metadata', () => { const result = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, features, @@ -332,6 +344,7 @@ describe('storybook-metadata', () => { expect( await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, framework: '@storybook/react-vite', @@ -347,6 +360,7 @@ describe('storybook-metadata', () => { it('should return the number of refs', async () => { const res = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, refs: { @@ -361,6 +375,7 @@ describe('storybook-metadata', () => { it('only reports addon options for addon-essentials', async () => { const res = await computeStorybookMetadata({ packageJson: packageJsonMock, + packageJsonPath, mainConfig: { ...mainJsMock, addons: [ @@ -395,6 +410,7 @@ describe('storybook-metadata', () => { [metaFramework]: 'x.x.x', }, } as PackageJson, + packageJsonPath, mainConfig: mainJsMock, }); expect(res.metaFramework).toEqual({ diff --git a/code/core/src/telemetry/storybook-metadata.ts b/code/core/src/telemetry/storybook-metadata.ts index 75804b813fc2..5258eef0ffb7 100644 --- a/code/core/src/telemetry/storybook-metadata.ts +++ b/code/core/src/telemetry/storybook-metadata.ts @@ -1,3 +1,5 @@ +import { dirname } from 'node:path'; + import { getProjectRoot, getStorybookConfiguration, @@ -9,10 +11,12 @@ import type { PackageJson, StorybookConfig } from '@storybook/core/types'; import { readConfig } from '@storybook/core/csf-tools'; import { detect, getNpmVersion } from 'detect-package-manager'; -import { findPackage } from 'fd-package-json'; +import { findPackage, findPackagePath } from 'fd-package-json'; +import { getApplicationFileCount } from './get-application-file-count'; import { getChromaticVersionSpecifier } from './get-chromatic-version'; import { getFrameworkInfo } from './get-framework-info'; +import { getHasRouterPackage } from './get-has-router-package'; import { getMonorepoType } from './get-monorepo-type'; import { getPortableStoriesFileCount } from './get-portable-stories-usage'; import { getActualPackageVersion, getActualPackageVersions } from './package-json'; @@ -41,9 +45,11 @@ export const sanitizeAddonName = (name: string) => { // Analyze a combination of information from main.js and package.json // to provide telemetry over a Storybook project export const computeStorybookMetadata = async ({ + packageJsonPath, packageJson, mainConfig, }: { + packageJsonPath: string; packageJson: PackageJson; mainConfig: StorybookConfig & Record; }): Promise => { @@ -100,6 +106,8 @@ export const computeStorybookMetadata = async ({ ) ); + metadata.hasRouterPackage = getHasRouterPackage(packageJson); + const monorepoType = getMonorepoType(); if (monorepoType) { metadata.monorepo = monorepoType; @@ -209,11 +217,13 @@ export const computeStorybookMetadata = async ({ const storybookVersion = storybookPackages[storybookInfo.frameworkPackage]?.version; const portableStoriesFileCount = await getPortableStoriesFileCount(); + const applicationFileCount = await getApplicationFileCount(dirname(packageJsonPath)); return { ...metadata, ...frameworkInfo, portableStoriesFileCount, + applicationFileCount, storybookVersion, storybookVersionSpecifier: storybookInfo.version, language, @@ -223,13 +233,29 @@ export const computeStorybookMetadata = async ({ }; }; +async function getPackageJsonDetails() { + const packageJsonPath = await findPackagePath(process.cwd()); + if (packageJsonPath) { + return { + packageJsonPath, + packageJson: (await findPackage(packageJsonPath)) || {}, + }; + } + + // If we don't find a `package.json`, we assume it "would have" been in the current working directory + return { + packageJsonPath: process.cwd(), + packageJson: {}, + }; +} + let cachedMetadata: StorybookMetadata; export const getStorybookMetadata = async (_configDir?: string) => { if (cachedMetadata) { return cachedMetadata; } - const packageJson = (await findPackage(process.cwd())) || {}; + const { packageJson, packageJsonPath } = await getPackageJsonDetails(); // TODO: improve the way configDir is extracted, as a "storybook" script might not be present // Scenarios: // 1. user changed it to something else e.g. "storybook:dev" @@ -243,6 +269,6 @@ export const getStorybookMetadata = async (_configDir?: string) => { ) as string)) ?? '.storybook'; const mainConfig = await loadMainConfig({ configDir }); - cachedMetadata = await computeStorybookMetadata({ mainConfig, packageJson }); + cachedMetadata = await computeStorybookMetadata({ mainConfig, packageJson, packageJsonPath }); return cachedMetadata; }; diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index 757f5afc197e..e43373e5e61b 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -59,6 +59,7 @@ export type StorybookMetadata = { version: string; }; testPackages?: Record; + hasRouterPackage?: boolean; hasStorybookEslint?: boolean; hasStaticDirs?: boolean; hasCustomWebpack?: boolean; @@ -69,6 +70,7 @@ export type StorybookMetadata = { usesGlobals?: boolean; }; portableStoriesFileCount?: number; + applicationFileCount?: number; }; export interface Payload { diff --git a/code/core/src/types/modules/addons.ts b/code/core/src/types/modules/addons.ts index 47f5aec6412b..b2c7b4ac2f3c 100644 --- a/code/core/src/types/modules/addons.ts +++ b/code/core/src/types/modules/addons.ts @@ -5,7 +5,7 @@ import type { TestProviderConfig, TestingModuleProgressReportProgress } from '.. import type { RenderData as RouterData } from '../../router/types'; import type { ThemeVars } from '../../theming/types'; import type { API_SidebarOptions } from './api'; -import type { API_HashEntry, API_StatusState, API_StatusUpdate } from './api-stories'; +import type { API_HashEntry, API_StoryEntry } from './api-stories'; import type { Args, ArgsStoryFn as ArgsStoryFnForFramework, @@ -392,7 +392,7 @@ export interface Addon_BaseType { /** @unstable */ paramKey?: string; /** @unstable */ - disabled?: boolean; + disabled?: boolean | ((parameters: API_StoryEntry['parameters']) => boolean); /** @unstable */ hidden?: boolean; } diff --git a/code/core/src/types/modules/api.ts b/code/core/src/types/modules/api.ts index 30edbe36ad29..f5e2d6f32100 100644 --- a/code/core/src/types/modules/api.ts +++ b/code/core/src/types/modules/api.ts @@ -155,6 +155,7 @@ export type API_StoryMapper = (ref: API_ComposedRef, story: SetStoriesStory) => export interface API_LoadedRefData { index?: API_IndexHash; + filteredIndex?: API_IndexHash; indexError?: Error; previewInitialized: boolean; } @@ -180,6 +181,7 @@ export type API_ComposedRefUpdate = Partial< | 'type' | 'expanded' | 'index' + | 'filteredIndex' | 'versions' | 'loginUrl' | 'version' diff --git a/code/deprecated/builder-manager/package.json b/code/deprecated/builder-manager/package.json index a331b3fa6bb3..9998fd59a573 100644 --- a/code/deprecated/builder-manager/package.json +++ b/code/deprecated/builder-manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-manager", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook manager builder", "keywords": [ "storybook" diff --git a/code/deprecated/channels/package.json b/code/deprecated/channels/package.json index a4f681e62e04..988ed72abb2c 100644 --- a/code/deprecated/channels/package.json +++ b/code/deprecated/channels/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channels", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/client-logger/package.json b/code/deprecated/client-logger/package.json index 86f72277da7d..f4de8eedbefa 100644 --- a/code/deprecated/client-logger/package.json +++ b/code/deprecated/client-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/client-logger", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/components/package.json b/code/deprecated/components/package.json index 4f2752072b53..28de35eec46a 100644 --- a/code/deprecated/components/package.json +++ b/code/deprecated/components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/components", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/deprecated/core-common/package.json b/code/deprecated/core-common/package.json index 6dfb83618c4f..372be0dfea26 100644 --- a/code/deprecated/core-common/package.json +++ b/code/deprecated/core-common/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-common", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/deprecated/core-events/package.json b/code/deprecated/core-events/package.json index 20dff0d9ce2d..bcf2157df22c 100644 --- a/code/deprecated/core-events/package.json +++ b/code/deprecated/core-events/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-events", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Event names used in storybook core", "keywords": [ "storybook" diff --git a/code/deprecated/core-server/package.json b/code/deprecated/core-server/package.json index 6b77123b1c36..9cf49f637df2 100644 --- a/code/deprecated/core-server/package.json +++ b/code/deprecated/core-server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-server", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/deprecated/csf-tools/package.json b/code/deprecated/csf-tools/package.json index 74be5d5e9eb1..3c0aa71c00c3 100644 --- a/code/deprecated/csf-tools/package.json +++ b/code/deprecated/csf-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-tools", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Parse and manipulate CSF and Storybook config files", "keywords": [ "storybook" diff --git a/code/deprecated/docs-tools/package.json b/code/deprecated/docs-tools/package.json index fb5a9c6bc527..002c500c1cb0 100644 --- a/code/deprecated/docs-tools/package.json +++ b/code/deprecated/docs-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/docs-tools", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Shared utility functions for frameworks to implement docs", "keywords": [ "storybook" diff --git a/code/deprecated/manager-api/package.json b/code/deprecated/manager-api/package.json index 4afb8ef29a3e..a203750e5675 100644 --- a/code/deprecated/manager-api/package.json +++ b/code/deprecated/manager-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager-api", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook Manager API & Context", "keywords": [ "storybook" diff --git a/code/deprecated/manager/package.json b/code/deprecated/manager/package.json index a3890fa1e4f7..b8ad3dc9d18b 100644 --- a/code/deprecated/manager/package.json +++ b/code/deprecated/manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook UI", "keywords": [ "storybook" diff --git a/code/deprecated/node-logger/package.json b/code/deprecated/node-logger/package.json index 7afefb239034..9b854e1e73d8 100644 --- a/code/deprecated/node-logger/package.json +++ b/code/deprecated/node-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/node-logger", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/preview-api/package.json b/code/deprecated/preview-api/package.json index dbd86a65cc29..682e0a595d1f 100644 --- a/code/deprecated/preview-api/package.json +++ b/code/deprecated/preview-api/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview-api", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/preview/package.json b/code/deprecated/preview/package.json index c7e152ac19b8..91cad5bc2737 100644 --- a/code/deprecated/preview/package.json +++ b/code/deprecated/preview/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preview", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/deprecated/router/package.json b/code/deprecated/router/package.json index bb6305160f05..f5c22b5ef835 100644 --- a/code/deprecated/router/package.json +++ b/code/deprecated/router/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/router", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook Router", "keywords": [ "storybook" diff --git a/code/deprecated/telemetry/package.json b/code/deprecated/telemetry/package.json index 6a54c1ff5729..d5b569580464 100644 --- a/code/deprecated/telemetry/package.json +++ b/code/deprecated/telemetry/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/telemetry", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Telemetry logging for crash reports and usage statistics", "keywords": [ "storybook" diff --git a/code/deprecated/theming/package.json b/code/deprecated/theming/package.json index fce7c7f67bdb..e7078e2c55f3 100644 --- a/code/deprecated/theming/package.json +++ b/code/deprecated/theming/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/theming", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook Components", "keywords": [ "storybook" diff --git a/code/deprecated/types/package.json b/code/deprecated/types/package.json index 1e6eb337de10..0bbcb006eae1 100644 --- a/code/deprecated/types/package.json +++ b/code/deprecated/types/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/types", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Core Storybook TS Types", "keywords": [ "storybook" diff --git a/code/e2e-tests/composition.spec.ts b/code/e2e-tests/composition.spec.ts index 15bc6c4c4ed9..ec5a96e5898d 100644 --- a/code/e2e-tests/composition.spec.ts +++ b/code/e2e-tests/composition.spec.ts @@ -27,8 +27,12 @@ test.describe('composition', () => { await page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]').click(); await expect( - page.locator('[id="storybook\\@7\\.6\\.18_components-badge--default"]') - ).toBeVisible(); + page + .locator('iframe[title="storybook-ref-storybook\\@7\\.6\\.18"]') + .contentFrame() + .locator('#storybook-root') + .getByText('Default') + ).toBeVisible({ timeout: 15000 }); // Expect composed stories `to be available in the search await page.getByPlaceholder('Find components').fill('Button'); diff --git a/code/e2e-tests/preview-api.spec.ts b/code/e2e-tests/preview-api.spec.ts index cfabaf89674c..31b566027fd9 100644 --- a/code/e2e-tests/preview-api.spec.ts +++ b/code/e2e-tests/preview-api.spec.ts @@ -65,6 +65,7 @@ test.describe('preview-api', () => { const root = sbPage.previewRoot(); + await sbPage.viewAddonPanel('Controls'); const labelControl = sbPage.page.locator('#control-label'); await expect(root.getByText('Loaded. Click me')).toBeVisible(); diff --git a/code/e2e-tests/util.ts b/code/e2e-tests/util.ts index 057dc91f715d..d9faad22939a 100644 --- a/code/e2e-tests/util.ts +++ b/code/e2e-tests/util.ts @@ -1,3 +1,4 @@ +/* eslint-disable local-rules/no-uncategorized-errors */ import { toId } from '@storybook/csf'; import type { Expect, Page } from '@playwright/test'; @@ -61,6 +62,7 @@ export class SbPage { await this.expect(selected).toHaveAttribute('data-selected', 'true'); await this.previewRoot(); + await this.waitUntilLoaded(); } async navigateToUnattachedDocs(title: string, name = 'docs') { @@ -80,7 +82,25 @@ export class SbPage { const selected = storyLink; await this.expect(selected).toHaveAttribute('data-selected', 'true'); - await this.previewRoot(); + await this.waitForStoryLoaded(); + } + + async waitForStoryLoaded() { + try { + const root = this.previewRoot(); + // Wait until there is at least one child (a story element) in the preview iframe + await root.locator(':scope > *').first().waitFor({ + state: 'attached', + timeout: 10000, + }); + } catch (error: any) { + if (error.name === 'TimeoutError') { + throw new Error( + 'The Storybook iframe did not have children within the specified timeout. Did the story load correctly?' + ); + } + throw error; + } } async waitUntilLoaded() { @@ -112,6 +132,8 @@ export class SbPage { const storyLoadingPage = root.locator('.sb-preparing-story'); await docsLoadingPage.waitFor({ state: 'hidden' }); await storyLoadingPage.waitFor({ state: 'hidden' }); + + await this.waitForStoryLoaded(); } previewIframe() { @@ -143,6 +165,30 @@ export class SbPage { getCanvasBodyElement() { return this.previewIframe().locator('body'); } + + // utility to try and decrease flake + async retryTimes( + fn: () => Promise, + options?: { + retries?: number; + delay?: number; + } + ): Promise { + let attempts = 0; + const { retries = 3, delay = 0 } = options || {}; + while (attempts < retries) { + try { + await fn(); + return; + } catch (error) { + attempts++; + if (attempts === retries) { + throw error; + } + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + } } const templateName: keyof typeof allTemplates = process.env.STORYBOOK_TEMPLATE_NAME || ('' as any); diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 7308e70929ef..576788920f79 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/angular", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Angular: Develop Angular components in isolation with hot reloading.", "keywords": [ "storybook", diff --git a/code/frameworks/angular/src/builders/build-storybook/schema.json b/code/frameworks/angular/src/builders/build-storybook/schema.json index 6d976bf5dd0d..77e455cdb1be 100644 --- a/code/frameworks/angular/src/builders/build-storybook/schema.json +++ b/code/frameworks/angular/src/builders/build-storybook/schema.json @@ -77,6 +77,11 @@ "description": "Write Webpack Stats JSON to disk", "default": false }, + "statsJson": { + "type": ["boolean", "string"], + "description": "Write stats JSON to disk", + "default": false + }, "previewUrl": { "type": "string", "description": "Disables the default storybook preview and lets you use your own" diff --git a/code/frameworks/angular/src/builders/start-storybook/schema.json b/code/frameworks/angular/src/builders/start-storybook/schema.json index 729c3ea5b724..64d53bd5481e 100644 --- a/code/frameworks/angular/src/builders/start-storybook/schema.json +++ b/code/frameworks/angular/src/builders/start-storybook/schema.json @@ -139,6 +139,11 @@ "description": "Write Webpack Stats JSON to disk", "default": false }, + "statsJson": { + "type": ["boolean", "string"], + "description": "Write stats JSON to disk", + "default": false + }, "previewUrl": { "type": "string", "description": "Disables the default storybook preview and lets you use your own" diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json index 96f7f5cac31c..a397b8c1d5a6 100644 --- a/code/frameworks/ember/package.json +++ b/code/frameworks/ember/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/ember", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember", "bugs": { diff --git a/code/frameworks/experimental-nextjs-vite/package.json b/code/frameworks/experimental-nextjs-vite/package.json index 6eafe4815adc..daf389813ec1 100644 --- a/code/frameworks/experimental-nextjs-vite/package.json +++ b/code/frameworks/experimental-nextjs-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/experimental-nextjs-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Next.js and Vite", "keywords": [ "storybook", @@ -97,6 +97,7 @@ "dependencies": { "@storybook/builder-vite": "workspace:*", "@storybook/react": "workspace:*", + "@storybook/react-vite": "workspace:*", "@storybook/test": "workspace:*", "styled-jsx": "5.1.6", "vite-plugin-storybook-nextjs": "^1.1.0" diff --git a/code/frameworks/experimental-nextjs-vite/src/preset.ts b/code/frameworks/experimental-nextjs-vite/src/preset.ts index 0a725be35804..633f62a5dceb 100644 --- a/code/frameworks/experimental-nextjs-vite/src/preset.ts +++ b/code/frameworks/experimental-nextjs-vite/src/preset.ts @@ -4,6 +4,7 @@ import path from 'node:path'; import type { PresetProperty } from 'storybook/internal/types'; import type { StorybookConfigVite } from '@storybook/builder-vite'; +import { viteFinal as reactViteFinal } from '@storybook/react-vite/preset'; import { dirname, join } from 'path'; import vitePluginStorybookNextjs from 'vite-plugin-storybook-nextjs'; @@ -34,11 +35,13 @@ export const previewAnnotations: PresetProperty<'previewAnnotations'> = (entry = }; export const viteFinal: StorybookConfigVite['viteFinal'] = async (config, options) => { - config.plugins = config.plugins || []; + const reactConfig = await reactViteFinal(config, options); + const { plugins = [] } = reactConfig; + const { nextConfigPath } = await options.presets.apply('frameworkOptions'); const nextDir = nextConfigPath ? path.dirname(nextConfigPath) : undefined; - config.plugins.push(vitePluginStorybookNextjs({ dir: nextDir })); + plugins.push(vitePluginStorybookNextjs({ dir: nextDir })); - return config; + return reactConfig; }; diff --git a/code/frameworks/experimental-nextjs-vite/src/routing/app-router-provider.tsx b/code/frameworks/experimental-nextjs-vite/src/routing/app-router-provider.tsx index 68e01381948c..eec03995d266 100644 --- a/code/frameworks/experimental-nextjs-vite/src/routing/app-router-provider.tsx +++ b/code/frameworks/experimental-nextjs-vite/src/routing/app-router-provider.tsx @@ -44,8 +44,6 @@ function getSelectedParams(currentTree: FlightRouterState, params: Params = {}): continue; } - // Ensure catchAll and optional catchall are turned into an array - // Ensure catchAll and optional catchall are turned into an array const isCatchAll = isDynamicParameter && (segment[2] === 'c' || segment[2] === 'oc'); @@ -82,6 +80,16 @@ export const AppRouterProvider: React.FC @@ -106,10 +114,18 @@ export const AppRouterProvider: React.FC = v15.1.1 + parentTree: tree, + parentCacheNode: newLazyCacheNode, + // END url: pathname, + loading: null, }} > {children} diff --git a/code/frameworks/experimental-nextjs-vite/src/types.ts b/code/frameworks/experimental-nextjs-vite/src/types.ts index 8de91a4430d9..0221787dccb6 100644 --- a/code/frameworks/experimental-nextjs-vite/src/types.ts +++ b/code/frameworks/experimental-nextjs-vite/src/types.ts @@ -1,9 +1,7 @@ -import type { - CompatibleString, - StorybookConfig as StorybookConfigBase, -} from 'storybook/internal/types'; +import type { CompatibleString } from 'storybook/internal/types'; -import type { BuilderOptions, StorybookConfigVite } from '@storybook/builder-vite'; +import type { BuilderOptions } from '@storybook/builder-vite'; +import type { StorybookConfig as StorybookConfigReactVite } from '@storybook/react-vite'; type FrameworkName = CompatibleString<'@storybook/experimental-nextjs-vite'>; type BuilderName = CompatibleString<'@storybook/builder-vite'>; @@ -21,7 +19,7 @@ type StorybookConfigFramework = { name: FrameworkName; options: FrameworkOptions; }; - core?: StorybookConfigBase['core'] & { + core?: StorybookConfigReactVite['core'] & { builder?: | BuilderName | { @@ -32,9 +30,5 @@ type StorybookConfigFramework = { }; /** The interface for Storybook configuration in `main.ts` files. */ -export type StorybookConfig = Omit< - StorybookConfigBase, - keyof StorybookConfigVite | keyof StorybookConfigFramework -> & - StorybookConfigVite & - StorybookConfigFramework & {}; +export type StorybookConfig = Omit & + StorybookConfigFramework; diff --git a/code/frameworks/html-vite/package.json b/code/frameworks/html-vite/package.json index 83c716b1076e..d60457db363a 100644 --- a/code/frameworks/html-vite/package.json +++ b/code/frameworks/html-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for HTML and Vite: Develop HTML in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json index a0709bee9e39..754cc9019674 100644 --- a/code/frameworks/html-webpack5/package.json +++ b/code/frameworks/html-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 4e76bf20686d..87a35ff52905 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/nextjs", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Next.js", "keywords": [ "storybook", diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts index 76edac25c81c..4b10922c9df9 100644 --- a/code/frameworks/nextjs/src/config/webpack.ts +++ b/code/frameworks/nextjs/src/config/webpack.ts @@ -1,8 +1,7 @@ import type { NextConfig } from 'next'; import type { Configuration as WebpackConfig } from 'webpack'; -import { DefinePlugin } from 'webpack'; -import { addScopedAlias, resolveNextConfig, setAlias } from '../utils'; +import { addScopedAlias, resolveNextConfig } from '../utils'; const tryResolve = (path: string) => { try { @@ -48,12 +47,15 @@ export const configureConfig = async ({ addScopedAlias(baseConfig, 'react-dom/server', 'next/dist/compiled/react-dom/server'); } - setupRuntimeConfig(baseConfig, nextConfig); + await setupRuntimeConfig(baseConfig, nextConfig); return nextConfig; }; -const setupRuntimeConfig = (baseConfig: WebpackConfig, nextConfig: NextConfig): void => { +const setupRuntimeConfig = async ( + baseConfig: WebpackConfig, + nextConfig: NextConfig +): Promise => { const definePluginConfig: Record = { // this mimics what nextjs does client side // https://github.com/vercel/next.js/blob/57702cb2a9a9dba4b552e0007c16449cf36cfb44/packages/next/client/index.tsx#L101 @@ -67,5 +69,7 @@ const setupRuntimeConfig = (baseConfig: WebpackConfig, nextConfig: NextConfig): definePluginConfig['process.env.__NEXT_NEW_LINK_BEHAVIOR'] = newNextLinkBehavior; - baseConfig.plugins?.push(new DefinePlugin(definePluginConfig)); + // Load DefinePlugin with a dynamic import to ensure that Next.js can first + // replace webpack with its own internal instance, and we get that here. + baseConfig.plugins?.push(new (await import('webpack')).default.DefinePlugin(definePluginConfig)); }; diff --git a/code/frameworks/nextjs/src/preset.ts b/code/frameworks/nextjs/src/preset.ts index 3463910175e1..6b90ae543a22 100644 --- a/code/frameworks/nextjs/src/preset.ts +++ b/code/frameworks/nextjs/src/preset.ts @@ -10,31 +10,29 @@ import type { ConfigItem, PluginItem, TransformOptions } from '@babel/core'; import { loadPartialConfig } from '@babel/core'; import semver from 'semver'; -import { configureAliases } from './aliases/webpack'; -import { configureBabelLoader } from './babel/loader'; import nextBabelPreset from './babel/preset'; -import { configureCompatibilityAliases } from './compatibility/compatibility-map'; import { configureConfig } from './config/webpack'; -import { configureCss } from './css/webpack'; -import { configureNextExportMocks } from './export-mocks/webpack'; -import { configureFastRefresh } from './fastRefresh/webpack'; import TransformFontImports from './font/babel'; -import { configureNextFont } from './font/webpack/configureNextFont'; -import { configureImages } from './images/webpack'; -import { configureImports } from './imports/webpack'; -import { configureNodePolyfills } from './nodePolyfills/webpack'; -import { configureRSC } from './rsc/webpack'; -import { configureStyledJsx } from './styledJsx/webpack'; -import { configureSWCLoader } from './swc/loader'; import type { FrameworkOptions, StorybookConfig } from './types'; -import { configureRuntimeNextjsVersionResolution, getNextjsVersion } from './utils'; export const addons: PresetProperty<'addons'> = [ dirname(require.resolve(join('@storybook/preset-react-webpack', 'package.json'))), ]; export const core: PresetProperty<'core'> = async (config, options) => { - const framework = await options.presets.apply('framework'); + const framework = await options.presets.apply('framework'); + + // Load the Next.js configuration before we need it in webpackFinal (below). + // This gives Next.js an opportunity to override some of webpack's internals + // (see next/dist/server/config-utils.js) before @storybook/builder-webpack5 + // starts to use it. Without this, webpack's file system cache (fsCache: true) + // does not work. + await configureConfig({ + // Pass in a dummy webpack config object for now, since we don't want to + // modify the real one yet. We pass in the real one in webpackFinal. + baseConfig: {}, + nextConfigPath: typeof framework === 'string' ? undefined : framework.options.nextConfigPath, + }); return { ...config, @@ -144,6 +142,22 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (baseConfig, nextConfigPath, }); + // Use dynamic imports to ensure these modules that use webpack load after + // Next.js has been configured (above), and has replaced webpack with its precompiled + // version. + const { configureNextFont } = await import('./font/webpack/configureNextFont'); + const { configureRuntimeNextjsVersionResolution, getNextjsVersion } = await import('./utils'); + const { configureImports } = await import('./imports/webpack'); + const { configureCss } = await import('./css/webpack'); + const { configureImages } = await import('./images/webpack'); + const { configureStyledJsx } = await import('./styledJsx/webpack'); + const { configureNodePolyfills } = await import('./nodePolyfills/webpack'); + const { configureAliases } = await import('./aliases/webpack'); + const { configureFastRefresh } = await import('./fastRefresh/webpack'); + const { configureRSC } = await import('./rsc/webpack'); + const { configureSWCLoader } = await import('./swc/loader'); + const { configureBabelLoader } = await import('./babel/loader'); + const babelRCPath = join(getProjectRoot(), '.babelrc'); const babelConfigPath = join(getProjectRoot(), 'babel.config.js'); const hasBabelConfig = existsSync(babelRCPath) || existsSync(babelConfigPath); diff --git a/code/frameworks/nextjs/src/routing/app-router-provider.tsx b/code/frameworks/nextjs/src/routing/app-router-provider.tsx index 36e980b1a5fe..3f06a6668648 100644 --- a/code/frameworks/nextjs/src/routing/app-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/app-router-provider.tsx @@ -80,6 +80,16 @@ export const AppRouterProvider: React.FC @@ -104,8 +114,16 @@ export const AppRouterProvider: React.FC = v15.1.1 + parentTree: tree, + parentCacheNode: newLazyCacheNode, + // END url: pathname, loading: null, }} diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json index 681d0ec88df4..4fc5571a5f1b 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Preact and Vite: Develop Preact components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json index a26425c1e6ca..c1404318b69e 100644 --- a/code/frameworks/preact-webpack5/package.json +++ b/code/frameworks/preact-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/frameworks/react-native-web-vite/package.json b/code/frameworks/react-native-web-vite/package.json index 6a777924f4c0..147f1c243bed 100644 --- a/code/frameworks/react-native-web-vite/package.json +++ b/code/frameworks/react-native-web-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-native-web-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Develop react-native components an isolated web environment with hot reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/react-native-web-vite/src/preset.ts b/code/frameworks/react-native-web-vite/src/preset.ts index 3ccd4240bf65..44e847171dda 100644 --- a/code/frameworks/react-native-web-vite/src/preset.ts +++ b/code/frameworks/react-native-web-vite/src/preset.ts @@ -1,4 +1,3 @@ -// @ts-expect-error FIXME import { viteFinal as reactViteFinal } from '@storybook/react-vite/preset'; import { esbuildFlowPlugin, flowPlugin } from '@bunchtogether/vite-plugin-flow'; diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index c67870c9567d..a1edb0aa9bc6 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for React and Vite: Develop React components in isolation with Hot Reloading.", "keywords": [ "storybook" @@ -35,6 +35,16 @@ "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "dist/index.d.ts" + ], + "preset": [ + "dist/preset.d.ts" + ] + } + }, "files": [ "dist/**/*", "README.md", diff --git a/code/frameworks/react-vite/src/preset.ts b/code/frameworks/react-vite/src/preset.ts index cef1a270f33b..a01721dadacc 100644 --- a/code/frameworks/react-vite/src/preset.ts +++ b/code/frameworks/react-vite/src/preset.ts @@ -12,7 +12,7 @@ export const core: PresetProperty<'core'> = { renderer: getAbsolutePath('@storybook/react'), }; -export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => { +export const viteFinal: NonNullable = async (config, { presets }) => { const { plugins = [] } = config; // Add docgen plugin diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index eed70f2c4fb3..7db983e04e07 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json index a89088118de4..5a5a024bad2a 100644 --- a/code/frameworks/server-webpack5/package.json +++ b/code/frameworks/server-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index bd0c531637f8..c93bda611e59 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Svelte and Vite: Develop Svelte components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json index 198ff72986af..458e2c9290bc 100644 --- a/code/frameworks/svelte-webpack5/package.json +++ b/code/frameworks/svelte-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json index cd3d90298520..11ad71aaf0c2 100644 --- a/code/frameworks/sveltekit/package.json +++ b/code/frameworks/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/sveltekit", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for SvelteKit", "keywords": [ "storybook", diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json index bab6eddec078..c1f245b7514c 100644 --- a/code/frameworks/vue3-vite/package.json +++ b/code/frameworks/vue3-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Vue3 and Vite: Develop Vue3 components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json index e2968fb6cf99..73e8c8033baf 100644 --- a/code/frameworks/vue3-webpack5/package.json +++ b/code/frameworks/vue3-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json index d918f543bc11..7fe54f06d4ef 100644 --- a/code/frameworks/web-components-vite/package.json +++ b/code/frameworks/web-components-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-vite", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for web-components and Vite: Develop Web Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index 57b0679e9f4f..5db1d433cdf9 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-webpack5", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.", "keywords": [ "lit", diff --git a/code/lib/blocks/package.json b/code/lib/blocks/package.json index 430cfd9969d7..02778aa210a0 100644 --- a/code/lib/blocks/package.json +++ b/code/lib/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/blocks", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Doc Blocks", "keywords": [ "storybook" diff --git a/code/lib/cli-sb/package.json b/code/lib/cli-sb/package.json index eb660ccf8b08..99d8537a512d 100644 --- a/code/lib/cli-sb/package.json +++ b/code/lib/cli-sb/package.json @@ -1,6 +1,6 @@ { "name": "sb", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json index 8a9f87817b3d..8b7d3aae3a32 100644 --- a/code/lib/cli-storybook/package.json +++ b/code/lib/cli-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/cli", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.test.ts index 71110733d896..7bd831d082b7 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.test.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.test.ts @@ -141,7 +141,8 @@ describe('addonA11yAddonTest', () => { `; vi.mocked(readFileSync).mockReturnValue(source); - const transformedCode = transformSetupFile(setupFile); + const s = readFileSync(setupFile, 'utf8'); + const transformedCode = transformSetupFile(s); expect(transformedCode).toMatchInlineSnapshot(` "import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; import { beforeAll } from 'vitest'; @@ -169,7 +170,35 @@ describe('addonA11yAddonTest', () => { `; vi.mocked(readFileSync).mockReturnValue(source); - const transformedCode = transformSetupFile(setupFile); + const s = readFileSync(setupFile, 'utf8'); + const transformedCode = transformSetupFile(s); + expect(transformedCode).toMatchInlineSnapshot(` + "import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; + import { beforeAll } from 'vitest'; + import { setProjectAnnotations } from 'storybook'; + import * as projectAnnotations from './preview'; + + const project = setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]); + + beforeAll(project.beforeAll);" + `); + }); + + it('should transform setup file correctly - project annotation is not an array', () => { + const setupFile = '/path/to/vitest.setup.ts'; + const source = dedent` + import { beforeAll } from 'vitest'; + import { setProjectAnnotations } from 'storybook'; + import * as projectAnnotations from './preview'; + + const project = setProjectAnnotations(projectAnnotations); + + beforeAll(project.beforeAll); + `; + vi.mocked(readFileSync).mockReturnValue(source); + + const s = readFileSync(setupFile, 'utf8'); + const transformedCode = transformSetupFile(s); expect(transformedCode).toMatchInlineSnapshot(` "import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; import { beforeAll } from 'vitest'; diff --git a/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.ts b/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.ts index ae848cf57689..6b85146407f6 100644 --- a/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.ts +++ b/code/lib/cli-storybook/src/automigrate/fixes/addon-a11y-addon-test.ts @@ -22,8 +22,7 @@ interface AddonA11yAddonTestOptions { */ export const addonA11yAddonTest: Fix = { id: 'addonA11yAddonTest', - // TODO: Change to the correct version after testing - versionRange: ['<8.5.0', '*'], + versionRange: ['<8.5.0', '>=8.5.0'], promptType(result) { if (result.setupFile === null) { @@ -53,7 +52,11 @@ export const addonA11yAddonTest: Fix = { try { if (vitestSetupFile) { - const transformedSetupCode = transformSetupFile(vitestSetupFile); + const source = readFileSync(vitestSetupFile, 'utf8'); + if (source.includes('@storybook/addon-a11y')) { + return null; + } + const transformedSetupCode = transformSetupFile(source); return { setupFile: vitestSetupFile, transformedSetupCode, @@ -124,8 +127,7 @@ export const addonA11yAddonTest: Fix = { }, }; -export function transformSetupFile(setupFile: string) { - const source = readFileSync(setupFile, 'utf8'); +export function transformSetupFile(source: string) { const j = jscodeshift.withParser('ts'); const root = j(source); @@ -148,9 +150,14 @@ export function transformSetupFile(setupFile: string) { throw new Error('Could not find setProjectAnnotations call in vitest.setup file'); } - // Add a11yAddonAnnotations to the annotations array - setProjectAnnotationsCall.find(j.ArrayExpression).forEach((p) => { - p.value.elements.unshift(j.identifier('a11yAddonAnnotations')); + // Add a11yAddonAnnotations to the annotations array or create a new array if argument is a string + setProjectAnnotationsCall.forEach((p) => { + if (p.value.arguments.length === 1 && p.value.arguments[0].type === 'ArrayExpression') { + p.value.arguments[0].elements.unshift(j.identifier('a11yAddonAnnotations')); + } else if (p.value.arguments.length === 1 && p.value.arguments[0].type === 'Identifier') { + const arg = p.value.arguments[0]; + p.value.arguments[0] = j.arrayExpression([j.identifier('a11yAddonAnnotations'), arg]); + } }); // Add the import declaration at the top diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 8c4627711f63..c63ae7acdf82 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook's CLI - install, dev, build, upgrade, and more", "keywords": [ "cli", diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index 606e5edba3e6..ec3cad1f1fe0 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/codemod", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "A collection of codemod scripts written with JSCodeshift", "keywords": [ "storybook" diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json index 24f68690cfa3..3224d7089b96 100644 --- a/code/lib/core-webpack/package.json +++ b/code/lib/core-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/create-storybook/package.json b/code/lib/create-storybook/package.json index 237aa9cee837..5e5ee2a48bfc 100644 --- a/code/lib/create-storybook/package.json +++ b/code/lib/create-storybook/package.json @@ -1,6 +1,6 @@ { "name": "create-storybook", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Initialize Storybook into your project", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/lib/create-storybook", "bugs": { diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json index cfee2f12bbb7..dc49ef2e1913 100644 --- a/code/lib/csf-plugin/package.json +++ b/code/lib/csf-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-plugin", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Enrich CSF files via static analysis", "keywords": [ "storybook" diff --git a/code/lib/instrumenter/package.json b/code/lib/instrumenter/package.json index eef050e80786..a4c16dcff69d 100644 --- a/code/lib/instrumenter/package.json +++ b/code/lib/instrumenter/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/instrumenter", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/lib/react-dom-shim/package.json b/code/lib/react-dom-shim/package.json index 79ac3f3060c1..8fa32975c148 100644 --- a/code/lib/react-dom-shim/package.json +++ b/code/lib/react-dom-shim/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-dom-shim", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index f2fd5488f141..60857a4d2170 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/source-loader", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Source loader", "keywords": [ "lib", diff --git a/code/lib/test/package.json b/code/lib/test/package.json index 63247b810f01..40202d1c5c3e 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/test", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "", "keywords": [ "storybook" diff --git a/code/nx.json b/code/nx.json index 688fc38b5dd6..eaaabd089ea4 100644 --- a/code/nx.json +++ b/code/nx.json @@ -3,6 +3,7 @@ "extends": "nx/presets/npm.json", "nxCloudAccessToken": "NGVmYTkxMmItYzY3OS00MjkxLTk1ZDktZDFmYTFmNmVlNGY4fHJlYWQ=", "defaultBase": "next", + "useLegacyCache": true, "parallel": 8, "plugins": [ { diff --git a/code/package.json b/code/package.json index 17e8e9c048d6..7b9fd27a6318 100644 --- a/code/package.json +++ b/code/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/root", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "private": true, "description": "Storybook root", "homepage": "https://storybook.js.org/", @@ -90,11 +90,11 @@ "type-fest": "~2.19" }, "dependencies": { - "@chromatic-com/storybook": "^3.2.0", + "@chromatic-com/storybook": "^3.2.2", "@happy-dom/global-registrator": "^14.12.0", - "@nx/eslint": "20.1.4", - "@nx/vite": "20.1.4", - "@nx/workspace": "20.1.4", + "@nx/eslint": "20.2.2", + "@nx/vite": "20.2.2", + "@nx/workspace": "20.2.2", "@playwright/test": "1.48.1", "@storybook/addon-a11y": "workspace:*", "@storybook/addon-actions": "workspace:*", @@ -202,7 +202,7 @@ "husky": "^4.3.7", "lint-staged": "^13.2.2", "mock-require": "^3.0.3", - "nx": "20.1.4", + "nx": "20.2.2", "prettier": "^3.1.1", "prettier-plugin-brace-style": "^0.6.2", "prettier-plugin-css-order": "^2.1.2", diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json index e15f9f178ee6..ea8849294e29 100644 --- a/code/presets/create-react-app/package.json +++ b/code/presets/create-react-app/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-create-react-app", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Create React App preset", "keywords": [ "storybook" diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json index 2d80b196ffb0..9094d24589c5 100644 --- a/code/presets/html-webpack/package.json +++ b/code/presets/html-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-html-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json index f483ee8dfce4..d87bfd54c980 100644 --- a/code/presets/preact-webpack/package.json +++ b/code/presets/preact-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-preact-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index 4c347def5b69..48b04685bd43 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-react-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading", "keywords": [ "storybook" diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json index 42d0b605a244..aaeb9c2dc159 100644 --- a/code/presets/server-webpack/package.json +++ b/code/presets/server-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-server-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json index 72c17dd4e8e8..1053d6ec17c7 100644 --- a/code/presets/svelte-webpack/package.json +++ b/code/presets/svelte-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-svelte-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json index b9a562aefc89..2477b603d753 100644 --- a/code/presets/vue3-webpack/package.json +++ b/code/presets/vue3-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preset-vue3-webpack", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json index 4e30884c2d84..3726b3e2a2a1 100644 --- a/code/renderers/html/package.json +++ b/code/renderers/html/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook HTML renderer", "keywords": [ "storybook" diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json index 67af51a9bbb4..411e0a72028d 100644 --- a/code/renderers/preact/package.json +++ b/code/renderers/preact/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Preact renderer", "keywords": [ "storybook" diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json index 77a70d1854d3..3a2bc736cfec 100644 --- a/code/renderers/react/package.json +++ b/code/renderers/react/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook React renderer", "keywords": [ "storybook" diff --git a/code/renderers/react/src/entry-preview.tsx b/code/renderers/react/src/entry-preview.tsx index 08d625b5729d..e1b214e449c0 100644 --- a/code/renderers/react/src/entry-preview.tsx +++ b/code/renderers/react/src/entry-preview.tsx @@ -10,9 +10,9 @@ export { renderToCanvas } from './renderToCanvas'; export { mount } from './mount'; export const decorators: Decorator[] = [ - (Story, context) => { + (story, context) => { if (!context.parameters?.react?.rsc) { - return ; + return story(); } const major = semver.major(React.version); @@ -21,10 +21,6 @@ export const decorators: Decorator[] = [ throw new Error('React Server Components require React >= 18.3'); } - return ( - - - - ); + return {story()}; }, ]; diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 5aecd20df1d6..484c3eb38f82 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Server renderer", "keywords": [ "storybook" diff --git a/code/renderers/svelte/package.json b/code/renderers/svelte/package.json index bf328e615353..7aa8fec6f5c9 100644 --- a/code/renderers/svelte/package.json +++ b/code/renderers/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Svelte renderer", "keywords": [ "storybook" diff --git a/code/renderers/vue3/package.json b/code/renderers/vue3/package.json index b5b1d437fcf3..4aef83519c25 100644 --- a/code/renderers/vue3/package.json +++ b/code/renderers/vue3/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook Vue 3 renderer", "keywords": [ "storybook" diff --git a/code/renderers/vue3/src/docs/sourceDecorator.ts b/code/renderers/vue3/src/docs/sourceDecorator.ts index 7eb8734305af..cc2f922f63f1 100644 --- a/code/renderers/vue3/src/docs/sourceDecorator.ts +++ b/code/renderers/vue3/src/docs/sourceDecorator.ts @@ -107,7 +107,6 @@ ${template}`; * Checks if the source code generation should be skipped for the given Story context. Will be true * if one of the following is true: * - * - View mode is not "docs" * - Story is no arg story * - Story has set custom source code via parameters.docs.source.code * - Story has set source type to "code" via parameters.docs.source.type @@ -120,13 +119,10 @@ export const shouldSkipSourceCodeGeneration = (context: StoryContext): boolean = } const isArgsStory = context?.parameters.__isArgsStory; - const isDocsViewMode = context?.viewMode === 'docs'; // never render if the user is forcing the block to render code, or // if the user provides code, or if it's not an args story. - return ( - !isDocsViewMode || !isArgsStory || sourceParams?.code || sourceParams?.type === SourceType.CODE - ); + return !isArgsStory || sourceParams?.code || sourceParams?.type === SourceType.CODE; }; /** diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json index 324f56b82e7a..c2f23a997864 100644 --- a/code/renderers/web-components/package.json +++ b/code/renderers/web-components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components", - "version": "8.5.0-alpha.19", + "version": "8.5.0-beta.2", "description": "Storybook web-components renderer", "keywords": [ "lit", diff --git a/code/sandbox/experimental-nextjs-vite-default-ts/project.json b/code/sandbox/experimental-nextjs-vite-default-ts/project.json new file mode 100644 index 000000000000..ae9d595865dd --- /dev/null +++ b/code/sandbox/experimental-nextjs-vite-default-ts/project.json @@ -0,0 +1,21 @@ +{ + "name": "experimental-nextjs-vite/default-ts", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "implicitDependencies": [ + "storybook", + "core", + "addon-essentials", + "addon-interactions", + "addon-links", + "addon-onboarding", + "blocks", + "experimental-nextjs-vite" + ], + "targets": { + "sandbox": {}, + "sb:dev": {}, + "sb:build": {} + }, + "tags": ["ci:normal", "ci:merged", "ci:daily"] +} diff --git a/code/yarn.lock b/code/yarn.lock index 5f206a86fd7a..5c1f23864a42 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -2532,21 +2532,18 @@ __metadata: languageName: node linkType: hard -"@chromatic-com/storybook@npm:^3.2.0": - version: 3.2.0 - resolution: "@chromatic-com/storybook@npm:3.2.0" +"@chromatic-com/storybook@npm:^3.2.2": + version: 3.2.2 + resolution: "@chromatic-com/storybook@npm:3.2.2" dependencies: - "@storybook/channels": "npm:^8.3.0" - "@storybook/telemetry": "npm:^8.3.0" - "@storybook/types": "npm:^8.3.0" chromatic: "npm:^11.15.0" filesize: "npm:^10.0.12" jsonfile: "npm:^6.1.0" react-confetti: "npm:^6.1.0" strip-ansi: "npm:^7.1.0" peerDependencies: - storybook: "*" - checksum: 10c0/59485e69a55df6b1998e19bf0e129fa1f9a86d3ae541f4dcb6f6dd945e7b9a6258ce75e244aaa73821039cef8c57424402a3c0bf63a66b4511a2ac0b5611103b + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + checksum: 10c0/7b8da1ddb399c804337ff28a28594b548392b7bead52f66615b98e201cdeb4d31184b9e355791ba5d0d8cfdd2bea7d38355ecd0058f26f4790f9a887107bde0f languageName: node linkType: hard @@ -4336,9 +4333,9 @@ __metadata: languageName: node linkType: hard -"@nx/devkit@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/devkit@npm:20.1.4" +"@nx/devkit@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/devkit@npm:20.2.2" dependencies: ejs: "npm:^3.1.7" enquirer: "npm:~2.3.6" @@ -4350,32 +4347,32 @@ __metadata: yargs-parser: "npm:21.1.1" peerDependencies: nx: ">= 19 <= 21" - checksum: 10c0/e8ae35e9b421cf558b35b792fcff1f769438be45808c2d4e1317e6c56779b773d2d05c3234e2431c258670b1880b5b4fbe22ac472ff639320df0b5bcf3b2ee95 + checksum: 10c0/2e23120846f6f03a25a81dd69b3cacb5ddfd6bae070411668dcb12c507db8265d81b577a88db348ce4ef89dd8177af6532c9514a42524369d6ed33473a41abe2 languageName: node linkType: hard -"@nx/eslint@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/eslint@npm:20.1.4" +"@nx/eslint@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/eslint@npm:20.2.2" dependencies: - "@nx/devkit": "npm:20.1.4" - "@nx/js": "npm:20.1.4" + "@nx/devkit": "npm:20.2.2" + "@nx/js": "npm:20.2.2" semver: "npm:^7.5.3" tslib: "npm:^2.3.0" - typescript: "npm:~5.4.2" + typescript: "npm:~5.6.2" peerDependencies: "@zkochan/js-yaml": 0.0.7 eslint: ^8.0.0 || ^9.0.0 peerDependenciesMeta: "@zkochan/js-yaml": optional: true - checksum: 10c0/3fa816de9232b478469e9d7f6eaea4a0d47f22c8ee5a510f7d4759261497b11248c49a73a771f2fa356524f5c4f02c673f9c47af3c5503c445c21a9924a0a8fd + checksum: 10c0/79b7f28b391b8f49572063d1056f2a15097d8e544c4c65ab982879e2c6680eca5c42d3e30b0d8544fb4e6372a4b124a19d00e78e7567e231e294379fa2eb1470 languageName: node linkType: hard -"@nx/js@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/js@npm:20.1.4" +"@nx/js@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/js@npm:20.2.2" dependencies: "@babel/core": "npm:^7.23.2" "@babel/plugin-proposal-decorators": "npm:^7.22.7" @@ -4384,8 +4381,8 @@ __metadata: "@babel/preset-env": "npm:^7.23.2" "@babel/preset-typescript": "npm:^7.22.5" "@babel/runtime": "npm:^7.22.6" - "@nx/devkit": "npm:20.1.4" - "@nx/workspace": "npm:20.1.4" + "@nx/devkit": "npm:20.2.2" + "@nx/workspace": "npm:20.2.2" "@zkochan/js-yaml": "npm:0.0.7" babel-plugin-const-enum: "npm:^1.0.1" babel-plugin-macros: "npm:^2.8.0" @@ -4394,7 +4391,6 @@ __metadata: columnify: "npm:^1.6.0" detect-port: "npm:^1.5.1" enquirer: "npm:~2.3.6" - fast-glob: "npm:3.2.7" ignore: "npm:^5.0.4" js-tokens: "npm:^4.0.0" jsonc-parser: "npm:3.2.0" @@ -4404,6 +4400,7 @@ __metadata: ora: "npm:5.3.0" semver: "npm:^7.5.3" source-map-support: "npm:0.5.19" + tinyglobby: "npm:^0.2.10" ts-node: "npm:10.9.1" tsconfig-paths: "npm:^4.1.2" tslib: "npm:^2.3.0" @@ -4412,86 +4409,86 @@ __metadata: peerDependenciesMeta: verdaccio: optional: true - checksum: 10c0/cefd563fffebcb8858fe528005aff69f44232bb42e4bfadfa703e37b0a1db9010f8400126639be3fd19beb81e2135c8a8b239ff37ed3f203b040a311038206bf + checksum: 10c0/2f920ca70187f81c1afcac5254aa9d41a8047e149803b0e036b14fade9fde1b00cad7b4ff6b0c46e9ec78d878c8f128857e8288ed0fe9d0d7bf27db6310d718d languageName: node linkType: hard -"@nx/nx-darwin-arm64@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-darwin-arm64@npm:20.1.4" +"@nx/nx-darwin-arm64@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-darwin-arm64@npm:20.2.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@nx/nx-darwin-x64@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-darwin-x64@npm:20.1.4" +"@nx/nx-darwin-x64@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-darwin-x64@npm:20.2.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@nx/nx-freebsd-x64@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-freebsd-x64@npm:20.1.4" +"@nx/nx-freebsd-x64@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-freebsd-x64@npm:20.2.2" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@nx/nx-linux-arm-gnueabihf@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-linux-arm-gnueabihf@npm:20.1.4" +"@nx/nx-linux-arm-gnueabihf@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-linux-arm-gnueabihf@npm:20.2.2" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@nx/nx-linux-arm64-gnu@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-linux-arm64-gnu@npm:20.1.4" +"@nx/nx-linux-arm64-gnu@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-linux-arm64-gnu@npm:20.2.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@nx/nx-linux-arm64-musl@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-linux-arm64-musl@npm:20.1.4" +"@nx/nx-linux-arm64-musl@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-linux-arm64-musl@npm:20.2.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@nx/nx-linux-x64-gnu@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-linux-x64-gnu@npm:20.1.4" +"@nx/nx-linux-x64-gnu@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-linux-x64-gnu@npm:20.2.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@nx/nx-linux-x64-musl@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-linux-x64-musl@npm:20.1.4" +"@nx/nx-linux-x64-musl@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-linux-x64-musl@npm:20.2.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@nx/nx-win32-arm64-msvc@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-win32-arm64-msvc@npm:20.1.4" +"@nx/nx-win32-arm64-msvc@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-win32-arm64-msvc@npm:20.2.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@nx/nx-win32-x64-msvc@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/nx-win32-x64-msvc@npm:20.1.4" +"@nx/nx-win32-x64-msvc@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/nx-win32-x64-msvc@npm:20.2.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@nx/vite@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/vite@npm:20.1.4" +"@nx/vite@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/vite@npm:20.2.2" dependencies: - "@nx/devkit": "npm:20.1.4" - "@nx/js": "npm:20.1.4" + "@nx/devkit": "npm:20.2.2" + "@nx/js": "npm:20.2.2" "@phenomnomnominal/tsquery": "npm:~5.0.1" "@swc/helpers": "npm:~0.5.0" enquirer: "npm:~2.3.6" @@ -4500,21 +4497,21 @@ __metadata: peerDependencies: vite: ^5.0.0 vitest: ^1.3.1 || ^2.0.0 - checksum: 10c0/48230903e2b5c2c6610677fe501e144285e543a6137500715b643063e8758b6df14fc2c2bd39dbd6ff69612539185f15e24ffc5e1a1d1e245e146b8bb902cf14 + checksum: 10c0/f46877b8e8dfd2989a11b48cb57d2b95e62e1e88f81892ede414dff762983e886d01dc2455e7a34b37c8ff1bab9e73d83cdfecf698b2457e618e1fa5dac4daaa languageName: node linkType: hard -"@nx/workspace@npm:20.1.4": - version: 20.1.4 - resolution: "@nx/workspace@npm:20.1.4" +"@nx/workspace@npm:20.2.2": + version: 20.2.2 + resolution: "@nx/workspace@npm:20.2.2" dependencies: - "@nx/devkit": "npm:20.1.4" + "@nx/devkit": "npm:20.2.2" chalk: "npm:^4.1.0" enquirer: "npm:~2.3.6" - nx: "npm:20.1.4" + nx: "npm:20.2.2" tslib: "npm:^2.3.0" yargs-parser: "npm:21.1.1" - checksum: 10c0/ad0bd040f87fce8bc7e01bc5122c8d52698743cbb5652cac423f54b64aedc7fe48ba090d8030dd9f4b205cd20f3e6a393936a395ab39247bd8c6b7ae0ac1692e + checksum: 10c0/ab3a7bd1787d7534682ac6b1dbe928aea168c15c95636f68fa7885f6da2f9e4da4ff34991c385bacc5f27f0bfd850ab1553883651c5f94f4162dbac8118e2c7b languageName: node linkType: hard @@ -5403,6 +5400,13 @@ __metadata: languageName: node linkType: hard +"@sec-ant/readable-stream@npm:^0.4.1": + version: 0.4.1 + resolution: "@sec-ant/readable-stream@npm:0.4.1" + checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.3": version: 4.1.4 resolution: "@sideway/address@npm:4.1.4" @@ -5496,6 +5500,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^4.0.0": + version: 4.0.0 + resolution: "@sindresorhus/merge-streams@npm:4.0.0" + checksum: 10c0/482ee543629aa1933b332f811a1ae805a213681ecdd98c042b1c1b89387df63e7812248bb4df3910b02b3cc5589d3d73e4393f30e197c9dde18046ccd471fc6b + languageName: node + linkType: hard + "@storybook/addon-a11y@workspace:*, @storybook/addon-a11y@workspace:addons/a11y": version: 0.0.0-use.local resolution: "@storybook/addon-a11y@workspace:addons/a11y" @@ -5506,6 +5517,7 @@ __metadata: "@storybook/test": "workspace:*" "@testing-library/react": "npm:^14.0.0" axe-core: "npm:^4.2.0" + execa: "npm:^9.5.2" picocolors: "npm:^1.1.0" pretty-format: "npm:^29.7.0" react: "npm:^18.2.0" @@ -6022,15 +6034,6 @@ __metadata: languageName: unknown linkType: soft -"@storybook/channels@npm:^8.3.0": - version: 8.3.6 - resolution: "@storybook/channels@npm:8.3.6" - peerDependencies: - storybook: ^8.3.6 - checksum: 10c0/3c34ed2b03c60c6ed1160d9a0efdb836be892e333556848ff492c16ab6d92521207512670d42f69d681f521e50f130a00f692610a3ca63228a8d2b49be57f4fa - languageName: node - linkType: hard - "@storybook/channels@workspace:deprecated/channels": version: 0.0.0-use.local resolution: "@storybook/channels@workspace:deprecated/channels" @@ -6403,7 +6406,6 @@ __metadata: "@storybook/icons": "npm:^1.2.12" "@storybook/instrumenter": "workspace:*" "@storybook/test": "workspace:*" - "@storybook/theming": "workspace:*" "@types/istanbul-lib-report": "npm:^3.0.3" "@types/node": "npm:^22.0.0" "@types/semver": "npm:^7" @@ -6427,6 +6429,7 @@ __metadata: slash: "npm:^5.0.0" strip-ansi: "npm:^7.1.0" tinyglobby: "npm:^0.2.10" + tree-kill: "npm:^1.2.2" ts-dedent: "npm:^2.2.0" typescript: "npm:^5.3.2" vitest: "npm:^2.1.3" @@ -6451,6 +6454,7 @@ __metadata: dependencies: "@storybook/builder-vite": "workspace:*" "@storybook/react": "workspace:*" + "@storybook/react-vite": "workspace:*" "@storybook/test": "workspace:*" "@types/node": "npm:^18.0.0" next: "npm:^15.0.3" @@ -7019,11 +7023,11 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/root@workspace:." dependencies: - "@chromatic-com/storybook": "npm:^3.2.0" + "@chromatic-com/storybook": "npm:^3.2.2" "@happy-dom/global-registrator": "npm:^14.12.0" - "@nx/eslint": "npm:20.1.4" - "@nx/vite": "npm:20.1.4" - "@nx/workspace": "npm:20.1.4" + "@nx/eslint": "npm:20.2.2" + "@nx/vite": "npm:20.2.2" + "@nx/workspace": "npm:20.2.2" "@playwright/test": "npm:1.48.1" "@storybook/addon-a11y": "workspace:*" "@storybook/addon-actions": "workspace:*" @@ -7131,7 +7135,7 @@ __metadata: husky: "npm:^4.3.7" lint-staged: "npm:^13.2.2" mock-require: "npm:^3.0.3" - nx: "npm:20.1.4" + nx: "npm:20.2.2" prettier: "npm:^3.1.1" prettier-plugin-brace-style: "npm:^0.6.2" prettier-plugin-css-order: "npm:^2.1.2" @@ -7309,15 +7313,6 @@ __metadata: languageName: unknown linkType: soft -"@storybook/telemetry@npm:^8.3.0": - version: 8.3.6 - resolution: "@storybook/telemetry@npm:8.3.6" - peerDependencies: - storybook: ^8.3.6 - checksum: 10c0/b4fd8d0e238335249aa82dea49bde56813e0c771e67dd7110fb9e038d1e2bd64aa03e76ee865ef6a7f0fd5220d02bd7bcf3273bb4c19403e3d527d9dd7a258d4 - languageName: node - linkType: hard - "@storybook/telemetry@workspace:deprecated/telemetry": version: 0.0.0-use.local resolution: "@storybook/telemetry@workspace:deprecated/telemetry" @@ -7367,15 +7362,6 @@ __metadata: languageName: unknown linkType: soft -"@storybook/types@npm:^8.3.0": - version: 8.3.6 - resolution: "@storybook/types@npm:8.3.6" - peerDependencies: - storybook: ^8.3.6 - checksum: 10c0/482f55e34877f9eb94a8ff4627a254f3b5442f91f13363e6837e3a9c220a369be1c6ce4652b870b7fa4e522c3365825651f9e04a21bda76b104a6c1f7435e274 - languageName: node - linkType: hard - "@storybook/types@workspace:*, @storybook/types@workspace:deprecated/types": version: 0.0.0-use.local resolution: "@storybook/types@workspace:deprecated/types" @@ -15392,6 +15378,26 @@ __metadata: languageName: node linkType: hard +"execa@npm:^9.5.2": + version: 9.5.2 + resolution: "execa@npm:9.5.2" + dependencies: + "@sindresorhus/merge-streams": "npm:^4.0.0" + cross-spawn: "npm:^7.0.3" + figures: "npm:^6.1.0" + get-stream: "npm:^9.0.0" + human-signals: "npm:^8.0.0" + is-plain-obj: "npm:^4.1.0" + is-stream: "npm:^4.0.1" + npm-run-path: "npm:^6.0.0" + pretty-ms: "npm:^9.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^4.0.0" + yoctocolors: "npm:^2.0.0" + checksum: 10c0/94782a6282e03253224406c29068d18f9095cc251a45d1f19ac3d8f2a9db2cbe32fb8ceb039db1451d8fce3531135a6c0c559f76d634f85416268fc4a6995365 + languageName: node + linkType: hard + "exectimer@npm:^2.2.2": version: 2.2.2 resolution: "exectimer@npm:2.2.2" @@ -15553,19 +15559,6 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:3.2.7": - version: 3.2.7 - resolution: "fast-glob@npm:3.2.7" - dependencies: - "@nodelib/fs.stat": "npm:^2.0.2" - "@nodelib/fs.walk": "npm:^1.2.3" - glob-parent: "npm:^5.1.2" - merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10c0/cc820a9acbd99c51267d525ed3c0c368b57d273f8d34e2401eef824390ff38ff419af3c0308d4ec1aef3dae0e24d1ac1dfe3156e5c702d63416a4c877ab7e0c4 - languageName: node - linkType: hard - "fast-glob@npm:3.3.2, fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.2, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.1, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" @@ -15702,6 +15695,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^6.1.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -16556,6 +16558,16 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^9.0.0": + version: 9.0.1 + resolution: "get-stream@npm:9.0.1" + dependencies: + "@sec-ant/readable-stream": "npm:^0.4.1" + is-stream: "npm:^4.0.1" + checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" @@ -17592,6 +17604,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^8.0.0": + version: 8.0.0 + resolution: "human-signals@npm:8.0.0" + checksum: 10c0/e4dac4f7d3eb791ed04129fc6a85bd454a9102d3e3b76c911d0db7057ebd60b2956b435b5b5712aec18960488ede3c21ef7c56e42cdd70760c0d84d3c05cd92e + languageName: node + linkType: hard + "humanize-ms@npm:^1.2.1": version: 1.2.1 resolution: "humanize-ms@npm:1.2.1" @@ -18372,7 +18391,7 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^4.0.0": +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": version: 4.1.0 resolution: "is-plain-obj@npm:4.1.0" checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e @@ -18458,6 +18477,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^4.0.1": + version: 4.0.1 + resolution: "is-stream@npm:4.0.1" + checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f + languageName: node + linkType: hard + "is-string@npm:^1.0.5, is-string@npm:^1.0.7": version: 1.0.7 resolution: "is-string@npm:1.0.7" @@ -18492,6 +18518,13 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.1": version: 2.0.1 resolution: "is-weakmap@npm:2.0.1" @@ -22103,6 +22136,16 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^6.0.0": + version: 6.0.0 + resolution: "npm-run-path@npm:6.0.0" + dependencies: + path-key: "npm:^4.0.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/b223c8a0dcd608abf95363ea5c3c0ccc3cd877daf0102eaf1b0f2390d6858d8337fbb7c443af2403b067a7d2c116d10691ecd22ab3c5273c44da1ff8d07753bd + languageName: node + linkType: hard + "npmlog@npm:^6.0.0": version: 6.0.2 resolution: "npmlog@npm:6.0.2" @@ -22136,21 +22179,21 @@ __metadata: languageName: node linkType: hard -"nx@npm:20.1.4": - version: 20.1.4 - resolution: "nx@npm:20.1.4" +"nx@npm:20.2.2": + version: 20.2.2 + resolution: "nx@npm:20.2.2" dependencies: "@napi-rs/wasm-runtime": "npm:0.2.4" - "@nx/nx-darwin-arm64": "npm:20.1.4" - "@nx/nx-darwin-x64": "npm:20.1.4" - "@nx/nx-freebsd-x64": "npm:20.1.4" - "@nx/nx-linux-arm-gnueabihf": "npm:20.1.4" - "@nx/nx-linux-arm64-gnu": "npm:20.1.4" - "@nx/nx-linux-arm64-musl": "npm:20.1.4" - "@nx/nx-linux-x64-gnu": "npm:20.1.4" - "@nx/nx-linux-x64-musl": "npm:20.1.4" - "@nx/nx-win32-arm64-msvc": "npm:20.1.4" - "@nx/nx-win32-x64-msvc": "npm:20.1.4" + "@nx/nx-darwin-arm64": "npm:20.2.2" + "@nx/nx-darwin-x64": "npm:20.2.2" + "@nx/nx-freebsd-x64": "npm:20.2.2" + "@nx/nx-linux-arm-gnueabihf": "npm:20.2.2" + "@nx/nx-linux-arm64-gnu": "npm:20.2.2" + "@nx/nx-linux-arm64-musl": "npm:20.2.2" + "@nx/nx-linux-x64-gnu": "npm:20.2.2" + "@nx/nx-linux-x64-musl": "npm:20.2.2" + "@nx/nx-win32-arm64-msvc": "npm:20.2.2" + "@nx/nx-win32-x64-msvc": "npm:20.2.2" "@yarnpkg/lockfile": "npm:^1.1.0" "@yarnpkg/parsers": "npm:3.0.2" "@zkochan/js-yaml": "npm:0.0.7" @@ -22180,6 +22223,7 @@ __metadata: tmp: "npm:~0.2.1" tsconfig-paths: "npm:^4.1.2" tslib: "npm:^2.3.0" + yaml: "npm:^2.6.0" yargs: "npm:^17.6.2" yargs-parser: "npm:21.1.1" peerDependencies: @@ -22214,7 +22258,7 @@ __metadata: bin: nx: bin/nx.js nx-cloud: bin/nx-cloud.js - checksum: 10c0/a9407ea70ecf9b2f465c94552124ef01d90828025540fa5733e1d3d35e6adab41adfe0c0545ebed37b0e7059e4dfee972351949fd9b643adb13e6daba4dd0214 + checksum: 10c0/2046068295be6d33fe11505978ec1a770af272c959ef9aba5341c343f590dbed634958a07dc365d90e9feb0b62b9a06289c7cf3d88090e13062ece13e187b6f3 languageName: node linkType: hard @@ -22847,6 +22891,13 @@ __metadata: languageName: node linkType: hard +"parse-ms@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-ms@npm:4.0.0" + checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16 + languageName: node + linkType: hard + "parse-node-version@npm:^1.0.1": version: 1.0.1 resolution: "parse-node-version@npm:1.0.1" @@ -23689,6 +23740,15 @@ __metadata: languageName: node linkType: hard +"pretty-ms@npm:^9.0.0": + version: 9.2.0 + resolution: "pretty-ms@npm:9.2.0" + dependencies: + parse-ms: "npm:^4.0.0" + checksum: 10c0/ab6d066f90e9f77020426986e1b018369f41575674544c539aabec2e63a20fec01166d8cf6571d0e165ad11cfe5a8134a2a48a36d42ab291c59c6deca5264cbb + languageName: node + linkType: hard + "prettyjson@npm:^1.2.1": version: 1.2.5 resolution: "prettyjson@npm:1.2.5" @@ -27013,6 +27073,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-final-newline@npm:4.0.0" + checksum: 10c0/b0cf2b62d597a1b0e3ebc42b88767f0a0d45601f89fd379a928a1812c8779440c81abba708082c946445af1d6b62d5f16e2a7cf4f30d9d6587b89425fae801ff + languageName: node + linkType: hard + "strip-indent@npm:^3.0.0": version: 3.0.0 resolution: "strip-indent@npm:3.0.0" @@ -27756,7 +27823,7 @@ __metadata: languageName: node linkType: hard -"tree-kill@npm:1.2.2": +"tree-kill@npm:1.2.2, tree-kill@npm:^1.2.2": version: 1.2.2 resolution: "tree-kill@npm:1.2.2" bin: @@ -28112,7 +28179,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.3.2, typescript@npm:~5.4.2": +"typescript@npm:^5.3.2": version: 5.4.5 resolution: "typescript@npm:5.4.5" bin: @@ -28122,6 +28189,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:~5.6.2": + version: 5.6.3 + resolution: "typescript@npm:5.6.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/44f61d3fb15c35359bc60399cb8127c30bae554cd555b8e2b46d68fa79d680354b83320ad419ff1b81a0bdf324197b29affe6cc28988cd6a74d4ac60c94f9799 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A5.4.3#optional!builtin": version: 5.4.3 resolution: "typescript@patch:typescript@npm%3A5.4.3#optional!builtin::version=5.4.3&hash=5adc0c" @@ -28142,7 +28219,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.3.2#optional!builtin, typescript@patch:typescript@npm%3A~5.4.2#optional!builtin": +"typescript@patch:typescript@npm%3A^5.3.2#optional!builtin": version: 5.4.5 resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c" bin: @@ -28152,6 +28229,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A~5.6.2#optional!builtin": + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=b45daf" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ac8307bb06bbfd08ae7137da740769b7d8c3ee5943188743bb622c621f8ad61d244767480f90fbd840277fbf152d8932aa20c33f867dea1bb5e79b187ca1a92f + languageName: node + linkType: hard + "unbox-primitive@npm:^1.0.2": version: 1.0.2 resolution: "unbox-primitive@npm:1.0.2" @@ -28226,6 +28313,13 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + "unified-args@npm:^11.0.0": version: 11.0.1 resolution: "unified-args@npm:11.0.1" @@ -30129,6 +30223,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.6.0": + version: 2.6.1 + resolution: "yaml@npm:2.6.1" + bin: + yaml: bin.mjs + checksum: 10c0/aebf07f61c72b38c74d2b60c3a3ccf89ee4da45bcd94b2bfb7899ba07a5257625a7c9f717c65a6fc511563d48001e01deb1d9e55f0133f3e2edf86039c8c1be7 + languageName: node + linkType: hard + "yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" @@ -30230,6 +30333,13 @@ __metadata: languageName: node linkType: hard +"yoctocolors@npm:^2.0.0": + version: 2.1.1 + resolution: "yoctocolors@npm:2.1.1" + checksum: 10c0/85903f7fa96f1c70badee94789fade709f9d83dab2ec92753d612d84fcea6d34c772337a9f8914c6bed2f5fc03a428ac5d893e76fab636da5f1236ab725486d0 + languageName: node + linkType: hard + "zimmerframe@npm:^1.1.2": version: 1.1.2 resolution: "zimmerframe@npm:1.1.2" diff --git a/docs/_assets/writing-tests/addon-a11y-with-addon-test.png b/docs/_assets/writing-tests/addon-a11y-with-addon-test.png new file mode 100644 index 000000000000..a8656ac25d1d Binary files /dev/null and b/docs/_assets/writing-tests/addon-a11y-with-addon-test.png differ diff --git a/docs/_assets/writing-tests/addon-test-module-coverage-summary.png b/docs/_assets/writing-tests/addon-test-module-coverage-summary.png new file mode 100644 index 000000000000..99aa10a0055f Binary files /dev/null and b/docs/_assets/writing-tests/addon-test-module-coverage-summary.png differ diff --git a/docs/_assets/writing-tests/addon-test-module-edit-coverage.png b/docs/_assets/writing-tests/addon-test-module-edit-coverage.png new file mode 100644 index 000000000000..9e783a324380 Binary files /dev/null and b/docs/_assets/writing-tests/addon-test-module-edit-coverage.png differ diff --git a/docs/_assets/writing-tests/addon-test-module-edit.png b/docs/_assets/writing-tests/addon-test-module-edit.png new file mode 100644 index 000000000000..8634fa9e5bb7 Binary files /dev/null and b/docs/_assets/writing-tests/addon-test-module-edit.png differ diff --git a/docs/_assets/writing-tests/addon-test-module-expanded-with-vta.png b/docs/_assets/writing-tests/addon-test-module-expanded-with-vta.png index c879b46c39c7..2debcbd05e46 100644 Binary files a/docs/_assets/writing-tests/addon-test-module-expanded-with-vta.png and b/docs/_assets/writing-tests/addon-test-module-expanded-with-vta.png differ diff --git a/docs/_assets/writing-tests/addon-test-module-expanded.png b/docs/_assets/writing-tests/addon-test-module-expanded.png index 6bebb8fc4e9b..7ed6e20858b0 100644 Binary files a/docs/_assets/writing-tests/addon-test-module-expanded.png and b/docs/_assets/writing-tests/addon-test-module-expanded.png differ diff --git a/docs/_assets/writing-tests/addon-test-overview.mp4 b/docs/_assets/writing-tests/addon-test-overview.mp4 index 301011486506..19afb7c3d1dc 100644 Binary files a/docs/_assets/writing-tests/addon-test-overview.mp4 and b/docs/_assets/writing-tests/addon-test-overview.mp4 differ diff --git a/docs/_assets/writing-tests/addon-test-sidebar-item-menu-edit.png b/docs/_assets/writing-tests/addon-test-sidebar-item-menu-edit.png new file mode 100644 index 000000000000..0734d73c2f23 Binary files /dev/null and b/docs/_assets/writing-tests/addon-test-sidebar-item-menu-edit.png differ diff --git a/docs/_assets/writing-tests/addon-test-sidebar-item-menu.png b/docs/_assets/writing-tests/addon-test-sidebar-item-menu.png new file mode 100644 index 000000000000..42939fa197af Binary files /dev/null and b/docs/_assets/writing-tests/addon-test-sidebar-item-menu.png differ diff --git a/docs/_assets/writing-tests/addon-test-vitest-error.png b/docs/_assets/writing-tests/addon-test-vitest-error.png index 0b3e4b052022..feaad36ab1d7 100644 Binary files a/docs/_assets/writing-tests/addon-test-vitest-error.png and b/docs/_assets/writing-tests/addon-test-vitest-error.png differ diff --git a/docs/_assets/writing-tests/vitest-plugin-vscode-coverage.png b/docs/_assets/writing-tests/vitest-plugin-vscode-coverage.png new file mode 100644 index 000000000000..1442d1c1242f Binary files /dev/null and b/docs/_assets/writing-tests/vitest-plugin-vscode-coverage.png differ diff --git a/docs/_snippets/addon-a11y-meta-tag.md b/docs/_snippets/addon-a11y-meta-tag.md new file mode 100644 index 000000000000..3ef18124964e --- /dev/null +++ b/docs/_snippets/addon-a11y-meta-tag.md @@ -0,0 +1,236 @@ +```ts filename="Button.stories.ts" renderer="angular" language="ts" +import type { Meta } from '@storybook/angular/'; + +import { Button } from './button.component'; + +const meta: Meta -export default { - title: 'Addons/Test', +const meta = { + title: 'Addons/Group/Test', component: Component, } as Meta; +export default meta; + +type Story = StoryObj; + const { pass } = instrument({ pass: async () => {}, }, { intercept: true }) -export const ExpectedFailure = { +export const ExpectedFailure: Story = { args: { forceFailure: false, }, @@ -28,34 +32,61 @@ export const ExpectedFailure = { throw new Error('Expected failure'); } } -} satisfies StoryAnnotations; +}; -export const ExpectedSuccess = { +export const ExpectedSuccess: Story = { play: async () => { await pass(); } -} satisfies StoryAnnotations; +}; -export const LongRunning = { +export const LongRunning: Story = { loaders: [async () => new Promise((resolve) => setTimeout(resolve, 800))], -} satisfies StoryAnnotations; +}; // Tests will pass in browser, but fail in CLI -export const MismatchFailure = { +export const MismatchFailure: Story = { play: async () => { await pass(); if(!globalThis.__vitest_browser__) { throw new Error('Expected failure'); } } -} satisfies StoryAnnotations; +}; // Tests will fail in browser, but pass in CLI -export const MismatchSuccess = { +export const MismatchSuccess: Story = { play: async () => { await pass(); if(globalThis.__vitest_browser__) { throw new Error('Unexpected success'); } + }, + tags: ['fail-on-purpose'], +}; + +export const PreviewHeadTest: Story = { + play: async () => { + const styles = window.getComputedStyle(document.body); + // set in preview-head.html + expect(styles.backgroundColor).toBe('rgb(250, 250, 210)'); + // set in main.js#previewHead + expect(styles.borderColor).toBe('rgb(255, 0, 0)'); } -} satisfies StoryAnnotations; \ No newline at end of file +}; + +export const StaticDirTest: Story = { + play: async () => { + const path = '/test-static-dirs/static.js'; + const { staticFunction } = await import(/* @vite-ignore */path); + expect(staticFunction()).toBe(true); + } +} + +export const ViteFinalTest: Story = { + play: async () => { + // @ts-expect-error TS doesn't know about the alias + const { aliasedFunction } = await import('test-alias'); + expect(aliasedFunction()).toBe(true); + } +} diff --git a/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.playwright.tsx b/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.playwright.tsx index db4590c82fab..ee201e587159 100644 --- a/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.playwright.tsx +++ b/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.playwright.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { createTest } from '@storybook/react/experimental-playwright'; import { test as base, expect } from '@playwright/experimental-ct-react'; import stories, { SingleComposedStory, WithSpanishGlobal } from './Button.stories.playwright'; diff --git a/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.stories.tsx index 9d2cf8230964..2c976b43cc43 100644 --- a/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.stories.tsx +++ b/test-storybooks/portable-stories-kitchen-sink/react/stories/Button.stories.tsx @@ -67,6 +67,8 @@ export const CSF3Primary: CSF3Story = { size: 'large', primary: true, }, + // Accessibility is failing for the Button + tags: ['fail-on-purpose'], }; export const CSF3Button: CSF3Story = { diff --git a/test-storybooks/portable-stories-kitchen-sink/react/stories/OtherComponent.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react/stories/OtherComponent.stories.tsx new file mode 100644 index 000000000000..dd410fe9c748 --- /dev/null +++ b/test-storybooks/portable-stories-kitchen-sink/react/stories/OtherComponent.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, type StoryObj } from '@storybook/react' + +const Component = () => + +const meta = { + title: 'Addons/Group/Other', + component: Component, +} as Meta; + +export default meta; + +type Story = StoryObj; + +export const Passes: Story = { +}; + +export const Fails: Story = { + play: async () => { + throw new Error('Expected failure'); + }, + tags: ['fail-on-purpose'], +}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react/vitest.workspace.ts b/test-storybooks/portable-stories-kitchen-sink/react/vitest.workspace.ts index f1657f3acddd..effd954bd780 100644 --- a/test-storybooks/portable-stories-kitchen-sink/react/vitest.workspace.ts +++ b/test-storybooks/portable-stories-kitchen-sink/react/vitest.workspace.ts @@ -5,14 +5,15 @@ export default defineWorkspace([ { extends: "vite.config.ts", plugins: [ - storybookTest(), + storybookTest(process.env.SKIP_FAIL_ON_PURPOSE ? { + tags: { + exclude: ["fail-on-purpose"], + } + } : undefined), ], test: { name: "storybook", pool: "threads", - include: [ - "stories/AddonTest.stories.?(c|m)[jt]s?(x)", - ], deps: { optimizer: { web: { @@ -30,4 +31,4 @@ export default defineWorkspace([ environment: "happy-dom", }, }, -]); \ No newline at end of file +]); diff --git a/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock index e3484dc01721..b8242119352f 100644 --- a/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock +++ b/test-storybooks/portable-stories-kitchen-sink/react/yarn.lock @@ -12,7 +12,7 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.3.0": version: 2.3.0 resolution: "@ampproject/remapping@npm:2.3.0" dependencies: @@ -135,6 +135,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-string-parser@npm:7.25.9" + checksum: 10/c28656c52bd48e8c1d9f3e8e68ecafd09d949c57755b0d353739eb4eae7ba4f7e67e92e4036f1cd43378cc1397a2c943ed7bcaf5949b04ab48607def0258b775 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-validator-identifier@npm:7.25.7" @@ -142,6 +149,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 10/3f9b649be0c2fd457fa1957b694b4e69532a668866b8a0d81eabfa34ba16dbf3107b39e0e7144c55c3c652bf773ec816af8df4a61273a2bb4eb3145ca9cf478e + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.25.7": version: 7.25.7 resolution: "@babel/helper-validator-option@npm:7.25.7" @@ -182,6 +196,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.25.4": + version: 7.26.2 + resolution: "@babel/parser@npm:7.26.2" + dependencies: + "@babel/types": "npm:^7.26.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10/8baee43752a3678ad9f9e360ec845065eeee806f1fdc8e0f348a8a0e13eef0959dabed4a197c978896c493ea205c804d0a1187cc52e4a1ba017c7935bab4983d + languageName: node + linkType: hard + "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -437,6 +462,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/types@npm:7.26.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10/40780741ecec886ed9edae234b5eb4976968cc70d72b4e5a40d55f83ff2cc457de20f9b0f4fe9d858350e43dab0ea496e7ef62e2b2f08df699481a76df02cd6e + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -444,12 +479,12 @@ __metadata: languageName: node linkType: hard -"@bundled-es-modules/cookie@npm:^2.0.0": - version: 2.0.0 - resolution: "@bundled-es-modules/cookie@npm:2.0.0" +"@bundled-es-modules/cookie@npm:^2.0.1": + version: 2.0.1 + resolution: "@bundled-es-modules/cookie@npm:2.0.1" dependencies: - cookie: "npm:^0.5.0" - checksum: 10/c8ef02aa5d3f6c786cfa407e1c93b4af29c600eb09990973f47a7a49e4771c1bec37c8f8e567638bb9cbc41f4e38d065ff1d8eaf9bf91f0c3613a6d60bc82c8c + cookie: "npm:^0.7.2" + checksum: 10/0038a5e82c41bfcd722afedabeb6961a5f15747b3681d7f4b61e35eb1e33130039e10ee9250dc9c9e4d3915ce1aeee717c0fb92225111574f0a030411abc0987 languageName: node linkType: hard @@ -911,58 +946,48 @@ __metadata: languageName: node linkType: hard -"@inquirer/confirm@npm:^3.0.0": - version: 3.2.0 - resolution: "@inquirer/confirm@npm:3.2.0" +"@inquirer/confirm@npm:^5.0.0": + version: 5.0.2 + resolution: "@inquirer/confirm@npm:5.0.2" dependencies: - "@inquirer/core": "npm:^9.1.0" - "@inquirer/type": "npm:^1.5.3" - checksum: 10/6b032a26c64075dc14769558720b17f09bc6784a223bbf2c85ec42e491be6ce4c4b83518433c47e05d7e8836ba680ab1b2f6b9c553410d4326582308a1fd2259 + "@inquirer/core": "npm:^10.1.0" + "@inquirer/type": "npm:^3.0.1" + peerDependencies: + "@types/node": ">=18" + checksum: 10/4e775b80b689adeb0b2852ed79b368ef23a82fe3d5f580a562f4af7cdf002a19e0ec1b3b95acc6d49427a72c0fcb5b6548e0cdcafe2f0d3f3d6a923e04aabd0c languageName: node linkType: hard -"@inquirer/core@npm:^9.1.0": - version: 9.2.1 - resolution: "@inquirer/core@npm:9.2.1" +"@inquirer/core@npm:^10.1.0": + version: 10.1.0 + resolution: "@inquirer/core@npm:10.1.0" dependencies: - "@inquirer/figures": "npm:^1.0.6" - "@inquirer/type": "npm:^2.0.0" - "@types/mute-stream": "npm:^0.0.4" - "@types/node": "npm:^22.5.5" - "@types/wrap-ansi": "npm:^3.0.0" + "@inquirer/figures": "npm:^1.0.8" + "@inquirer/type": "npm:^3.0.1" ansi-escapes: "npm:^4.3.2" cli-width: "npm:^4.1.0" - mute-stream: "npm:^1.0.0" + mute-stream: "npm:^2.0.0" signal-exit: "npm:^4.1.0" strip-ansi: "npm:^6.0.1" wrap-ansi: "npm:^6.2.0" yoctocolors-cjs: "npm:^2.1.2" - checksum: 10/bf35e46e70add8ffa9e9d4ae6b528ac660484afca082bca31af95ce8a145a2f8c8d0d07cc7a8627771452e68ade9849c9c9c450a004133ed10ac2d6730900452 + checksum: 10/5d097d0484c1b758f788b792d29395199bdc84af3e8cd4d9273e31de2c5202839b6edf299056956044ba7fb097c4cee7b5c0288e094a380c045082b044f9946e languageName: node linkType: hard -"@inquirer/figures@npm:^1.0.6": - version: 1.0.7 - resolution: "@inquirer/figures@npm:1.0.7" - checksum: 10/ce896860de9d822a7c2a212667bcfd0f04cf2ce86d9a2411cc9c077bb59cd61732cb5f72ac66e88d52912466eec433f005bf8a25efa658f41e1a32f3977080bd - languageName: node - linkType: hard - -"@inquirer/type@npm:^1.5.3": - version: 1.5.5 - resolution: "@inquirer/type@npm:1.5.5" - dependencies: - mute-stream: "npm:^1.0.0" - checksum: 10/bd3f3d7510785af4ad599e042e99e4be6380f52f79f3db140fe6fed0a605acf27b1a0a20fb5cc688eaf7b8aa0c36dacb1d89c7bba4586f38cbf58ba9f159e7b5 +"@inquirer/figures@npm:^1.0.8": + version: 1.0.8 + resolution: "@inquirer/figures@npm:1.0.8" + checksum: 10/0e5e4fbb15e799e818c598fcc3558ef076daf78662149711b046723fd6316381e95f7d5573d6ef0062095ad22c6ac98833033f0948df5c722932107a567fd9c3 languageName: node linkType: hard -"@inquirer/type@npm:^2.0.0": - version: 2.0.0 - resolution: "@inquirer/type@npm:2.0.0" - dependencies: - mute-stream: "npm:^1.0.0" - checksum: 10/e85f359866c28cce06272d2d51cc17788a5c9de9fda7f181c27775dd26821de0dacbc947b521cfe2009cd2965ec54696799035ef3a25a9a5794e47d8e8bdf794 +"@inquirer/type@npm:^3.0.1": + version: 3.0.1 + resolution: "@inquirer/type@npm:3.0.1" + peerDependencies: + "@types/node": ">=18" + checksum: 10/af412f1e7541d43554b02199ae71a2039a1bff5dc51ceefd87de9ece55b199682733b28810fb4b6cb3ed4a159af4cc4a26d4bb29c58dd127e7d9dbda0797d8e7 languageName: node linkType: hard @@ -1287,7 +1312,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: @@ -1309,9 +1334,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.35.8": - version: 0.35.9 - resolution: "@mswjs/interceptors@npm:0.35.9" +"@mswjs/interceptors@npm:^0.37.0": + version: 0.37.3 + resolution: "@mswjs/interceptors@npm:0.37.3" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -1319,7 +1344,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10/9eaf8d7876c9a38c2c9a1259873f8ad27ab41c68a49f7e14a55cd9f596458d9232adb85a5084b044d4eead3be1e7ef5bf54ed6d774d16b02d96caf1e7faa2ab3 + checksum: 10/3d3e2e073feead8702c18dc97e5201785865292b32bd882c4d80461adc3380483b33517c55d7c6c1e53723f5e2ecf50cca0412e6ecd2eb771f4eaabfa2138932 languageName: node linkType: hard @@ -1780,11 +1805,11 @@ __metadata: linkType: soft "@storybook/components@file:../../../code/deprecated/components::locator=portable-stories-react%40workspace%3A.": - version: 8.5.0-alpha.18 - resolution: "@storybook/components@file:../../../code/deprecated/components#../../../code/deprecated/components::hash=aad2fe&locator=portable-stories-react%40workspace%3A." + version: 8.5.0-alpha.20 + resolution: "@storybook/components@file:../../../code/deprecated/components#../../../code/deprecated/components::hash=40954d&locator=portable-stories-react%40workspace%3A." peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10/44d4a8a54fd32d94b03dc9f29e2d68ca3c37156b56055b94493f5f626786a02349c195859c48b4f8fa25e209ce12f385305d601135f7e63bdc4541fa0645cae5 + checksum: 10/ff9107e9c84c5d522a0067f77e217a9963c4bdde27032fdd38efae906f67df7b65488969a9cee89715d49fa89213fe04a72af825ac118e72e835f4be648409ff languageName: node linkType: hard @@ -1840,15 +1865,14 @@ __metadata: linkType: hard "@storybook/experimental-addon-test@file:../../../code/addons/test::locator=portable-stories-react%40workspace%3A.": - version: 8.5.0-alpha.18 - resolution: "@storybook/experimental-addon-test@file:../../../code/addons/test#../../../code/addons/test::hash=21369f&locator=portable-stories-react%40workspace%3A." + version: 8.5.0-alpha.20 + resolution: "@storybook/experimental-addon-test@file:../../../code/addons/test#../../../code/addons/test::hash=1aa098&locator=portable-stories-react%40workspace%3A." dependencies: "@storybook/csf": "npm:0.1.12" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.12" "@storybook/instrumenter": "workspace:*" "@storybook/test": "workspace:*" - "@storybook/theming": "workspace:*" polished: "npm:^4.2.2" prompts: "npm:^2.4.0" ts-dedent: "npm:^2.2.0" @@ -1864,7 +1888,7 @@ __metadata: optional: true vitest: optional: true - checksum: 10/2081814e214dc1dd31144870a6a4ea7637c9c241ab02044488be57e19402c206c0037d449197f77bb4262147703f6d0b27f09c9f6cc2ee358c97fd7d1cdfa908 + checksum: 10/42188bb3814cae7c04df03c75994f8df971cc2ce71bda9e536211b96468bbdf2b8babc78526fdec1b2ddbd4d703118474f23c31f2bddffb6fa8947dc35ef0f19 languageName: node linkType: hard @@ -1897,20 +1921,20 @@ __metadata: linkType: soft "@storybook/manager-api@file:../../../code/deprecated/manager-api::locator=portable-stories-react%40workspace%3A.": - version: 8.5.0-alpha.18 - resolution: "@storybook/manager-api@file:../../../code/deprecated/manager-api#../../../code/deprecated/manager-api::hash=c1892e&locator=portable-stories-react%40workspace%3A." + version: 8.5.0-alpha.20 + resolution: "@storybook/manager-api@file:../../../code/deprecated/manager-api#../../../code/deprecated/manager-api::hash=92b213&locator=portable-stories-react%40workspace%3A." peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10/405745f48728bfa4d8340fe2403bca0d60f803ed346c12d20a63ab9472ba1d8d1021aa12ee0fd36a6b41aec42d8c2a657acffeb9c8663aa32edcda490882e7f5 + checksum: 10/c63e624ab2d90f2992ecb2a650d9e54ace0f7f699d425bc68f616f870651a2b5f3c6c7d66be84aaab9200a9f01686376a7532f905f0dbd4dddb5542afe855584 languageName: node linkType: hard "@storybook/preview-api@file:../../../code/deprecated/preview-api::locator=portable-stories-react%40workspace%3A.": - version: 8.5.0-alpha.18 - resolution: "@storybook/preview-api@file:../../../code/deprecated/preview-api#../../../code/deprecated/preview-api::hash=0085a8&locator=portable-stories-react%40workspace%3A." + version: 8.5.0-alpha.20 + resolution: "@storybook/preview-api@file:../../../code/deprecated/preview-api#../../../code/deprecated/preview-api::hash=b70449&locator=portable-stories-react%40workspace%3A." peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10/29c5f7134b4300bac03c42f57c3138e24315516b23515893cfb4a144786327fb6e0a90d5470d951eb22e055466769fc9ad4577658afc7da43b60870c0d06e767 + checksum: 10/3251de34aca72eb6c82b7aa8fde6e877e0974294d97eb60f63819b890b399cd7703a4dfde151180cf81918b36b3523bdbafe0731d232bda7d77c9f3b8fc5b75b languageName: node linkType: hard @@ -1991,11 +2015,11 @@ __metadata: linkType: soft "@storybook/theming@file:../../../code/deprecated/theming::locator=portable-stories-react%40workspace%3A.": - version: 8.5.0-alpha.18 - resolution: "@storybook/theming@file:../../../code/deprecated/theming#../../../code/deprecated/theming::hash=dd5360&locator=portable-stories-react%40workspace%3A." + version: 8.5.0-alpha.20 + resolution: "@storybook/theming@file:../../../code/deprecated/theming#../../../code/deprecated/theming::hash=db0fbd&locator=portable-stories-react%40workspace%3A." peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10/8f8ecbd709ff4a8e0bafa19642497aad0d6ee40f2e613793a4248cb3825765783dd40eb57d180cb6cc4ad1f8acbd4e4a1a1b3448245b89e28581e25289c88c86 + checksum: 10/de77ed744ce172b1007cf94b4b5ff1cf9cf8492d4ddb9ac1ca61d76b18ed73f76e911c1518c1b66ba37c8d2d4e0f63f2eb3bddc64cd0beb7d64f952a45f9b8b7 languageName: node linkType: hard @@ -2356,16 +2380,7 @@ __metadata: languageName: node linkType: hard -"@types/mute-stream@npm:^0.0.4": - version: 0.0.4 - resolution: "@types/mute-stream@npm:0.0.4" - dependencies: - "@types/node": "npm:*" - checksum: 10/af8d83ad7b68ea05d9357985daf81b6c9b73af4feacb2f5c2693c7fd3e13e5135ef1bd083ce8d5bdc8e97acd28563b61bb32dec4e4508a8067fcd31b8a098632 - languageName: node - linkType: hard - -"@types/node@npm:*, @types/node@npm:^22.5.5": +"@types/node@npm:*": version: 22.7.7 resolution: "@types/node@npm:22.7.7" dependencies: @@ -2456,13 +2471,6 @@ __metadata: languageName: node linkType: hard -"@types/wrap-ansi@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/wrap-ansi@npm:3.0.0" - checksum: 10/8aa644946ca4e859668c36b8e2bcf2ac4bdee59dac760414730ea57be8a93ae9166ebd40a088f2ab714843aaea2a2a67f0e6e6ec11cfc9c8701b2466ca1c4089 - languageName: node - linkType: hard - "@types/yargs-parser@npm:*": version: 21.0.3 resolution: "@types/yargs-parser@npm:21.0.3" @@ -2697,21 +2705,21 @@ __metadata: linkType: hard "@vitest/browser@npm:^2.1.3": - version: 2.1.3 - resolution: "@vitest/browser@npm:2.1.3" + version: 2.1.8 + resolution: "@vitest/browser@npm:2.1.8" dependencies: "@testing-library/dom": "npm:^10.4.0" "@testing-library/user-event": "npm:^14.5.2" - "@vitest/mocker": "npm:2.1.3" - "@vitest/utils": "npm:2.1.3" - magic-string: "npm:^0.30.11" - msw: "npm:^2.3.5" - sirv: "npm:^2.0.4" + "@vitest/mocker": "npm:2.1.8" + "@vitest/utils": "npm:2.1.8" + magic-string: "npm:^0.30.12" + msw: "npm:^2.6.4" + sirv: "npm:^3.0.0" tinyrainbow: "npm:^1.2.0" ws: "npm:^8.18.0" peerDependencies: playwright: "*" - vitest: 2.1.3 + vitest: 2.1.8 webdriverio: "*" peerDependenciesMeta: playwright: @@ -2720,7 +2728,33 @@ __metadata: optional: true webdriverio: optional: true - checksum: 10/e639496fa529140fb9e7dce97890c5b75fffbfb41881bee5ef25b194832d3cadcb77490d9b54777bfa968b993f6878649fe4961d6ef312ca1222b9a2fc8d4f12 + checksum: 10/6063e02222440347bbc23b2c54e259078aa83a29869337b9ffd642be5a4321ac3ddf3c0bbe4eac5237eb0bb8b9fa17d21d2c31299376de407716e3c7dd3b704c + languageName: node + linkType: hard + +"@vitest/coverage-v8@npm:^2.1.3": + version: 2.1.8 + resolution: "@vitest/coverage-v8@npm:2.1.8" + dependencies: + "@ampproject/remapping": "npm:^2.3.0" + "@bcoe/v8-coverage": "npm:^0.2.3" + debug: "npm:^4.3.7" + istanbul-lib-coverage: "npm:^3.2.2" + istanbul-lib-report: "npm:^3.0.1" + istanbul-lib-source-maps: "npm:^5.0.6" + istanbul-reports: "npm:^3.1.7" + magic-string: "npm:^0.30.12" + magicast: "npm:^0.3.5" + std-env: "npm:^3.8.0" + test-exclude: "npm:^7.0.1" + tinyrainbow: "npm:^1.2.0" + peerDependencies: + "@vitest/browser": 2.1.8 + vitest: 2.1.8 + peerDependenciesMeta: + "@vitest/browser": + optional: true + checksum: 10/2e1e7fe2a20c1eec738f6d84d890bed4aa5138094943dd1229962c2c42428a1a517c8a4ad4fb52637d7494f044440e061e9bc5982a83df95223db185d5a28f4d languageName: node linkType: hard @@ -2736,35 +2770,34 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:2.1.3": - version: 2.1.3 - resolution: "@vitest/expect@npm:2.1.3" +"@vitest/expect@npm:2.1.8": + version: 2.1.8 + resolution: "@vitest/expect@npm:2.1.8" dependencies: - "@vitest/spy": "npm:2.1.3" - "@vitest/utils": "npm:2.1.3" - chai: "npm:^5.1.1" + "@vitest/spy": "npm:2.1.8" + "@vitest/utils": "npm:2.1.8" + chai: "npm:^5.1.2" tinyrainbow: "npm:^1.2.0" - checksum: 10/94e61e01f14cfcd9ced0e7ac1bbdeee55ff4bf68f09d8f244fd7d73f97b106f35d10cba3fe7a0132464c312206f2eee9e83b16a8d761101b61da053890062858 + checksum: 10/3594149dd67dfac884a90f8b6a35687cdddd2f5f764562819bf7b66ae2eacfd4aa5e8914155deb4082fbe5a3792dced2fd7e59a948ffafe67acba4d2229dfe5f languageName: node linkType: hard -"@vitest/mocker@npm:2.1.3": - version: 2.1.3 - resolution: "@vitest/mocker@npm:2.1.3" +"@vitest/mocker@npm:2.1.8": + version: 2.1.8 + resolution: "@vitest/mocker@npm:2.1.8" dependencies: - "@vitest/spy": "npm:2.1.3" + "@vitest/spy": "npm:2.1.8" estree-walker: "npm:^3.0.3" - magic-string: "npm:^0.30.11" + magic-string: "npm:^0.30.12" peerDependencies: - "@vitest/spy": 2.1.3 - msw: ^2.3.5 + msw: ^2.4.9 vite: ^5.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - checksum: 10/84be8830d6e965109730257d7a84b3d7594db0998ae55decdbfc304857c1c7d29b49f1f5b23f2addcbce1bd7e8bb33832407737a9bb3f95cb3bf7bb312db4d9d + checksum: 10/f04060f42102caa4cca72059e63c1ecae8b8e091aaa61a2d4a914b129fc711ada4ad117eb0184e49e363757784ed1117fdbf9f4a81a45fe575fd92769740a970 languageName: node linkType: hard @@ -2777,33 +2810,33 @@ __metadata: languageName: node linkType: hard -"@vitest/pretty-format@npm:2.1.3, @vitest/pretty-format@npm:^2.1.3": - version: 2.1.3 - resolution: "@vitest/pretty-format@npm:2.1.3" +"@vitest/pretty-format@npm:2.1.8, @vitest/pretty-format@npm:^2.1.8": + version: 2.1.8 + resolution: "@vitest/pretty-format@npm:2.1.8" dependencies: tinyrainbow: "npm:^1.2.0" - checksum: 10/d9382ee93f0f32e2ef8fe03bda818e5277f052a50ddb05b6a6cf0864b2ccb228484f12f130c05faf62dc2140292ffafc213f2941b0fa24058b3ee2943daa286c + checksum: 10/f0f60c007424194887ad398d202867d58d850154de327993925041e2972357544eea95a22e0bb3a62a470b006ff8de5f691d2078708dcd7f625e24f8a06b26e7 languageName: node linkType: hard -"@vitest/runner@npm:2.1.3": - version: 2.1.3 - resolution: "@vitest/runner@npm:2.1.3" +"@vitest/runner@npm:2.1.8": + version: 2.1.8 + resolution: "@vitest/runner@npm:2.1.8" dependencies: - "@vitest/utils": "npm:2.1.3" + "@vitest/utils": "npm:2.1.8" pathe: "npm:^1.1.2" - checksum: 10/cdf9b82d388c1cc148753f4a8632dfcadf9c4a1c0e065fdcd485d5af824af62507fd7eab9efb21244009775c05773ccb59547043af522a5ab6d216433321066e + checksum: 10/27f265a3ab1e20297b948b06232bfa4dc9fda44d1f9bb6206baa9e6fa643b71143ebfd2d1771570296b7ee74a12d684e529a830f545ad61235cefb454e94a8e9 languageName: node linkType: hard -"@vitest/snapshot@npm:2.1.3": - version: 2.1.3 - resolution: "@vitest/snapshot@npm:2.1.3" +"@vitest/snapshot@npm:2.1.8": + version: 2.1.8 + resolution: "@vitest/snapshot@npm:2.1.8" dependencies: - "@vitest/pretty-format": "npm:2.1.3" - magic-string: "npm:^0.30.11" + "@vitest/pretty-format": "npm:2.1.8" + magic-string: "npm:^0.30.12" pathe: "npm:^1.1.2" - checksum: 10/2c0c4ad8abb758f2f76d1d6094f8928360437e09d0a59e0c6a85a544c892cc41a5324ebbc5657a66c8a3793e51cbf58e357c7f71e899f4e5c5eb76e8c9745abf + checksum: 10/71edf4f574d317579c605ed0a7ecab7ee96fddcebc777bd130774a770ddc692c538f9f5b3dfde89af83ecb36f7338fe880943c83cede58f55e3556768a1a0749 languageName: node linkType: hard @@ -2816,12 +2849,29 @@ __metadata: languageName: node linkType: hard -"@vitest/spy@npm:2.1.3": - version: 2.1.3 - resolution: "@vitest/spy@npm:2.1.3" +"@vitest/spy@npm:2.1.8": + version: 2.1.8 + resolution: "@vitest/spy@npm:2.1.8" dependencies: - tinyspy: "npm:^3.0.0" - checksum: 10/94d6f1bc34da5d0c973d9382c133b938e555fcf2d238edf0aaad3de1a98dd57ebf7c104ba229c6beec48122d2e6f55386d8d2cf96a5804dc95ac683a54754cc7 + tinyspy: "npm:^3.0.2" + checksum: 10/9a1cb9cf6b23c122681469b5890d91ca26fc8d74953b3d46d293a5d2a4944490106891f6a178cd732ab7a8abbda339f43681c81d1594565ecc3bf3e7f9b7735f + languageName: node + linkType: hard + +"@vitest/ui@npm:^2.1.3": + version: 2.1.8 + resolution: "@vitest/ui@npm:2.1.8" + dependencies: + "@vitest/utils": "npm:2.1.8" + fflate: "npm:^0.8.2" + flatted: "npm:^3.3.1" + pathe: "npm:^1.1.2" + sirv: "npm:^3.0.0" + tinyglobby: "npm:^0.2.10" + tinyrainbow: "npm:^1.2.0" + peerDependencies: + vitest: 2.1.8 + checksum: 10/7ff0532b3b0e3f93c037cad3528b8fde8a93188f3222b92faae42e0fdd996e9284b362c4e5e9d725ce0a019870d3b0b7ad80c1874f74b43ffc5a1d703803fdd8 languageName: node linkType: hard @@ -2837,14 +2887,14 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:2.1.3, @vitest/utils@npm:^2.1.1": - version: 2.1.3 - resolution: "@vitest/utils@npm:2.1.3" +"@vitest/utils@npm:2.1.8, @vitest/utils@npm:^2.1.1": + version: 2.1.8 + resolution: "@vitest/utils@npm:2.1.8" dependencies: - "@vitest/pretty-format": "npm:2.1.3" - loupe: "npm:^3.1.1" + "@vitest/pretty-format": "npm:2.1.8" + loupe: "npm:^3.1.2" tinyrainbow: "npm:^1.2.0" - checksum: 10/f064e6634cb84c925a17d8937df7441d150c3e24fa5bbd6304151d11dab6cdeb0cb3d5a95a9aacb8b416c87fb0d9aa8c6b9cc5e174191784231e8345948d6d18 + checksum: 10/be1f4254347199fb5c1d9de8e4537dad4af3f434c033e7cd023165bd4b7e9de16fa0f86664256ab331120585df95ed6be8eea58b209b510651b49f6482051733 languageName: node linkType: hard @@ -3455,16 +3505,16 @@ __metadata: languageName: node linkType: hard -"chai@npm:^5.1.1": - version: 5.1.1 - resolution: "chai@npm:5.1.1" +"chai@npm:^5.1.1, chai@npm:^5.1.2": + version: 5.1.2 + resolution: "chai@npm:5.1.2" dependencies: assertion-error: "npm:^2.0.1" check-error: "npm:^2.1.1" deep-eql: "npm:^5.0.1" loupe: "npm:^3.1.0" pathval: "npm:^2.0.0" - checksum: 10/ee67279a5613bd36dc1dc13660042429ae2f1dc5a9030a6abcf381345866dfb5bce7bc10b9d74c8de86b6f656489f654bbbef3f3361e06925591e6a00c72afff + checksum: 10/e8c2bbc83cb5a2f87130d93056d4cfbbe04106e12aa798b504816dbe3fa538a9f68541b472e56cbf0f54558b501d7e31867d74b8218abcd5a8cc8ba536fba46c languageName: node linkType: hard @@ -3695,10 +3745,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.5.0": - version: 0.5.0 - resolution: "cookie@npm:0.5.0" - checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 +"cookie@npm:^0.7.2": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f languageName: node linkType: hard @@ -3853,7 +3903,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.7": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -4174,6 +4224,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.5.4": + version: 1.5.4 + resolution: "es-module-lexer@npm:1.5.4" + checksum: 10/f29c7c97a58eb17640dcbd71bd6ef754ad4f58f95c3073894573d29dae2cad43ecd2060d97ed5b866dfb7804d5590fb7de1d2c5339a5fceae8bd60b580387fc5 + languageName: node + linkType: hard + "esbuild-register@npm:^3.5.0": version: 3.6.0 resolution: "esbuild-register@npm:3.6.0" @@ -4634,6 +4691,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.1.0": + version: 1.1.0 + resolution: "expect-type@npm:1.1.0" + checksum: 10/05fca80ddc7d493a89361f783c6b000750fa04a8226bc24701f3b90adb0efc2fb467f2a0baaed4015a02d8b9034ef5bb87521df9dba980f50b1105bd596ef833 + languageName: node + linkType: hard + "expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" @@ -4753,6 +4817,25 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.2": + version: 6.4.2 + resolution: "fdir@npm:6.4.2" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10/5ff80d1d2034e75cc68be175401c9f64c4938a6b2c1e9a0c27f2d211ffbe491fd86d29e4576825d9da8aff9bd465f0283427c2dddc11653457906c46d3bbc448 + languageName: node + linkType: hard + +"fflate@npm:^0.8.2": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10/2bd26ba6d235d428de793c6a0cd1aaa96a06269ebd4e21b46c8fd1bd136abc631acf27e188d47c3936db090bf3e1ede11d15ce9eae9bffdc4bfe1b9dc66ca9cb + languageName: node + linkType: hard + "figures@npm:^3.2.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -4818,6 +4901,13 @@ __metadata: languageName: node linkType: hard +"flatted@npm:^3.3.1": + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10/ac3c159742e01d0e860a861164bcfd35bb567ccbebb8a0dd041e61cf3c64a435b917dd1e7ed1c380c2ebca85735fb16644485ec33665bc6aafc3b316aa1eed44 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -5030,7 +5120,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.4.1": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -5726,7 +5816,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" checksum: 10/40bbdd1e937dfd8c830fa286d0f665e81b7a78bdabcd4565f6d5667c99828bda3db7fb7ac6b96a3e2e8a2461ddbc5452d9f8bc7d00cb00075fa6a3e99f5b6a81 @@ -5759,7 +5849,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-report@npm:^3.0.0": +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" dependencies: @@ -5781,7 +5871,18 @@ __metadata: languageName: node linkType: hard -"istanbul-reports@npm:^3.1.3": +"istanbul-lib-source-maps@npm:^5.0.6": + version: 5.0.6 + resolution: "istanbul-lib-source-maps@npm:5.0.6" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.23" + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + checksum: 10/569dd0a392ee3464b1fe1accbaef5cc26de3479eacb5b91d8c67ebb7b425d39fd02247d85649c3a0e9c29b600809fa60b5af5a281a75a89c01f385b1e24823a2 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.3, istanbul-reports@npm:^3.1.7": version: 3.1.7 resolution: "istanbul-reports@npm:3.1.7" dependencies: @@ -6593,7 +6694,7 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^3.1.0, loupe@npm:^3.1.1": +"loupe@npm:^3.1.0, loupe@npm:^3.1.1, loupe@npm:^3.1.2": version: 3.1.2 resolution: "loupe@npm:3.1.2" checksum: 10/8f5734e53fb64cd914aa7d986e01b6d4c2e3c6c56dcbd5428d71c2703f0ab46b5ab9f9eeaaf2b485e8a1c43f865bdd16ec08ae1a661c8f55acdbd9f4d59c607a @@ -6634,7 +6735,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.0, magic-string@npm:^0.30.11": +"magic-string@npm:^0.30.0": version: 0.30.12 resolution: "magic-string@npm:0.30.12" dependencies: @@ -6643,6 +6744,26 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.12": + version: 0.30.14 + resolution: "magic-string@npm:0.30.14" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + checksum: 10/8ca0f8937c2824e48ebc70e7e065a193c467713639cc6e5972aaba0fa5417b375a6f62c383410a19a66e618c386bb7253fbd3ccbfb0144bb310f0ba772121f12 + languageName: node + linkType: hard + +"magicast@npm:^0.3.5": + version: 0.3.5 + resolution: "magicast@npm:0.3.5" + dependencies: + "@babel/parser": "npm:^7.25.4" + "@babel/types": "npm:^7.25.4" + source-map-js: "npm:^1.2.0" + checksum: 10/3a2dba6b0bdde957797361d09c7931ebdc1b30231705360eeb40ed458d28e1c3112841c3ed4e1b87ceb28f741e333c7673cd961193aa9fdb4f4946b202e6205a + languageName: node + linkType: hard + "make-dir@npm:^4.0.0": version: 4.0.0 resolution: "make-dir@npm:4.0.0" @@ -6892,15 +7013,16 @@ __metadata: languageName: node linkType: hard -"msw@npm:^2.3.5": - version: 2.4.11 - resolution: "msw@npm:2.4.11" +"msw@npm:^2.6.4": + version: 2.6.6 + resolution: "msw@npm:2.6.6" dependencies: - "@bundled-es-modules/cookie": "npm:^2.0.0" + "@bundled-es-modules/cookie": "npm:^2.0.1" "@bundled-es-modules/statuses": "npm:^1.0.1" "@bundled-es-modules/tough-cookie": "npm:^0.1.6" - "@inquirer/confirm": "npm:^3.0.0" - "@mswjs/interceptors": "npm:^0.35.8" + "@inquirer/confirm": "npm:^5.0.0" + "@mswjs/interceptors": "npm:^0.37.0" + "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/until": "npm:^2.1.0" "@types/cookie": "npm:^0.6.0" "@types/statuses": "npm:^2.0.4" @@ -6920,14 +7042,14 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/d073ede4bfc7f1f41f7a0cb05b3d20d9befc1658e53faacd3f217a7cb78e3e748a3ee8e937e2a4d93fd09f16b35cba00d71df767736dd567ac15fd8e01aa7d6e + checksum: 10/7762ba5f1570789328af27167e03c2b8eb4981faa476ae47d74c125c90ddc1792bc28b9ce1100bbc4e105b55e3e7d65e7cae8d27fa7677b6516e42a63c38b7a3 languageName: node linkType: hard -"mute-stream@npm:^1.0.0": - version: 1.0.0 - resolution: "mute-stream@npm:1.0.0" - checksum: 10/36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7 +"mute-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "mute-stream@npm:2.0.0" + checksum: 10/d2e4fd2f5aa342b89b98134a8d899d8ef9b0a6d69274c4af9df46faa2d97aeb1f2ce83d867880d6de63643c52386579b99139801e24e7526c3b9b0a6d1e18d6c languageName: node linkType: hard @@ -7298,6 +7420,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10/ce617b8da36797d09c0baacb96ca8a44460452c89362d7cb8f70ca46b4158ba8bc3606912de7c818eb4a939f7f9015cef3c766ec8a0c6bfc725fdc078e39c717 + languageName: node + linkType: hard + "pify@npm:^2.2.0": version: 2.3.0 resolution: "pify@npm:2.3.0" @@ -7380,6 +7509,8 @@ __metadata: "@typescript-eslint/parser": "npm:^6.21.0" "@vitejs/plugin-react": "npm:^4.2.1" "@vitest/browser": "npm:^2.1.3" + "@vitest/coverage-v8": "npm:^2.1.3" + "@vitest/ui": "npm:^2.1.3" cypress: "npm:^13.6.4" eslint: "npm:^8.56.0" eslint-plugin-react-hooks: "npm:^4.6.0" @@ -7991,14 +8122,14 @@ __metadata: languageName: node linkType: hard -"sirv@npm:^2.0.4": - version: 2.0.4 - resolution: "sirv@npm:2.0.4" +"sirv@npm:^3.0.0": + version: 3.0.0 + resolution: "sirv@npm:3.0.0" dependencies: "@polka/url": "npm:^1.0.0-next.24" mrmime: "npm:^2.0.0" totalist: "npm:^3.0.0" - checksum: 10/24f42cf06895017e589c9d16fc3f1c6c07fe8b0dbafce8a8b46322cfba67b7f2498610183954cb0e9d089c8cb60002a7ee7e8bca6a91a0d7042bfbc3473c95c3 + checksum: 10/94dbd5df7cf4965f7c5941767117cbf9709e1d25de1d619a114c3f77fc63c124b5a5255717af2a0de637bb83d0b0defd0822d01420764b56432b53281b1d675d languageName: node linkType: hard @@ -8066,7 +8197,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10/ff9d8c8bf096d534a5b7707e0382ef827b4dd360a577d3f34d2b9f48e12c9d230b5747974ee7c607f0df65113732711bb701fe9ece3c7edbd43cb2294d707df3 @@ -8157,10 +8288,10 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.7.0": - version: 3.7.0 - resolution: "std-env@npm:3.7.0" - checksum: 10/6ee0cca1add3fd84656b0002cfbc5bfa20340389d9ba4720569840f1caa34bce74322aef4c93f046391583e50649d0cf81a5f8fe1d411e50b659571690a45f12 +"std-env@npm:^3.8.0": + version: 3.8.0 + resolution: "std-env@npm:3.8.0" + checksum: 10/034176196cfcaaab16dbdd96fc9e925a9544799fb6dc5a3e36fe43270f3a287c7f779d785b89edaf22cef2b5f1dcada2aae67430b8602e785ee74bdb3f671768 languageName: node linkType: hard @@ -8359,6 +8490,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^7.0.1": + version: 7.0.1 + resolution: "test-exclude@npm:7.0.1" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^10.4.1" + minimatch: "npm:^9.0.4" + checksum: 10/e6f6f4e1df2e7810e082e8d7dfc53be51a931e6e87925f5e1c2ef92cc1165246ba3bf2dae6b5d86251c16925683dba906bd41e40169ebc77120a2d1b5a0dbbe0 + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -8394,17 +8536,27 @@ __metadata: languageName: node linkType: hard -"tinyexec@npm:^0.3.0": +"tinyexec@npm:^0.3.1": version: 0.3.1 resolution: "tinyexec@npm:0.3.1" checksum: 10/0537c70590d52d354f40c0255ff0f654a3d18ddb3812b440ddf9d436edf516c8057838ad5a38744c0c59670ec03e3cf23fbe04ae3d49f031d948274e99002569 languageName: node linkType: hard -"tinypool@npm:^1.0.0": - version: 1.0.1 - resolution: "tinypool@npm:1.0.1" - checksum: 10/eaceb93784b8e27e60c0e3e2c7d11c29e1e79b2a025b2c232215db73b90fe22bd4753ad53fc8e801c2b5a63b94a823af549555d8361272bc98271de7dd4a9925 +"tinyglobby@npm:^0.2.10": + version: 0.2.10 + resolution: "tinyglobby@npm:0.2.10" + dependencies: + fdir: "npm:^6.4.2" + picomatch: "npm:^4.0.2" + checksum: 10/10c976866d849702edc47fc3fef27d63f074c40f75ef17171ecc1452967900699fa1e62373681dd58e673ddff2e3f6094bcd0a2101e3e4b30f4c2b9da41397f2 + languageName: node + linkType: hard + +"tinypool@npm:^1.0.1": + version: 1.0.2 + resolution: "tinypool@npm:1.0.2" + checksum: 10/6109322f14b3763f65c8fa49fddab72cd3edd96b82dd50e05e63de74867329ff5353bff4377281ec963213d9314f37f4a353e9ee34bbac85fd4c1e4a568d6076 languageName: node linkType: hard @@ -8415,7 +8567,7 @@ __metadata: languageName: node linkType: hard -"tinyspy@npm:^3.0.0": +"tinyspy@npm:^3.0.0, tinyspy@npm:^3.0.2": version: 3.0.2 resolution: "tinyspy@npm:3.0.2" checksum: 10/5db671b2ff5cd309de650c8c4761ca945459d7204afb1776db9a04fb4efa28a75f08517a8620c01ee32a577748802231ad92f7d5b194dc003ee7f987a2a06337 @@ -8759,17 +8911,18 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:2.1.3": - version: 2.1.3 - resolution: "vite-node@npm:2.1.3" +"vite-node@npm:2.1.8": + version: 2.1.8 + resolution: "vite-node@npm:2.1.8" dependencies: cac: "npm:^6.7.14" - debug: "npm:^4.3.6" + debug: "npm:^4.3.7" + es-module-lexer: "npm:^1.5.4" pathe: "npm:^1.1.2" vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: 10/8ba6b145cbb02a492c7bb1f0490d02383000462f234ed61d24f650547163825c16f14e6908ee1eb661403bd0a7a3fb3cdbedf116cc015b1e5cdf7bb992872a01 + checksum: 10/0ff0ed7a6fb234d3ddc4946e4c1150229980cac9f34fb4bd7f443aab0aae2da5b73ac20ff68af1df476545807dc23189247194e8cea0dcdfa394311c73f04429 languageName: node linkType: hard @@ -8833,33 +8986,34 @@ __metadata: linkType: hard "vitest@npm:^2.1.3": - version: 2.1.3 - resolution: "vitest@npm:2.1.3" - dependencies: - "@vitest/expect": "npm:2.1.3" - "@vitest/mocker": "npm:2.1.3" - "@vitest/pretty-format": "npm:^2.1.3" - "@vitest/runner": "npm:2.1.3" - "@vitest/snapshot": "npm:2.1.3" - "@vitest/spy": "npm:2.1.3" - "@vitest/utils": "npm:2.1.3" - chai: "npm:^5.1.1" - debug: "npm:^4.3.6" - magic-string: "npm:^0.30.11" + version: 2.1.8 + resolution: "vitest@npm:2.1.8" + dependencies: + "@vitest/expect": "npm:2.1.8" + "@vitest/mocker": "npm:2.1.8" + "@vitest/pretty-format": "npm:^2.1.8" + "@vitest/runner": "npm:2.1.8" + "@vitest/snapshot": "npm:2.1.8" + "@vitest/spy": "npm:2.1.8" + "@vitest/utils": "npm:2.1.8" + chai: "npm:^5.1.2" + debug: "npm:^4.3.7" + expect-type: "npm:^1.1.0" + magic-string: "npm:^0.30.12" pathe: "npm:^1.1.2" - std-env: "npm:^3.7.0" + std-env: "npm:^3.8.0" tinybench: "npm:^2.9.0" - tinyexec: "npm:^0.3.0" - tinypool: "npm:^1.0.0" + tinyexec: "npm:^0.3.1" + tinypool: "npm:^1.0.1" tinyrainbow: "npm:^1.2.0" vite: "npm:^5.0.0" - vite-node: "npm:2.1.3" + vite-node: "npm:2.1.8" why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 2.1.3 - "@vitest/ui": 2.1.3 + "@vitest/browser": 2.1.8 + "@vitest/ui": 2.1.8 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -8877,7 +9031,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10/f6079a88583045b551e6526c08774aeac4a9cf85b132793a03f9470c013326abd7fce3985e3c2217dc0dac2fadeee3506e3dc51e215f10862b2fe9da9289af0f + checksum: 10/c2552c068f6faac82eb4e6debb9ed505c0e8016fd6e0a0f0e0dbb5b5417922fbcde80c54af0d3b5a5503a5d6ad6862b6e95b9b59b8b7e98bb553217b9c6fc227 languageName: node linkType: hard