diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 077e70147763..05f683fc4658 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,20 @@ +## 8.0.0-alpha.8 + +- Addon Links: Remove LinkTo from direct import - [#25418](https://github.com/storybookjs/storybook/pull/25418), thanks [@yannbf](https://github.com/yannbf)! +- Addon docs: Remove deprecated parameters - [#25469](https://github.com/storybookjs/storybook/pull/25469), thanks [@yannbf](https://github.com/yannbf)! +- Builder Vite: Remove StorybookViteConfig type in favor of StorybookConfig - [#25441](https://github.com/storybookjs/storybook/pull/25441), thanks [@yannbf](https://github.com/yannbf)! +- Core: Error on explicit actions while rendering or playing - [#25238](https://github.com/storybookjs/storybook/pull/25238), thanks [@kasperpeulen](https://github.com/kasperpeulen)! +- Core: Remove collapseAll and expandAll methods - [#25486](https://github.com/storybookjs/storybook/pull/25486), thanks [@yannbf](https://github.com/yannbf)! +- Core: Remove storyIndexers in favor of experimental_indexers - [#25468](https://github.com/storybookjs/storybook/pull/25468), thanks [@yannbf](https://github.com/yannbf)! +- Core: Remove unused staticDir type - [#25415](https://github.com/storybookjs/storybook/pull/25415), thanks [@yannbf](https://github.com/yannbf)! +- Doc blocks: Remove deprecated props from Description block - [#25457](https://github.com/storybookjs/storybook/pull/25457), thanks [@yannbf](https://github.com/yannbf)! +- Manager API: Remove deprecated navigateToSettingsPage method - [#25467](https://github.com/storybookjs/storybook/pull/25467), thanks [@yannbf](https://github.com/yannbf)! +- React: Remove deprecated setGlobalConfig portable stories api - [#25442](https://github.com/storybookjs/storybook/pull/25442), thanks [@yannbf](https://github.com/yannbf)! +- TypeScript: Remove deprecated addons module types - [#25485](https://github.com/storybookjs/storybook/pull/25485), thanks [@yannbf](https://github.com/yannbf)! +- Types: Remove DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta types - [#25477](https://github.com/storybookjs/storybook/pull/25477), thanks [@yannbf](https://github.com/yannbf)! +- Types: Remove Framework in favor of Renderer types - [#25476](https://github.com/storybookjs/storybook/pull/25476), thanks [@yannbf](https://github.com/yannbf)! +- UI: Remove deprecated WithTooltip props - [#25440](https://github.com/storybookjs/storybook/pull/25440), thanks [@yannbf](https://github.com/yannbf)! + ## 8.0.0-alpha.7 - Addon-Docs: Upgrade to MDX3 - [#25303](https://github.com/storybookjs/storybook/pull/25303), thanks [@yannbf](https://github.com/yannbf)! diff --git a/MIGRATION.md b/MIGRATION.md index 6e673bbea530..9d894c1b89cd 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -29,6 +29,17 @@ - [Require Svelte 4 and up](#require-svelte-4-and-up) - [Deprecations which are now removed](#deprecations-which-are-now-removed) - [--use-npm flag in storybook CLI](#--use-npm-flag-in-storybook-cli) + - [`setGlobalConfig` from `@storybook/react`](#setglobalconfig-from-storybookreact) + - [StorybookViteConfig type from @storybook/builder-vite](#storybookviteconfig-type-from-storybookbuilder-vite) + - [props from WithTooltipComponent from @storybook/components](#props-from-withtooltipcomponent-from-storybookcomponents) + - [LinkTo direct import from addon-links](#linkto-direct-import-from-addon-links) + - [DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta TypeScript types](#decoratorfn-story-componentstory-componentstoryobj-componentstoryfn-and-componentmeta-typescript-types) + - ["Framework" TypeScript types](#framework-typescript-types) + - [`navigateToSettingsPage` method from Storybook's manager-api](#navigatetosettingspage-method-from-storybooks-manager-api) + - [storyIndexers](#storyindexers) + - [Deprecated docs parameters](#deprecated-docs-parameters) + - [Description Doc block properties](#description-doc-block-properties) + - [Manager API expandAll and collapseAll methods](#manager-api-expandall-and-collapseall-methods) - [From version 7.5.0 to 7.6.0](#from-version-750-to-760) - [CommonJS with Vite is deprecated](#commonjs-with-vite-is-deprecated) - [Using implicit actions during rendering is deprecated](#using-implicit-actions-during-rendering-is-deprecated) @@ -573,6 +584,112 @@ Starting in 8.0, Storybook requires Svelte 4 and up. The `--use-npm` is now removed. Use `--package-manager=npm` instead. [More info here](#cli-option---use-npm-deprecated). +#### `setGlobalConfig` from `@storybook/react` + +The `setGlobalConfig` (used for reusing stories in your tests) is now removed in favor of `setProjectAnnotations`. + +```ts +import { setProjectAnnotations } from `@storybook/testing-react`. +``` + +#### StorybookViteConfig type from @storybook/builder-vite + +The `StorybookViteConfig` type is now removed in favor of `StorybookConfig`: + +```ts +import type { StorybookConfig } from '@storybook/react-vite'; +``` + +#### props from WithTooltipComponent from @storybook/components + +The deprecated properties `tooltipShown`, `closeOnClick`, and `onVisibilityChange` of `WithTooltipComponent` from `@storybook/components` are now removed. Please replace them: + +```tsx + + ... + +``` + +#### LinkTo direct import from addon-links + +The `LinkTo` (React component) direct import from `@storybook/addon-links` is now removed. You have to import it from `@storybook/addon-links/react` instead. + +```ts +// before +import LinkTo from '@storybook/addon-links'; + +// after +import LinkTo from '@storybook/addon-links/react'; +``` + +#### DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta TypeScript types + +The `Story` type is now removed in favor of `StoryFn` and `StoryObj`. More info [here](#story-type-deprecated). + +The `DecoratorFn` type is now removed in favor of `Decorator`. [More info](#renamed-decoratorfn-to-decorator). + +For React, the `ComponentStory`, `ComponentStoryObj`, `ComponentStoryFn` and `ComponentMeta` types are now removed in favor of `StoryFn`, `StoryObj` and `Meta`. [More info](#componentstory-componentstoryobj-componentstoryfn-and-componentmeta-types-are-deprecated). + +#### "Framework" TypeScript types + +The Framework types such as `ReactFramework` are now removed in favor of Renderer types such as `ReactRenderer`. This affects all frameworks. [More info](#renamed-xframework-to-xrenderer). + +#### `navigateToSettingsPage` method from Storybook's manager-api + +The `navigateToSettingsPage` method from manager-api is now removed in favor of `changeSettingsTab`. + +```ts +export const Component = () => { + const api = useStorybookApi(); + + const someHandler = () => { + // Old method: api.navigateToSettingsPage('/settings/about'); + api.changeSettingsTab('about'); // the /settings path is not necessary anymore + }; + + // ... +} +``` + +#### storyIndexers + +The Storybook's main.js configuration property `storyIndexers` is now removed in favor of `experimental_indexers`. [More info](#storyindexers-is-replaced-with-experimental_indexers). + +#### Deprecated docs parameters + +The following story and meta parameters are now removed: + +```ts +parameters.docs.iframeHeight // becomes docs.story.iframeHeight +parameters.docs.inlineStories // becomes docs.story.inline +parameters.jsx.transformSource // becomes parameters.docs.source.transform +parameters.docs.transformSource // becomes parameters.docs.source.transform +parameters.docs.source.transformSource // becomes parameters.docs.source.transform +``` + +More info [here](#autodocs-changes) and [here](#source-block). + +#### Description Doc block properties + +`children`, `markdown` and `type` are now removed in favor of the `of` property. [More info](#doc-blocks). + +#### Manager API expandAll and collapseAll methods + +The `collapseAll` and `expandAll` APIs (possibly used by addons) are now removed. Please emit events for these actions instead: + +```ts +import { STORIES_COLLAPSE_ALL, STORIES_EXPAND_ALL } from '@storybook/core-events'; +import { useStorybookApi } from '@storybook/manager-api'; + +const api = useStorybookApi() +api.collapseAll() // becomes api.emit(STORIES_COLLAPSE_ALL) +api.expandAll() // becomes api.emit(STORIES_EXPAND_ALL) +``` + ## From version 7.5.0 to 7.6.0 #### CommonJS with Vite is deprecated @@ -2168,6 +2285,8 @@ During the 7.0 dev cycle we will be preparing recommendations and utilities to m #### `Story` type deprecated +_Has codemod_ + In 6.x you were able to do this: ```ts @@ -2176,24 +2295,43 @@ import type { Story } from '@storybook/react'; export const MyStory: Story = () =>
; ``` -But this will produce a deprecation warning in 7.0 because `Story` has been deprecated. -To fix the deprecation warning, use the `StoryFn` type: +However with the introduction of CSF3, the `Story` type has been deprecated in favor of two other types: `StoryFn` for CSF2 and `StoryObj` for CSF3. ```ts -import type { StoryFn } from '@storybook/react'; +import type { StoryFn, StoryObj } from '@storybook/react'; -export const MyStory: StoryFn = () =>
; +export const MyCsf2Story: StoryFn = () =>
; +export const MyCsf3Story: StoryObj = { + render: () =>
+}; ``` This change is part of our move to CSF3, which uses objects instead of functions to represent stories. You can read more about the CSF3 format here: https://storybook.js.org/blog/component-story-format-3-0/ +We have set up a codemod that attempts to automatically migrate your code for you (update the glob to suit your needs): + +``` +npx storybook@next migrate upgrade-deprecated-types --glob="**/*.stories.tsx" +``` + #### `ComponentStory`, `ComponentStoryObj`, `ComponentStoryFn` and `ComponentMeta` types are deprecated -The type of StoryObj and StoryFn have been changed in 7.0 so that both the "component" as "the props of the component" will be accepted as the generic parameter. +_Has codemod_ + +The type of `StoryObj` and `StoryFn` have been changed in 7.0 so that both the "component" as "the props of the component" will be accepted as the generic parameter. You can now replace the types: + +``` +ComponentStory -> StoryFn (CSF2) or StoryObj (CSF3) +ComponentStoryObj -> StoryObj +ComponentStoryFn -> StoryFn +ComponentMeta -> Meta +``` + +Here are a few examples: ```ts -import type { Story } from '@storybook/react'; +import type { StoryFn, StoryObj } from '@storybook/react'; import { Button, ButtonProps } from './Button'; // This works in 7.0, making the ComponentX types redundant. @@ -2213,6 +2351,12 @@ export const CSF2Story: StoryFn = (args) =>
- {caughtException && !caughtException.message?.startsWith('ignoredException') && ( + {caughtException && !isTestAssertionError(caughtException) && ( Caught exception in play function - - This story threw an error after it finished rendering which means your interactions - couldn' t be run.Go to this story' s play function in {fileName} to fix. - - {caughtException.stack || `${caughtException.name}: ${caughtException.message}`} + {printSerializedError(caughtException)} )} + {unhandledErrors && ( + + Unhandled Errors + + Found {unhandledErrors.length} unhandled error{unhandledErrors.length > 1 ? 's' : ''}{' '} + while running the play function. This might cause false positive assertions. Resolve + unhandled errors or ignore unhandled errors with setting the + test.dangerouslyIgnoreUnhandledErrors{' '} + parameter to true. + + + {unhandledErrors.map((error, i) => ( + + {printSerializedError(error)} + + ))} + + )}
{!isPlaying && !caughtException && interactions.length === 0 && ( @@ -150,3 +171,13 @@ export const InteractionsPanel: React.FC = React.memo( ); } ); + +interface SerializedError { + name: string; + stack?: string; + message: string; +} + +function printSerializedError(error: SerializedError) { + return error.stack || `${error.name}: ${error.message}`; +} diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts index 54c7c18faab5..80515b644dab 100644 --- a/code/addons/interactions/src/preview.ts +++ b/code/addons/interactions/src/preview.ts @@ -1,61 +1,22 @@ -/* eslint-disable no-param-reassign,no-underscore-dangle */ -/// - -import { addons } from '@storybook/preview-api'; -import { global } from '@storybook/global'; -import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events'; +/* eslint-disable no-underscore-dangle */ import type { - Renderer, - ArgsEnhancer, + Args, + LoaderFunction, PlayFunction, PlayFunctionContext, StepLabel, - Args, } from '@storybook/types'; import { instrument } from '@storybook/instrumenter'; -import { ModuleMocker } from 'jest-mock'; - -const JestMock = new ModuleMocker(global); -const fn = JestMock.fn.bind(JestMock); - -// Aliasing `fn` to `action` here, so we get a more descriptive label in the UI. -const { action } = instrument({ action: fn }, { retain: true }); -const channel = addons.getChannel(); -const spies: any[] = []; - -channel.on(FORCE_REMOUNT, () => spies.forEach((mock) => mock?.mockClear?.())); -channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => { - if (newPhase === 'loading') spies.forEach((mock) => mock?.mockClear?.()); -}); -const addSpies = (id: string, val: any, key?: string): any => { - try { - if (Object.prototype.toString.call(val) === '[object Object]') { - // We have to mutate the original object for this to survive HMR. - // eslint-disable-next-line no-restricted-syntax - for (const [k, v] of Object.entries(val)) val[k] = addSpies(id, v, k); - return val; - } - if (Array.isArray(val)) { - return val.map((item, index) => addSpies(id, item, `${key}[${index}]`)); - } - if (typeof val === 'function' && val.isAction && !val._isMockFunction) { - Object.defineProperty(val, 'name', { value: key, writable: false }); - Object.defineProperty(val, '__storyId__', { value: id, writable: false }); - const spy = action(val); - spies.push(spy); - return spy; - } - } catch (e) { - // ignore - } - return val; -}; - -const addActionsFromArgTypes: ArgsEnhancer = ({ id, initialArgs }) => - addSpies(id, initialArgs); +export const { step: runStep } = instrument( + { + step: (label: StepLabel, play: PlayFunction, context: PlayFunctionContext) => + play(context), + }, + { intercept: true } +); -const instrumentSpies: ArgsEnhancer = ({ initialArgs }) => { +const instrumentSpies: LoaderFunction = ({ initialArgs }) => { const argTypesWithAction = Object.entries(initialArgs).filter( ([, value]) => typeof value === 'function' && @@ -68,20 +29,13 @@ const instrumentSpies: ArgsEnhancer = ({ initialArgs }) => { const instrumented = instrument({ [key]: () => value }, { retain: true })[key]; acc[key] = instrumented(); // this enhancer is being called multiple times + // eslint-disable-next-line no-param-reassign value._instrumented = true; return acc; }, {} as Args); }; -export const argsEnhancers = [addActionsFromArgTypes, instrumentSpies]; - -export const { step: runStep } = instrument( - { - step: (label: StepLabel, play: PlayFunction, context: PlayFunctionContext) => - play(context), - }, - { intercept: true } -); +export const argsEnhancers = [instrumentSpies]; export const parameters = { throwPlayFunctionExceptions: false, diff --git a/code/addons/interactions/src/utils.ts b/code/addons/interactions/src/utils.ts new file mode 100644 index 000000000000..1b08eca12a24 --- /dev/null +++ b/code/addons/interactions/src/utils.ts @@ -0,0 +1,23 @@ +export function isTestAssertionError(error: unknown) { + return isChaiError(error) || isJestError(error); +} + +export function isChaiError(error: unknown) { + return ( + error && + typeof error === 'object' && + 'name' in error && + typeof error.name === 'string' && + error.name === 'AssertionError' + ); +} + +export function isJestError(error: unknown) { + return ( + error && + typeof error === 'object' && + 'message' in error && + typeof error.message === 'string' && + error.message.startsWith('expect(') + ); +} diff --git a/code/addons/interactions/template/stories/basics.stories.ts b/code/addons/interactions/template/stories/basics.stories.ts index e8c14245aa90..cf6e34eddf2c 100644 --- a/code/addons/interactions/template/stories/basics.stories.ts +++ b/code/addons/interactions/template/stories/basics.stories.ts @@ -1,17 +1,18 @@ import { global as globalThis } from '@storybook/global'; import { - within, - waitFor, + expect, + fn, fireEvent, userEvent, + waitFor, waitForElementToBeRemoved, -} from '@storybook/testing-library'; -import { expect } from '@storybook/jest'; + within, +} from '@storybook/test'; export default { component: globalThis.Components.Form, - argTypes: { - onSuccess: { type: 'function' }, + args: { + onSuccess: fn(), }, }; @@ -101,15 +102,14 @@ export const UserEventSetup = { const { args, canvasElement, step } = context; const user = userEvent.setup(); const canvas = within(canvasElement); - await step('Select, type and paste on input using user-event v14 setup', async () => { - const input = await canvas.getByRole('textbox'); + await step('Select and type on input using user-event v14 setup', async () => { + const input = canvas.getByRole('textbox'); await user.click(input); - await user.type(input, 'Pasting: '); - await user.paste('foobar'); + await user.type(input, 'Typing ...'); }); await step('Tab and press enter on submit button', async () => { await user.pointer([ - { keys: '[TouchA>]', target: await canvas.getByRole('textbox') }, + { keys: '[TouchA>]', target: canvas.getByRole('textbox') }, { keys: '[/TouchA]' }, ]); await user.tab(); diff --git a/code/addons/interactions/template/stories/unhandled-errors.stories.ts b/code/addons/interactions/template/stories/unhandled-errors.stories.ts new file mode 100644 index 000000000000..fcaf0144ccd4 --- /dev/null +++ b/code/addons/interactions/template/stories/unhandled-errors.stories.ts @@ -0,0 +1,23 @@ +import { global as globalThis } from '@storybook/global'; +import { userEvent, within } from '@storybook/test'; + +export default { + component: globalThis.Components.Button, + args: { + label: 'Button', + }, + argTypes: { + onClick: { type: 'function' }, + }, + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + }, +}; + +export const Default = { + play: async (context) => { + const { args, canvasElement } = context; + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole('button')); + }, +}; diff --git a/code/addons/links/src/index.ts b/code/addons/links/src/index.ts index 47751e599680..524558abc6c6 100644 --- a/code/addons/links/src/index.ts +++ b/code/addons/links/src/index.ts @@ -1,20 +1 @@ -import { dedent } from 'ts-dedent'; - -let hasWarned = false; - -/** - * @deprecated please import this specific function from @storybook/addon-links/react - */ -export function LinkTo(): null { - if (!hasWarned) { - // eslint-disable-next-line no-console - console.error(dedent` - LinkTo has moved to addon-links/react: - import LinkTo from '@storybook/addon-links/react'; - `); - hasWarned = true; - } - return null; -} - export { linkTo, hrefTo, withLinks, navigate } from './utils'; diff --git a/code/addons/themes/src/theme-switcher.tsx b/code/addons/themes/src/theme-switcher.tsx index 4fc7ffa89256..7e366fe8359e 100644 --- a/code/addons/themes/src/theme-switcher.tsx +++ b/code/addons/themes/src/theme-switcher.tsx @@ -57,7 +57,7 @@ export const ThemeSwitcher = () => { { return ( (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; diff --git a/code/e2e-tests/addon-interactions.spec.ts b/code/e2e-tests/addon-interactions.spec.ts index d77ef67beeca..5169f3fe7332 100644 --- a/code/e2e-tests/addon-interactions.spec.ts +++ b/code/e2e-tests/addon-interactions.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable jest/no-disabled-tests */ +/* eslint-disable jest/no-disabled-tests,jest/valid-title */ import { test, expect } from '@playwright/test'; import process from 'process'; import { SbPage } from './util'; @@ -15,7 +15,6 @@ test.describe('addon-interactions', () => { test('should have interactions', async ({ page }) => { // templateName is e.g. 'vue-cli/default-js' test.skip( - // eslint-disable-next-line jest/valid-title /^(lit)/i.test(`${templateName}`), `Skipping ${templateName}, which does not support addon-interactions` ); @@ -44,7 +43,6 @@ test.describe('addon-interactions', () => { test('should step through interactions', async ({ page }) => { // templateName is e.g. 'vue-cli/default-js' test.skip( - // eslint-disable-next-line jest/valid-title /^(lit)/i.test(`${templateName}`), `Skipping ${templateName}, which does not support addon-interactions` ); @@ -116,4 +114,23 @@ test.describe('addon-interactions', () => { await expect(interactionsTab).toBeVisible(); await expect(formInput).toHaveValue('final value'); }); + + test('should show unhandled errors', async ({ page }) => { + test.skip( + /^(lit)/i.test(`${templateName}`), + `Skipping ${templateName}, which does not support addon-interactions` + ); + // We trigger the implicit action error here, but angular works a bit different with implicit actions. + test.skip(/^(angular)/i.test(`${templateName}`)); + + const sbPage = new SbPage(page); + + await sbPage.deepLinkToStory(storybookUrl, 'addons/interactions/unhandled-errors', 'default'); + await sbPage.viewAddonPanel('Interactions'); + + const panel = sbPage.panelContent(); + await expect(panel).toContainText(/Fail/); + await expect(panel).toContainText(/Found 1 unhandled error/); + await expect(panel).toBeVisible(); + }); }); diff --git a/code/frameworks/angular/src/client/public-types.ts b/code/frameworks/angular/src/client/public-types.ts index 7d81b1dfbd65..e96e3d89c151 100644 --- a/code/frameworks/angular/src/client/public-types.ts +++ b/code/frameworks/angular/src/client/public-types.ts @@ -9,6 +9,7 @@ import { StrictArgs, ProjectAnnotations, } from '@storybook/types'; +import { EventEmitter } from '@angular/core'; import { AngularRenderer } from './types'; export type { Args, ArgTypes, Parameters, StrictArgs } from '@storybook/types'; @@ -20,21 +21,21 @@ export type { AngularRenderer }; * * @see [Default export](https://storybook.js.org/docs/formats/component-story-format/#default-export) */ -export type Meta = ComponentAnnotations; +export type Meta = ComponentAnnotations>; /** * Story function that represents a CSFv2 component example. * * @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports) */ -export type StoryFn = AnnotatedStoryFn; +export type StoryFn = AnnotatedStoryFn>; /** * Story function that represents a CSFv3 component example. * * @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports) */ -export type StoryObj = StoryAnnotations; +export type StoryObj = StoryAnnotations>; /** * @deprecated Use `StoryFn` instead. @@ -51,3 +52,7 @@ export type Decorator = DecoratorFunction = LoaderFunction; export type StoryContext = GenericStoryContext; export type Preview = ProjectAnnotations; + +type TransformEventType = { + [K in keyof T]: T[K] extends EventEmitter ? (e: E) => void : T[K]; +}; diff --git a/code/frameworks/angular/src/client/types.ts b/code/frameworks/angular/src/client/types.ts index ef3c4428c21e..100b3e912312 100644 --- a/code/frameworks/angular/src/client/types.ts +++ b/code/frameworks/angular/src/client/types.ts @@ -37,10 +37,6 @@ export interface StoryFnAngularReturnType { userDefinedTemplate?: boolean; } -/** - * @deprecated Use `AngularRenderer` instead. - */ -export type AngularFramework = AngularRenderer; export interface AngularRenderer extends WebRenderer { component: any; storyResult: StoryFnAngularReturnType; diff --git a/code/frameworks/angular/template/cli/button.stories.ts b/code/frameworks/angular/template/cli/button.stories.ts index 19661b149fdb..886310bed9ad 100644 --- a/code/frameworks/angular/template/cli/button.stories.ts +++ b/code/frameworks/angular/template/cli/button.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/angular'; - +import { fn } from '@storybook/test'; import { ButtonComponent } from './button.component'; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories @@ -7,17 +7,13 @@ const meta: Meta = { title: 'Example/Button', component: ButtonComponent, tags: ['autodocs'], - render: (args: ButtonComponent) => ({ - props: { - backgroundColor: null, - ...args, - }, - }), argTypes: { backgroundColor: { control: 'color', }, }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onClick: fn() }, }; export default meta; diff --git a/code/frameworks/ember/src/client/preview/types.ts b/code/frameworks/ember/src/client/preview/types.ts index 5df0b2632aa5..147f4928f240 100644 --- a/code/frameworks/ember/src/client/preview/types.ts +++ b/code/frameworks/ember/src/client/preview/types.ts @@ -13,10 +13,6 @@ export interface OptionsArgs { element: any; } -/** - * @deprecated Use `EmberRenderer` instead. - */ -export type EmberFramework = EmberRenderer; export interface EmberRenderer extends WebRenderer { component: any; storyResult: OptionsArgs; diff --git a/code/frameworks/nextjs/template/cli/js/Button.stories.js b/code/frameworks/nextjs/template/cli/js/Button.stories.js index 3a3f67ec8fb4..97b9ec0ed85d 100644 --- a/code/frameworks/nextjs/template/cli/js/Button.stories.js +++ b/code/frameworks/nextjs/template/cli/js/Button.stories.js @@ -1,3 +1,4 @@ +import { fn } from '@storybook/test'; import { Button } from './Button'; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export @@ -14,6 +15,8 @@ export default { argTypes: { backgroundColor: { control: 'color' }, }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onClick: fn() }, }; // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args diff --git a/code/frameworks/nextjs/template/cli/ts-3-8/Button.stories.ts b/code/frameworks/nextjs/template/cli/ts-3-8/Button.stories.ts index b65080126a44..2054fc59231e 100644 --- a/code/frameworks/nextjs/template/cli/ts-3-8/Button.stories.ts +++ b/code/frameworks/nextjs/template/cli/ts-3-8/Button.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; - +import { fn } from '@storybook/test'; import { Button } from './Button'; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export @@ -16,6 +16,8 @@ const meta: Meta = { argTypes: { backgroundColor: { control: 'color' }, }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onClick: fn() }, }; export default meta; diff --git a/code/frameworks/nextjs/template/cli/ts-3-8/Header.tsx b/code/frameworks/nextjs/template/cli/ts-3-8/Header.tsx index 01504601311d..c806ddf023e8 100644 --- a/code/frameworks/nextjs/template/cli/ts-3-8/Header.tsx +++ b/code/frameworks/nextjs/template/cli/ts-3-8/Header.tsx @@ -9,9 +9,9 @@ type User = { interface HeaderProps { user?: User; - onLogin: () => void; - onLogout: () => void; - onCreateAccount: () => void; + onLogin?: () => void; + onLogout?: () => void; + onCreateAccount?: () => void; } export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( diff --git a/code/frameworks/nextjs/template/cli/ts-4-9/Button.stories.ts b/code/frameworks/nextjs/template/cli/ts-4-9/Button.stories.ts index 742c3aa7b029..455a9d8601c9 100644 --- a/code/frameworks/nextjs/template/cli/ts-4-9/Button.stories.ts +++ b/code/frameworks/nextjs/template/cli/ts-4-9/Button.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; - +import { fn } from '@storybook/test'; import { Button } from './Button'; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export @@ -16,6 +16,8 @@ const meta = { argTypes: { backgroundColor: { control: 'color' }, }, + // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args + args: { onClick: fn() }, } satisfies Meta; export default meta; diff --git a/code/frameworks/nextjs/template/cli/ts-4-9/Header.tsx b/code/frameworks/nextjs/template/cli/ts-4-9/Header.tsx index 01504601311d..c806ddf023e8 100644 --- a/code/frameworks/nextjs/template/cli/ts-4-9/Header.tsx +++ b/code/frameworks/nextjs/template/cli/ts-4-9/Header.tsx @@ -9,9 +9,9 @@ type User = { interface HeaderProps { user?: User; - onLogin: () => void; - onLogout: () => void; - onCreateAccount: () => void; + onLogin?: () => void; + onLogout?: () => void; + onCreateAccount?: () => void; } export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index d7a40cefbda9..5dac6552c584 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -58,7 +58,7 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.1", "@types/node": "^18.0.0", - "svelte": "^5.0.0-next.16", + "svelte": "^5.0.0-next.28", "typescript": "^5.3.2", "vite": "^4.0.0" }, diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 70daf5764dd7..75d12dc01499 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -167,9 +167,7 @@ const getFrameworkDetails = ( const stripVersions = (addons: string[]) => addons.map((addon) => getPackageDetails(addon)[0]); const hasInteractiveStories = (rendererId: SupportedRenderers) => - ['react', 'angular', 'preact', 'svelte', 'vue', 'vue3', 'html', 'solid', 'qwik'].includes( - rendererId - ); + ['react', 'angular', 'preact', 'svelte', 'vue3', 'html', 'solid', 'qwik'].includes(rendererId); const hasFrameworkTemplates = (framework?: SupportedFrameworks) => framework ? ['angular', 'nextjs'].includes(framework) : false; @@ -263,16 +261,16 @@ export async function baseGenerator( ...(extraAddonsToInstall || []), ].filter(Boolean); + // TODO: migrate template stories in solid and qwik to use @storybook/test + if (['solid', 'qwik'].includes(rendererId)) { + addonPackages.push('@storybook/testing-library'); + } else { + addonPackages.push('@storybook/test'); + } + if (hasInteractiveStories(rendererId)) { addons.push('@storybook/addon-interactions'); addonPackages.push('@storybook/addon-interactions'); - - // TODO: migrate template stories in solid and qwik to use @storybook/test - if (['solid', 'qwik'].includes(rendererId)) { - addonPackages.push('@storybook/testing-library'); - } else { - addonPackages.push('@storybook/test'); - } } const files = await fse.readdir(process.cwd()); diff --git a/code/lib/cli/src/generators/configure.test.ts b/code/lib/cli/src/generators/configure.test.ts index 3e4076f4da43..c65710124faf 100644 --- a/code/lib/cli/src/generators/configure.test.ts +++ b/code/lib/cli/src/generators/configure.test.ts @@ -128,7 +128,6 @@ describe('configurePreview', () => { "/** @type { import('@storybook/react').Preview } */ const preview = { parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, @@ -159,7 +158,6 @@ describe('configurePreview', () => { const preview: Preview = { parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, @@ -210,7 +208,6 @@ describe('configurePreview', () => { const preview: Preview = { parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, diff --git a/code/lib/cli/src/generators/configure.ts b/code/lib/cli/src/generators/configure.ts index bbc9992be1ed..38cd265df83c 100644 --- a/code/lib/cli/src/generators/configure.ts +++ b/code/lib/cli/src/generators/configure.ts @@ -152,7 +152,6 @@ export async function configurePreview(options: ConfigurePreviewOptions) { : '' }const preview${isTypescript ? ': Preview' : ''} = { parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, diff --git a/code/lib/core-events/src/errors/preview-errors.ts b/code/lib/core-events/src/errors/preview-errors.ts index 50cb0609861a..26544d7efd8e 100644 --- a/code/lib/core-events/src/errors/preview-errors.ts +++ b/code/lib/core-events/src/errors/preview-errors.ts @@ -63,7 +63,7 @@ export class ImplicitActionsDuringRendering extends StorybookError { template() { return dedent` - We detected that you use an implicit action arg during ${this.data.phase} of your story. + We detected that you use an implicit action arg while ${this.data.phase} of your story. ${this.data.deprecated ? `\nThis is deprecated and won't work in Storybook 8 anymore.\n` : ``} Please provide an explicit spy to your args like this: import { fn } from '@storybook/test'; diff --git a/code/lib/core-events/src/errors/server-errors.ts b/code/lib/core-events/src/errors/server-errors.ts index 5f9f98d7ec8e..efdf01f0a45d 100644 --- a/code/lib/core-events/src/errors/server-errors.ts +++ b/code/lib/core-events/src/errors/server-errors.ts @@ -120,6 +120,8 @@ export class CouldNotEvaluateFrameworkError extends StorybookError { } } +// this error is not used anymore, but we keep it to maintain unique its error code +// which is used for telemetry export class ConflictingStaticDirConfigError extends StorybookError { readonly category = Category.CORE_SERVER; @@ -138,7 +140,6 @@ export class ConflictingStaticDirConfigError extends StorybookError { `; } } - export class InvalidStoriesEntryError extends StorybookError { readonly category = Category.CORE_COMMON; diff --git a/code/lib/core-events/src/index.ts b/code/lib/core-events/src/index.ts index d8d6c41c01d3..6d98e6291200 100644 --- a/code/lib/core-events/src/index.ts +++ b/code/lib/core-events/src/index.ts @@ -38,6 +38,8 @@ enum events { STORY_RENDER_PHASE_CHANGED = 'storyRenderPhaseChanged', // Emitted when the play function throws PLAY_FUNCTION_THREW_EXCEPTION = 'playFunctionThrewException', + // Emitted when there were unhandled errors while playing the story + UNHANDLED_ERRORS_WHILE_PLAYING = 'unhandledErrorsWhilePlaying', // Tell the story store to update (a subset of) a stories arg values UPDATE_STORY_ARGS = 'updateStoryArgs', // The values of a stories args just changed @@ -88,6 +90,7 @@ export const { GLOBALS_UPDATED, NAVIGATE_URL, PLAY_FUNCTION_THREW_EXCEPTION, + UNHANDLED_ERRORS_WHILE_PLAYING, PRELOAD_ENTRIES, PREVIEW_BUILDER_PROGRESS, PREVIEW_KEYDOWN, @@ -124,10 +127,6 @@ export const { TELEMETRY_ERROR, } = events; -// Used to break out of the current render without showing a redbox -// eslint-disable-next-line local-rules/no-uncategorized-errors -export const IGNORED_EXCEPTION = new Error('ignoredException'); - export interface WhatsNewCache { lastDismissedPost?: string; lastReadPost?: string; diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts index 683da79eb0f1..2ae75f747e5d 100644 --- a/code/lib/core-server/src/build-static.ts +++ b/code/lib/core-server/src/build-static.ts @@ -94,16 +94,14 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption build, }); - const [features, core, staticDirs, indexers, deprecatedStoryIndexers, stories, docsOptions] = - await Promise.all([ - presets.apply('features'), - presets.apply('core'), - presets.apply('staticDirs'), - presets.apply('experimental_indexers', []), - presets.apply('storyIndexers', []), - presets.apply('stories'), - presets.apply('docs', {}), - ]); + const [features, core, staticDirs, indexers, stories, docsOptions] = await Promise.all([ + presets.apply('features'), + presets.apply('core'), + presets.apply('staticDirs'), + presets.apply('experimental_indexers', []), + presets.apply('stories'), + presets.apply('docs', {}), + ]); if (features?.storyStoreV7 === false) { deprecate( @@ -150,7 +148,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption const normalizedStories = normalizeStories(stories, directories); const generator = new StoryIndexGenerator(normalizedStories, { ...directories, - storyIndexers: deprecatedStoryIndexers, indexers, docs: docsOptions, storyStoreV7: !!features?.storyStoreV7, diff --git a/code/lib/core-server/src/presets/common-preset.ts b/code/lib/core-server/src/presets/common-preset.ts index e1248f1f8dd1..cb68267391fe 100644 --- a/code/lib/core-server/src/presets/common-preset.ts +++ b/code/lib/core-server/src/presets/common-preset.ts @@ -46,7 +46,7 @@ export const staticDirs: PresetPropertyFn<'staticDirs'> = async (values = []) => export const favicon = async ( value: string | undefined, - options: Pick + options: Pick ) => { if (value) { return value; @@ -191,7 +191,7 @@ export const features: PresetProperty<'features'> = async (existing) => ({ storyStoreV7: true, argTypeTargetsV7: true, legacyDecoratorFileOrder: false, - disallowImplicitActionsInRenderV8: false, + disallowImplicitActionsInRenderV8: true, }); export const csfIndexer: Indexer = { diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index 1fa90d63d8b8..bc161fff6ffb 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -42,7 +42,6 @@ const getStorySortParameterMock = vi.mocked(getStorySortParameter); const options: StoryIndexGeneratorOptions = { configDir: path.join(__dirname, '__mockdata__'), workingDir: path.join(__dirname, '__mockdata__'), - storyIndexers: [], indexers: [csfIndexer], storyStoreV7: true, docs: { defaultName: 'docs', autodocs: false }, diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index d8f4e8ba95ed..96e72091073a 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -11,20 +11,17 @@ import type { DocsIndexEntry, ComponentTitle, NormalizedStoriesSpecifier, - StoryIndexer, DocsOptions, Path, Tag, StoryIndex, StoryName, Indexer, - IndexerOptions, - DeprecatedIndexer, StorybookConfigRaw, } from '@storybook/types'; import { userOrAutoTitleFromSpecifier, sortStoriesV7 } from '@storybook/preview-api'; import { commonGlobOptions, normalizeStoryPath } from '@storybook/core-common'; -import { deprecate, logger, once } from '@storybook/node-logger'; +import { logger, once } from '@storybook/node-logger'; import { getStorySortParameter } from '@storybook/csf-tools'; import { storyNameFromExport, toId } from '@storybook/csf'; import { analyze } from '@storybook/docs-mdx'; @@ -53,7 +50,6 @@ export type StoryIndexGeneratorOptions = { workingDir: Path; configDir: Path; storyStoreV7: boolean; - storyIndexers: StoryIndexer[]; indexers: Indexer[]; docs: DocsOptions; build?: StorybookConfigRaw['build']; @@ -284,25 +280,10 @@ export class StoryIndexGenerator { return title; }; - const indexer = (this.options.indexers as StoryIndexer[]) - .concat(this.options.storyIndexers) - .find((ind) => ind.test.exec(absolutePath)); + const indexer = this.options.indexers.find((ind) => ind.test.exec(absolutePath)); invariant(indexer, `No matching indexer found for ${absolutePath}`); - if (indexer.indexer) { - deprecate( - dedent`'storyIndexers' is deprecated, please use 'experimental_indexers' instead. Found a deprecated indexer with matcher: ${indexer.test} - - Refer to the migration guide at https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storyindexers-is-replaced-with-experimental_indexers` - ); - return this.extractStoriesFromDeprecatedIndexer({ - indexer: indexer.indexer, - indexerOptions: { makeTitle: defaultMakeTitle }, - absolutePath, - importPath, - }); - } - const indexInputs = await indexer.createIndex(absolutePath, { makeTitle: defaultMakeTitle }); const entries: ((StoryIndexEntryWithMetaId | DocsCacheEntry) & { tags: Tag[] })[] = @@ -362,69 +343,6 @@ export class StoryIndexGenerator { }; } - async extractStoriesFromDeprecatedIndexer({ - indexer, - indexerOptions, - absolutePath, - importPath, - }: { - indexer: DeprecatedIndexer['indexer']; - indexerOptions: IndexerOptions; - absolutePath: Path; - importPath: Path; - }) { - const csf = await indexer(absolutePath, indexerOptions); - - const entries = []; - - const componentTags = csf.meta.tags || []; - csf.stories.forEach(({ id, name, tags: storyTags, parameters }) => { - if (!parameters?.docsOnly) { - const tags = (csf.meta.tags ?? []).concat(storyTags ?? [], 'story'); - invariant(csf.meta.title); - entries.push({ - id, - title: csf.meta.title, - name, - importPath, - tags, - type: 'story', - // We need to keep track of the csf meta id so we know the component id when referencing docs below in `extractDocs` - metaId: csf.meta.id, - }); - } - }); - - if (csf.stories.length) { - const { autodocs } = this.options.docs; - const componentAutodocs = componentTags.includes(AUTODOCS_TAG); - const autodocsOptedIn = autodocs === true || (autodocs === 'tag' && componentAutodocs); - // We need a docs entry attached to the CSF file if either: - // a) it is a stories.mdx transpiled to CSF, OR - // b) we have docs page enabled for this file - if (componentTags.includes(STORIES_MDX_TAG) || autodocsOptedIn) { - const name = this.options.docs.defaultName ?? 'Docs'; - invariant(csf.meta.title, 'expected a title property in csf.meta'); - const id = toId(csf.meta.id || csf.meta.title, name); - entries.unshift({ - id, - title: csf.meta.title, - name, - importPath, - type: 'docs', - tags: [ - ...componentTags, - 'docs', - ...(autodocsOptedIn && !componentAutodocs ? [AUTODOCS_TAG] : []), - ], - storiesImports: [], - }); - } - } - - return { entries, type: 'stories', dependents: [] } as StoriesCacheEntry; - } - async extractDocs(specifier: NormalizedStoriesSpecifier, absolutePath: Path) { const relativePath = path.relative(this.options.workingDir, absolutePath); try { diff --git a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts index e76fd6b277b6..451a350755b2 100644 --- a/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts +++ b/code/lib/core-server/src/utils/__tests__/index-extraction.test.ts @@ -15,7 +15,6 @@ vi.mock('@storybook/node-logger'); const options: StoryIndexGeneratorOptions = { configDir: path.join(__dirname, '..', '__mockdata__'), workingDir: path.join(__dirname, '..', '__mockdata__'), - storyIndexers: [], indexers: [], storyStoreV7: true, docs: { defaultName: 'docs', autodocs: false }, diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts index 810b95244c72..078ddcb012d4 100644 --- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts @@ -23,14 +23,12 @@ export async function getStoryIndexGenerator( workingDir, }; const stories = options.presets.apply('stories'); - const deprecatedStoryIndexers = options.presets.apply('storyIndexers', []); const indexers = options.presets.apply('experimental_indexers', []); const docsOptions = options.presets.apply('docs', {}); const normalizedStories = normalizeStories(await stories, directories); const generator = new StoryIndexGenerator(normalizedStories, { ...directories, - storyIndexers: await deprecatedStoryIndexers, indexers: await indexers, docs: await docsOptions, workingDir, diff --git a/code/lib/core-server/src/utils/server-statics.ts b/code/lib/core-server/src/utils/server-statics.ts index aa687c5bcc88..2a0b93e1ca41 100644 --- a/code/lib/core-server/src/utils/server-statics.ts +++ b/code/lib/core-server/src/utils/server-statics.ts @@ -69,7 +69,7 @@ export const parseStaticDir = async (arg: string) => { throw new Error( dedent(chalk` Failed to load static files, no such directory: {cyan ${staticPath}} - Make sure this directory exists, or omit the {bold -s (--static-dir)} option. + Make sure this directory exists. `) ); } diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts index 61fb64b71649..8d1849e40380 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -42,7 +42,6 @@ const getInitializedStoryIndexGenerator = async ( inputNormalizedStories = normalizedStories ) => { const options: StoryIndexGeneratorOptions = { - storyIndexers: [], indexers: [csfIndexer], configDir: workingDir, workingDir, diff --git a/code/lib/csf-tools/src/enrichCsf.test.ts b/code/lib/csf-tools/src/enrichCsf.test.ts index d9871e935226..562cacd8c04b 100644 --- a/code/lib/csf-tools/src/enrichCsf.test.ts +++ b/code/lib/csf-tools/src/enrichCsf.test.ts @@ -641,7 +641,7 @@ describe('enrichCsf', () => { title: 'Button', parameters: { foo: 'bar', - docs: { inlineStories: true } + docs: { story: { inline: true } } } } export const Basic = () => React.createElement(Button); @@ -652,7 +652,7 @@ describe('enrichCsf', () => { title: 'Button', parameters: { foo: 'bar', - docs: { inlineStories: true } + docs: { story: { inline: true } } } } export const Basic = () => ; -}; -\`\`\` - -\`\`\` -code block with with no language -const a = fn({ -b: 2, -}); -\`\`\` -`, - }, -}; -export const Children: Story = { - render: (args) => ( - - {`# My Example Markdown - -An \`inline\` codeblock - -\`\`\`tsx -// TypeScript React code block -export const MyStory = () => { -return ; -}; -\`\`\` - -\`\`\` -code block with with no language -const a = fn({ -b: 2, -}); -\`\`\` -`} - - ), -}; diff --git a/code/ui/blocks/src/blocks/internal/InternalSource.stories.tsx b/code/ui/blocks/src/blocks/internal/InternalSource.stories.tsx index 7490f1d72184..035f1c5e1086 100644 --- a/code/ui/blocks/src/blocks/internal/InternalSource.stories.tsx +++ b/code/ui/blocks/src/blocks/internal/InternalSource.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import * as ParametersStories from '../../examples/SourceParameters.stories'; import { Source } from '../Source'; @@ -29,21 +28,3 @@ export const Ids: Story = { ], }, }; - -export const SourceTransformSourceParameter: Story = { - args: { - of: ParametersStories.SourceTransformSource, - }, -}; - -export const DocsTransformSourceParameter: Story = { - args: { - of: ParametersStories.DocsTransformSource, - }, -}; - -export const JsxTransformSourceParameter: Story = { - args: { - of: ParametersStories.JsxTransformSource, - }, -}; diff --git a/code/ui/blocks/src/examples/SimpleSizeTest.tsx b/code/ui/blocks/src/examples/SimpleSizeTest.tsx index 72e43ab45855..34021666c625 100644 --- a/code/ui/blocks/src/examples/SimpleSizeTest.tsx +++ b/code/ui/blocks/src/examples/SimpleSizeTest.tsx @@ -12,7 +12,7 @@ export const SimpleSizeTest = () => { margin: '-4rem -20px', }} > -

+

This story does nothing. Its only purpose is to show how its size renders in different conditions (inline/iframe/fixed height) when used in a {''} block.

diff --git a/code/ui/blocks/src/examples/SourceParameters.stories.tsx b/code/ui/blocks/src/examples/SourceParameters.stories.tsx index 08ebaf5cb46f..ec832b109e88 100644 --- a/code/ui/blocks/src/examples/SourceParameters.stories.tsx +++ b/code/ui/blocks/src/examples/SourceParameters.stories.tsx @@ -50,56 +50,6 @@ export const Transform = { }, }; -// deprecated -export const SourceTransformSource = { - args: { something: 'else' }, - parameters: { - docs: { - source: { - transformSource: ( - src: string, - storyContext: StoryContext - ) => dedent`// this comment has been added via parameters.docs.source.transformSource! - // this is the story id: ${storyContext.id} - // these are the current args: ${JSON.stringify(storyContext.args)} - ${src}`, - }, - }, - }, -}; - -// deprecated -export const DocsTransformSource = { - args: { something: 'else' }, - parameters: { - docs: { - transformSource: ( - src: string, - storyContext: StoryContext - ) => dedent`// this comment has been added via parameters.docs.transformSource! - // this is the story id: ${storyContext.id} - // these are the current args: ${JSON.stringify(storyContext.args)} - ${src}`, - }, - }, -}; - -// deprecated -export const JsxTransformSource = { - args: { something: 'else' }, - parameters: { - jsx: { - transformSource: ( - src: string, - storyContext: StoryContext - ) => dedent`// this comment has been added via parameters.jsx.transformSource! - // this is the story id: ${storyContext.id} - // these are the current args: ${JSON.stringify(storyContext.args)} - ${src}`, - }, - }, -}; - export const Code = { parameters: { docs: { source: { code } } }, }; diff --git a/code/ui/components/package.json b/code/ui/components/package.json index 148a6097ae54..74d3f290d88a 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -72,6 +72,7 @@ "devDependencies": { "@popperjs/core": "^2.6.0", "@radix-ui/react-scroll-area": "^1.0.5", + "@storybook/test": "workspace:*", "@types/react-syntax-highlighter": "11.0.5", "@types/util-deprecate": "^1.0.0", "css": "^3.0.0", diff --git a/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx b/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx index 76eab8d7c113..8ccb459095a0 100644 --- a/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx +++ b/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx @@ -1,6 +1,7 @@ import type { FunctionComponent, ComponentProps } from 'react'; import React from 'react'; import type { StoryObj } from '@storybook/react'; +import { expect, screen } from '@storybook/test'; import { styled } from '@storybook/theming'; import { TooltipMessage } from './TooltipMessage'; import { WithToolTipState as WithTooltip } from './WithTooltip'; @@ -104,6 +105,9 @@ export const SimpleClickStartOpen: StoryObj> Click me!
), + play: async () => { + await expect(await screen.findByText('Lorem ipsum dolor sit')).toBeInTheDocument(); + }, }; export const SimpleClickCloseOnClick: StoryObj> = { diff --git a/code/ui/components/src/components/tooltip/WithTooltip.tsx b/code/ui/components/src/components/tooltip/WithTooltip.tsx index bece4bc64278..e76c898d6dd0 100644 --- a/code/ui/components/src/components/tooltip/WithTooltip.tsx +++ b/code/ui/components/src/components/tooltip/WithTooltip.tsx @@ -36,18 +36,6 @@ export interface WithTooltipPureProps tooltip: ReactNode | ((p: WithHideFn) => ReactNode); children: ReactNode; onDoubleClick?: () => void; - /** - * @deprecated use `defaultVisible` property instead. This property will be removed in SB 8.0 - */ - tooltipShown?: boolean; - /** - * @deprecated use `closeOnOutsideClick` property instead. This property will be removed in SB 8.0 - */ - closeOnClick?: boolean; - /** - * @deprecated use `onVisibleChange` property instead. This property will be removed in SB 8.0 - */ - onVisibilityChange?: (visibility: boolean) => void | boolean; /** * If `true`, a click outside the trigger element closes the tooltip * @default false @@ -68,9 +56,6 @@ const WithTooltipPure: FC = ({ children, closeOnTriggerHidden, mutationObserverOptions, - closeOnClick, - tooltipShown, - onVisibilityChange, defaultVisible, delayHide, visible, @@ -94,15 +79,12 @@ const WithTooltipPure: FC = ({ { trigger, placement, - defaultVisible: defaultVisible ?? tooltipShown, + defaultVisible, delayHide, interactive, - closeOnOutsideClick: closeOnOutsideClick ?? closeOnClick, + closeOnOutsideClick, closeOnTriggerHidden, - onVisibleChange: (_isVisible) => { - onVisibilityChange?.(_isVisible); - onVisibleChange?.(_isVisible); - }, + onVisibleChange, delayShow, followCursor, mutationObserverOptions, diff --git a/code/ui/manager/src/components/layout/Layout.stories.tsx b/code/ui/manager/src/components/layout/Layout.stories.tsx index 593e83dea5ab..aea6f4a1a5f2 100644 --- a/code/ui/manager/src/components/layout/Layout.stories.tsx +++ b/code/ui/manager/src/components/layout/Layout.stories.tsx @@ -4,6 +4,7 @@ import React, { useState } from 'react'; import { styled } from '@storybook/theming'; import type { Meta, StoryObj } from '@storybook/react'; +import { fn } from '@storybook/test'; import { Layout } from './Layout'; import { LayoutProvider } from './LayoutProvider'; import MobileNavigationStoriesMeta from '../mobile/navigation/MobileNavigation.stories'; @@ -58,6 +59,7 @@ const meta = { slotSidebar: , slotPanel: , slotPages: , + setManagerLayoutState: fn(), }, parameters: { theme: 'light', diff --git a/code/ui/manager/src/components/preview/Preview.stories.tsx b/code/ui/manager/src/components/preview/Preview.stories.tsx index 624542b2f68c..0a639c9db64e 100644 --- a/code/ui/manager/src/components/preview/Preview.stories.tsx +++ b/code/ui/manager/src/components/preview/Preview.stories.tsx @@ -7,7 +7,7 @@ import { Location, BaseLocationProvider } from '@storybook/router'; import { ThemeProvider, ensure as ensureTheme, themes } from '@storybook/theming'; -import type { DecoratorFn } from '@storybook/react'; +import type { Decorator } from '@storybook/react'; import { Preview } from './Preview'; import { PrettyFakeProvider } from '../../FakeProvider'; @@ -80,7 +80,7 @@ export default { ); - }) as DecoratorFn, + }) as Decorator, ], }; diff --git a/code/ui/manager/src/components/sidebar/Heading.stories.tsx b/code/ui/manager/src/components/sidebar/Heading.stories.tsx index b958ebe2e1b1..34e08e7c02ea 100644 --- a/code/ui/manager/src/components/sidebar/Heading.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Heading.stories.tsx @@ -1,7 +1,7 @@ /* eslint-disable storybook/use-storybook-testing-library */ // @TODO: use addon-interactions and remove the rule disable above import React from 'react'; -import type { ComponentMeta, ComponentStoryObj, ComponentStoryFn } from '@storybook/react'; +import type { Meta, StoryObj, StoryFn } from '@storybook/react'; import { ThemeProvider, useTheme } from '@storybook/theming'; import type { Theme } from '@storybook/theming'; import { action } from '@storybook/addon-actions'; @@ -9,7 +9,7 @@ import { screen } from '@testing-library/dom'; import { Heading } from './Heading'; -type Story = ComponentStoryFn; +type Story = StoryFn; export default { component: Heading, @@ -19,7 +19,7 @@ export default { decorators: [ (storyFn) =>
{storyFn()}
, ], -} as ComponentMeta; +} as Meta; const menuItems = [ { title: 'Menu Item 1', onClick: action('onActivateMenuItem'), id: '1' }, @@ -223,7 +223,7 @@ export const NoBrand: Story = () => { ); }; -export const SkipToCanvasLinkFocused: ComponentStoryObj = { +export const SkipToCanvasLinkFocused: StoryObj = { args: { menu: menuItems, skipLinkHref: '#storybook-preview-wrapper', diff --git a/code/ui/manager/src/container/Menu.tsx b/code/ui/manager/src/container/Menu.tsx index 9646acf2cddf..2c8fb64c01a3 100644 --- a/code/ui/manager/src/container/Menu.tsx +++ b/code/ui/manager/src/container/Menu.tsx @@ -64,7 +64,7 @@ export const useMenu = ( () => ({ id: 'about', title: 'About your Storybook', - onClick: () => api.navigateToSettingsPage('/settings/about'), + onClick: () => api.changeSettingsTab('about'), }), [api] ); @@ -76,7 +76,7 @@ export const useMenu = ( () => ({ id: 'whats-new', title: "What's new?", - onClick: () => api.navigateToSettingsPage('/settings/whats-new'), + onClick: () => api.changeSettingsTab('whats-new'), right: whatsNewNotificationsEnabled && isWhatsNewUnread && ( Check it out ), @@ -88,7 +88,7 @@ export const useMenu = ( () => ({ id: 'shortcuts', title: 'Keyboard shortcuts', - onClick: () => api.navigateToSettingsPage('/settings/shortcuts'), + onClick: () => api.changeSettingsTab('shortcuts'), right: enableShortcuts ? : null, style: { borderBottom: `4px solid ${theme.appBorderColor}`, diff --git a/code/ui/manager/src/globals/exports.ts b/code/ui/manager/src/globals/exports.ts index adeceb1a1e8c..1da23ad426dc 100644 --- a/code/ui/manager/src/globals/exports.ts +++ b/code/ui/manager/src/globals/exports.ts @@ -139,7 +139,6 @@ export default { 'FORCE_REMOUNT', 'FORCE_RE_RENDER', 'GLOBALS_UPDATED', - 'IGNORED_EXCEPTION', 'NAVIGATE_URL', 'PLAY_FUNCTION_THREW_EXCEPTION', 'PRELOAD_ENTRIES', @@ -173,6 +172,7 @@ export default { 'STORY_UNCHANGED', 'TELEMETRY_ERROR', 'TOGGLE_WHATS_NEW_NOTIFICATIONS', + 'UNHANDLED_ERRORS_WHILE_PLAYING', 'UPDATE_GLOBALS', 'UPDATE_QUERY_PARAMS', 'UPDATE_STORY_ARGS', diff --git a/code/ui/manager/src/settings/SettingsFooter.stories.tsx b/code/ui/manager/src/settings/SettingsFooter.stories.tsx index 09ba093fb6d9..5bd0bb8f3b82 100644 --- a/code/ui/manager/src/settings/SettingsFooter.stories.tsx +++ b/code/ui/manager/src/settings/SettingsFooter.stories.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { DecoratorFn } from '@storybook/react'; +import type { Decorator } from '@storybook/react'; import SettingsFooter from './SettingsFooter'; export default { @@ -11,7 +11,7 @@ export default {
- )) as DecoratorFn, + )) as Decorator, ], }; diff --git a/code/ui/manager/src/settings/shortcuts.stories.tsx b/code/ui/manager/src/settings/shortcuts.stories.tsx index 47d7397584e4..555f5b6b0b6a 100644 --- a/code/ui/manager/src/settings/shortcuts.stories.tsx +++ b/code/ui/manager/src/settings/shortcuts.stories.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { actions as makeActions } from '@storybook/addon-actions'; -import type { DecoratorFn } from '@storybook/react'; +import type { Decorator } from '@storybook/react'; import { ShortcutsScreen } from './shortcuts'; import { defaultShortcuts } from './defaultShortcuts'; @@ -26,7 +26,7 @@ export default { >
- )) as DecoratorFn, + )) as Decorator, ], }; diff --git a/code/yarn.lock b/code/yarn.lock index bddedd2eaaec..d97a6fb453c3 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -4708,6 +4708,7 @@ __metadata: "@storybook/node-logger": "workspace:*" "@storybook/preview-api": "workspace:*" "@storybook/react-dom-shim": "workspace:*" + "@storybook/test": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" fs-extra: "npm:^11.1.0" @@ -5069,6 +5070,7 @@ __metadata: "@storybook/icons": "npm:^1.2.1" "@storybook/manager-api": "workspace:*" "@storybook/preview-api": "workspace:*" + "@storybook/test": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" "@types/color-convert": "npm:^2.0.0" @@ -5342,6 +5344,7 @@ __metadata: "@storybook/csf": "npm:^0.1.2" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.1" + "@storybook/test": "workspace:*" "@storybook/theming": "workspace:*" "@storybook/types": "workspace:*" "@types/react-syntax-highlighter": "npm:11.0.5" @@ -6393,6 +6396,7 @@ __metadata: react-dom: "npm:^18.2.0" semver: "npm:^7.3.7" serve-static: "npm:^1.14.1" + svelte: "npm:^5.0.0-next.28" trash: "npm:^7.0.0" ts-dedent: "npm:^2.0.0" ts-node: "npm:^10.9.1" @@ -6492,7 +6496,7 @@ __metadata: "@sveltejs/vite-plugin-svelte": "npm:^3.0.1" "@types/node": "npm:^18.0.0" magic-string: "npm:^0.30.0" - svelte: "npm:^5.0.0-next.16" + svelte: "npm:^5.0.0-next.28" svelte-preprocess: "npm:^5.1.1" sveltedoc-parser: "npm:^4.2.1" ts-dedent: "npm:^2.2.0" @@ -6535,7 +6539,7 @@ __metadata: "@storybook/types": "workspace:*" "@sveltejs/vite-plugin-svelte": "npm:^3.0.1" expect-type: "npm:^0.15.0" - svelte: "npm:^5.0.0-next.22" + svelte: "npm:^5.0.0-next.28" svelte-check: "npm:^3.6.1" sveltedoc-parser: "npm:^4.2.1" ts-dedent: "npm:^2.0.0" @@ -27314,29 +27318,9 @@ __metadata: languageName: node linkType: hard -"svelte@npm:^5.0.0-next.16": - version: 5.0.0-next.22 - resolution: "svelte@npm:5.0.0-next.22" - dependencies: - "@ampproject/remapping": "npm:^2.2.1" - "@jridgewell/sourcemap-codec": "npm:^1.4.15" - acorn: "npm:^8.10.0" - acorn-typescript: "npm:^1.4.11" - aria-query: "npm:^5.3.0" - axobject-query: "npm:^4.0.0" - esm-env: "npm:^1.0.0" - esrap: "npm:^1.2.1" - is-reference: "npm:^3.0.1" - locate-character: "npm:^3.0.0" - magic-string: "npm:^0.30.4" - zimmerframe: "npm:^1.1.0" - checksum: 121c0ffe925f3393581742a970d58588ac642f15d17062af16f46b4729064d48214ee158261fbcdff78b6ee143f4da361d3e696507c8c7c31580e2040e366539 - languageName: node - linkType: hard - -"svelte@npm:^5.0.0-next.22": - version: 5.0.0-next.26 - resolution: "svelte@npm:5.0.0-next.26" +"svelte@npm:^5.0.0-next.28": + version: 5.0.0-next.28 + resolution: "svelte@npm:5.0.0-next.28" dependencies: "@ampproject/remapping": "npm:^2.2.1" "@jridgewell/sourcemap-codec": "npm:^1.4.15" @@ -27350,7 +27334,7 @@ __metadata: locate-character: "npm:^3.0.0" magic-string: "npm:^0.30.4" zimmerframe: "npm:^1.1.0" - checksum: 76e5874b7afd8f9770b716ea3422c5d0e7e45a85c01bc1fa7daa43b3ae4d38ee073da7a958e3826d0370718fbdf9477769785754b8606a7403460e027f034b60 + checksum: d309cd3d1a9fe16c67a626af867288b02f6e7c49311c851aeb0f36feb5ab9603ca5594338fb933dbbada41b26faea6dcef52ed6ab3e86f54626545e53059eb28 languageName: node linkType: hard diff --git a/docs/api/doc-block-description.md b/docs/api/doc-block-description.md index 1c188a239349..ce8254d7f572 100644 --- a/docs/api/doc-block-description.md +++ b/docs/api/doc-block-description.md @@ -37,30 +37,6 @@ Specifies where to pull the description from. It can either point to a story or Descriptions are pulled from the JSDoc comments or parameters, and they are rendered as markdown. See [Writing descriptions](#writing-descriptions) for more details. -### `children` - -(⛔️ **Deprecated**) - -Type: `string` - -See [Migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#description-block-parametersnotes-and-parametersinfo). - -### `markdown` - -(⛔️ **Deprecated**) - -Type: `string` - -See [Migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#description-block-parametersnotes-and-parametersinfo). - -### `type` - -(⛔️ **Deprecated**) - -Type: `'info' | 'notes' | 'docgen' | 'auto'` - -See [Migration guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#description-block-parametersnotes-and-parametersinfo). - ## Writing descriptions There are multiple places to write the description of a component/story, depending on what you want to achieve. Descriptions can be written at the story level to describe each story of a component, or they can be written at the meta or component level to describe the component in general. diff --git a/docs/configure/styling-and-css.md b/docs/configure/styling-and-css.md index f2e70a9b6729..630e6e25ab7d 100644 --- a/docs/configure/styling-and-css.md +++ b/docs/configure/styling-and-css.md @@ -2,15 +2,18 @@ title: 'Styling and CSS' --- + + + There are many ways to include CSS in a web application, and correspondingly there are many ways to include CSS in Storybook. Usually, it is best to try and replicate what your application does with styling in Storybook’s configuration. -If you're using Vite to build your Storybook, you're covered! Storybook will use your vite config file which supports most popular styling tools out-of-the-box 🎉. However, if you're using Webpack, you may need some extra configuration. To make this easier, we recommend using [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/). +## CSS -**Note**: If you're using Storybook with Angular or Next.js, you can skip this section. Storybook will automatically use the same styling configuration as your application. +Storybook supports importing CSS files in a few different ways. Storybook will inject these tags into the preview iframe where your components render, not the Storybook Manager UI. The best way to import CSS depends on your project's configuration and your preferences. -## Importing CSS files +### Import bundled CSS (Recommended) -Storybook is pre-configured to recognize imports for CSS files. To add global CSS for all your stories, import it in [`.storybook/preview.js`](./index.md#configure-story-rendering). +All Storybooks are pre-configured to recognize imports for CSS files. To add global CSS for all your stories, import it in [`.storybook/preview.ts`](./overview.md#configure-story-rendering). These files will be subject to HMR, so you can see your changes without restarting your Storybook server. @@ -23,13 +26,89 @@ Storybook is pre-configured to recognize imports for CSS files. To add global CS -If your component files import their CSS files, this will work too. The noticeable exception to this is if you're using CSS processor tools like Sass or Postcss. +If your component files import their CSS files, this will work too. However, if you're using CSS processor tools like Sass or Postcss, you may need some more configuration. + +### Include static CSS + +If you have a global CSS file that you want to include in all your stories, you can import it in [`.storybook/preview-head.html`](./story-rendering.md#adding-to-head). +However, these files will not be subject to HMR, so you'll need to restart your Storybook server to see your changes. + + + + + + -## CSS processors +## CSS modules -If you're using Vite as your builder, you're covered! Vite supports Sass and PostCSS out-of-the-box 🎉 +### Vite -However, if you're using Webpack and want to use Sass and PostCss, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools. Or if you'd prefer, you can customize [Storybook's webpack configuration yourself](../builders/webpack.md#override-the-default-configuration) to include the appropriate loader(s). +Vite comes with CSS modules support out-of-the-box. If you have customized the CSS modules configuration in your `vite.config.js` this will automatically be applied to your Storybook as well. Read more about [Vite's CSS modules support](https://vitejs.dev/guide/features.html#css-modules). + +### Webpack + + + + +Storybook recreates your Next.js configuration, so you can use CSS modules in your stories without any extra configuration. + + + + +If you're using Webpack and want to use CSS modules, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools. + +## PostCSS + +### Vite + +Vite comes with PostCSS support out-of-the-box. If you have customized the PostCSS configuration in your `vite.config.js` this will automatically be applied to your Storybook as well. Read more about [Vite's PostCSS support](https://vitejs.dev/guide/features.html#postcss). + +### Webpack + + + + +Storybook recreates your Next.js configuration, so you can use PostCSS in your stories without any extra configuration. + + + + +If you're using Webpack and want to use PostCSS, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools. + +## CSS pre-processors + +### Vite + +Vite comes with Sass, Less, and Stylus support out-of-the-box. Read more about [Vite's CSS Pre-processor support](https://vitejs.dev/guide/features.html#css-pre-processors). + +### Webpack + + + + +Storybook recreates your Next.js configuration, so you can use Sass in your stories without any extra configuration. + + + + +If you're using Webpack and want to use Sass or less, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools. Or if you'd prefer, you can customize [Storybook's webpack configuration yourself](../builders/webpack.md#override-the-default-configuration) to include the appropriate loader(s). ## CSS-in-JS @@ -37,51 +116,77 @@ CSS-in-JS libraries are designed to use basic JavaScript, and they often work in ## Adding webfonts +### `.storybook/preview-head.html` + If you need webfonts to be available, you may need to add some code to the [`.storybook/preview-head.html`](./story-rendering.md#adding-to-head) file. We recommend including any assets with your Storybook if possible, in which case you likely want to configure the [static file location](./images-and-assets.md#serving-static-files-via-storybook-configuration). - +### `.storybook/preview.ts` -## Troubleshooting +If you're using something like [`fontsource`](https://fontsource.org/) for your fonts, you can import the needed css files in your [`.storybook/preview.ts`](./overview.md#configure-story-rendering) file. -### Styles aren't being applied with Angular + -The latest Angular releases introduced significant changes in configuring and styling projects. If you're working with an Angular version greater than 13 and your styles aren't being applied, you may need to check your `angular.json` and adjust the `builder` configuration to import your CSS: + + -```json -{ - "my-project": { - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "styles": ["src/styles.css", "src/styles.scss"] - } - } - } - } -} -``` +Storybook for Angular relies on the Angular CLI to build your stories. This means that you can use any CSS preprocessor that the Angular CLI supports. You can read more about this in the [Angular CLI documentation](https://angular.io/guide/workspace-config#style-script-config). -Additionally, if you need Storybook-specific styles that are separate from your application, you can configure the styles with [Storybook's custom builder](../get-started/install.md#troubleshooting), which will override the application's styles: +## Global styles + +To add global styles to your Storybook, you can add them to the `styles` array in your `angular.json` file. This will add the styles to the preview iframe where your components render, not the Storybook Manager UI. + +Don't forget to also add your global styles to your `build-storybook` target in your `angular.json` file. This will ensure that your global styles are included in the static build of your Storybook as well. ```json -{ + { "storybook": { "builder": "@storybook/angular:start-storybook", "options": { - "browserTarget": "my-default-project:build", - "styles": [".storybook/custom-styles.scss"] + "configDir": ".storybook", + "browserTarget": "angular-latest:build", + "compodoc": true, + "compodocArgs": ["-e", "json", "-d", "."], + "port": 6006, + "styles": [ + // Add your global styles here + "@angular/material/prebuilt-themes/indigo-pink.css", + "@fontsource/roboto/300.css", + "@fontsource/roboto/400.css", + "@fontsource/roboto/500.css", + "@fontsource/roboto/700.css", + "@fontsource/material-icons", + "src/styles.scss" + ] + } + }, + "build-storybook": { + "builder": "@storybook/angular:build-storybook", + "options": { + "configDir": ".storybook", + "browserTarget": "angular-latest:build", + "compodoc": true, + "compodocArgs": ["-e", "json", "-d", "."], + "styles": [ + // Add your global styles here + "@angular/material/prebuilt-themes/indigo-pink.css", + "@fontsource/roboto/300.css", + "@fontsource/roboto/400.css", + "@fontsource/roboto/500.css", + "@fontsource/roboto/700.css", + "@fontsource/material-icons", + "src/styles.scss" + ], + "outputDir": "storybook-static" } } } ``` -### NX component libraries not loading styles +## Troubleshooting -If you're working with Storybook and [Nx libraries](https://nx.dev/structure/library-types), -you can extend your project's configuration (i.e., `project.json`) and provide the application's styles. +If you're working with Storybook and [Nx libraries](https://nx.dev/structure/library-types), you can extend your project's configuration (i.e., project.json) and provide the application's styles. -For earlier Nx versions (before `14.1.8`), your configuration would look like this: +For earlier Nx versions (before 14.1.8), your configuration would look like this: ```json "build-storybook": { @@ -99,7 +204,7 @@ For earlier Nx versions (before `14.1.8`), your configuration would look like th } ``` -Starting with version `14.1.8`, Nx uses the Storybook builder directly, which means any configuration supplied to the builder also applies to the NX setup. If you're working with a library, you'll need to configure the styling options ( e.g., preprocessors) inside the `build-storybook` `options` configuration object. For example: +Starting with version 14.1.8, Nx uses the Storybook builder directly, which means any configuration supplied to the builder also applies to the NX setup. If you're working with a library, you'll need to configure the styling options ( e.g., preprocessors) inside the `build-storybook` options configuration object. For example: ```json "storybook": { @@ -126,6 +231,7 @@ Starting with version `14.1.8`, Nx uses the Storybook builder directly, which me } ``` -When Nx runs, it will load Storybook's configuration and styling based on [`storybook`'s `browserTarget`](https://nx.dev/storybook/extra-topics-for-angular-projects#setting-up-browsertarget). +When Nx runs, it will load Storybook's configuration and styling based on [`storybook.browserTarget`](https://nx.dev/storybook/extra-topics-for-angular-projects#setting-up-browsertarget). + diff --git a/docs/sharing/storybook-composition.md b/docs/sharing/storybook-composition.md index 987d453e51df..50a78daadf6b 100644 --- a/docs/sharing/storybook-composition.md +++ b/docs/sharing/storybook-composition.md @@ -17,7 +17,7 @@ You’ll see the composed Storybook’s stories in the sidebar alongside your ow ## Compose published Storybooks -In your [`.storybook/main.js`](../configure/index.md#configure-story-rendering) file add a `refs` field with information about the reference Storybook. Pass in a URL to a statically built Storybook. +In your [`.storybook/main.js|ts`](../configure/index.md#configure-story-rendering) file add a `refs` field with information about the reference Storybook. Pass in a URL to a statically built Storybook. @@ -30,15 +30,15 @@ In your [`.storybook/main.js`](../configure/index.md#configure-story-rendering) - + -Limitation: Addons in composed Storybooks will not work as they normally do in a non-composed Storybook. +Addons in composed Storybooks will not work as they normally do in a non-composed Storybook. ## Compose local Storybooks -You can also compose multiple Storybooks that are running locally. For instance, if you have a React Storybook and an Angular Storybook running on different ports, you can update your configuration file (i.e., `.storybook/main.js`) and reference them; +You can also compose multiple Storybooks that are running locally. For instance, if you have a React Storybook and an Angular Storybook running on different ports, you can update your configuration file (i.e., `.storybook/main.js|ts`) and reference them as follows: @@ -70,17 +70,13 @@ You can also compose Storybooks based on the current development environment (e. -Similar to the other fields available in Storybook’s configuration file, the `refs` field can also be a function that accepts a config parameter containing Storybook’s configuration object. Check the [Webpack documentation](../builders/webpack.md#override-the-default-configuration) to learn more about it. +Similar to other fields available in Storybook’s configuration file, the `refs` field can also be a function that accepts a `config` parameter containing Storybook’s configuration object. See the [API reference](../api/main-config-refs.md) for more information. ## Improve your Storybook composition -So far, we've seen how we can use composition with local or published Storybooks. As your Storybook will grow in time with your stories or through composition with other Storybooks, you can optimize the deployment process using various methods. - -### With feature flags - -If you're using Storybook 6.4, or higher, you can optimize your composition via the `buildStoriesJson` feature flag, allowing you to generate `index.json` and `stories.json` files with the required information, providing you with a seamless integration without the need for additional dependencies. For example: +Out of the box, Storybook allows you to compose Storybooks both locally and remotely with a minor change to your configuration. However, as your Storybook grows, you might want to optimize the composition process to improve the overall performance and user experience of your Storybook by enabling the `buildStoriesJson` feature flag that will generate the `index.json` and `stories.json` files with the required information to populate the UI with your composed Storybook stories automatically. For example: @@ -93,62 +89,32 @@ If you're using Storybook 6.4, or higher, you can optimize your composition via - + -If you already enabled automatic code splitting via the [`storyStoreV7`](https://storybook.js.org/docs/builders/webpack#code-splitting), you won't need this flag as it will automatically generate the `index.json` file. +If you're working with a Storybook version 7.0 or higher, this flag is enabled by default. However, if you're working with an older version and you configured your Storybook to use the [`storyStoreV7`](../api/main-config-features.md#storystorev7) feature flag, you won't need this flag as it will automatically generate the required `index.json` file for you to use. -When you compose a Storybook featuring this flag, it will use the information retrieved from the file to populate the UI with your composed Storybook stories automatically. Here's an example of the output generated by the `index.json` file: - - - - - - +## Troubleshooting -### With the CLI +### Storybook composition is not working with my project -If you're working with an outdated Storybook version or have a project-specific requirement that prevents you from enabling the `buildStoriesJson` feature flag. In that case, you can use Storybook's CLI to automatically generate the `index.json` file when you deploy your Storybook. For example: - -```shell -npx storybook extract -``` - - - -`storybook extract` uses [Puppeteer](https://www.npmjs.com/package/puppeteer), which downloads and installs Chromium. Set the environment `SB_CHROMIUM_PATH` to configure your local Chromium installation. - - - -Although a good approach to improve composition, it comes with a cost, as it will require an additional dependency being added and increased build times. Once it finishes executing, it will generate an `index.json` file in the default build directory (`storybook-static`) with the information related to your Storybook. Here’s an example of the file contents: +If you're working with an outdated Storybook version or have a project-specific requirement that prevents you from updating your Storybook to the latest version, you can rely on the Storybook CLI to generate the `index.json` file when you deploy your Storybook. For example: -Linking to a Storybook deployed using this approach will yield all the stories and other relevant information displayed in the UI. - -If you need, you can also add additional arguments to this command. For instance, if you want to generate the `index.json` file into a custom directory, you can use the following: - -```shell -npx storybook extract my-built-storybook-directory my-other-directory/index.json -``` - -When executed, it will look up a built Storybook in the `my-built-storybook-directory` and create the `index.json` file in the `my-other-directory` with all the necessary information. - - + -If you need to use the arguments, you’ll need to include both of them, or the command will fail. +The usage of a specific version of the CLI is intended as the `extract` command is not available in Storybook 8.0 or higher. It also requires you to provide additional configuration to generate the `index.json` file accurately. See the [previous documentation](../../../release-7-5/docs/sharing/storybook-composition.md) for more information. diff --git a/docs/snippets/common/storybook-extract-result.json.mdx b/docs/snippets/common/storybook-extract-result.json.mdx deleted file mode 100644 index 0bf8a886f784..000000000000 --- a/docs/snippets/common/storybook-extract-result.json.mdx +++ /dev/null @@ -1,38 +0,0 @@ -```json -{ - "v": 2, - "globalParameters": {}, - "kindParameters": { - "components/myComponent": { - "fileName": 445, - "framework": "react" - }, - "components/myOtherComponent": { - "fileName": 447, - "framework": "react" - } - }, - "stories": { - "components-mycomponent--simple": { - "id": "components-mycomponent--simple", - "name": "Simple", - "kind": "components/myComponent", - "story": "Simple", - "parameters": { - "__id": "components-mycomponent--simple", - "__isArgsStory": true - } - }, - "components-myothercomponent--simple": { - "id": "components-myothercomponent--simple", - "name": "Simple", - "kind": "components/myothercomponent", - "story": "Simple", - "parameters": { - "__id": "components-myothercomponent--simple", - "__isArgsStory": true - } - } - } -} -``` diff --git a/docs/snippets/common/storybook-extract-specific-version.npx.js.mdx b/docs/snippets/common/storybook-extract-specific-version.npx.js.mdx new file mode 100644 index 000000000000..828643a532c0 --- /dev/null +++ b/docs/snippets/common/storybook-extract-specific-version.npx.js.mdx @@ -0,0 +1,3 @@ +```shell +npx storybook@7.5.3 extract +``` diff --git a/docs/snippets/common/storybook-extract-specific-version.pnpm.js.mdx b/docs/snippets/common/storybook-extract-specific-version.pnpm.js.mdx new file mode 100644 index 000000000000..55b882441147 --- /dev/null +++ b/docs/snippets/common/storybook-extract-specific-version.pnpm.js.mdx @@ -0,0 +1,3 @@ +```shell +pnpm dlx storybook@7.5.3 extract +``` diff --git a/docs/snippets/common/storybook-extract-specific-version.yarn.js.mdx b/docs/snippets/common/storybook-extract-specific-version.yarn.js.mdx new file mode 100644 index 000000000000..b821fcd2357a --- /dev/null +++ b/docs/snippets/common/storybook-extract-specific-version.yarn.js.mdx @@ -0,0 +1,3 @@ +```shell +yarn dlx storybook@7.5.3 extract +``` diff --git a/docs/snippets/common/storybook-storiesjsonflag-result.json.mdx b/docs/snippets/common/storybook-storiesjsonflag-result.json.mdx deleted file mode 100644 index bc8c9814631a..000000000000 --- a/docs/snippets/common/storybook-storiesjsonflag-result.json.mdx +++ /dev/null @@ -1,33 +0,0 @@ -```json -{ - "v": 3, - "stories": { - "components-mycomponent--simple": { - "id": "components-mycomponent--simple", - "title": "Components/MyComponent", - "name": "Simple", - "importPath": "./src/components/MyComponent.stories.jsx", - "kind": "Components/MyComponent", - "story": "Simple", - "parameters": { - "__id": "components-mycomponent--simple", - "docsOnly": false, - "fileName": "./src/components/MyComponent.stories.jsx" - } - }, - "components-myothercomponent--simple": { - "id": "components-myothercomponent--simple", - "title": "Components/MyOtherComponent", - "name": "Simple", - "importPath": "./src/components/MyOtherComponent.stories.jsx", - "kind": "Example/Button", - "story": "Simple", - "parameters": { - "__id": "components-myothercomponent--simple", - "docsOnly": false, - "fileName": "./src/components/MyOtherComponent.stories.jsx" - } - } - } -} -``` diff --git a/docs/versions/next.json b/docs/versions/next.json index fcd9f1cce99a..7de16aa408a1 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.0.0-alpha.7","info":{"plain":"- Addon-Docs: Upgrade to MDX3 - [#25303](https://github.com/storybookjs/storybook/pull/25303), thanks [@yannbf](https://github.com/yannbf)!\n- CLI: Add Storyshots migration notice - [#25327](https://github.com/storybookjs/storybook/pull/25327), thanks [@yannbf](https://github.com/yannbf)!\n- CLI: Fix regex used in upgrade command - [#25284](https://github.com/storybookjs/storybook/pull/25284), thanks [@yannbf](https://github.com/yannbf)!\n- CLI: Remove --use-npm flag - [#25414](https://github.com/storybookjs/storybook/pull/25414), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove unused warnOnLegacyHierarchySeparator type - [#25416](https://github.com/storybookjs/storybook/pull/25416), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove vite plugins and drop Vite 3 support - [#25427](https://github.com/storybookjs/storybook/pull/25427), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Maintenance: Add comment to deprecation notice in Button component - [#25411](https://github.com/storybookjs/storybook/pull/25411), thanks [@yannbf](https://github.com/yannbf)!\n- UI: Fix about page layout - [#25396](https://github.com/storybookjs/storybook/pull/25396), thanks [@cdedreuille](https://github.com/cdedreuille)!\n- Viewport: Store viewport, rotation in globals - [#25423](https://github.com/storybookjs/storybook/pull/25423), thanks [@shilman](https://github.com/shilman)!\n- Vite: Fix Vite 5 CJS warnings - [#25005](https://github.com/storybookjs/storybook/pull/25005), thanks [@JReinhold](https://github.com/JReinhold)!"}} +{"version":"8.0.0-alpha.8","info":{"plain":"- Addon Links: Remove LinkTo from direct import - [#25418](https://github.com/storybookjs/storybook/pull/25418), thanks [@yannbf](https://github.com/yannbf)!\n- Addon docs: Remove deprecated parameters - [#25469](https://github.com/storybookjs/storybook/pull/25469), thanks [@yannbf](https://github.com/yannbf)!\n- Builder Vite: Remove StorybookViteConfig type in favor of StorybookConfig - [#25441](https://github.com/storybookjs/storybook/pull/25441), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Error on explicit actions while rendering or playing - [#25238](https://github.com/storybookjs/storybook/pull/25238), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Core: Remove collapseAll and expandAll methods - [#25486](https://github.com/storybookjs/storybook/pull/25486), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove storyIndexers in favor of experimental_indexers - [#25468](https://github.com/storybookjs/storybook/pull/25468), thanks [@yannbf](https://github.com/yannbf)!\n- Core: Remove unused staticDir type - [#25415](https://github.com/storybookjs/storybook/pull/25415), thanks [@yannbf](https://github.com/yannbf)!\n- Doc blocks: Remove deprecated props from Description block - [#25457](https://github.com/storybookjs/storybook/pull/25457), thanks [@yannbf](https://github.com/yannbf)!\n- Manager API: Remove deprecated navigateToSettingsPage method - [#25467](https://github.com/storybookjs/storybook/pull/25467), thanks [@yannbf](https://github.com/yannbf)!\n- React: Remove deprecated setGlobalConfig portable stories api - [#25442](https://github.com/storybookjs/storybook/pull/25442), thanks [@yannbf](https://github.com/yannbf)!\n- TypeScript: Remove deprecated addons module types - [#25485](https://github.com/storybookjs/storybook/pull/25485), thanks [@yannbf](https://github.com/yannbf)!\n- Types: Remove DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta types - [#25477](https://github.com/storybookjs/storybook/pull/25477), thanks [@yannbf](https://github.com/yannbf)!\n- Types: Remove Framework in favor of Renderer types - [#25476](https://github.com/storybookjs/storybook/pull/25476), thanks [@yannbf](https://github.com/yannbf)!\n- UI: Remove deprecated WithTooltip props - [#25440](https://github.com/storybookjs/storybook/pull/25440), thanks [@yannbf](https://github.com/yannbf)!"}} diff --git a/scripts/release/__tests__/ensure-next-ahead.test.ts b/scripts/release/__tests__/ensure-next-ahead.test.ts index 7e7aa3e18666..d7a3f99c03b7 100644 --- a/scripts/release/__tests__/ensure-next-ahead.test.ts +++ b/scripts/release/__tests__/ensure-next-ahead.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable global-require */ /* eslint-disable no-underscore-dangle */ import path from 'path'; import { vi, describe, it, expect, beforeEach } from 'vitest'; diff --git a/scripts/release/__tests__/label-patches.test.ts b/scripts/release/__tests__/label-patches.test.ts index 8f221f88b961..29da21758c6e 100644 --- a/scripts/release/__tests__/label-patches.test.ts +++ b/scripts/release/__tests__/label-patches.test.ts @@ -138,7 +138,7 @@ it('should label the PR associated with cherry picks in the current branch', asy .trim() : text ) - .filter((it) => it !== ''); + .filter((text) => text !== ''); expect(stderrCalls).toMatchInlineSnapshot(` [ diff --git a/scripts/release/__tests__/version.test.ts b/scripts/release/__tests__/version.test.ts index cfe382b15aee..2f5ffb8ea9b0 100644 --- a/scripts/release/__tests__/version.test.ts +++ b/scripts/release/__tests__/version.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable global-require */ /* eslint-disable no-underscore-dangle */ import { describe, it, expect, vi } from 'vitest'; import path from 'path'; diff --git a/scripts/release/__tests__/write-changelog.test.ts b/scripts/release/__tests__/write-changelog.test.ts index 80c7f036508f..5aa4c8319054 100644 --- a/scripts/release/__tests__/write-changelog.test.ts +++ b/scripts/release/__tests__/write-changelog.test.ts @@ -1,5 +1,5 @@ /* eslint-disable jest/no-mocks-import */ -/* eslint-disable global-require */ + /* eslint-disable no-underscore-dangle */ import path from 'path'; import dedent from 'ts-dedent'; diff --git a/test-storybooks/external-docs/components/AccountForm.stories.tsx b/test-storybooks/external-docs/components/AccountForm.stories.tsx index c4579abaf5e3..10280adb253a 100644 --- a/test-storybooks/external-docs/components/AccountForm.stories.tsx +++ b/test-storybooks/external-docs/components/AccountForm.stories.tsx @@ -1,6 +1,6 @@ /* eslint-disable storybook/use-storybook-testing-library */ // @TODO: use addon-interactions and remove the rule disable above -import type { ComponentStoryObj, ComponentMeta } from '@storybook/react'; +import type { StoryObj, Meta } from '@storybook/react'; import { screen } from '@testing-library/dom'; import userEvent from '@testing-library/user-event'; import { AccountForm } from './AccountForm'; @@ -12,9 +12,9 @@ export default { parameters: { layout: 'centered', }, -} as ComponentMeta; +} as Meta; -type Story = ComponentStoryObj; +type Story = StoryObj; export const Standard: Story = { args: { passwordVerification: false }, @@ -54,7 +54,7 @@ export const StandardFailHover: Story = { }, }; -export const Verification: ComponentStoryObj = { +export const Verification: StoryObj = { args: { passwordVerification: true }, };