diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md
index fce4b4696b3a..2b0e49086ea5 100644
--- a/CHANGELOG.prerelease.md
+++ b/CHANGELOG.prerelease.md
@@ -1,3 +1,19 @@
+## 8.0.0-rc.4
+
+- Actions: Fix attaching action after a spy is restored to original function - [#26364](https://github.com/storybookjs/storybook/pull/26364), thanks @kasperpeulen!
+- CLI: Add explicit actions to header story - [#26352](https://github.com/storybookjs/storybook/pull/26352), thanks @kasperpeulen!
+- CLI: Automigration for upgrading storybook related dependencies - [#26377](https://github.com/storybookjs/storybook/pull/26377), thanks @ndelangen!
+- CLI: Fix doctor compatibility check - [#26363](https://github.com/storybookjs/storybook/pull/26363), thanks @yannbf!
+- CLI: Fix fn reference in preact templates - [#26384](https://github.com/storybookjs/storybook/pull/26384), thanks @kasperpeulen!
+- CLI: Remove duplicated dependency warning - [#26385](https://github.com/storybookjs/storybook/pull/26385), thanks @yannbf!
+- CLI: Vite migration link (shorter) - [#26379](https://github.com/storybookjs/storybook/pull/26379), thanks @ndelangen!
+- Composition: Fix refs not loading when there's multiple - [#26356](https://github.com/storybookjs/storybook/pull/26356), thanks @ndelangen!
+- Dependencies: Broaden `esbuild` version range - [#26405](https://github.com/storybookjs/storybook/pull/26405), thanks @ndelangen!
+- Maintenance: Replace `@storybook/testing-library` with `@storybook/test` in monorepo - [#26351](https://github.com/storybookjs/storybook/pull/26351), thanks @ndelangen!
+- Maintenance: What's new modal changes - [#26355](https://github.com/storybookjs/storybook/pull/26355), thanks @kasperpeulen!
+- Portable Stories: Fix injected root element changing layout - [#26387](https://github.com/storybookjs/storybook/pull/26387), thanks @JReinhold!
+- React: Support all React component types in JSX Decorator - [#26382](https://github.com/storybookjs/storybook/pull/26382), thanks @yannbf!
+
## 8.0.0-rc.3
- Addon-themes: Fix switcher initialization after first start - [#26353](https://github.com/storybookjs/storybook/pull/26353), thanks @valentinpalkovic!
diff --git a/MIGRATION.md b/MIGRATION.md
index 4b6dcdc8e1bc..561a9a61b899 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -4,7 +4,6 @@
- [Portable stories](#portable-stories)
- [Project annotations are now merged instead of overwritten in composeStory](#project-annotations-are-now-merged-instead-of-overwritten-in-composestory)
- [Type change in `composeStories` API](#type-change-in-composestories-api)
- - [DOM structure changed in portable stories](#dom-structure-changed-in-portable-stories)
- [Composed Vue stories are now components instead of functions](#composed-vue-stories-are-now-components-instead-of-functions)
- [Tab addons are now routed to a query parameter](#tab-addons-are-now-routed-to-a-query-parameter)
- [Default keyboard shortcuts changed](#default-keyboard-shortcuts-changed)
@@ -440,35 +439,6 @@ await Primary.play!(...) // if you want a runtime error when the play function d
There are plans to make the type of the play function be inferred based on your imported story's play function in a near future, so the types will be 100% accurate.
-#### DOM structure changed in portable stories
-
-The portable stories API now adds a wrapper to your stories with a unique id based on your story id, such as:
-
-```html
-
-
-
-```
-
-This means that if you take DOM snapshots of your stories, they will be affected and you will have to update them.
-
-The id calculation is based on different heuristics based on your Meta title and Story name. When using `composeStories`, the id can be inferred automatically. However, when using `composeStory` and your story does not explicitly have a `storyName` property, the story name can't be inferred automatically. As a result, its name will be "Unnamed Story", resulting in a wrapper id like `"#storybook-story-button--unnamed-story"`. If the id matters to you and you want to fix it, you have to specify the `exportsName` property like so:
-
-```ts
-test("snapshots the story with custom id", () => {
- const Primary = composeStory(
- stories.Primary,
- stories.default,
- undefined,
- // If you do not want the `unnamed-story` id, you have to pass the name of the story as a parameter
- "Primary"
- );
-
- const { baseElement } = render();
- expect(baseElement).toMatchSnapshot();
-});
-```
-
#### Composed Vue stories are now components instead of functions
`composeStory` (and `composeStories`) from `@storybook/vue3` now return Vue components rather than story functions that return components. This means that when rendering these composed stories you just pass the composed story _without_ first calling it.
@@ -1105,6 +1075,7 @@ const preview: Preview = {
export default preview;
```
+
To learn more about the available icons and their names, see the [Storybook documentation](https://storybook.js.org/docs/8.0/faq#what-icons-are-available-for-my-toolbar-or-my-addon).
#### Removals in @storybook/types
diff --git a/code/addons/actions/src/loaders.ts b/code/addons/actions/src/loaders.ts
index cc6c8494b7fc..3acfa9795eef 100644
--- a/code/addons/actions/src/loaders.ts
+++ b/code/addons/actions/src/loaders.ts
@@ -2,6 +2,8 @@
import type { LoaderFunction } from '@storybook/types';
import { action } from './runtime';
+export const tinySpyInternalState = Symbol.for('tinyspy:spy');
+
const attachActionsToFunctionMocks: LoaderFunction = (context) => {
const {
args,
@@ -15,7 +17,11 @@ const attachActionsToFunctionMocks: LoaderFunction = (context) => {
typeof value === 'function' && '_isMockFunction' in value && value._isMockFunction
)
.forEach(([key, value]) => {
- const previous = value.getMockImplementation();
+ // See this discussion for context:
+ // https://github.com/vitest-dev/vitest/pull/5352
+ const previous =
+ value.getMockImplementation() ??
+ (tinySpyInternalState in value ? value[tinySpyInternalState]?.getOriginal() : undefined);
if (previous?._actionAttached !== true && previous?.isAction !== true) {
const implementation = (...params: unknown[]) => {
action(key)(...params);
diff --git a/code/addons/docs/template/stories/docspage/autoplay.stories.ts b/code/addons/docs/template/stories/docspage/autoplay.stories.ts
index 36fc395949d8..6ebdc43b3ad7 100644
--- a/code/addons/docs/template/stories/docspage/autoplay.stories.ts
+++ b/code/addons/docs/template/stories/docspage/autoplay.stories.ts
@@ -1,6 +1,5 @@
import { global as globalThis } from '@storybook/global';
-import { expect } from '@storybook/test';
-import { within } from '@storybook/testing-library';
+import { expect, within } from '@storybook/test';
export default {
component: globalThis.Components.Pre,
diff --git a/code/addons/interactions/package.json b/code/addons/interactions/package.json
index 4a7f597bbcb5..da2ff1d5b004 100644
--- a/code/addons/interactions/package.json
+++ b/code/addons/interactions/package.json
@@ -65,7 +65,7 @@
"@storybook/instrumenter": "workspace:*",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
- "@storybook/testing-library": "next",
+ "@storybook/test": "workspace:*",
"@storybook/theming": "workspace:*",
"@types/node": "^18.0.0",
"formik": "^2.2.9",
diff --git a/code/addons/interactions/src/components/Interaction.stories.tsx b/code/addons/interactions/src/components/Interaction.stories.tsx
index b18cd7136c6a..a6f8bd3a3b46 100644
--- a/code/addons/interactions/src/components/Interaction.stories.tsx
+++ b/code/addons/interactions/src/components/Interaction.stories.tsx
@@ -1,7 +1,6 @@
import type { StoryObj, Meta } from '@storybook/react';
-import { expect } from '@storybook/test';
import { CallStates } from '@storybook/instrumenter';
-import { userEvent, within } from '@storybook/testing-library';
+import { userEvent, within, expect } from '@storybook/test';
import { getCalls } from '../mocks';
import { Interaction } from './Interaction';
diff --git a/code/addons/interactions/src/components/InteractionsPanel.stories.tsx b/code/addons/interactions/src/components/InteractionsPanel.stories.tsx
index a2435113ef02..89f7ef115b59 100644
--- a/code/addons/interactions/src/components/InteractionsPanel.stories.tsx
+++ b/code/addons/interactions/src/components/InteractionsPanel.stories.tsx
@@ -2,8 +2,7 @@ import React from 'react';
import type { StoryObj, Meta } from '@storybook/react';
import { CallStates } from '@storybook/instrumenter';
import { styled } from '@storybook/theming';
-import { userEvent, within, waitFor } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { userEvent, within, waitFor, expect } from '@storybook/test';
import isChromatic from 'chromatic/isChromatic';
import { getCalls, getInteractions } from '../mocks';
diff --git a/code/addons/onboarding/package.json b/code/addons/onboarding/package.json
index 668d04b45220..62077801df70 100644
--- a/code/addons/onboarding/package.json
+++ b/code/addons/onboarding/package.json
@@ -55,7 +55,6 @@
"@storybook/react": "workspace:*",
"@storybook/telemetry": "workspace:*",
"@storybook/test": "workspace:*",
- "@storybook/testing-library": "next",
"@storybook/theming": "workspace:*",
"@storybook/types": "workspace:*",
"framer-motion": "^11.0.3",
diff --git a/code/addons/onboarding/src/components/List/List.stories.tsx b/code/addons/onboarding/src/components/List/List.stories.tsx
index 380fd07ca4cc..9ff667586fc3 100644
--- a/code/addons/onboarding/src/components/List/List.stories.tsx
+++ b/code/addons/onboarding/src/components/List/List.stories.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
-import { userEvent, waitFor, within } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { userEvent, waitFor, within, expect } from '@storybook/test';
import { List } from './List';
import { ListItem } from './ListItem/ListItem';
diff --git a/code/addons/onboarding/src/components/Modal/Modal.stories.tsx b/code/addons/onboarding/src/components/Modal/Modal.stories.tsx
index 51d19c49b4f5..527aa87d4323 100644
--- a/code/addons/onboarding/src/components/Modal/Modal.stories.tsx
+++ b/code/addons/onboarding/src/components/Modal/Modal.stories.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
-import { userEvent, within } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { userEvent, within, expect } from '@storybook/test';
import { Modal } from './Modal';
diff --git a/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
index 67b31843dc45..6a87a2147c0a 100644
--- a/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
+++ b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
@@ -1,8 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import { PulsatingEffect } from './PulsatingEffect';
import React from 'react';
-import { within } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { within, expect } from '@storybook/test';
const meta: Meta = {
component: PulsatingEffect,
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
index d2284dbd913f..d2fe6ba470b4 100644
--- a/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
@@ -1,8 +1,7 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
-import { waitFor, within } from '@storybook/testing-library';
-import { expect, fn } from '@storybook/test';
+import { waitFor, within, expect, fn } from '@storybook/test';
import { STORY_INDEX_INVALIDATED, STORY_RENDERED } from '@storybook/core-events';
import { WriteStoriesModal } from './WriteStoriesModal';
import typescriptSnippet from './code/typescript';
diff --git a/code/builders/builder-manager/package.json b/code/builders/builder-manager/package.json
index 637cdaf29eb3..7a95fbd78831 100644
--- a/code/builders/builder-manager/package.json
+++ b/code/builders/builder-manager/package.json
@@ -52,7 +52,7 @@
"@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10",
"browser-assert": "^1.2.1",
"ejs": "^3.1.8",
- "esbuild": "^18.0.0 || ^19.0.0 || ^0.20.0",
+ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0",
"esbuild-plugin-alias": "^0.2.1",
"express": "^4.17.3",
"fs-extra": "^11.1.0",
diff --git a/code/frameworks/angular/README.md b/code/frameworks/angular/README.md
index d97e1ab93f2a..4bfadb7f16f4 100644
--- a/code/frameworks/angular/README.md
+++ b/code/frameworks/angular/README.md
@@ -1,324 +1,3 @@
# Storybook for Angular
-- [Storybook for Angular](#storybook-for-angular)
- - [Getting Started](#getting-started)
- - [Setup Storybook for your Angular projects](#setup-storybook-for-your-angular-projects)
- - [Run Storybook](#run-storybook)
- - [Setup Compodoc](#setup-compodoc)
- - [Automatic setup](#automatic-setup)
- - [Manual setup](#manual-setup)
- - [moduleMetadata decorator](#modulemetadata-decorator)
- - [applicationConfig decorator](#applicationconfig-decorator)
- - [FAQ](#faq)
- - [How do I migrate to an Angular Storybook builder?](#how-do-i-migrate-to-an-angular-storybook-builder)
- - [Do you have only one Angular project in your workspace?](#do-you-have-only-one-angular-project-in-your-workspace)
- - [Adjust your `package.json`](#adjust-your-packagejson)
- - [I have multiple projects in my Angular workspace](#i-have-multiple-projects-in-my-angular-workspace)
-
-Storybook for Angular is a UI development environment for your Angular components.
-With it, you can visualize different states of your UI components and develop them interactively.
-
-![Storybook Screenshot](https://github.com/storybookjs/storybook/blob/main/media/storybook-intro.gif)
-
-Storybook runs outside of your app.
-So you can develop UI components in isolation without worrying about app specific dependencies and requirements.
-
-## Getting Started
-
-```sh
-cd my-angular-app
-npx storybook@latest init
-```
-
-## Setup Storybook for your Angular projects
-
-Storybook supports Angular multi-project workspace. You can setup Storybook for each project in the workspace. When running `npx storybook@latest init` you will be asked for which project Storybook should be set up. Essentially, during initialization, the `.storybook` folder will be created and the `angular.json` will be edited to add the Storybook configuration for the selected project. The configuration looks approximately like this:
-
-```json
-// angular.json
-{
- ...
- "projects": {
- ...
- "your-project": {
- ...
- "architect": {
- ...
- "storybook": {
- "builder": "@storybook/angular:start-storybook",
- "options": {
- // the path to the storybook config directory
- "configDir": ".storybook",
- // the build target of your project
- "browserTarget": "your-project:build",
- // the port you want to start Storybook on
- "port": 6006
- // further options are available and can be found in
- // https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular/src/builders/start-storybook/schema.json
- }
- },
- "build-storybook": {
- "builder": "@storybook/angular:build-storybook",
- "options": {
- "configDir": ".storybook",
- "browserTarget": "your-project:build",
- "outputDir": "dist/storybook/your-project"
- // further options are available and can be found in
- // https://github.com/storybookjs/storybook/tree/next/code/frameworks/angular/src/builders/build-storybook/schema.json
- }
- }
- }
- }
- }
-}
-```
-
-## Run Storybook
-
-To run Storybook for a particular project, please run:
-
-```sh
-ng run :storybook
-```
-
-To build Storybook, run:
-
-```sh
-ng run :build-storybook
-```
-
-You will find the output in `dist/storybook/your-project`.
-
-For more information visit: [storybook.js.org](https://storybook.js.org)
-
-## Setup Compodoc
-
-You can include JSDoc comments above components, directives, and other parts of your Angular code to include documentation for those elements. Compodoc uses these comments to generate documentation for your application. In Storybook, it is useful to add explanatory comments above @Inputs and @Outputs, since these are the main elements that Storybook displays in its user interface. The @Inputs and @Outputs are the elements that you can interact with in Storybook, such as controls.
-
-### Automatic setup
-
-When installing Storybook via `sb init`, you will be given the option to set up Compodoc automatically.
-
-### Manual setup
-
-If you have already installed Storybook, you can set up Compodoc manually.
-
-Install the following dependencies:
-
-```sh
-npm i -D @compodoc/compodoc
-```
-
-Add the following option to your to the Storybook Builder:
-
-```json
-{
- ...
- "projects": {
- ...
- "your-project": {
- ...
- "architect": {
- ...
- "storybook": {
- "builder": "@storybook/angular:start-storybook",
- "options": {
- ...
- "compodoc": true,
- "compodocArgs": [
- "-e",
- "json",
- "-d",
- // Where to store the generated documentation. It's usually the root of your Angular project. It's not necessarily the root of your Angular Workspace!
- "."
- ],
- }
- },
- "build-storybook": {
- "builder": "@storybook/angular:build-storybook",
- "options": {
- ...
- "compodoc": true,
- "compodocArgs": [
- "-e",
- "json",
- "-d",
- "."
- ],
- }
- }
- }
- }
- }
-}
-```
-
-Go to your `.storybook/preview.js` and add the following:
-
-```js
-import { setCompodocJson } from '@storybook/addon-docs/angular';
-import docJson from '../documentation.json';
-setCompodocJson(docJson);
-
-const preview: Preview = {
- ...
-};
-
-export default preview;
-```
-
-## moduleMetadata decorator
-
-If your component has dependencies on other Angular directives and modules, these can be supplied using the moduleMetadata decorator either for all stories or for individual stories.
-
-```js
-import { StoryFn, Meta, moduleMetadata } from '@storybook/angular';
-import { SomeComponent } from './some.component';
-
-export default {
- component: SomeComponent,
- decorators: [
- // Apply metadata to all stories
- moduleMetadata({
- // import necessary ngModules or standalone components
- imports: [...],
- // declare components that are used in the template
- declarations: [...],
- // List of providers that should be available to the root component and all its children.
- providers: [...],
- }),
- ],
-} as Meta;
-
-const Template = (): StoryFn => (args) => ({
- props: args,
-});
-
-export const Base = Template();
-
-export const WithCustomProvider = Template();
-WithCustomProvider.decorators = [
- // Apply metadata to a specific story
- moduleMetadata({
- imports: [...],
- declarations: [...],
- providers: [...]
- }),
-];
-```
-
-## applicationConfig decorator
-
-If your component relies on application-wide providers, like the ones defined by BrowserAnimationsModule or any other modules which use the forRoot pattern to provide a ModuleWithProviders, you can use the applicationConfig decorator on the meta default export to provide them to the [bootstrapApplication function](https://angular.io/guide/standalone-components#configuring-dependency-injection), which we use to bootstrap the component in Storybook.
-
-```js
-
-import { StoryObj, Meta, applicationConfig } from '@storybook/angular';
-import { BrowserAnimationsModule, provideAnimations } from '@angular/platform-browser/animations';
-import { importProvidersFrom } from '@angular/core';
-import { ChipsModule } from './angular-src/chips.module';
-
-const meta: Meta = {
- component: ChipsGroupComponent,
- decorators: [
- // Apply application config to all stories
- applicationConfig({
- // List of providers and environment providers that should be available to the root component and all its children.
- providers: [
- ...
- // Import application-wide providers from a module
- importProvidersFrom(BrowserAnimationsModule)
- // Or use provide-style functions if available instead, e.g.
- provideAnimations()
- ],
- }),
- ],
-};
-
-export default meta;
-
-type Story = StoryObj;
-
-export const WithCustomApplicationProvider: Story = {
- render: () => ({
- // Apply application config to a specific story
- applicationConfig: {
- // The providers will be merged with the ones defined in the applicationConfig decorators providers array of the global meta object
- providers: [...]
- }
- })
-}
-```
-
-## FAQ
-
-### How do I migrate to an Angular Storybook builder?
-
-The Storybook [Angular builder](https://angular.io/guide/glossary#builder) is a new way to run Storybook in an Angular workspace. It is a drop-in replacement for running `storybook dev` and `storybook build` directly.
-
-You can run `npx storybook@next automigrate` to try let Storybook detect and automatically fix your configuration. Otherwise, you can follow the next steps to manually adjust your configuration.
-
-#### Do you have only one Angular project in your workspace?
-
-In this case go to your `angular.json` and add `storybook` and `build-storybook` entries in `architect` section of your project like shown above.
-
-##### Adjust your `package.json`
-
-Go to your `package.json` and adjust your script section. Usually, it will look like this:
-
-```json
-{
- "scripts": {
- "storybook": "start-storybook -p 6006", // or `storybook dev -p 6006`
- "build-storybook": "build-storybook" // or `storybook build`
- }
-}
-```
-
-Now, you can run Storybook with `ng run :storybook` and build it with `ng run :build-storybook`. Adjust the scripts in your `package.json` accordingly.
-
-```json
-{
- "scripts": {
- "storybook": "ng run :storybook", // or `storybook dev -p 6006`
- "build-storybook": "ng run :build-storybook" // or `storybook build`
- }
-}
-```
-
-Also remove the compodoc part in your script section if you have set it up previously.
-It is now built-in in `@storybook/angular` and you don't have to call it explicitly:
-
-```json
-{
- "scripts": {
- "docs:json": "compodoc -p tsconfig.json -e json -d ./documentation",
- "storybook": "npm run docs:json && start-storybook -p 6006",
- "build-storybook": "npm run docs:json && build-storybook"
- }
-}
-```
-
-Change it to:
-
-```json
-{
- "scripts": {
- "storybook": "ng run :storybook",
- "build-storybook": "ng run :build-storybook"
- }
-}
-```
-
-#### I have multiple projects in my Angular workspace
-
-In this case you have to adjust your `angular.json` and `package.json` as described above for each project in which you want to use Storybook. Please note, that each project should have a dedicated `.storybook` folder, which should be placed in the root of the project.
-
-You can run `npx sb init` sequentially for each project to setup Storybook for each of them to automatically create the `.storybook` folder and create the necessary configuration in your `angular.json`.
-
-You can then use [Storybook composition](https://storybook.js.org/docs/angular/sharing/storybook-composition) to composite multiple Storybooks into one.
-
----
-
-Storybook also comes with a lot of [addons](https://storybook.js.org/addons) and a great API to customize as you wish.
-You can also build a [static version](https://storybook.js.org/docs/angular/sharing/publish-storybook) of your Storybook and deploy it anywhere you want.
+See [documentation](https://storybook.js.org/docs/8.0/get-started/angular?renderer=angular) for installation instructions, usage examples, APIs, and more.
diff --git a/code/frameworks/angular/template/cli/header.stories.ts b/code/frameworks/angular/template/cli/header.stories.ts
index 3222518ace44..3f3fb684e855 100644
--- a/code/frameworks/angular/template/cli/header.stories.ts
+++ b/code/frameworks/angular/template/cli/header.stories.ts
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/angular';
import { HeaderComponent } from './header.component';
+import { fn } from '@storybook/test';
const meta: Meta = {
title: 'Example/Header',
@@ -11,6 +12,11 @@ const meta: Meta = {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
+ },
};
export default meta;
diff --git a/code/frameworks/angular/template/stories/core/applicationConfig/with-browser-animations.stories.ts b/code/frameworks/angular/template/stories/core/applicationConfig/with-browser-animations.stories.ts
index f61db00d8f0e..ef30854c26f9 100644
--- a/code/frameworks/angular/template/stories/core/applicationConfig/with-browser-animations.stories.ts
+++ b/code/frameworks/angular/template/stories/core/applicationConfig/with-browser-animations.stories.ts
@@ -1,7 +1,6 @@
import { Meta, StoryObj, applicationConfig } from '@storybook/angular';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { within, userEvent } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { within, userEvent, expect } from '@storybook/test';
import { importProvidersFrom } from '@angular/core';
import { OpenCloseComponent } from '../moduleMetadata/angular-src/open-close-component/open-close.component';
diff --git a/code/frameworks/angular/template/stories/core/applicationConfig/with-noop-browser-animations.stories.ts b/code/frameworks/angular/template/stories/core/applicationConfig/with-noop-browser-animations.stories.ts
index 1a4341ec77cf..3369b9949d33 100644
--- a/code/frameworks/angular/template/stories/core/applicationConfig/with-noop-browser-animations.stories.ts
+++ b/code/frameworks/angular/template/stories/core/applicationConfig/with-noop-browser-animations.stories.ts
@@ -1,7 +1,6 @@
import { Meta, StoryObj } from '@storybook/angular';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { within, userEvent } from '@storybook/testing-library';
-import { expect } from '@storybook/test';
+import { within, userEvent, expect } from '@storybook/test';
import { importProvidersFrom } from '@angular/core';
import { OpenCloseComponent } from '../moduleMetadata/angular-src/open-close-component/open-close.component';
diff --git a/code/frameworks/ember/template/cli/Button.stories.js b/code/frameworks/ember/template/cli/Button.stories.js
index 61de1a4f9cc9..c8fffd70feb0 100644
--- a/code/frameworks/ember/template/cli/Button.stories.js
+++ b/code/frameworks/ember/template/cli/Button.stories.js
@@ -1,6 +1,7 @@
import { hbs } from 'ember-cli-htmlbars';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
+import { fn } from '@storybook/test';
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
export default {
@@ -14,20 +15,19 @@ export default {
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/ember/writing-docs/autodocs
tags: ['autodocs'],
+ args: { onClick: fn() },
};
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Text = {
args: {
label: 'Button',
- onClick: action('onClick'),
},
};
export const Emoji = {
args: {
label: 'π π π π―',
- onClick: action('onClick'),
},
};
diff --git a/code/frameworks/nextjs/template/cli/js/Header.stories.js b/code/frameworks/nextjs/template/cli/js/Header.stories.js
index a1d32b3ad65e..982cd970fb5c 100644
--- a/code/frameworks/nextjs/template/cli/js/Header.stories.js
+++ b/code/frameworks/nextjs/template/cli/js/Header.stories.js
@@ -1,3 +1,4 @@
+import { fn } from '@storybook/test';
import { Header } from './Header';
export default {
@@ -9,6 +10,11 @@ export default {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
+ },
};
export const LoggedIn = {
args: {
diff --git a/code/frameworks/nextjs/template/cli/ts-3-8/Header.stories.ts b/code/frameworks/nextjs/template/cli/ts-3-8/Header.stories.ts
index 82a109720879..feddeae98faf 100644
--- a/code/frameworks/nextjs/template/cli/ts-3-8/Header.stories.ts
+++ b/code/frameworks/nextjs/template/cli/ts-3-8/Header.stories.ts
@@ -1,4 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
import { Header } from './Header';
const meta: Meta = {
@@ -10,6 +12,11 @@ const meta: Meta = {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
+ },
};
export default meta;
diff --git a/code/frameworks/nextjs/template/cli/ts-4-9/Header.stories.ts b/code/frameworks/nextjs/template/cli/ts-4-9/Header.stories.ts
index 046982e62673..39d15874f4c1 100644
--- a/code/frameworks/nextjs/template/cli/ts-4-9/Header.stories.ts
+++ b/code/frameworks/nextjs/template/cli/ts-4-9/Header.stories.ts
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
import { Header } from './Header';
const meta = {
@@ -10,6 +11,11 @@ const meta = {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
+ },
} satisfies Meta;
export default meta;
diff --git a/code/frameworks/nextjs/template/stories/Image.stories.jsx b/code/frameworks/nextjs/template/stories/Image.stories.jsx
index 8fa4f6a53de1..79ab308e1286 100644
--- a/code/frameworks/nextjs/template/stories/Image.stories.jsx
+++ b/code/frameworks/nextjs/template/stories/Image.stories.jsx
@@ -1,6 +1,5 @@
import React, { useRef, useState } from 'react';
import Image from 'next/image';
-import { waitFor } from '@storybook/testing-library';
import Accessibility from '../../assets/accessibility.svg';
import AvifImage from '../../assets/avif-test-image.avif';
diff --git a/code/frameworks/nextjs/template/stories_nextjs-default-js/Head.stories.jsx b/code/frameworks/nextjs/template/stories_nextjs-default-js/Head.stories.jsx
index f031096d6ced..1e43bb39eba6 100644
--- a/code/frameworks/nextjs/template/stories_nextjs-default-js/Head.stories.jsx
+++ b/code/frameworks/nextjs/template/stories_nextjs-default-js/Head.stories.jsx
@@ -1,7 +1,6 @@
-import { expect } from '@storybook/test';
import Head from 'next/head';
import React from 'react';
-import { within, userEvent, waitFor } from '@storybook/testing-library';
+import { waitFor, expect } from '@storybook/test';
function Component() {
return (
diff --git a/code/frameworks/vue3-vite/README.md b/code/frameworks/vue3-vite/README.md
index bb1eb15f980e..08eb5ae95c13 100644
--- a/code/frameworks/vue3-vite/README.md
+++ b/code/frameworks/vue3-vite/README.md
@@ -1,44 +1,3 @@
-# Storybook for Vue 3 and Vite
+# Storybook for Vue and Vite
-Storybook for Vue 3 is a UI development environment for your Vue 3 components.
-With it, you can visualize different states of your UI components and develop them interactively.
-
-![Storybook Screenshot](https://github.com/storybookjs/storybook/blob/main/media/storybook-intro.gif)
-
-Storybook runs outside of your app.
-So you can develop UI components in isolation without worrying about app specific dependencies and requirements.
-
-## Getting Started
-
-```sh
-cd my-vue3-app
-npx storybook@latest init
-```
-
-For more information visit: [storybook.js.org](https://storybook.js.org)
-
----
-
-Storybook also comes with a lot of [addons](https://storybook.js.org/addons) and a great API to customize as you wish.
-You can also build a [static version](https://storybook.js.org/docs/sharing/publish-storybook) of your Storybook and deploy it anywhere you want.
-
-## Extending the Vue application
-
-Storybook creates a [Vue 3 application](https://vuejs.org/api/application.html#application-api) for your component preview.
-When using global custom components (`app.component`), directives (`app.directive`), extensions (`app.use`), or other application methods, you will need to configure those in the `./storybook/preview.js` file.
-
-Therefore, Storybook provides you with a `setup` function exported from this package, which receives as a callback your Storybook instance, which you can interact with and add your custom configuration.
-
-```js
-// .storybook/preview.js
-
-import { setup } from '@storybook/vue3';
-
-setup((app) => {
- app.use(MyPlugin);
- app.component('my-component', MyComponent);
- app.mixin({
- /* My mixin */
- });
-});
-```
+See [documentation](https://storybook.js.org/docs/8.0/get-started/vue3-vite?renderer=vue) for installation instructions, usage examples, APIs, and more.
diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts
index 17b3d4942be1..022074fa8301 100644
--- a/code/lib/cli/src/automigrate/fixes/index.ts
+++ b/code/lib/cli/src/automigrate/fixes/index.ts
@@ -26,6 +26,7 @@ import { removeJestTestingLibrary } from './remove-jest-testing-library';
import { addonsAPI } from './addons-api';
import { mdx1to3 } from './mdx-1-to-3';
import { addonPostCSS } from './addon-postcss';
+import { upgradeStorybookRelatedDependencies } from './upgrade-storybook-related-dependencies';
export * from '../types';
@@ -56,6 +57,7 @@ export const allFixes: Fix[] = [
removeLegacyMDX1,
webpack5CompilerSetup,
mdx1to3,
+ upgradeStorybookRelatedDependencies,
];
export const initFixes: Fix[] = [eslintPlugin];
diff --git a/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.test.ts b/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.test.ts
new file mode 100644
index 000000000000..0c16309647bd
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.test.ts
@@ -0,0 +1,95 @@
+import { describe, afterEach, it, expect, vi } from 'vitest';
+import type { StorybookConfig } from '@storybook/types';
+import type { JsPackageManager } from '@storybook/core-common';
+import * as docsUtils from '../../doctor/getIncompatibleStorybookPackages';
+
+import { upgradeStorybookRelatedDependencies } from './upgrade-storybook-related-dependencies';
+
+vi.mock('../../doctor/getIncompatibleStorybookPackages');
+
+const check = async ({
+ packageManager,
+ main: mainConfig = {},
+ storybookVersion = '8.0.0',
+}: {
+ packageManager: Partial;
+ main?: Partial & Record;
+ storybookVersion?: string;
+}) => {
+ return upgradeStorybookRelatedDependencies.check({
+ packageManager: packageManager as any,
+ configDir: '',
+ mainConfig: mainConfig as any,
+ storybookVersion,
+ });
+};
+
+describe('upgrade-storybook-related-dependencies fix', () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('should detect storyshots registered in main.js', async () => {
+ const analyzedPackages = [
+ {
+ packageName: '@chromatic-com/storybook',
+ packageVersion: '1.2.9',
+ availableUpgrade: '2.0.0',
+ hasIncompatibleDependencies: false,
+ },
+ {
+ packageName: '@storybook/jest',
+ packageVersion: '0.2.3',
+ availableUpgrade: '1.0.0',
+ hasIncompatibleDependencies: false,
+ },
+ {
+ packageName: '@storybook/preset-create-react-app',
+ packageVersion: '3.2.0',
+ availableUpgrade: '8.0.0',
+ hasIncompatibleDependencies: true,
+ },
+ {
+ packageName: 'storybook',
+ packageVersion: '8.0.0',
+ availableUpgrade: undefined,
+ hasIncompatibleDependencies: true,
+ },
+ ];
+ vi.mocked(docsUtils.getIncompatibleStorybookPackages).mockResolvedValue(analyzedPackages);
+ await expect(
+ check({
+ packageManager: {
+ getAllDependencies: async () => ({
+ '@chromatic-com/storybook': '1.2.9',
+ '@storybook/jest': '0.2.3',
+ '@storybook/preset-create-react-app': '3.2.0',
+ storybook: '8.0.0',
+ }),
+ latestVersion: async (pkgName) =>
+ analyzedPackages.find((pkg) => pkg.packageName === pkgName)?.availableUpgrade || '',
+ },
+ })
+ ).resolves.toMatchInlineSnapshot(`
+ {
+ "upgradable": [
+ {
+ "afterVersion": "2.0.0",
+ "beforeVersion": "1.2.9",
+ "packageName": "@chromatic-com/storybook",
+ },
+ {
+ "afterVersion": "1.0.0",
+ "beforeVersion": "0.2.3",
+ "packageName": "@storybook/jest",
+ },
+ {
+ "afterVersion": "8.0.0",
+ "beforeVersion": "3.2.0",
+ "packageName": "@storybook/preset-create-react-app",
+ },
+ ],
+ }
+ `);
+ });
+});
diff --git a/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts b/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts
new file mode 100644
index 000000000000..5614b7e35ad0
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/upgrade-storybook-related-dependencies.ts
@@ -0,0 +1,162 @@
+import { dedent } from 'ts-dedent';
+import { cyan, yellow } from 'chalk';
+import { valid, coerce } from 'semver';
+import type { JsPackageManager } from '@storybook/core-common';
+import { isCorePackage } from '@storybook/core-common';
+import type { Fix } from '../types';
+import { getIncompatibleStorybookPackages } from '../../doctor/getIncompatibleStorybookPackages';
+
+type PackageMetadata = {
+ packageName: string;
+ beforeVersion: string | null;
+ afterVersion: string | null;
+};
+
+interface Options {
+ upgradable: PackageMetadata[];
+}
+
+async function getLatestVersions(
+ packageManager: JsPackageManager,
+ packages: [string, string][]
+): Promise {
+ return Promise.all(
+ packages.map(async ([packageName, beforeVersion]) => ({
+ packageName,
+ beforeVersion: coerce(beforeVersion)?.toString() || null,
+ afterVersion: await packageManager.latestVersion(packageName).catch(() => null),
+ }))
+ );
+}
+
+function isPackageUpgradable(
+ afterVersion: string,
+ packageName: string,
+ allDependencies: Record
+) {
+ const installedVersion = coerce(allDependencies[packageName])?.toString();
+
+ return valid(afterVersion) && afterVersion !== installedVersion;
+}
+
+/**
+ * Is the user upgrading to the `latest` version of Storybook?
+ * Let's try to pull along some of the storybook related dependencies to `latest` as well!
+ *
+ * We communicate clearly that this migration is a helping hand, but not a complete solution.
+ * The user should still manually check for other dependencies that might be incompatible.
+ *
+ * see: https://github.com/storybookjs/storybook/issues/25731#issuecomment-1977346398
+ */
+export const upgradeStorybookRelatedDependencies = {
+ id: 'upgradeStorybookRelatedDependencies',
+ versionRange: ['*.*.*', '*.*.*'],
+ promptType: 'auto',
+ promptDefaultValue: false,
+
+ async check({ packageManager, storybookVersion }) {
+ const analyzedPackages = await getIncompatibleStorybookPackages({
+ currentStorybookVersion: storybookVersion,
+ packageManager,
+ skipErrors: true,
+ });
+
+ const allDependencies = (await packageManager.getAllDependencies()) as Record;
+ const storybookDependencies = Object.keys(allDependencies)
+ .filter((dep) => dep.includes('storybook'))
+ .filter((dep) => !isCorePackage(dep));
+ const incompatibleDependencies = analyzedPackages
+ .filter((pkg) => pkg.hasIncompatibleDependencies)
+ .map((pkg) => pkg.packageName);
+
+ const uniquePackages = Array.from(
+ new Set([...storybookDependencies, ...incompatibleDependencies])
+ ).map((packageName) => [packageName, allDependencies[packageName]]) as [string, string][];
+
+ const packageVersions = await getLatestVersions(packageManager, uniquePackages);
+
+ const upgradablePackages = packageVersions.filter(
+ ({ packageName, afterVersion, beforeVersion }) => {
+ if (beforeVersion === null || afterVersion === null) {
+ return false;
+ }
+
+ return isPackageUpgradable(afterVersion, packageName, allDependencies);
+ }
+ );
+
+ return upgradablePackages.length > 0 ? { upgradable: upgradablePackages } : null;
+ },
+
+ prompt({ upgradable }) {
+ return dedent`
+ You're upgrading to the latest version of Storybook. We recommend upgrading the following packages:
+ ${upgradable
+ .map(({ packageName, afterVersion, beforeVersion }) => {
+ return `- ${cyan(packageName)}: ${cyan(beforeVersion)} => ${cyan(afterVersion)}`;
+ })
+ .join('\n')}
+
+ After upgrading, we will run the dedupe command, which could possibly have effects on dependencies that are not Storybook related.
+ see: https://docs.npmjs.com/cli/commands/npm-dedupe
+
+ Do you want to proceed (upgrade the detected packages)?
+ `;
+ },
+
+ async run({ result: { upgradable }, packageManager, dryRun }) {
+ if (dryRun) {
+ console.log(dedent`
+ We would have upgrade the following:
+ ${upgradable
+ .map(
+ ({ packageName, afterVersion, beforeVersion }) =>
+ `${packageName}: ${beforeVersion} => ${afterVersion}`
+ )
+ .join('\n')}
+ `);
+ return;
+ }
+
+ if (upgradable.length > 0) {
+ const packageJson = await packageManager.readPackageJson();
+
+ upgradable.forEach((item) => {
+ if (!item) {
+ return;
+ }
+
+ const { packageName, afterVersion: version } = item;
+ const prefixed = `^${version}`;
+
+ if (packageJson.dependencies?.[packageName]) {
+ packageJson.dependencies[packageName] = prefixed;
+ }
+ if (packageJson.devDependencies?.[packageName]) {
+ packageJson.devDependencies[packageName] = prefixed;
+ }
+ if (packageJson.peerDependencies?.[packageName]) {
+ packageJson.peerDependencies[packageName] = prefixed;
+ }
+ });
+
+ await packageManager.writePackageJson(packageJson);
+ await packageManager.installDependencies();
+
+ await packageManager
+ .executeCommand({ command: 'dedupe', args: [], stdio: 'ignore' })
+ .catch(() => {});
+
+ console.log();
+ console.log(dedent`
+ We upgraded ${yellow(upgradable.length)} packages:
+ ${upgradable
+ .map(({ packageName, afterVersion, beforeVersion }) => {
+ return `- ${cyan(packageName)}: ${cyan(beforeVersion)} => ${cyan(afterVersion)}`;
+ })
+ .join('\n')}
+ `);
+ }
+ console.log();
+ },
+} satisfies Fix;
diff --git a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
index 6a8dd9ec0e02..9203b45f225f 100644
--- a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
+++ b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
@@ -105,7 +105,7 @@ export const viteConfigFile = {
If you do already have these plugins, you can ignore this message.
You can find more information on how to do this here:
- https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-specific-vite-plugins-have-to-be-explicitly-added
+ https://storybook.js.org/docs/8.0/migration-guide/#missing-viteconfigjs-file
This change was necessary to support newer versions of Vite.
`;
@@ -115,7 +115,7 @@ export const viteConfigFile = {
Please add a vite.config.js file to your project root.
You can find more information on how to do this here:
- https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-specific-vite-plugins-have-to-be-explicitly-added
+ https://storybook.js.org/docs/8.0/migration-guide/#missing-viteconfigjs-file
This change was necessary to support newer versions of Vite.
`;
diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
index f43e84370852..eb49848ab2c0 100644
--- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
+++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
@@ -129,35 +129,7 @@ describe('getMigrationSummary', () => {
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/docs/8.0/migration-guide
- And reach out on Discord if you need help: https://discord.gg/storybook
-
- βββββββββββββββββββββββββββββββββββββββββββββββββ
-
- Critical: The following dependencies are duplicated and WILL cause unexpected behavior:
-
- @storybook/instrumenter:
- 6.0.0, 7.1.0
-
- @storybook/core-common:
- 6.0.0, 7.1.0
-
-
-
-
- Attention: The following dependencies are duplicated which might cause unexpected behavior:
-
- @storybook/addon-essentials:
- 7.0.0, 7.1.0
-
-
-
-
- Please try de-duplicating these dependencies by running yarn dedupe
-
-
-
-
- You can find more information for a given dependency by running yarn why "
+ And reach out on Discord if you need help: https://discord.gg/storybook"
`);
});
diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
index aa0503865749..12c8ac07bfa7 100644
--- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
+++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
@@ -4,7 +4,6 @@ import dedent from 'ts-dedent';
import type { InstallationMetadata } from '@storybook/core-common';
import type { FixSummary } from '../types';
import { FixStatus } from '../types';
-import { getDuplicatedDepsWarnings } from '../../doctor/getDuplicatedDepsWarnings';
export const messageDivider = '\n\n';
const segmentDivider = '\n\nβββββββββββββββββββββββββββββββββββββββββββββββββ\n\n';
@@ -75,14 +74,6 @@ export function getMigrationSummary({
And reach out on Discord if you need help: ${chalk.yellow('https://discord.gg/storybook')}
`);
- const duplicatedDepsMessage = installationMetadata
- ? getDuplicatedDepsWarnings(installationMetadata)
- : getDuplicatedDepsWarnings();
-
- if (duplicatedDepsMessage) {
- messages.push(duplicatedDepsMessage.join(messageDivider));
- }
-
const hasNoFixes = Object.values(fixResults).every((r) => r === FixStatus.UNNECESSARY);
const hasFailures = Object.values(fixResults).some(
(r) => r === FixStatus.FAILED || r === FixStatus.CHECK_FAILED
diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts
index 8a84476b5e93..20cbf98a4166 100644
--- a/code/lib/cli/src/automigrate/index.ts
+++ b/code/lib/cli/src/automigrate/index.ts
@@ -29,6 +29,9 @@ import { getMigrationSummary } from './helpers/getMigrationSummary';
import { getStorybookData } from './helpers/mainConfigFile';
import { doctor } from '../doctor';
+import { upgradeStorybookRelatedDependencies } from './fixes/upgrade-storybook-related-dependencies';
+import dedent from 'ts-dedent';
+
const logger = console;
const LOG_FILE_NAME = 'migration-storybook.log';
const LOG_FILE_PATH = join(process.cwd(), LOG_FILE_NAME);
@@ -56,8 +59,16 @@ const cleanup = () => {
};
const logAvailableMigrations = () => {
- const availableFixes = allFixes.map((f) => chalk.yellow(f.id)).join(', ');
- logger.info(`\nThe following migrations are available: ${availableFixes}`);
+ const availableFixes = allFixes
+ .map((f) => chalk.yellow(f.id))
+ .map((x) => `- ${x}`)
+ .join('\n');
+
+ console.log();
+ logger.info(dedent`
+ The following migrations are available:
+ ${availableFixes}
+ `);
};
export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
@@ -84,7 +95,7 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
throw new Error('Could not determine main config path');
}
- await automigrate({
+ const outcome = await automigrate({
...options,
packageManager,
storybookVersion,
@@ -94,7 +105,9 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
isUpgrade: false,
});
- await doctor({ configDir, packageManager: options.packageManager });
+ if (outcome) {
+ await doctor({ configDir, packageManager: options.packageManager });
+ }
};
export const automigrate = async ({
@@ -121,8 +134,21 @@ export const automigrate = async ({
return null;
}
- const selectedFixes = inputFixes || allFixes;
- const fixes = fixId ? selectedFixes.filter((f) => f.id === fixId) : selectedFixes;
+ const selectedFixes: Fix[] =
+ inputFixes ||
+ allFixes.filter((fix) => {
+ // we only allow this automigration when the user explicitly asks for it, or they are upgrading to the latest version of storybook
+ if (
+ fix.id === upgradeStorybookRelatedDependencies.id &&
+ isUpgrade !== 'latest' &&
+ fixId !== upgradeStorybookRelatedDependencies.id
+ ) {
+ return false;
+ }
+
+ return true;
+ });
+ const fixes: Fix[] = fixId ? selectedFixes.filter((f) => f.id === fixId) : selectedFixes;
if (fixId && fixes.length === 0) {
logger.info(`π No migrations found for ${chalk.magenta(fixId)}.`);
@@ -143,7 +169,7 @@ export const automigrate = async ({
mainConfigPath,
storybookVersion,
beforeVersion,
- isUpgrade,
+ isUpgrade: !!isUpgrade,
dryRun,
yes,
});
@@ -314,7 +340,7 @@ export async function runFixes({
type: 'confirm',
name: 'fix',
message: `Do you want to run the '${chalk.cyan(f.id)}' migration on your project?`,
- initial: true,
+ initial: f.promptDefaultValue ?? true,
},
{
onCancel: () => {
diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts
index d8cc9f06af3e..8dad3d3d2fa9 100644
--- a/code/lib/cli/src/automigrate/types.ts
+++ b/code/lib/cli/src/automigrate/types.ts
@@ -37,6 +37,7 @@ type BaseFix = {
versionRange: [from: string, to: string];
check: (options: CheckOptions) => Promise;
prompt: (result: ResultType) => string;
+ promptDefaultValue?: boolean;
};
type PromptType =
@@ -74,7 +75,7 @@ export interface AutofixOptions extends Omit {
- const storybookVersion = semver.coerce(currentStorybookVersion);
- const packageVersion = semver.coerce(installedVersion);
- return storybookVersion?.major !== packageVersion?.major;
-};
-
export const checkPackageCompatibility = async (dependency: string, context: Context) => {
const { currentStorybookVersion, skipErrors, packageManager } = context;
try {
@@ -46,12 +40,12 @@ export const checkPackageCompatibility = async (dependency: string, context: Con
...peerDependencies,
})
.filter(([dep]) => storybookCorePackages[dep as keyof typeof storybookCorePackages])
- .find(([, version]) => {
+ .find(([_, versionRange]) => {
// prevent issues with "tag" based versions e.g. "latest" or "next" instead of actual numbers
return (
- version &&
- semver.validRange(version) &&
- isPackageIncompatible(version, currentStorybookVersion)
+ versionRange &&
+ semver.validRange(versionRange) &&
+ !semver.satisfies(currentStorybookVersion, versionRange)
);
});
diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts
index 182e49798148..b28021be7049 100644
--- a/code/lib/cli/src/sandbox-templates.ts
+++ b/code/lib/cli/src/sandbox-templates.ts
@@ -443,7 +443,7 @@ const baseTemplates = {
},
'qwik-vite/default-ts': {
name: 'Qwik CLI Latest (Vite | TypeScript)',
- script: 'yarn create qwik basic {{beforeDir}}',
+ script: 'npm create qwik basic {{beforeDir}}',
// TODO: The community template does not provide standard stories, which is required for e2e tests. Reenable once it does.
inDevelopment: true,
expected: {
diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts
index 179c7d806a2f..e49f668f4f87 100644
--- a/code/lib/cli/src/upgrade.ts
+++ b/code/lib/cli/src/upgrade.ts
@@ -261,7 +261,7 @@ export const doUpgrade = async ({
mainConfigPath,
beforeVersion,
storybookVersion: currentVersion,
- isUpgrade: true,
+ isUpgrade: isOutdated ? true : 'latest',
});
}
diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json
index 043fe7f08f3e..807bdceaef05 100644
--- a/code/lib/core-common/package.json
+++ b/code/lib/core-common/package.json
@@ -52,7 +52,7 @@
"@yarnpkg/libzip": "2.3.0",
"chalk": "^4.1.0",
"cross-spawn": "^7.0.3",
- "esbuild": "^18.0.0 || ^19.0.0 || ^0.20.0",
+ "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0",
"esbuild-register": "^3.5.0",
"execa": "^5.0.0",
"file-system-cache": "2.3.0",
diff --git a/code/lib/manager-api/src/modules/refs.ts b/code/lib/manager-api/src/modules/refs.ts
index bc4b94755a53..4a5fca881f48 100644
--- a/code/lib/manager-api/src/modules/refs.ts
+++ b/code/lib/manager-api/src/modules/refs.ts
@@ -43,7 +43,7 @@ export interface SubAPI {
* @param {string} id - The ID of the composed ref.
* @param {API_ComposedRefUpdate} ref - The update object for the composed ref.
*/
- updateRef: (id: string, ref: API_ComposedRefUpdate) => void;
+ updateRef: (id: string, ref: API_ComposedRefUpdate) => Promise;
/**
* Gets all composed refs.
* @returns {API_Refs} - The composed refs object.
@@ -60,7 +60,7 @@ export interface SubAPI {
* @param {string} id - The ID of the composed ref.
* @param {string} url - The new URL for the composed ref.
*/
- changeRefVersion: (id: string, url: string) => void;
+ changeRefVersion: (id: string, url: string) => Promise;
/**
* Changes the state of a composed ref by its ID and previewInitialized flag.
* @param {string} id - The ID of the composed ref.
@@ -168,12 +168,12 @@ export const init: ModuleFn = (
return Object.values(refs).find(({ url }: any) => url.match(source));
},
- changeRefVersion: (id, url) => {
+ changeRefVersion: async (id, url) => {
const { versions, title } = api.getRefs()[id];
const ref: API_SetRefData = { id, url, versions, title, index: {}, expanded: true };
- api.setRef(id, { ...ref, type: 'unknown' }, false);
- api.checkRef(ref);
+ await api.setRef(id, { ...ref, type: 'unknown' }, false);
+ await api.checkRef(ref);
},
changeRefState: (id, previewInitialized) => {
const { [id]: ref, ...updated } = api.getRefs();
@@ -276,7 +276,7 @@ export const init: ModuleFn = (
return refs;
},
- setRef: (id, { storyIndex, setStoriesData, ...rest }, ready = false) => {
+ setRef: async (id, { storyIndex, setStoriesData, ...rest }, ready = false) => {
if (singleStory) {
return;
}
@@ -307,10 +307,10 @@ export const init: ModuleFn = (
index = addRefIds(index, ref);
}
- api.updateRef(id, { ...ref, ...rest, index, internal_index });
+ await api.updateRef(id, { ...ref, ...rest, index, internal_index });
},
- updateRef: (id, data) => {
+ updateRef: async (id, data) => {
const { [id]: ref, ...updated } = api.getRefs();
updated[id] = { ...ref, ...data };
@@ -320,7 +320,7 @@ export const init: ModuleFn = (
return obj;
}, {});
- store.setState({
+ await store.setState({
refs: ordered,
});
},
@@ -331,8 +331,11 @@ export const init: ModuleFn = (
const initialState: SubState['refs'] = refs;
if (runCheck) {
- Object.entries(refs).forEach(([id, ref]) => {
- api.checkRef({ ...ref!, stories: {} } as API_SetRefData);
+ new Promise(async (resolve) => {
+ for (const ref of Object.values(refs)) {
+ await api.checkRef({ ...ref!, stories: {} } as API_SetRefData);
+ }
+ resolve(undefined);
});
}
diff --git a/code/lib/manager-api/src/modules/whatsnew.ts b/code/lib/manager-api/src/modules/whatsnew.ts
index eeeb9558a59f..8f465325d503 100644
--- a/code/lib/manager-api/src/modules/whatsnew.ts
+++ b/code/lib/manager-api/src/modules/whatsnew.ts
@@ -92,10 +92,10 @@ export const init: ModuleFn = ({ fullAPI, store, provider }) => {
id: WHATS_NEW_NOTIFICATION_ID,
link: '/settings/whats-new',
content: {
- headline: whatsNewData.excerpt,
- subHeadline: "Click to learn what's new in Storybook",
+ headline: whatsNewData.title,
+ subHeadline: "Learn what's new in Storybook",
},
- icon: { name: 'hearthollow' },
+ icon: { name: 'storybook' },
onClear({ dismissed }: any) {
if (dismissed) {
setWhatsNewCache({ lastDismissedPost: whatsNewData.url });
diff --git a/code/lib/manager-api/src/tests/refs.test.ts b/code/lib/manager-api/src/tests/refs.test.ts
index 791325c27337..950b5e3e7a63 100644
--- a/code/lib/manager-api/src/tests/refs.test.ts
+++ b/code/lib/manager-api/src/tests/refs.test.ts
@@ -171,6 +171,9 @@ describe('Refs API', () => {
// given
initRefs({ provider, store } as any);
+ // the `runCheck` is async, so we need to wait for it to finish
+ await vi.waitFor(() => fetchMock.mock.calls.length > 0);
+
expect(fetchMock.mock.calls).toMatchInlineSnapshot(`
[
[
@@ -207,6 +210,9 @@ describe('Refs API', () => {
};
initRefs({ provider, store } as any);
+ // the `runCheck` is async, so we need to wait for it to finish
+ await vi.waitFor(() => fetchMock.mock.calls.length > 0);
+
expect(fetchMock.mock.calls).toMatchInlineSnapshot(`
[
[
diff --git a/code/lib/preview-api/src/index.ts b/code/lib/preview-api/src/index.ts
index 63d45114dc23..e47cdaa0a0dd 100644
--- a/code/lib/preview-api/src/index.ts
+++ b/code/lib/preview-api/src/index.ts
@@ -56,7 +56,6 @@ export {
filterArgTypes,
sanitizeStoryContextUpdate,
setProjectAnnotations,
- getPortableStoryWrapperId,
inferControls,
userOrAutoTitleFromSpecifier,
userOrAutoTitle,
diff --git a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts
index 0974f0908526..bee091bfda4b 100644
--- a/code/lib/preview-api/src/modules/store/csf/portable-stories.ts
+++ b/code/lib/preview-api/src/modules/store/csf/portable-stories.ts
@@ -26,10 +26,6 @@ import { normalizeProjectAnnotations } from './normalizeProjectAnnotations';
let globalProjectAnnotations: ProjectAnnotations = {};
-export function getPortableStoryWrapperId(storyId: string) {
- return `storybook-story-${storyId}`;
-}
-
export function setProjectAnnotations(
projectAnnotations: ProjectAnnotations | ProjectAnnotations[]
) {
@@ -99,11 +95,7 @@ export function composeStory = {
layout: 'fullscreen',
},
// More on argTypes: https://storybook.js.org/docs/api/argtypes
- argTypes: {
- onLogin: { action: 'onLogin' },
- onLogout: { action: 'onLogout' },
- onCreateAccount: { action: 'onCreateAccount' },
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
},
};
diff --git a/code/renderers/html/template/cli/ts-4-9/Header.stories.ts b/code/renderers/html/template/cli/ts-4-9/Header.stories.ts
index 7570a625a869..189c6c8abebd 100644
--- a/code/renderers/html/template/cli/ts-4-9/Header.stories.ts
+++ b/code/renderers/html/template/cli/ts-4-9/Header.stories.ts
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/html';
+import { fn } from '@storybook/test';
import type { HeaderProps } from './Header';
import { createHeader } from './Header';
@@ -12,10 +13,10 @@ const meta = {
layout: 'fullscreen',
},
// More on argTypes: https://storybook.js.org/docs/api/argtypes
- argTypes: {
- onLogin: { action: 'onLogin' },
- onLogout: { action: 'onLogout' },
- onCreateAccount: { action: 'onCreateAccount' },
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
},
} satisfies Meta;
diff --git a/code/renderers/preact/template/cli/Header.stories.jsx b/code/renderers/preact/template/cli/Header.stories.jsx
index 58b353a57db2..160f7fdff166 100644
--- a/code/renderers/preact/template/cli/Header.stories.jsx
+++ b/code/renderers/preact/template/cli/Header.stories.jsx
@@ -1,3 +1,4 @@
+import { fn } from '@storybook/test';
import { Header } from './Header';
export default {
@@ -9,10 +10,10 @@ export default {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
- argTypes: {
- onLogin: { action: 'onLogin' },
- onLogout: { action: 'onLogout' },
- onCreateAccount: { action: 'onCreateAccount' },
+ args: {
+ onLogin: fn(),
+ onLogout: fn(),
+ onCreateAccount: fn(),
},
};
diff --git a/code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap b/code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap
index 2779b21001ab..ce16b3dc224e 100644
--- a/code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap
+++ b/code/renderers/react/src/__test__/__snapshots__/portable-stories.test.tsx.snap
@@ -3,17 +3,12 @@
exports[`Renders CSF2Secondary story 1`] = `
-
-
-
+ Children coming from story args!
+
`;
@@ -21,17 +16,12 @@ exports[`Renders CSF2Secondary story 1`] = `
exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
-
-
-
+ foo
+
`;
@@ -39,17 +29,12 @@ exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
exports[`Renders CSF3Button story 1`] = `
-
-
-
+ foo
+
`;
@@ -57,23 +42,18 @@ exports[`Renders CSF3Button story 1`] = `
exports[`Renders CSF3ButtonWithRender story 1`] = `
-
-
-
- I am a custom render function
-
-
-
+
+
+ I am a custom render function
+
+
@@ -82,14 +62,9 @@ exports[`Renders CSF3ButtonWithRender story 1`] = `
exports[`Renders CSF3InputFieldFilled story 1`] = `
-
-
-
+
`;
@@ -97,17 +72,12 @@ exports[`Renders CSF3InputFieldFilled story 1`] = `
exports[`Renders CSF3Primary story 1`] = `
-
-
-
+ foo
+
`;
@@ -115,21 +85,16 @@ exports[`Renders CSF3Primary story 1`] = `
exports[`Renders LoaderStory story 1`] = `
-
-
-
- loaded data
-
-
- mockFn return value
-
+
+
+ loaded data
+
+
+ mockFn return value
diff --git a/code/renderers/react/src/docs/jsxDecorator.test.tsx b/code/renderers/react/src/docs/jsxDecorator.test.tsx
index bfb20fdd5f0d..6ed0f0eda179 100644
--- a/code/renderers/react/src/docs/jsxDecorator.test.tsx
+++ b/code/renderers/react/src/docs/jsxDecorator.test.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-underscore-dangle */
import type { FC, PropsWithChildren } from 'react';
import React, { StrictMode, createElement, Profiler } from 'react';
import type { Mock } from 'vitest';
@@ -5,7 +6,7 @@ import { vi, describe, it, expect, beforeEach } from 'vitest';
import PropTypes from 'prop-types';
import { addons, useEffect } from '@storybook/preview-api';
import { SNIPPET_RENDERED } from '@storybook/docs-tools';
-import { renderJsx, jsxDecorator } from './jsxDecorator';
+import { renderJsx, jsxDecorator, getReactSymbolName } from './jsxDecorator';
vi.mock('@storybook/preview-api');
const mockedAddons = vi.mocked(addons);
@@ -16,6 +17,18 @@ expect.addSnapshotSerializer({
test: (val) => typeof val === 'string',
});
+describe('converts React Symbol to displayName string', () => {
+ const symbolCases = [
+ ['react.suspense', 'React.Suspense'],
+ ['react.strict_mode', 'React.StrictMode'],
+ ['react.server_context.defaultValue', 'React.ServerContext.DefaultValue'],
+ ];
+
+ it.each(symbolCases)('"%s" to "%s"', (symbol, expectedValue) => {
+ expect(getReactSymbolName(Symbol(symbol))).toEqual(expectedValue);
+ });
+});
+
describe('renderJsx', () => {
it('basic', () => {
expect(renderJsx(