diff --git a/CHANGELOG.md b/CHANGELOG.md index 96041db16ab..a2458954ccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 7.6.7 + +- Core: Skip no-framework error when ignorePreview=true - [#25286](https://github.com/storybookjs/storybook/pull/25286), thanks [@ndelangen](https://github.com/ndelangen)! +- Dependencies: Semver dependency fixes - [#25283](https://github.com/storybookjs/storybook/pull/25283), thanks [@ndelangen](https://github.com/ndelangen)! +- Vite: Fix pre-transform error in Vite 5 - [#25329](https://github.com/storybookjs/storybook/pull/25329), thanks [@yannbf](https://github.com/yannbf)! +- Vue3: Fix pnp by making compiler-core a dependency - [#25311](https://github.com/storybookjs/storybook/pull/25311), thanks [@shilman](https://github.com/shilman)! + ## 7.6.6 - SvelteKit: Support 2.0 modules with mocks - [#25244](https://github.com/storybookjs/storybook/pull/25244), thanks [@paoloricciuti](https://github.com/paoloricciuti)! diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index b9883dc793e..05f683fc465 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,47 @@ +## 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)! +- CLI: Add Storyshots migration notice - [#25327](https://github.com/storybookjs/storybook/pull/25327), thanks [@yannbf](https://github.com/yannbf)! +- CLI: Fix regex used in upgrade command - [#25284](https://github.com/storybookjs/storybook/pull/25284), thanks [@yannbf](https://github.com/yannbf)! +- CLI: Remove --use-npm flag - [#25414](https://github.com/storybookjs/storybook/pull/25414), thanks [@yannbf](https://github.com/yannbf)! +- Core: Remove unused warnOnLegacyHierarchySeparator type - [#25416](https://github.com/storybookjs/storybook/pull/25416), thanks [@yannbf](https://github.com/yannbf)! +- Core: Remove vite plugins and drop Vite 3 support - [#25427](https://github.com/storybookjs/storybook/pull/25427), thanks [@kasperpeulen](https://github.com/kasperpeulen)! +- Maintenance: Add comment to deprecation notice in Button component - [#25411](https://github.com/storybookjs/storybook/pull/25411), thanks [@yannbf](https://github.com/yannbf)! +- UI: Fix about page layout - [#25396](https://github.com/storybookjs/storybook/pull/25396), thanks [@cdedreuille](https://github.com/cdedreuille)! +- Viewport: Store viewport, rotation in globals - [#25423](https://github.com/storybookjs/storybook/pull/25423), thanks [@shilman](https://github.com/shilman)! +- Vite: Fix Vite 5 CJS warnings - [#25005](https://github.com/storybookjs/storybook/pull/25005), thanks [@JReinhold](https://github.com/JReinhold)! + +## 8.0.0-alpha.6 + +- NextJS: Autoconfigure public directory for new projects - [#25279](https://github.com/storybookjs/storybook/pull/25279), thanks [@shilman](https://github.com/shilman)! +- Vite: Fix pre-transform error in Vite 5 - [#25329](https://github.com/storybookjs/storybook/pull/25329), thanks [@yannbf](https://github.com/yannbf)! +- Vue3: Fix pnp by making compiler-core a dependency - [#25311](https://github.com/storybookjs/storybook/pull/25311), thanks [@shilman](https://github.com/shilman)! + +## 8.0.0-alpha.5 + +- Core: Remove the `-s` flag from build & dev - [#25266](https://github.com/storybookjs/storybook/pull/25266), thanks [@ndelangen](https://github.com/ndelangen)! +- Core: Skip no-framework error when ignorePreview=true - [#25286](https://github.com/storybookjs/storybook/pull/25286), thanks [@ndelangen](https://github.com/ndelangen)! +- Core: Unique outputDir/cacheDir for each configDir - [#25264](https://github.com/storybookjs/storybook/pull/25264), thanks [@ndelangen](https://github.com/ndelangen)! +- Dependencies: Semver dependency fixes - [#25283](https://github.com/storybookjs/storybook/pull/25283), thanks [@ndelangen](https://github.com/ndelangen)! +- NextJS: Mock out `server-only` package for RSC - [#25263](https://github.com/storybookjs/storybook/pull/25263), thanks [@shilman](https://github.com/shilman)! + ## 8.0.0-alpha.4 - API: Remove stories.json support - [#25236](https://github.com/storybookjs/storybook/pull/25236), thanks [@shilman](https://github.com/shilman)! diff --git a/MIGRATION.md b/MIGRATION.md index 787e893df97..d054a003c49 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,8 +1,15 @@

Migration

- [From version 7.x to 8.0.0](#from-version-7x-to-800) + - [Removed deprecated shim packages](#removed-deprecated-shim-packages) + - [Framework-specific Vite plugins have to be explicitly added](#framework-specific-vite-plugins-have-to-be-explicitly-added) - [Implicit actions can not be used during rendering (for example in the play function)](#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function) + - [MDX related changes](#mdx-related-changes) + - [MDX is upgraded to v3](#mdx-is-upgraded-to-v3) + - [Dropping support for \*.stories.mdx (CSF in MDX) format and MDX1 support](#dropping-support-for-storiesmdx-csf-in-mdx-format-and-mdx1-support) + - [Dropping support for id, name and story in Story block](#dropping-support-for-id-name-and-story-in-story-block) - [Core changes](#core-changes) + - [Dropping support for Yarn 1](#dropping-support-for-yarn-1) - [Dropping support for Node.js 16](#dropping-support-for-nodejs-16) - [Autotitle breaking fixes](#autotitle-breaking-fixes) - [React v18 in the manager UI (including addons)](#react-v18-in-the-manager-ui-including-addons) @@ -10,12 +17,30 @@ - [UI layout state has changed shape](#ui-layout-state-has-changed-shape) - [New UI and props for Button and IconButton components](#new-ui-and-props-for-button-and-iconbutton-components) - [Icons is deprecated](#icons-is-deprecated) - - [React-docgen component analysis by default](#react-docgen-component-analysis-by-default) - [Removed postinstall](#removed-postinstall) - [Removed stories.json](#removed-storiesjson) - [Framework-specific changes](#framework-specific-changes) - - [Angular: Drop support for Angular \< 15](#angular-drop-support-for-angular--15) - - [Next.js: Drop support for version \< 13.5](#nextjs-drop-support-for-version--135) + - [React](#react) + - [`react-docgen` component analysis by default](#react-docgen-component-analysis-by-default) + - [Next.js](#nextjs) + - [Require Next.js 13.5 and up](#require-nextjs-135-and-up) + - [Angular](#angular) + - [Require Angular 15 and up](#require-angular-15-and-up) + - [Svelte](#svelte) + - [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) @@ -331,6 +356,36 @@ ## From version 7.x to 8.0.0 +### Removed deprecated shim packages + +In Storybook 7, these packages existed for backwards compatibility, but were marked as deprecated: + +- `@storybook/addons` - this package has been split into 2 packages: `@storybook/preview-api` and `@storybook/manager-api`, see more here: [New Addons API](#new-addons-api). +- `@storybook/channel-postmessage` - this package has been merged into `@storybook/channel`. +- `@storybook/channel-websocket` - this package has been merged into `@storybook/channel`. +- `@storybook/client-api` - this package has been merged into `@storybook/preview-api`. +- `@storybook/core-client` - this package has been merged into `@storybook/preview-api`. +- `@storybook/preview-web` - this package has been merged into `@storybook/preview-api`. +- `@storybook/store` - this package has been merged into `@storybook/preview-api`. +- `@storybook/api` - this package has been replaced with `@storybook/manager-api`. + +This section explains the rationale, and the required changed you might have to make: [New Addons API](#new-addons-api) + +### Framework-specific Vite plugins have to be explicitly added + +In Storybook 7, we would automatically add frameworks-specific Vite plugins, e.g. `@vitejs/plugin-react` if not installed. +In Storybook 8 those plugins have to be added explicitly in the user's `vite.config.ts`: + +```ts +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}); +``` + ### Implicit actions can not be used during rendering (for example in the play function) In Storybook 7, we inferred if the component accepts any action props, @@ -380,8 +435,32 @@ To summarize: - This allows users and (test) integrators to run or build storybook without docgen, boosting the user performance and allows tools to give quicker feedback. - This will make sure that we can one day lazy load docgen, without changing how stories are rendered. +### MDX related changes + +#### MDX is upgraded to v3 + +Storybook now uses MDX3 under the hood. This change contains many improvements and a few small breaking changes that probably won't affect you. However we recommend checking the [migration notes from MDX here](https://mdxjs.com/blog/v3/). + +#### Dropping support for *.stories.mdx (CSF in MDX) format and MDX1 support + +In Storybook 7, we deprecated the ability of using MDX both for documentation and for defining stories in the same .stories.mdx file. It is now removed, and Storybook won't support .stories.mdx files anymore. We provide migration scripts to help you onto the new format. + +If you were using the [legacy MDX1 format](#legacy-mdx1-support), you will have to remove the `legacyMdx1` main.js feature flag and the `@storybook/mdx1-csf` package. + +Alongside with this change, the `jsxOptions` configuration was removed as it is not used anymore. + +[More info here](https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf). + +#### Dropping support for id, name and story in Story block + +Referencing stories by `id`, `name` or `story` in the Story block is not possible anymore. [More info here](#story-block). + ### Core changes +#### Dropping support for Yarn 1 + +Storybook will stop providing fixes aimed at Yarn 1 projects. This does not necessarily mean that Storybook will stop working for Yarn 1 projects, just that the team won't provide more fixes aimed at it. For context, it's been 6 years since the release of Yarn 1, and Yarn is currently in version 4, which was [released in October 2023](https://yarnpkg.com/blog/release/4.0). + #### Dropping support for Node.js 16 In Storybook 8, we have dropped Node.js 16 support since it reached end-of-life on 2023-09-11. Storybook 8 supports Node.js 18 and above. @@ -407,22 +486,24 @@ Addon authors are advised to upgrade to react v18. #### Storyshots has been removed -Storyshots was an addon for storybook which allowed users to turn their stories into automated snapshot-tests. +Storyshots was an addon for Storybook which allowed users to turn their stories into automated snapshot tests. + +Every story would automatically be taken into account and create a snapshot file. -Every story would automatically be taken into account and created a snapshot-file for. +Snapshot testing has since fallen out of favor and is no longer recommended. -Snapshot-testing has since fallen out of favor and is no longer recommended. +In addition to its limited use, and high chance of false positives, Storyshots ran code developed to run in the browser in NodeJS via JSDOM. +JSDOM has limitations and is not a perfect emulation of the browser environment; therefore, Storyshots was always a pain to set up and maintain. -In addition to it's limited use, and high chance of false-positives, storyshots ran code developed to run in the browser in NodeJS via JSDOM. -JSDOM has limitations and is not a perfect emulation of the browser environment; therefore storyshots was always a pain to setup and maintain. +The Storybook team has built the test-runner as a direct replacement, which utilizes Playwright to connect to an actual browser where Storybook runs the code. -The storybook team has build the test-runner as a direct replacement, which utilizes playwright to connect to an actual browser where storybook runs the code. +In addition, CSF has expanded to allow for play functions to be defined on stories, which allows for more complex testing scenarios, fully integrated within Storybook itself (and supported by the test-runner, and not Storyshots). -In addition CSF has expanded to allow for play-function to be defined on stories, which allows for more complex testing scenarios, fully integrated within storybook itself (and supported by the test-runner, and not storyshots). +Finally, storyStoreV7: true (the default and only option in Storybook 8), was not supported by Storyshots. -Finally `storyStoreV7: true` (the default and only options in storybook 8), was not supported by storyshots. +By removing Storyshots, the Storybook team was unblocked from moving (eventually) to an ESM-only Storybook, which is a big step towards a more modern Storybook. -By removing storyshots, the storybook team was unblocked from moving (eventually) to an ESM-only storybook, which is a big step towards a more modern storybook. +Please check the [migration guide](https://storybook.js.org/docs/writing-tests/storyshots-migration-guide) that we prepared. #### UI layout state has changed shape @@ -454,7 +535,21 @@ The `IconButton` doesn't have any deprecated props but it now uses the new `Butt In Storybook 8.0 we are introducing a new icon library available with `@storybook/icons`. We are deprecating the `Icons` component in `@storybook/components` and recommend that addon creators and Storybook maintainers use the new `@storybook/icons` component instead. -#### React-docgen component analysis by default +#### Removed postinstall + +We removed the `@storybook/postinstall` package, which provided some utilities for addons to programmatically modify user configuration files on install. This package was years out of date, so this should be a non-disruptive change. If your addon used the package, you can view the old source code [here](https://github.com/storybookjs/storybook/tree/release-7-5/code/lib/postinstall) and adapt it into your addon. + +#### Removed stories.json + +In addition to the built storybook, `storybook build` generates two files, `index.json` and `stories.json`, that list out the contents of the Storybook. `stories.json` is a legacy format and we included it for backwards compatibility. As of 8.0 we no longer build `stories.json` by default, and we will remove it completely in 9.0. + +In the meantime if you have code that relies on `stories.json`, you can find code that transforms the "v4" `index.json` to the "v3" `stories.json` format (and their respective TS types): https://github.com/storybookjs/storybook/blob/release-7-5/code/lib/core-server/src/utils/stories-json.ts#L71-L91 + +### Framework-specific changes + +#### React + +##### `react-docgen` component analysis by default In Storybook 7, we used `react-docgen-typescript` to analyze React component props and auto-generate controls. In Storybook 8, we have moved to `react-docgen` as the new default. `react-docgen` is dramatically more efficient, shaving seconds off of dev startup times. However, it only analyzes basic TypeScript constructs. @@ -470,25 +565,135 @@ export default { For more information see: https://storybook.js.org/docs/react/api/main-config-typescript#reactdocgen -#### Removed postinstall +#### Next.js -We removed the `@storybook/postinstall` package, which provided some utilities for addons to programmatically modify user configuration files on install. This package was years out of date, so this should be a non-disruptive change. If your addon used the package, you can view the old source code [here](https://github.com/storybookjs/storybook/tree/release-7-5/code/lib/postinstall) and adapt it into your addon. +##### Require Next.js 13.5 and up -#### Removed stories.json +Starting in 8.0, Storybook requires Next.js 13.5 and up. -In addition to the built storybook, `storybook build` generates two files, `index.json` and `stories.json`, that list out the contents of the Storybook. `stories.json` is a legacy format and we included it for backwards compatibility. As of 8.0 we no longer build `stories.json` by default, and we will remove it completely in 9.0. +#### Angular -In the meantime if you have code that relies on `stories.json`, you can find code that transforms the "v4" `index.json` to the "v3" `stories.json` format (and their respective TS types): https://github.com/storybookjs/storybook/blob/release-7-5/code/lib/core-server/src/utils/stories-json.ts#L71-L91 +##### Require Angular 15 and up -### Framework-specific changes +Starting in 8.0, Storybook requires Angular 15 and up. + +#### Svelte + +##### Require Svelte 4 and up + +Starting in 8.0, Storybook requires Svelte 4 and up. + +### Deprecations which are now removed + +#### --use-npm flag in storybook CLI + +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 +``` -#### Angular: Drop support for Angular \< 15 +More info [here](#autodocs-changes) and [here](#source-block). -Starting in 8.0, we drop support for Angular < 15 +#### Description Doc block properties -#### Next.js: Drop support for version \< 13.5 +`children`, `markdown` and `type` are now removed in favor of the `of` property. [More info](#doc-blocks). -Starting in 8.0, we drop support for Next.js < 13.5. +#### 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 @@ -549,7 +754,7 @@ To summarize: #### typescript.skipBabel deprecated -We will remove the `typescript.skipBabel` option in Storybook 8.0.0. Please use `typescirpt.skipCompiler` instead. +We will remove the `typescript.skipBabel` option in Storybook 8.0.0. Please use `typescript.skipCompiler` instead. #### Primary doc block accepts of prop @@ -2085,6 +2290,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 @@ -2093,24 +2300,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. @@ -2130,6 +2356,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 54c7c18faab..80515b644da 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 00000000000..1b08eca12a2 --- /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 e8c14245aa9..cf6e34eddf2 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 00000000000..fcaf0144ccd --- /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/jest/package.json b/code/addons/jest/package.json index 030b19ae4b2..264d5c218b5 100644 --- a/code/addons/jest/package.json +++ b/code/addons/jest/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-jest", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "React storybook addon that show component jest report", "keywords": [ "addon", diff --git a/code/addons/links/package.json b/code/addons/links/package.json index 28d53b499f8..89d399d22d4 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-links", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Link stories together to build demos and prototypes with your UI components", "keywords": [ "addon", diff --git a/code/addons/links/src/index.ts b/code/addons/links/src/index.ts index 47751e59968..524558abc6c 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/measure/package.json b/code/addons/measure/package.json index ed43ed640a7..fceabf0e9de 100644 --- a/code/addons/measure/package.json +++ b/code/addons/measure/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-measure", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Inspect layouts by visualizing the box model", "keywords": [ "storybook-addons", diff --git a/code/addons/outline/package.json b/code/addons/outline/package.json index f0816abfe22..77f90fabe7a 100644 --- a/code/addons/outline/package.json +++ b/code/addons/outline/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-outline", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Outline all elements with CSS to help with layout placement and alignment", "keywords": [ "storybook-addons", diff --git a/code/addons/storysource/package.json b/code/addons/storysource/package.json index a2c0a79d15f..905e79f7962 100644 --- a/code/addons/storysource/package.json +++ b/code/addons/storysource/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-storysource", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "View a story’s source code to see how it works and paste into your app", "keywords": [ "addon", diff --git a/code/addons/storysource/preset.js b/code/addons/storysource/preset.js index dd83b37f74c..b120250981f 100644 --- a/code/addons/storysource/preset.js +++ b/code/addons/storysource/preset.js @@ -1 +1 @@ -import './dist/preset'; +require('./dist/preset'); diff --git a/code/addons/themes/package.json b/code/addons/themes/package.json index d06cacc7c11..ba7d6ad06b3 100644 --- a/code/addons/themes/package.json +++ b/code/addons/themes/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/addon-themes", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Switch between multiple themes for you components in Storybook", "keywords": [ "css", diff --git a/code/addons/themes/src/theme-switcher.tsx b/code/addons/themes/src/theme-switcher.tsx index 4fc7ffa8925..7e366fe8359 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 ( { - return list - .filter((i) => i.id !== responsiveViewport.id || active.id !== i.id) - .map((i) => { - return { - ...i, - onClick: () => { - set({ ...state, selected: i.id }); - close(); - }, - }; - }); -}); +const toLinks = memoize(50)( + (list: ViewportItem[], active: LinkBase, updateGlobals, close): Link[] => { + return list + .filter((i) => i.id !== responsiveViewport.id || active.id !== i.id) + .map((i) => { + return { + ...i, + onClick: () => { + updateGlobals({ viewport: i.id }); + close(); + }, + }; + }); + } +); interface LinkBase { id: string; @@ -95,11 +97,6 @@ const IconButtonLabel = styled.div(({ theme }) => ({ marginLeft: 10, })); -interface ViewportToolState { - isRotated: boolean; - selected: string | null; -} - const getStyles = ( prevStyles: ViewportStyles | undefined, styles: Styles, @@ -115,22 +112,20 @@ const getStyles = ( export const ViewportTool: FC = memo( withTheme(({ theme }: { theme: Theme }) => { + const [globals, updateGlobals] = useGlobals(); + const { viewports = MINIMAL_VIEWPORTS, - defaultOrientation = 'portrait', - defaultViewport = responsiveViewport.id, + defaultOrientation, + defaultViewport, disable, } = useParameter(PARAM_KEY, {}); - const [state, setState] = useAddonState(ADDON_ID, { - selected: defaultViewport, - isRotated: defaultOrientation === 'landscape', - }); const list = toList(viewports); const api = useStorybookApi(); const [isTooltipVisible, setIsTooltipVisible] = useState(false); - if (!list.find((i) => i.id === defaultViewport)) { + if (defaultViewport && !list.find((i) => i.id === defaultViewport)) { // eslint-disable-next-line no-console console.warn( `Cannot find "defaultViewport" of "${defaultViewport}" in addon-viewport configs, please check the "viewports" setting in the configuration.` @@ -138,28 +133,32 @@ export const ViewportTool: FC = memo( } useEffect(() => { - registerShortcuts(api, setState, Object.keys(viewports)); - }, [viewports]); + registerShortcuts(api, globals, updateGlobals, Object.keys(viewports)); + }, [viewports, globals.viewport]); useEffect(() => { - setState({ - selected: - defaultViewport || - (state.selected && viewports[state.selected] ? state.selected : responsiveViewport.id), - isRotated: defaultOrientation === 'landscape', - }); - }, [defaultOrientation, defaultViewport]); + const defaultRotated = defaultOrientation === 'landscape'; + + if ( + (defaultViewport && globals.viewport !== defaultViewport) || + (defaultOrientation && globals.viewportRotated !== defaultRotated) + ) { + updateGlobals({ + viewport: defaultViewport, + viewportRotated: defaultRotated, + }); + } + }, [defaultOrientation, defaultViewport, globals, updateGlobals]); - const { selected, isRotated } = state; const item = - list.find((i) => i.id === selected) || + list.find((i) => i.id === globals.viewport) || list.find((i) => i.id === defaultViewport) || list.find((i) => i.default) || responsiveViewport; const ref = useRef(); - const styles = getStyles(ref.current, item.styles, isRotated); + const styles = getStyles(ref.current, item.styles, globals.viewportRotated); useEffect(() => { ref.current = styles; @@ -174,7 +173,7 @@ export const ViewportTool: FC = memo( ( - + )} closeOnOutsideClick onVisibleChange={setIsTooltipVisible} @@ -184,13 +183,13 @@ export const ViewportTool: FC = memo( title="Change the size of the preview" active={isTooltipVisible || !!styles} onDoubleClick={() => { - setState({ ...state, selected: responsiveViewport.id }); + updateGlobals({ viewport: responsiveViewport.id }); }} > {styles ? ( - {isRotated ? `${item.title} (L)` : `${item.title} (P)`} + {globals.viewportRotated ? `${item.title} (L)` : `${item.title} (P)`} ) : null} @@ -215,7 +214,7 @@ export const ViewportTool: FC = memo( key="viewport-rotate" title="Rotate viewport" onClick={() => { - setState({ ...state, isRotated: !isRotated }); + updateGlobals({ viewportRotated: !globals.viewportRotated }); }} > diff --git a/code/addons/viewport/src/preview.ts b/code/addons/viewport/src/preview.ts new file mode 100644 index 00000000000..afe0f1b5f0b --- /dev/null +++ b/code/addons/viewport/src/preview.ts @@ -0,0 +1 @@ +export const globals = { viewport: 'reset', viewportRotated: false }; diff --git a/code/addons/viewport/src/shortcuts.ts b/code/addons/viewport/src/shortcuts.ts index 39cff92af12..47fd37cbb99 100644 --- a/code/addons/viewport/src/shortcuts.ts +++ b/code/addons/viewport/src/shortcuts.ts @@ -1,10 +1,6 @@ import type { API } from '@storybook/manager-api'; import { ADDON_ID } from './constants'; - -type State = { - selected: string; - isRotated: boolean; -}; +import { globals as defaultGlobals } from './preview'; const getCurrentViewportIndex = (viewportsKeys: string[], current: string): number => viewportsKeys.indexOf(current); @@ -23,16 +19,19 @@ const getPreviousViewport = (viewportsKeys: string[], current: string): string = : viewportsKeys[currentViewportIndex - 1]; }; -export const registerShortcuts = async (api: API, setState: any, viewportsKeys: string[]) => { +export const registerShortcuts = async ( + api: API, + globals: any, + updateGlobals: any, + viewportsKeys: string[] +) => { await api.setAddonShortcut(ADDON_ID, { label: 'Previous viewport', defaultShortcut: ['shift', 'V'], actionName: 'previous', action: () => { - const { selected, isRotated } = api.getAddonState(ADDON_ID); - setState({ - selected: getPreviousViewport(viewportsKeys, selected), - isRotated, + updateGlobals({ + viewport: getPreviousViewport(viewportsKeys, globals.viewport), }); }, }); @@ -42,10 +41,8 @@ export const registerShortcuts = async (api: API, setState: any, viewportsKeys: defaultShortcut: ['V'], actionName: 'next', action: () => { - const { selected, isRotated } = api.getAddonState(ADDON_ID); - setState({ - selected: getNextViewport(viewportsKeys, selected), - isRotated, + updateGlobals({ + viewport: getNextViewport(viewportsKeys, globals.viewport), }); }, }); @@ -55,11 +52,7 @@ export const registerShortcuts = async (api: API, setState: any, viewportsKeys: defaultShortcut: ['alt', 'V'], actionName: 'reset', action: () => { - const { isRotated } = api.getAddonState(ADDON_ID); - setState({ - selected: 'reset', - isRotated, - }); + updateGlobals(defaultGlobals); }, }); }; diff --git a/code/builders/builder-manager/package.json b/code/builders/builder-manager/package.json index 389dedc5073..3dc07399dec 100644 --- a/code/builders/builder-manager/package.json +++ b/code/builders/builder-manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-manager", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook manager builder", "keywords": [ "storybook" @@ -49,14 +49,12 @@ "@storybook/manager": "workspace:*", "@storybook/node-logger": "workspace:*", "@types/ejs": "^3.1.1", - "@types/find-cache-dir": "^3.2.1", "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", "browser-assert": "^1.2.1", "ejs": "^3.1.8", "esbuild": "^0.18.0", "esbuild-plugin-alias": "^0.2.1", "express": "^4.17.3", - "find-cache-dir": "^3.0.0", "fs-extra": "^11.1.0", "process": "^0.11.10", "util": "^0.12.4" diff --git a/code/builders/builder-manager/src/index.ts b/code/builders/builder-manager/src/index.ts index 89b9846184e..b7923a64a2b 100644 --- a/code/builders/builder-manager/src/index.ts +++ b/code/builders/builder-manager/src/index.ts @@ -40,10 +40,8 @@ export const getConfig: ManagerBuilder['getConfig'] = async (options) => { ? [...addonsEntryPoints, customManagerEntryPoint] : addonsEntryPoints; - const realEntryPoints = await wrapManagerEntries(entryPoints); - return { - entryPoints: realEntryPoints, + entryPoints: await wrapManagerEntries(entryPoints, options.cacheKey), outdir: join(options.outputDir || './', 'sb-addons'), format: 'iife', write: false, diff --git a/code/builders/builder-manager/src/utils/managerEntries.ts b/code/builders/builder-manager/src/utils/managerEntries.ts index 51413bfb852..c30357851dd 100644 --- a/code/builders/builder-manager/src/utils/managerEntries.ts +++ b/code/builders/builder-manager/src/utils/managerEntries.ts @@ -1,5 +1,5 @@ -import findCacheDirectory from 'find-cache-dir'; import fs from 'fs-extra'; +import { resolvePathInStorybookCache } from '@storybook/core-common'; import { join, parse, relative, sep } from 'node:path'; import slash from 'slash'; @@ -34,11 +34,11 @@ const sanitizeFinal = (path: string) => { * * We need to wrap each managerEntry with a try-catch because if we do not, a failing managerEntry can stop execution of other managerEntries. */ -export async function wrapManagerEntries(entrypoints: string[]) { +export async function wrapManagerEntries(entrypoints: string[], uniqueId?: string) { return Promise.all( entrypoints.map(async (entry, i) => { const { name, dir } = parse(entry); - const cacheLocation = findCacheDirectory({ name: 'sb-manager' }); + const cacheLocation = resolvePathInStorybookCache('sb-manager', uniqueId); if (!cacheLocation) { throw new Error('Could not create/find cache directory'); diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index c8ecb6885ea..c4b0712b60c 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "A plugin to run and build Storybooks with Vite", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite/#readme", "bugs": { @@ -57,14 +57,12 @@ "express": "^4.17.3", "find-cache-dir": "^3.0.0", "fs-extra": "^11.1.0", - "magic-string": "^0.30.0", - "rollup": "^2.25.0 || ^3.3.0" + "magic-string": "^0.30.0" }, "devDependencies": { "@types/express": "^4.17.13", "@types/node": "^18.0.0", "glob": "^10.0.0", - "rollup": "^3.20.1", "slash": "^5.0.0", "typescript": "^5.3.2", "vite": "^4.0.4" @@ -72,7 +70,7 @@ "peerDependencies": { "@preact/preset-vite": "*", "typescript": ">= 4.3.x", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", + "vite": "^4.0.0 || ^5.0.0", "vite-plugin-glimmerx": "*" }, "peerDependenciesMeta": { diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts index 80b1ac87537..9cdac7233d6 100644 --- a/code/builders/builder-vite/src/index.ts +++ b/code/builders/builder-vite/src/index.ts @@ -5,26 +5,17 @@ import type { RequestHandler } from 'express'; import type { ViteDevServer } from 'vite'; import express from 'express'; import { dirname, join, parse } from 'path'; -import type { Options, StorybookConfig as StorybookBaseConfig } from '@storybook/types'; +import type { Options } from '@storybook/types'; import { transformIframeHtml } from './transform-iframe-html'; import { createViteServer } from './vite-server'; import { build as viteBuild } from './build'; -import type { ViteBuilder, StorybookConfigVite } from './types'; +import type { ViteBuilder } from './types'; export { withoutVitePlugins } from './utils/without-vite-plugins'; export { hasVitePlugins } from './utils/has-vite-plugins'; export * from './types'; -/** - * @deprecated - * - * Import `StorybookConfig` from your framework, such as: - * - * `import type { StorybookConfig } from '@storybook/react-vite';` - */ -export type StorybookViteConfig = StorybookBaseConfig & StorybookConfigVite; - const getAbsolutePath = (input: I): I => dirname(require.resolve(join(input, 'package.json'))) as any; diff --git a/code/builders/builder-vite/src/vite-config.test.ts b/code/builders/builder-vite/src/vite-config.test.ts index 9437bdaee67..0dfd0534ee3 100644 --- a/code/builders/builder-vite/src/vite-config.test.ts +++ b/code/builders/builder-vite/src/vite-config.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import type { Options, Presets } from '@storybook/types'; +// eslint-disable-next-line @typescript-eslint/no-restricted-imports import { loadConfigFromFile } from 'vite'; import { commonConfig } from './vite-config'; diff --git a/code/builders/builder-vite/src/vite-config.ts b/code/builders/builder-vite/src/vite-config.ts index b4d1744d383..f4f258902b2 100644 --- a/code/builders/builder-vite/src/vite-config.ts +++ b/code/builders/builder-vite/src/vite-config.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import findCacheDirectory from 'find-cache-dir'; import type { ConfigEnv, InlineConfig as ViteInlineConfig, @@ -7,7 +6,12 @@ import type { UserConfig as ViteConfig, InlineConfig, } from 'vite'; -import { isPreservingSymlinks, getFrameworkName, getBuilderOptions } from '@storybook/core-common'; +import { + isPreservingSymlinks, + getFrameworkName, + getBuilderOptions, + resolvePathInStorybookCache, +} from '@storybook/core-common'; import { globalsNameReferenceMap } from '@storybook/preview/globals'; import type { Options } from '@storybook/types'; import { @@ -54,7 +58,7 @@ export async function commonConfig( const sbConfig: InlineConfig = { configFile: false, - cacheDir: findCacheDirectory({ name: 'sb-vite' }), + cacheDir: resolvePathInStorybookCache('sb-vite', options.cacheKey), root: projectRoot, // Allow storybook deployed as subfolder. See https://github.com/storybookjs/builder-vite/issues/238 base: './', diff --git a/code/builders/builder-vite/src/vite-server.ts b/code/builders/builder-vite/src/vite-server.ts index ce4631cabae..0b1e8043502 100644 --- a/code/builders/builder-vite/src/vite-server.ts +++ b/code/builders/builder-vite/src/vite-server.ts @@ -11,6 +11,8 @@ export async function createViteServer(options: Options, devServer: Server) { const config = { ...commonCfg, + // Needed in Vite 5: https://github.com/storybookjs/storybook/issues/25256 + assetsInclude: ['/sb-preview/**'], // Set up dev server server: { middlewareMode: true, diff --git a/code/builders/builder-webpack5/package.json b/code/builders/builder-webpack5/package.json index cb2b8fe3d02..5796195c88b 100644 --- a/code/builders/builder-webpack5/package.json +++ b/code/builders/builder-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/builder-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/e2e-tests/addon-interactions.spec.ts b/code/e2e-tests/addon-interactions.spec.ts index d77ef67beec..5169f3fe733 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/package.json b/code/frameworks/angular/package.json index 1f532e8e7d2..52924cc3fb4 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/angular", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Angular: Develop Angular components in isolation with hot reloading.", "keywords": [ "storybook", diff --git a/code/frameworks/angular/src/client/public-types.ts b/code/frameworks/angular/src/client/public-types.ts index 7d81b1dfbd6..e96e3d89c15 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 ef3c4428c21..100b3e91231 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 19661b149fd..886310bed9a 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/angular/template/stories/angular-mdx.stories.mdx b/code/frameworks/angular/template/stories/angular-mdx.stories.mdx deleted file mode 100644 index afe20c14379..00000000000 --- a/code/frameworks/angular/template/stories/angular-mdx.stories.mdx +++ /dev/null @@ -1,25 +0,0 @@ -import { global as globalThis } from '@storybook/global'; -import { moduleMetadata } from '@storybook/angular'; -import { Meta, Story, Canvas } from '@storybook/addon-docs'; - -export const Button = globalThis.Components.Button; - - - -# Angular-specific MDX Stories - - - - {{ - template: ``, - props: { - text: 'hello button', - onClick: () => {}, - }, - }} - - diff --git a/code/frameworks/angular/template/stories/basics/README.stories.mdx b/code/frameworks/angular/template/stories/basics/README.mdx similarity index 100% rename from code/frameworks/angular/template/stories/basics/README.stories.mdx rename to code/frameworks/angular/template/stories/basics/README.mdx diff --git a/code/frameworks/angular/template/stories/core/README.stories.mdx b/code/frameworks/angular/template/stories/core/README.mdx similarity index 100% rename from code/frameworks/angular/template/stories/core/README.stories.mdx rename to code/frameworks/angular/template/stories/core/README.mdx diff --git a/code/frameworks/angular/template/stories/others/ngx-translate/README.stories.mdx b/code/frameworks/angular/template/stories/others/ngx-translate/README.mdx similarity index 100% rename from code/frameworks/angular/template/stories/others/ngx-translate/README.stories.mdx rename to code/frameworks/angular/template/stories/others/ngx-translate/README.mdx diff --git a/code/frameworks/ember/package.json b/code/frameworks/ember/package.json index bfb7e1d3ff3..af49a0b41b5 100644 --- a/code/frameworks/ember/package.json +++ b/code/frameworks/ember/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/ember", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.", "homepage": "https://github.com/storybookjs/storybook/tree/next/code/frameworks/ember", "bugs": { diff --git a/code/frameworks/ember/src/client/preview/types.ts b/code/frameworks/ember/src/client/preview/types.ts index 5df0b2632aa..147f4928f24 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/html-vite/package.json b/code/frameworks/html-vite/package.json index f29f1567786..3b0eec15fc1 100644 --- a/code/frameworks/html-vite/package.json +++ b/code/frameworks/html-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for HTML and Vite: Develop HTML in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/html-webpack5/package.json b/code/frameworks/html-webpack5/package.json index da35b84d27f..dc9fad92e94 100644 --- a/code/frameworks/html-webpack5/package.json +++ b/code/frameworks/html-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/html-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 8eb6878e1a8..80be950d751 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/nextjs", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Next.js", "keywords": [ "storybook", @@ -98,6 +98,7 @@ "@storybook/preview-api": "workspace:*", "@storybook/react": "workspace:*", "@types/node": "^18.0.0", + "@types/semver": "^7.3.4", "css-loader": "^6.7.3", "find-up": "^5.0.0", "fs-extra": "^11.1.0", diff --git a/code/frameworks/nextjs/template/cli/js/Button.stories.js b/code/frameworks/nextjs/template/cli/js/Button.stories.js index 3a3f67ec8fb..97b9ec0ed85 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 b65080126a4..2054fc59231 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 01504601311..c806ddf023e 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 742c3aa7b02..455a9d8601c 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 01504601311..c806ddf023e 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/preact-vite/package.json b/code/frameworks/preact-vite/package.json index bccba808391..296ce62a0f3 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Preact and Vite: Develop Preact components in isolation with Hot Reloading.", "keywords": [ "storybook" @@ -47,7 +47,6 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@preact/preset-vite": "^2.0.0", "@storybook/builder-vite": "workspace:*", "@storybook/preact": "workspace:*" }, @@ -58,7 +57,7 @@ }, "peerDependencies": { "preact": ">=10", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^4.0.0 || ^5.0.0" }, "engines": { "node": ">=16" diff --git a/code/frameworks/preact-vite/src/preset.ts b/code/frameworks/preact-vite/src/preset.ts index 2d4e18d77e9..4002182ea92 100644 --- a/code/frameworks/preact-vite/src/preset.ts +++ b/code/frameworks/preact-vite/src/preset.ts @@ -1,6 +1,4 @@ -import { hasVitePlugins } from '@storybook/builder-vite'; import type { PresetProperty } from '@storybook/types'; -import preact from '@preact/preset-vite'; import { dirname, join } from 'path'; import type { StorybookConfig } from './types'; @@ -13,14 +11,6 @@ export const core: PresetProperty<'core', StorybookConfig> = { }; export const viteFinal: StorybookConfig['viteFinal'] = async (config) => { - const { plugins = [] } = config; - - // Add Preact plugin if not present - if (!(await hasVitePlugins(plugins, ['vite:preact-jsx']))) { - plugins.push(preact()); - } - // TODO: Add docgen plugin per issue https://github.com/storybookjs/storybook/issues/19739 - return config; }; diff --git a/code/frameworks/preact-webpack5/package.json b/code/frameworks/preact-webpack5/package.json index 5ed8ab87fd2..285a187a920 100644 --- a/code/frameworks/preact-webpack5/package.json +++ b/code/frameworks/preact-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/preact-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Preact: Develop Preact Component in isolation.", "keywords": [ "storybook" diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index ec13392db39..3b6d1e3b338 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for React and Vite: Develop React components in isolation with Hot Reloading.", "keywords": [ "storybook" @@ -51,7 +51,6 @@ "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "workspace:*", "@storybook/react": "workspace:*", - "@vitejs/plugin-react": "^3.0.1", "magic-string": "^0.30.0", "react-docgen": "^7.0.0" }, @@ -63,7 +62,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^4.0.0 || ^5.0.0" }, "engines": { "node": ">=16" diff --git a/code/frameworks/react-vite/src/preset.ts b/code/frameworks/react-vite/src/preset.ts index 207f60988eb..0defee00396 100644 --- a/code/frameworks/react-vite/src/preset.ts +++ b/code/frameworks/react-vite/src/preset.ts @@ -1,6 +1,5 @@ /* eslint-disable global-require */ import type { PresetProperty } from '@storybook/types'; -import { hasVitePlugins } from '@storybook/builder-vite'; import { dirname, join } from 'path'; import type { StorybookConfig } from './types'; @@ -15,12 +14,6 @@ export const core: PresetProperty<'core', StorybookConfig> = { export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => { const { plugins = [] } = config; - // Add react plugin if not present - if (!(await hasVitePlugins(plugins, ['vite:react-babel', 'vite:react-swc']))) { - const { default: react } = await import('@vitejs/plugin-react'); - plugins.push(react()); - } - // Add docgen plugin const { reactDocgen: reactDocgenOption, reactDocgenTypescriptOptions } = await presets.apply( 'typescript', diff --git a/code/frameworks/react-webpack5/package.json b/code/frameworks/react-webpack5/package.json index 44da3c64216..b849bafa047 100644 --- a/code/frameworks/react-webpack5/package.json +++ b/code/frameworks/react-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/react-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for React: Develop React Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/server-webpack5/package.json b/code/frameworks/server-webpack5/package.json index 06bdc2b2bbe..5b5ab6b6fe3 100644 --- a/code/frameworks/server-webpack5/package.json +++ b/code/frameworks/server-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/server-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Server: View HTML snippets from a server in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index 9dedf2eaf71..eece06f117b 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Svelte and Vite: Develop Svelte components in isolation with Hot Reloading.", "keywords": [ "storybook" @@ -50,21 +50,22 @@ "@storybook/builder-vite": "workspace:*", "@storybook/node-logger": "workspace:*", "@storybook/svelte": "workspace:*", - "@sveltejs/vite-plugin-svelte": "^2.4.2", "magic-string": "^0.30.0", "svelte-preprocess": "^5.1.1", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.2.0" }, "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" }, "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.0.0 || ^3.0.0", "svelte": "^4.0.0 || ^5.0.0-next.16", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^4.0.0 || ^5.0.0" }, "engines": { "node": "^14.18 || >=16" diff --git a/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts b/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts index 5ee6f7e53ce..1e33174e11a 100644 --- a/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts +++ b/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts @@ -6,7 +6,6 @@ import svelteDoc from 'sveltedoc-parser'; import type { SvelteComponentDoc, SvelteParserOptions } from 'sveltedoc-parser'; import { logger } from '@storybook/node-logger'; import { preprocess } from 'svelte/compiler'; -import { createFilter } from 'vite'; import { replace, typescript } from 'svelte-preprocess'; /* @@ -59,10 +58,12 @@ function getNameFromFilename(filename: string) { return base[0].toUpperCase() + base.slice(1); } -export function svelteDocgen(svelteOptions: Record = {}): PluginOption { +export async function svelteDocgen(svelteOptions: Record = {}): Promise { const cwd = process.cwd(); const { preprocess: preprocessOptions, logDocgen = false } = svelteOptions; const include = /\.(svelte)$/; + const { createFilter } = await import('vite'); + const filter = createFilter(include); let docPreprocessOptions: Parameters[1] | undefined; diff --git a/code/frameworks/svelte-vite/src/preset.ts b/code/frameworks/svelte-vite/src/preset.ts index 220812cf7c5..9ef71bb26e8 100644 --- a/code/frameworks/svelte-vite/src/preset.ts +++ b/code/frameworks/svelte-vite/src/preset.ts @@ -1,4 +1,3 @@ -import { hasVitePlugins } from '@storybook/builder-vite'; import type { PresetProperty } from '@storybook/types'; import { dirname, join } from 'path'; import type { StorybookConfig } from './types'; @@ -17,16 +16,11 @@ export const viteFinal: NonNullable = async (confi const { plugins = [] } = config; // TODO: set up eslint import to use typescript resolver // eslint-disable-next-line import/no-unresolved - const { svelte, loadSvelteConfig } = await import('@sveltejs/vite-plugin-svelte'); + const { loadSvelteConfig } = await import('@sveltejs/vite-plugin-svelte'); const svelteConfig = await loadSvelteConfig(); - // Add svelte plugin if the user does not have a Vite config of their own - if (!(await hasVitePlugins(plugins, ['vite-plugin-svelte']))) { - plugins.push(svelte()); - } - // Add docgen plugin - plugins.push(svelteDocgen(svelteConfig)); + plugins.push(await svelteDocgen(svelteConfig)); await handleSvelteKit(plugins, options); diff --git a/code/frameworks/svelte-webpack5/package.json b/code/frameworks/svelte-webpack5/package.json index bd7c166692e..40ea6428710 100644 --- a/code/frameworks/svelte-webpack5/package.json +++ b/code/frameworks/svelte-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/svelte-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/sveltekit/package.json b/code/frameworks/sveltekit/package.json index f52a2a20e56..b97ffa27aad 100644 --- a/code/frameworks/sveltekit/package.json +++ b/code/frameworks/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/sveltekit", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for SvelteKit", "keywords": [ "storybook", diff --git a/code/frameworks/sveltekit/src/plugins/config-overrides.ts b/code/frameworks/sveltekit/src/plugins/config-overrides.ts index d132764d6e5..db5294a1324 100644 --- a/code/frameworks/sveltekit/src/plugins/config-overrides.ts +++ b/code/frameworks/sveltekit/src/plugins/config-overrides.ts @@ -1,4 +1,4 @@ -import { type Plugin } from 'vite'; +import type { Plugin } from 'vite'; export function configOverrides() { return { diff --git a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts index f338760c390..32f6eb0c41b 100644 --- a/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts +++ b/code/frameworks/sveltekit/src/plugins/mock-sveltekit-stores.ts @@ -1,7 +1,7 @@ import { resolve } from 'node:path'; import type { Plugin } from 'vite'; -export function mockSveltekitStores() { +export async function mockSveltekitStores() { return { name: 'storybook:sveltekit-mock-stores', config: () => ({ diff --git a/code/frameworks/sveltekit/src/preset.ts b/code/frameworks/sveltekit/src/preset.ts index 7af34f9e420..53d616c1891 100644 --- a/code/frameworks/sveltekit/src/preset.ts +++ b/code/frameworks/sveltekit/src/preset.ts @@ -32,7 +32,7 @@ export const viteFinal: NonNullable = async (confi ]) ) .concat(configOverrides()) - .concat(mockSveltekitStores()); + .concat(await mockSveltekitStores()); return { ...baseConfig, plugins }; }; diff --git a/code/frameworks/vue3-vite/package.json b/code/frameworks/vue3-vite/package.json index 37d49111999..e055410a8bb 100644 --- a/code/frameworks/vue3-vite/package.json +++ b/code/frameworks/vue3-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Vue3 and Vite: Develop Vue3 components in isolation with Hot Reloading.", "keywords": [ "storybook" @@ -50,7 +50,6 @@ "@storybook/builder-vite": "workspace:*", "@storybook/core-server": "workspace:*", "@storybook/vue3": "workspace:*", - "@vitejs/plugin-vue": "^4.0.0", "magic-string": "^0.30.0", "vue-docgen-api": "^4.40.0" }, @@ -60,7 +59,7 @@ "vite": "^4.0.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^4.0.0 || ^5.0.0" }, "engines": { "node": "^14.18 || >=16" diff --git a/code/frameworks/vue3-vite/src/plugins/vue-docgen.ts b/code/frameworks/vue3-vite/src/plugins/vue-docgen.ts index 6bed6a1fed9..f8008fc3a4b 100644 --- a/code/frameworks/vue3-vite/src/plugins/vue-docgen.ts +++ b/code/frameworks/vue3-vite/src/plugins/vue-docgen.ts @@ -1,10 +1,10 @@ import { parse } from 'vue-docgen-api'; import type { PluginOption } from 'vite'; -import { createFilter } from 'vite'; import MagicString from 'magic-string'; -export function vueDocgen(): PluginOption { +export async function vueDocgen(): Promise { const include = /\.(vue)$/; + const { createFilter } = await import('vite'); const filter = createFilter(include); return { diff --git a/code/frameworks/vue3-vite/src/preset.ts b/code/frameworks/vue3-vite/src/preset.ts index c0d97bd8da8..58ceb6afb84 100644 --- a/code/frameworks/vue3-vite/src/preset.ts +++ b/code/frameworks/vue3-vite/src/preset.ts @@ -1,6 +1,5 @@ -import { hasVitePlugins } from '@storybook/builder-vite'; import type { PresetProperty } from '@storybook/types'; -import { mergeConfig, type PluginOption } from 'vite'; +import type { PluginOption } from 'vite'; import { dirname, join } from 'path'; import type { StorybookConfig } from './types'; import { vueDocgen } from './plugins/vue-docgen'; @@ -16,15 +15,10 @@ export const core: PresetProperty<'core'> = { export const viteFinal: StorybookConfig['viteFinal'] = async (config, { presets }) => { const plugins: PluginOption[] = []; - // Add vue plugin if not present - if (!(config.plugins && (await hasVitePlugins(config.plugins, ['vite:vue'])))) { - const { default: vue } = await import('@vitejs/plugin-vue'); - plugins.push(vue()); - } - // Add docgen plugin - plugins.push(vueDocgen()); + plugins.push(await vueDocgen()); + const { mergeConfig } = await import('vite'); return mergeConfig(config, { plugins, resolve: { diff --git a/code/frameworks/vue3-webpack5/package.json b/code/frameworks/vue3-webpack5/package.json index 184583a7cf1..f133bd90aab 100644 --- a/code/frameworks/vue3-webpack5/package.json +++ b/code/frameworks/vue3-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/vue3-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for Vue 3: Develop Vue 3 Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-vite/package.json b/code/frameworks/web-components-vite/package.json index f72f4754705..97451f9f0dd 100644 --- a/code/frameworks/web-components-vite/package.json +++ b/code/frameworks/web-components-vite/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-vite", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for web-components and Vite: Develop Web Components in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/code/frameworks/web-components-webpack5/package.json b/code/frameworks/web-components-webpack5/package.json index d41a40ea985..391c4279903 100644 --- a/code/frameworks/web-components-webpack5/package.json +++ b/code/frameworks/web-components-webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/web-components-webpack5", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.", "keywords": [ "lit", diff --git a/code/lib/channels/package.json b/code/lib/channels/package.json index 5faa33d3f95..9b3659e76b9 100644 --- a/code/lib/channels/package.json +++ b/code/lib/channels/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/channels", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "", "keywords": [ "storybook" diff --git a/code/lib/cli-sb/package.json b/code/lib/cli-sb/package.json index f336503e3e2..2bedf547a13 100644 --- a/code/lib/cli-sb/package.json +++ b/code/lib/cli-sb/package.json @@ -1,6 +1,6 @@ { "name": "sb", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli-storybook/package.json b/code/lib/cli-storybook/package.json index 4aa75cf6a4a..d1cc935b29a 100644 --- a/code/lib/cli-storybook/package.json +++ b/code/lib/cli-storybook/package.json @@ -1,6 +1,6 @@ { "name": "storybook", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook CLI", "keywords": [ "storybook" diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index a1869def7aa..671a4f309bd 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/cli", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook's CLI - install, dev, build, upgrade, and more", "keywords": [ "cli", @@ -101,7 +101,6 @@ "devDependencies": { "@types/cross-spawn": "^6.0.2", "@types/prompts": "^2.0.9", - "@types/semver": "^7.3.4", "@types/util-deprecate": "^1.0.0", "boxen": "^5.1.2", "slash": "^5.0.0", diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index 20e1c42bc81..3c730d68da5 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -4,11 +4,7 @@ import { isAbsolute, join } from 'path'; import SemVer from 'semver'; import dedent from 'ts-dedent'; -import { - JsPackageManagerFactory, - useNpmWarning, - type PackageManagerName, -} from './js-package-manager'; +import { JsPackageManagerFactory, type PackageManagerName } from './js-package-manager'; import { getStorybookVersion, isCorePackage } from './utils'; const logger = console; @@ -71,13 +67,10 @@ const checkInstalled = (addonName: string, main: any) => { */ export async function add( addon: string, - options: { useNpm: boolean; packageManager: PackageManagerName; skipPostinstall: boolean } + options: { packageManager: PackageManagerName; skipPostinstall: boolean } ) { - let { packageManager: pkgMgr } = options; - if (options.useNpm) { - useNpmWarning(); - pkgMgr = 'npm'; - } + const { packageManager: pkgMgr } = options; + const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const packageJson = await packageManager.retrievePackageJson(); const { mainConfig, configDir } = getStorybookInfo(packageJson); diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts index bd33074805d..0c6202f2426 100644 --- a/code/lib/cli/src/automigrate/fixes/index.ts +++ b/code/lib/cli/src/automigrate/fixes/index.ts @@ -19,6 +19,7 @@ import { incompatibleAddons } from './incompatible-addons'; import { angularBuildersMultiproject } from './angular-builders-multiproject'; import { wrapRequire } from './wrap-require'; import { reactDocgen } from './react-docgen'; +import { storyshotsMigration } from './storyshots-migration'; export * from '../types'; @@ -42,6 +43,7 @@ export const allFixes: Fix[] = [ angularBuilders, wrapRequire, reactDocgen, + storyshotsMigration, ]; export const initFixes: Fix[] = [missingBabelRc, eslintPlugin]; diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts index 7c3fd013023..f844decf144 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts @@ -82,7 +82,7 @@ export const mdxgfm: Fix = { return dedent` In MDX1 you had the option of using GitHub flavored markdown. - Storybook 7.0 uses MDX2 for compiling MDX, and thus no longer supports GFM out of the box. + Storybook 8.0 uses MDX3 for compiling MDX, and thus no longer supports GFM out of the box. Because of this you need to explicitly add the GFM plugin in the addon-docs options: https://storybook.js.org/docs/react/writing-docs/mdx#lack-of-github-flavored-markdown-gfm diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts new file mode 100644 index 00000000000..4a7ccc9d335 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts @@ -0,0 +1,60 @@ +import { describe, afterEach, it, expect, vi } from 'vitest'; + +import type { StorybookConfig } from '@storybook/types'; +import { storyshotsMigration } from './storyshots-migration'; +import type { JsPackageManager } from '../../js-package-manager'; + +const check = async ({ + packageManager, + main: mainConfig = {}, + storybookVersion = '8.0.0', +}: { + packageManager: Partial; + main?: Partial & Record; + storybookVersion?: string; +}) => { + return storyshotsMigration.check({ + packageManager: packageManager as any, + configDir: '', + mainConfig: mainConfig as any, + storybookVersion, + }); +}; + +describe('storyshots-migration fix', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should detect storyshots registered in main.js', async () => { + await expect( + check({ + packageManager: { + getAllDependencies: async () => ({}), + }, + main: { addons: ['@storybook/addon-storyshots'] }, + }) + ).resolves.toBeTruthy(); + }); + + it('should detect storyshots in package.json', async () => { + await expect( + check({ + packageManager: { + getAllDependencies: async () => ({ '@storybook/addon-storyshots': '7.0.0' }), + }, + }) + ).resolves.toBeTruthy(); + }); + + it('no-op when storyshots is not present', async () => { + await expect( + check({ + packageManager: { + getAllDependencies: async () => ({}), + }, + main: { addons: ['@storybook/essentials'] }, + }) + ).resolves.toBeNull(); + }); +}); diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts new file mode 100644 index 00000000000..6d047f88449 --- /dev/null +++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts @@ -0,0 +1,30 @@ +import chalk from 'chalk'; +import dedent from 'ts-dedent'; +import type { Fix } from '../types'; + +export const storyshotsMigration: Fix = { + id: 'storyshots-migration', + promptOnly: true, + + async check({ mainConfig, packageManager }) { + const allDeps = await packageManager.getAllDependencies(); + const hasStoryshots = + allDeps['@storybook/addon-storyshots'] || + mainConfig.addons?.find((addon) => { + const addonName = typeof addon === 'string' ? addon : addon.name; + return addonName.includes('@storybook/addon-storyshots'); + }); + + return hasStoryshots ?? null; + }, + prompt() { + return dedent` + ${chalk.bold( + 'Attention' + )}: Storyshots is now officially deprecated, is no longer being maintained, and was removed in Storybook 8. + + We recommend following the migration guide we've prepared to help you during this transition period: + ${chalk.yellow('https://storybook.js.org/docs/writing-tests/storyshots-migration-guide')} + `; + }, +}; diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 1ef8ec9ce1a..3fd322e724d 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -9,7 +9,7 @@ import dedent from 'ts-dedent'; import { join } from 'path'; import { getStorybookInfo, loadMainConfig } from '@storybook/core-common'; import invariant from 'tiny-invariant'; -import { JsPackageManagerFactory, useNpmWarning } from '../js-package-manager'; +import { JsPackageManagerFactory } from '../js-package-manager'; import type { PackageManagerName } from '../js-package-manager'; import type { Fix, FixId, FixOptions, FixSummary } from './fixes'; @@ -55,7 +55,6 @@ export const automigrate = async ({ fixes: inputFixes, dryRun, yes, - useNpm, packageManager: pkgMgr, list, configDir: userSpecifiedConfigDir, @@ -86,7 +85,6 @@ export const automigrate = async ({ const { fixResults, fixSummary, preCheckFailure } = await runFixes({ fixes, - useNpm, pkgMgr, userSpecifiedConfigDir, rendererPackage, @@ -129,7 +127,6 @@ export async function runFixes({ fixes, dryRun, yes, - useNpm, pkgMgr, userSpecifiedConfigDir, rendererPackage, @@ -138,7 +135,6 @@ export async function runFixes({ fixes: Fix[]; yes?: boolean; dryRun?: boolean; - useNpm?: boolean; pkgMgr?: PackageManagerName; userSpecifiedConfigDir?: string; rendererPackage?: string; @@ -148,12 +144,6 @@ export async function runFixes({ fixResults: Record; fixSummary: FixSummary; }> { - if (useNpm) { - useNpmWarning(); - // eslint-disable-next-line no-param-reassign - pkgMgr = 'npm'; - } - const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const fixResults = {} as Record; diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index 1befb24bf22..5949d70db98 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -41,7 +41,6 @@ export interface FixOptions { fixes?: Fix[]; yes?: boolean; dryRun?: boolean; - useNpm?: boolean; packageManager?: PackageManagerName; configDir?: string; renderer?: string; diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index 2594863f0e9..5ee5c9d89ab 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -48,7 +48,6 @@ command('init') .option('-f --force', 'Force add Storybook') .option('-s --skip-install', 'Skip installing deps') .option('--package-manager ', 'Force package manager for installing deps') - .option('-N --use-npm', 'Use npm to install deps (deprecated)') .option('--use-pnp', 'Enable pnp mode for Yarn 2+') .option('-p --parser ', 'jscodeshift parser') .option('-t --type ', 'Add Storybook for a specific project type') @@ -65,7 +64,6 @@ command('add ') '--package-manager ', 'Force package manager for installing dependencies' ) - .option('-N --use-npm', 'Use NPM to install dependencies (deprecated)') .option('-s --skip-postinstall', 'Skip package specific postinstall config modifications') .action((addonName: string, options: any) => add(addonName, options)); @@ -79,7 +77,6 @@ command('upgrade') '--package-manager ', 'Force package manager for installing dependencies' ) - .option('-N --use-npm', 'Use NPM to install dependencies (deprecated)') .option('-y --yes', 'Skip prompting the user') .option('-n --dry-run', 'Only check for upgrades, do not install') .option('-t --tag ', 'Upgrade to a certain npm dist-tag (e.g. next, prerelease)') @@ -167,7 +164,6 @@ command('automigrate [fixId]') .option('-y --yes', 'Skip prompting the user') .option('-n --dry-run', 'Only check for fixes, do not actually run them') .option('--package-manager ', 'Force package manager') - .option('-N --use-npm', 'Use npm as package manager (deprecated)') .option('-l --list', 'List available migrations') .option('-c, --config-dir ', 'Directory of Storybook configurations to migrate') .option('-s --skip-install', 'Skip installing deps') @@ -196,7 +192,6 @@ command('doctor') command('dev') .option('-p, --port ', 'Port to run Storybook', (str) => parseInt(str, 10)) .option('-h, --host ', 'Host to run Storybook') - .option('-s, --static-dir ', 'Directory where to load static files from', parseList) .option('-c, --config-dir ', 'Directory where to load Storybook configurations from') .option( '--https', @@ -251,7 +246,6 @@ command('dev') }); command('build') - .option('-s, --static-dir ', 'Directory where to load static files from', parseList) .option('-o, --output-dir ', 'Directory where to store built files') .option('-c, --config-dir ', 'Directory where to load Storybook configurations from') .option('--quiet', 'Suppress verbose build output') diff --git a/code/lib/cli/src/generators/NEXTJS/index.ts b/code/lib/cli/src/generators/NEXTJS/index.ts index 2588b387312..27d220321dd 100644 --- a/code/lib/cli/src/generators/NEXTJS/index.ts +++ b/code/lib/cli/src/generators/NEXTJS/index.ts @@ -1,14 +1,20 @@ +import { join } from 'path'; +import { existsSync } from 'fs'; import { CoreBuilder } from '../../project_types'; import { baseGenerator } from '../baseGenerator'; import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { + let staticDir; + if (existsSync(join(process.cwd(), 'public'))) staticDir = 'public'; + await baseGenerator( packageManager, npmOptions, { ...options, builder: CoreBuilder.Webpack5 }, 'react', { + staticDir, extraAddons: ['@storybook/addon-onboarding'], }, 'nextjs' diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 5ad50927d01..707cd0b7890 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -163,9 +163,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; @@ -252,16 +250,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 3e4076f4da4..c65710124fa 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 bbc9992be1e..38cd265df83 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/cli/src/generators/types.ts b/code/lib/cli/src/generators/types.ts index 2f97a34df12..85ed0dbc600 100644 --- a/code/lib/cli/src/generators/types.ts +++ b/code/lib/cli/src/generators/types.ts @@ -41,7 +41,6 @@ export type Generator = ( export type CommandOptions = { packageManager: PackageManagerName; - useNpm?: boolean; usePnp?: boolean; type?: ProjectType; force?: any; diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index 5217bca197a..c0dbf5a3c7b 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -29,7 +29,7 @@ import svelteKitGenerator from './generators/SVELTEKIT'; import solidGenerator from './generators/SOLID'; import serverGenerator from './generators/SERVER'; import type { JsPackageManager } from './js-package-manager'; -import { JsPackageManagerFactory, useNpmWarning } from './js-package-manager'; +import { JsPackageManagerFactory } from './js-package-manager'; import type { NpmOptions } from './NpmOptions'; import type { CommandOptions, GeneratorOptions } from './generators/types'; import { HandledError } from './HandledError'; @@ -235,12 +235,7 @@ async function doInitiate( } | { shouldRunDev: false } > { - let { packageManager: pkgMgr } = options; - if (options.useNpm) { - useNpmWarning(); - - pkgMgr = 'npm'; - } + const { packageManager: pkgMgr } = options; const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr, diff --git a/code/lib/cli/src/js-package-manager/deprecations.ts b/code/lib/cli/src/js-package-manager/deprecations.ts deleted file mode 100644 index 5883fad3a29..00000000000 --- a/code/lib/cli/src/js-package-manager/deprecations.ts +++ /dev/null @@ -1,8 +0,0 @@ -import deprecate from 'util-deprecate'; - -export const useNpmWarning = deprecate( - () => {}, - `\`--use-npm\` is deprecated and will be removed in Storybook 8.0. -Please use the \`--package-manager=npm\` option instead. -Read more at https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#cli-option---use-npm-deprecated` -); diff --git a/code/lib/cli/src/js-package-manager/index.ts b/code/lib/cli/src/js-package-manager/index.ts index d9849cbff26..f95d6f9659b 100644 --- a/code/lib/cli/src/js-package-manager/index.ts +++ b/code/lib/cli/src/js-package-manager/index.ts @@ -1,5 +1,3 @@ -export * from './deprecations'; - export * from './JsPackageManagerFactory'; export * from './JsPackageManager'; diff --git a/code/lib/cli/src/upgrade.test.ts b/code/lib/cli/src/upgrade.test.ts index 3acb5fd92a0..6fcea5f8459 100644 --- a/code/lib/cli/src/upgrade.test.ts +++ b/code/lib/cli/src/upgrade.test.ts @@ -63,7 +63,7 @@ describe('addNxPackagesToReject', () => { const flags = ['--reject', '/preset-create-react-app/', '--some-flag', 'hello']; expect(addNxPackagesToReject(flags)).toMatchObject([ '--reject', - '/(preset-create-react-app|@nrwl/storybook|@nx/storybook)/', + '"/(preset-create-react-app|@nrwl/storybook|@nx/storybook)/"', '--some-flag', 'hello', ]); diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index aefd6bc8bc0..a4b22675e02 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -3,9 +3,13 @@ import { telemetry, getStorybookCoreVersion } from '@storybook/telemetry'; import semver from 'semver'; import { logger } from '@storybook/node-logger'; import { withTelemetry } from '@storybook/core-server'; +import { + ConflictingVersionTagsError, + UpgradeStorybookPackagesError, +} from '@storybook/core-events/server-errors'; import type { PackageJsonWithMaybeDeps, PackageManagerName } from './js-package-manager'; -import { getPackageDetails, JsPackageManagerFactory, useNpmWarning } from './js-package-manager'; +import { getPackageDetails, JsPackageManagerFactory } from './js-package-manager'; import { coerceSemver, commandLog } from './helpers'; import { automigrate } from './automigrate'; import { isCorePackage } from './utils'; @@ -117,7 +121,7 @@ export const addNxPackagesToReject = (flags: string[]) => { if (newFlags[index + 1].endsWith('/') && newFlags[index + 1].startsWith('/')) { // Remove last and first slash so that I can add the parentheses newFlags[index + 1] = newFlags[index + 1].substring(1, newFlags[index + 1].length - 1); - newFlags[index + 1] = `/(${newFlags[index + 1]}|@nrwl/storybook|@nx/storybook)/`; + newFlags[index + 1] = `"/(${newFlags[index + 1]}|@nrwl/storybook|@nx/storybook)/"`; } else { // Adding the two packages as comma-separated values // If the existing rejects are in regex format, they will be ignored. @@ -135,7 +139,6 @@ export interface UpgradeOptions { tag: string; prerelease: boolean; skipCheck: boolean; - useNpm: boolean; packageManager: PackageManagerName; dryRun: boolean; yes: boolean; @@ -147,28 +150,20 @@ export const doUpgrade = async ({ tag, prerelease, skipCheck, - useNpm, packageManager: pkgMgr, dryRun, configDir, yes, ...options }: UpgradeOptions) => { - if (useNpm) { - useNpmWarning(); - // eslint-disable-next-line no-param-reassign - pkgMgr = 'npm'; - } const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); const beforeVersion = await getStorybookCoreVersion(); - commandLog(`Checking for latest versions of '@storybook/*' packages`); + commandLog(`Checking for latest versions of '@storybook/*' packages\n`); if (tag && prerelease) { - throw new Error( - `Cannot set both --tag and --prerelease. Use --tag next to get the latest prereleae` - ); + throw new ConflictingVersionTagsError(); } let target = 'latest'; @@ -187,20 +182,40 @@ export const doUpgrade = async ({ flags.push(target); flags = addExtraFlags(EXTRA_FLAGS, flags, await packageManager.retrievePackageJson()); flags = addNxPackagesToReject(flags); - const check = spawnSync('npx', ['npm-check-updates@latest', '/storybook/', ...flags], { + + const command = 'npx'; + const checkArgs = ['npm-check-updates@latest', '/storybook/', ...flags]; + const check = spawnSync(command, checkArgs, { stdio: 'pipe', shell: true, }); + + if (check.stderr && !check.stderr.toString().includes('npm notice')) { + throw new UpgradeStorybookPackagesError({ + command, + args: checkArgs, + errorMessage: check.stderr.toString(), + }); + } + logger.info(check.stdout.toString()); - logger.info(check.stderr.toString()); - const checkSb = spawnSync('npx', ['npm-check-updates@latest', 'sb', ...flags], { + const checkSbArgs = ['npm-check-updates@latest', 'sb', ...flags]; + const checkSb = spawnSync(command, checkSbArgs, { stdio: 'pipe', shell: true, }); logger.info(checkSb.stdout.toString()); logger.info(checkSb.stderr.toString()); + if (checkSb.stderr && !checkSb.stderr.toString().includes('npm notice')) { + throw new UpgradeStorybookPackagesError({ + command, + args: checkSbArgs, + errorMessage: checkSb.stderr.toString(), + }); + } + if (!dryRun) { commandLog(`Installing upgrades`); await packageManager.installDependencies(); diff --git a/code/lib/cli/src/versions.ts b/code/lib/cli/src/versions.ts index 49bb1276857..efd8bdb1d81 100644 --- a/code/lib/cli/src/versions.ts +++ b/code/lib/cli/src/versions.ts @@ -1,83 +1,83 @@ // auto generated file, do not edit export default { - '@storybook/addon-a11y': '8.0.0-alpha.4', - '@storybook/addon-actions': '8.0.0-alpha.4', - '@storybook/addon-backgrounds': '8.0.0-alpha.4', - '@storybook/addon-controls': '8.0.0-alpha.4', - '@storybook/addon-docs': '8.0.0-alpha.4', - '@storybook/addon-essentials': '8.0.0-alpha.4', - '@storybook/addon-highlight': '8.0.0-alpha.4', - '@storybook/addon-interactions': '8.0.0-alpha.4', - '@storybook/addon-jest': '8.0.0-alpha.4', - '@storybook/addon-links': '8.0.0-alpha.4', - '@storybook/addon-mdx-gfm': '8.0.0-alpha.4', - '@storybook/addon-measure': '8.0.0-alpha.4', - '@storybook/addon-outline': '8.0.0-alpha.4', - '@storybook/addon-storysource': '8.0.0-alpha.4', - '@storybook/addon-themes': '8.0.0-alpha.4', - '@storybook/addon-toolbars': '8.0.0-alpha.4', - '@storybook/addon-viewport': '8.0.0-alpha.4', - '@storybook/angular': '8.0.0-alpha.4', - '@storybook/blocks': '8.0.0-alpha.4', - '@storybook/builder-manager': '8.0.0-alpha.4', - '@storybook/builder-vite': '8.0.0-alpha.4', - '@storybook/builder-webpack5': '8.0.0-alpha.4', - '@storybook/channels': '8.0.0-alpha.4', - '@storybook/cli': '8.0.0-alpha.4', - '@storybook/client-logger': '8.0.0-alpha.4', - '@storybook/codemod': '8.0.0-alpha.4', - '@storybook/components': '8.0.0-alpha.4', - '@storybook/core-common': '8.0.0-alpha.4', - '@storybook/core-events': '8.0.0-alpha.4', - '@storybook/core-server': '8.0.0-alpha.4', - '@storybook/core-webpack': '8.0.0-alpha.4', - '@storybook/csf-plugin': '8.0.0-alpha.4', - '@storybook/csf-tools': '8.0.0-alpha.4', - '@storybook/docs-tools': '8.0.0-alpha.4', - '@storybook/ember': '8.0.0-alpha.4', - '@storybook/html': '8.0.0-alpha.4', - '@storybook/html-vite': '8.0.0-alpha.4', - '@storybook/html-webpack5': '8.0.0-alpha.4', - '@storybook/instrumenter': '8.0.0-alpha.4', - '@storybook/manager': '8.0.0-alpha.4', - '@storybook/manager-api': '8.0.0-alpha.4', - '@storybook/nextjs': '8.0.0-alpha.4', - '@storybook/node-logger': '8.0.0-alpha.4', - '@storybook/preact': '8.0.0-alpha.4', - '@storybook/preact-vite': '8.0.0-alpha.4', - '@storybook/preact-webpack5': '8.0.0-alpha.4', - '@storybook/preset-create-react-app': '8.0.0-alpha.4', - '@storybook/preset-html-webpack': '8.0.0-alpha.4', - '@storybook/preset-preact-webpack': '8.0.0-alpha.4', - '@storybook/preset-react-webpack': '8.0.0-alpha.4', - '@storybook/preset-server-webpack': '8.0.0-alpha.4', - '@storybook/preset-svelte-webpack': '8.0.0-alpha.4', - '@storybook/preset-vue3-webpack': '8.0.0-alpha.4', - '@storybook/preset-web-components-webpack': '8.0.0-alpha.4', - '@storybook/preview': '8.0.0-alpha.4', - '@storybook/preview-api': '8.0.0-alpha.4', - '@storybook/react': '8.0.0-alpha.4', - '@storybook/react-dom-shim': '8.0.0-alpha.4', - '@storybook/react-vite': '8.0.0-alpha.4', - '@storybook/react-webpack5': '8.0.0-alpha.4', - '@storybook/router': '8.0.0-alpha.4', - '@storybook/server': '8.0.0-alpha.4', - '@storybook/server-webpack5': '8.0.0-alpha.4', - '@storybook/source-loader': '8.0.0-alpha.4', - '@storybook/svelte': '8.0.0-alpha.4', - '@storybook/svelte-vite': '8.0.0-alpha.4', - '@storybook/svelte-webpack5': '8.0.0-alpha.4', - '@storybook/sveltekit': '8.0.0-alpha.4', - '@storybook/telemetry': '8.0.0-alpha.4', - '@storybook/test': '8.0.0-alpha.4', - '@storybook/theming': '8.0.0-alpha.4', - '@storybook/types': '8.0.0-alpha.4', - '@storybook/vue3': '8.0.0-alpha.4', - '@storybook/vue3-vite': '8.0.0-alpha.4', - '@storybook/vue3-webpack5': '8.0.0-alpha.4', - '@storybook/web-components': '8.0.0-alpha.4', - '@storybook/web-components-vite': '8.0.0-alpha.4', - '@storybook/web-components-webpack5': '8.0.0-alpha.4', - sb: '8.0.0-alpha.4', - storybook: '8.0.0-alpha.4', + '@storybook/addon-a11y': '8.0.0-alpha.8', + '@storybook/addon-actions': '8.0.0-alpha.8', + '@storybook/addon-backgrounds': '8.0.0-alpha.8', + '@storybook/addon-controls': '8.0.0-alpha.8', + '@storybook/addon-docs': '8.0.0-alpha.8', + '@storybook/addon-essentials': '8.0.0-alpha.8', + '@storybook/addon-highlight': '8.0.0-alpha.8', + '@storybook/addon-interactions': '8.0.0-alpha.8', + '@storybook/addon-jest': '8.0.0-alpha.8', + '@storybook/addon-links': '8.0.0-alpha.8', + '@storybook/addon-mdx-gfm': '8.0.0-alpha.8', + '@storybook/addon-measure': '8.0.0-alpha.8', + '@storybook/addon-outline': '8.0.0-alpha.8', + '@storybook/addon-storysource': '8.0.0-alpha.8', + '@storybook/addon-themes': '8.0.0-alpha.8', + '@storybook/addon-toolbars': '8.0.0-alpha.8', + '@storybook/addon-viewport': '8.0.0-alpha.8', + '@storybook/angular': '8.0.0-alpha.8', + '@storybook/blocks': '8.0.0-alpha.8', + '@storybook/builder-manager': '8.0.0-alpha.8', + '@storybook/builder-vite': '8.0.0-alpha.8', + '@storybook/builder-webpack5': '8.0.0-alpha.8', + '@storybook/channels': '8.0.0-alpha.8', + '@storybook/cli': '8.0.0-alpha.8', + '@storybook/client-logger': '8.0.0-alpha.8', + '@storybook/codemod': '8.0.0-alpha.8', + '@storybook/components': '8.0.0-alpha.8', + '@storybook/core-common': '8.0.0-alpha.8', + '@storybook/core-events': '8.0.0-alpha.8', + '@storybook/core-server': '8.0.0-alpha.8', + '@storybook/core-webpack': '8.0.0-alpha.8', + '@storybook/csf-plugin': '8.0.0-alpha.8', + '@storybook/csf-tools': '8.0.0-alpha.8', + '@storybook/docs-tools': '8.0.0-alpha.8', + '@storybook/ember': '8.0.0-alpha.8', + '@storybook/html': '8.0.0-alpha.8', + '@storybook/html-vite': '8.0.0-alpha.8', + '@storybook/html-webpack5': '8.0.0-alpha.8', + '@storybook/instrumenter': '8.0.0-alpha.8', + '@storybook/manager': '8.0.0-alpha.8', + '@storybook/manager-api': '8.0.0-alpha.8', + '@storybook/nextjs': '8.0.0-alpha.8', + '@storybook/node-logger': '8.0.0-alpha.8', + '@storybook/preact': '8.0.0-alpha.8', + '@storybook/preact-vite': '8.0.0-alpha.8', + '@storybook/preact-webpack5': '8.0.0-alpha.8', + '@storybook/preset-create-react-app': '8.0.0-alpha.8', + '@storybook/preset-html-webpack': '8.0.0-alpha.8', + '@storybook/preset-preact-webpack': '8.0.0-alpha.8', + '@storybook/preset-react-webpack': '8.0.0-alpha.8', + '@storybook/preset-server-webpack': '8.0.0-alpha.8', + '@storybook/preset-svelte-webpack': '8.0.0-alpha.8', + '@storybook/preset-vue3-webpack': '8.0.0-alpha.8', + '@storybook/preset-web-components-webpack': '8.0.0-alpha.8', + '@storybook/preview': '8.0.0-alpha.8', + '@storybook/preview-api': '8.0.0-alpha.8', + '@storybook/react': '8.0.0-alpha.8', + '@storybook/react-dom-shim': '8.0.0-alpha.8', + '@storybook/react-vite': '8.0.0-alpha.8', + '@storybook/react-webpack5': '8.0.0-alpha.8', + '@storybook/router': '8.0.0-alpha.8', + '@storybook/server': '8.0.0-alpha.8', + '@storybook/server-webpack5': '8.0.0-alpha.8', + '@storybook/source-loader': '8.0.0-alpha.8', + '@storybook/svelte': '8.0.0-alpha.8', + '@storybook/svelte-vite': '8.0.0-alpha.8', + '@storybook/svelte-webpack5': '8.0.0-alpha.8', + '@storybook/sveltekit': '8.0.0-alpha.8', + '@storybook/telemetry': '8.0.0-alpha.8', + '@storybook/test': '8.0.0-alpha.8', + '@storybook/theming': '8.0.0-alpha.8', + '@storybook/types': '8.0.0-alpha.8', + '@storybook/vue3': '8.0.0-alpha.8', + '@storybook/vue3-vite': '8.0.0-alpha.8', + '@storybook/vue3-webpack5': '8.0.0-alpha.8', + '@storybook/web-components': '8.0.0-alpha.8', + '@storybook/web-components-vite': '8.0.0-alpha.8', + '@storybook/web-components-webpack5': '8.0.0-alpha.8', + sb: '8.0.0-alpha.8', + storybook: '8.0.0-alpha.8', }; diff --git a/code/lib/client-logger/package.json b/code/lib/client-logger/package.json index 157ba7ae102..328a6502566 100644 --- a/code/lib/client-logger/package.json +++ b/code/lib/client-logger/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/client-logger", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "", "keywords": [ "storybook" diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index f3dda81527b..f0401dc52f7 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/codemod", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "A collection of codemod scripts written with JSCodeshift", "keywords": [ "storybook" diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts index b5f68b8a7b5..51bd69be962 100644 --- a/code/lib/codemod/src/transforms/mdx-to-csf.ts +++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts @@ -136,6 +136,7 @@ export function transform(source: string, baseName: string): [string, string] { value: `/* ${nodeString} is deprecated, please migrate it to see: https://storybook.js.org/migration-guides/7.0 */`, }; storiesMap.set(idAttribute.value as string, { type: 'id' }); + // @ts-expect-error issue with mdast types parent?.children.splice(index as number, 0, newNode); // current index is the new comment, and index + 1 is current node // SKIP traversing current node, and continue with the node after that diff --git a/code/lib/core-common/package.json b/code/lib/core-common/package.json index 12485ad7e4b..7bd54a9428a 100644 --- a/code/lib/core-common/package.json +++ b/code/lib/core-common/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-common", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/core-common/src/utils/resolve-path-in-sb-cache.ts b/code/lib/core-common/src/utils/resolve-path-in-sb-cache.ts index de88c2e2c00..af7135817af 100644 --- a/code/lib/core-common/src/utils/resolve-path-in-sb-cache.ts +++ b/code/lib/core-common/src/utils/resolve-path-in-sb-cache.ts @@ -9,9 +9,9 @@ import findCacheDirectory from 'find-cache-dir'; * @param fileOrDirectoryName {string} Name of the file or directory * @return {string} Absolute path to the file or directory */ -export function resolvePathInStorybookCache(fileOrDirectoryName: string): string { +export function resolvePathInStorybookCache(fileOrDirectoryName: string, sub = 'default'): string { let cacheDirectory = findCacheDirectory({ name: 'storybook' }); - cacheDirectory ||= path.join(process.cwd(), '.cache/storybook'); + cacheDirectory ||= path.join(process.cwd(), '.cache', 'storybook'); - return path.join(cacheDirectory, fileOrDirectoryName); + return path.join(cacheDirectory, sub, fileOrDirectoryName); } diff --git a/code/lib/core-events/package.json b/code/lib/core-events/package.json index 8ed6a52ddfa..0041af7942d 100644 --- a/code/lib/core-events/package.json +++ b/code/lib/core-events/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-events", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Event names used in storybook core", "keywords": [ "storybook" diff --git a/code/lib/core-events/src/errors/preview-errors.ts b/code/lib/core-events/src/errors/preview-errors.ts index 50cb0609861..26544d7efd8 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 bacdd7d6055..efdf01f0a45 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; @@ -422,7 +423,7 @@ export class GenerateNewProjectOnInitError extends StorybookError { super(); } - template(): string { + template() { return dedent` There was an error while using ${this.data.packageManager} to create a new ${ this.data.projectType @@ -432,3 +433,35 @@ export class GenerateNewProjectOnInitError extends StorybookError { `; } } + +export class ConflictingVersionTagsError extends StorybookError { + readonly category = Category.CLI_UPGRADE; + + readonly code = 1; + + template() { + return 'Cannot set both --tag and --prerelease. Use --tag=next to get the latest prerelease.'; + } +} + +export class UpgradeStorybookPackagesError extends StorybookError { + readonly category = Category.CLI_UPGRADE; + + readonly code = 2; + + constructor(public data: { command: string; args: string[]; errorMessage: string }) { + super(); + } + + template() { + return dedent` + There was an error while trying to upgrade your Storybook dependencies. + + Command: + ${this.data.command} ${this.data.args.join(' ')} + + Error: + ${this.data.errorMessage} + `; + } +} diff --git a/code/lib/core-events/src/index.ts b/code/lib/core-events/src/index.ts index d8d6c41c01d..6d98e629120 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/package.json b/code/lib/core-server/package.json index 2fb8d372ff2..cdea9a4b00d 100644 --- a/code/lib/core-server/package.json +++ b/code/lib/core-server/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-server", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" @@ -68,7 +68,7 @@ "@storybook/core-events": "workspace:*", "@storybook/csf": "^0.1.2", "@storybook/csf-tools": "workspace:*", - "@storybook/docs-mdx": "^0.1.0", + "@storybook/docs-mdx": "3.0.0", "@storybook/global": "^5.0.0", "@storybook/manager": "workspace:*", "@storybook/node-logger": "workspace:*", diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts index 5e8a0a955c2..88c3ab483f0 100644 --- a/code/lib/core-server/src/build-dev.ts +++ b/code/lib/core-server/src/build-dev.ts @@ -1,5 +1,6 @@ import type { BuilderOptions, CLIOptions, LoadOptions, Options } from '@storybook/types'; import { + getProjectRoot, loadAllPresets, loadMainConfig, resolveAddonName, @@ -10,9 +11,9 @@ import { import prompts from 'prompts'; import invariant from 'tiny-invariant'; import { global } from '@storybook/global'; -import { telemetry } from '@storybook/telemetry'; +import { telemetry, oneWayHash } from '@storybook/telemetry'; -import { join, resolve } from 'path'; +import { join, relative, resolve } from 'path'; import { deprecate } from '@storybook/node-logger'; import dedent from 'ts-dedent'; import { readFile } from 'fs-extra'; @@ -49,17 +50,28 @@ export async function buildDevStandalone( name: 'shouldChangePort', message: `Port ${options.port} is not available. Would you like to run Storybook on port ${port} instead?`, }); - if (!shouldChangePort) process.exit(1); + if (!shouldChangePort) { + process.exit(1); + } + } + + const rootDir = getProjectRoot(); + const configDir = resolve(options.configDir); + const cacheKey = oneWayHash(relative(rootDir, configDir)); + + const cacheOutputDir = resolvePathInStorybookCache('public', cacheKey); + let outputDir = resolve(options.outputDir || cacheOutputDir); + if (options.smokeTest) { + outputDir = cacheOutputDir; } /* eslint-disable no-param-reassign */ options.port = port; options.versionCheck = versionCheck; options.configType = 'DEVELOPMENT'; - options.configDir = resolve(options.configDir); - options.outputDir = options.smokeTest - ? resolvePathInStorybookCache('public') - : resolve(options.outputDir || resolvePathInStorybookCache('public')); + options.configDir = configDir; + options.cacheKey = cacheKey; + options.outputDir = outputDir; options.serverChannelUrl = getServerChannelUrl(port, options); /* eslint-enable no-param-reassign */ @@ -67,10 +79,15 @@ export async function buildDevStandalone( const { framework } = config; const corePresets = []; - const frameworkName = typeof framework === 'string' ? framework : framework?.name; - validateFrameworkName(frameworkName); + let frameworkName = typeof framework === 'string' ? framework : framework?.name; + if (!options.ignorePreview) { + validateFrameworkName(frameworkName); + } + if (frameworkName) { + corePresets.push(join(frameworkName, 'preset')); + } - corePresets.push(join(frameworkName, 'preset')); + frameworkName = frameworkName || 'custom'; try { await warnOnIncompatibleAddons(config); diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts index b85932ca246..2ae75f747e5 100644 --- a/code/lib/core-server/src/build-static.ts +++ b/code/lib/core-server/src/build-static.ts @@ -1,6 +1,6 @@ import chalk from 'chalk'; import { copy, emptyDir, ensureDir } from 'fs-extra'; -import { dirname, isAbsolute, join, resolve } from 'path'; +import { dirname, join, relative, resolve } from 'path'; import { global } from '@storybook/global'; import { deprecate, logger } from '@storybook/node-logger'; import { getPrecedingUpgrade, telemetry } from '@storybook/telemetry'; @@ -12,21 +12,15 @@ import { normalizeStories, resolveAddonName, } from '@storybook/core-common'; -import { ConflictingStaticDirConfigError } from '@storybook/core-events/server-errors'; -import isEqual from 'lodash/isEqual.js'; import dedent from 'ts-dedent'; import { outputStats } from './utils/output-stats'; -import { - copyAllStaticFiles, - copyAllStaticFilesRelativeToMain, -} from './utils/copy-all-static-files'; +import { copyAllStaticFilesRelativeToMain } from './utils/copy-all-static-files'; import { getBuilders } from './utils/get-builders'; import { extractStoriesJson } from './utils/stories-json'; import { extractStorybookMetadata } from './utils/metadata'; import { StoryIndexGenerator } from './utils/StoryIndexGenerator'; import { summarizeIndex } from './utils/summarizeIndex'; -import { defaultStaticDirs } from './utils/constants'; import { buildOrThrow } from './utils/build-or-throw'; export type BuildStaticStandaloneOptions = CLIOptions & @@ -41,17 +35,11 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption throw new Error("Won't remove current directory. Check your outputDir!"); } - if (options.staticDir?.includes('/')) { - throw new Error("Won't copy root directory. Check your staticDirs!"); - } - - options.outputDir = isAbsolute(options.outputDir) - ? options.outputDir - : join(process.cwd(), options.outputDir); + options.outputDir = resolve(options.outputDir); options.configDir = resolve(options.configDir); /* eslint-enable no-param-reassign */ - logger.info(chalk`=> Cleaning outputDir: {cyan ${options.outputDir.replace(process.cwd(), '')}}`); + logger.info(chalk`=> Cleaning outputDir: {cyan ${relative(process.cwd(), options.outputDir)}}`); if (options.outputDir === '/') { throw new Error("Won't remove directory '/'. Check your outputDir!"); } @@ -65,7 +53,7 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption const frameworkName = typeof framework === 'string' ? framework : framework?.name; if (frameworkName) { corePresets.push(join(frameworkName, 'preset')); - } else { + } else if (!options.ignorePreview) { logger.warn(`you have not specified a framework in your ${options.configDir}/main.js`); } @@ -106,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( @@ -131,10 +117,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption build, }; - if (options.staticDir && !isEqual(staticDirs, defaultStaticDirs)) { - throw new ConflictingStaticDirConfigError(); - } - const effects: Promise[] = []; global.FEATURES = features; @@ -148,9 +130,6 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption copyAllStaticFilesRelativeToMain(staticDirs, options.outputDir, options.configDir) ); } - if (options.staticDir) { - effects.push(copyAllStaticFiles(options.staticDir, options.outputDir)); - } const coreServerPublicDir = join( dirname(require.resolve('@storybook/core-server/package.json')), @@ -169,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 815b9cb7241..cb68267391f 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; @@ -55,7 +55,7 @@ export const favicon = async ( const statics = staticDirsValue ? staticDirsValue.map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)) - : options.staticDir; + : []; if (statics && statics.length > 0) { const lists = await Promise.all( @@ -187,12 +187,11 @@ export const previewAnnotations = async (base: any, options: Options) => { export const features: PresetProperty<'features'> = async (existing) => ({ ...existing, - warnOnLegacyHierarchySeparator: true, buildStoriesJson: false, 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 fefb477426d..bc161fff6ff 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -6,8 +6,6 @@ import { describe, beforeEach, it, expect, vi } from 'vitest'; import path from 'path'; -// @ts-expect-error -- cannot find declaration file -import { createStoriesMdxIndexer } from '@storybook/addon-docs/preset'; import { normalizeStoriesEntry } from '@storybook/core-common'; import type { NormalizedStoriesSpecifier, StoryIndexEntry } from '@storybook/types'; import { readCsf, getStorySortParameter } from '@storybook/csf-tools'; @@ -44,8 +42,7 @@ const getStorySortParameterMock = vi.mocked(getStorySortParameter); const options: StoryIndexGeneratorOptions = { configDir: path.join(__dirname, '__mockdata__'), workingDir: path.join(__dirname, '__mockdata__'), - storyIndexers: [], - indexers: [csfIndexer, createStoriesMdxIndexer(false)], + indexers: [csfIndexer], storyStoreV7: true, docs: { defaultName: 'docs', autodocs: false }, }; @@ -292,51 +289,6 @@ describe('StoryIndexGenerator', () => { }); }); - describe('mdx tagged components', () => { - it('adds docs entry with docs enabled', async () => { - const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry( - './src/nested/Page.stories.mdx', - options - ); - - const generator = new StoryIndexGenerator([specifier], { - ...options, - }); - await generator.initialize(); - - expect(await generator.getIndex()).toMatchInlineSnapshot(` - { - "entries": { - "page--docs": { - "id": "page--docs", - "importPath": "./src/nested/Page.stories.mdx", - "name": "docs", - "storiesImports": [], - "tags": [ - "stories-mdx", - "docs", - ], - "title": "Page", - "type": "docs", - }, - "page--story-one": { - "id": "page--story-one", - "importPath": "./src/nested/Page.stories.mdx", - "name": "StoryOne", - "tags": [ - "stories-mdx", - "story", - ], - "title": "Page", - "type": "story", - }, - }, - "v": 4, - } - `); - }, 20_000); - }); - describe('autodocs', () => { const autodocsOptions = { ...options, diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index d8f4e8ba95e..96e72091073 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/__mockdata__/src/nested/Page.stories.mdx b/code/lib/core-server/src/utils/__mockdata__/src/nested/Page.stories.mdx deleted file mode 100644 index 402d2a302ec..00000000000 --- a/code/lib/core-server/src/utils/__mockdata__/src/nested/Page.stories.mdx +++ /dev/null @@ -1,5 +0,0 @@ -import { Meta, Story } from '@storybook/addon-docs'; - - - - 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 360a687eaae..451a350755b 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 }, @@ -604,53 +603,4 @@ describe('docs entries from story extraction', () => { } `); }); - it(`Only adds a docs entry and not a story entry when an input has the "docsOnly" tag`, async () => { - const relativePath = './src/nested/Page.stories.mdx'; - const absolutePath = path.join(options.workingDir, relativePath); - const specifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(relativePath, options); - - const generator = new StoryIndexGenerator([specifier], { - ...options, - docs: { defaultName: 'docs', autodocs: false }, - indexers: [ - { - test: /\.stories\.mdx?$/, - createIndex: async (fileName) => [ - { - exportName: '__page', - __id: 'page--page', - name: 'Page', - title: 'Page', - tags: [STORIES_MDX_TAG, 'stories-mdx-docsOnly'], - importPath: fileName, - type: 'story', - }, - ], - }, - ], - }); - const result = await generator.extractStories(specifier, absolutePath); - - expect(result).toMatchInlineSnapshot(` - { - "dependents": [], - "entries": [ - { - "id": "page--docs", - "importPath": "./src/nested/Page.stories.mdx", - "name": "docs", - "storiesImports": [], - "tags": [ - "stories-mdx", - "stories-mdx-docsOnly", - "docs", - ], - "title": "Page", - "type": "docs", - }, - ], - "type": "stories", - } - `); - }); }); diff --git a/code/lib/core-server/src/utils/copy-all-static-files.ts b/code/lib/core-server/src/utils/copy-all-static-files.ts index 44f7bf12d31..5b0fb522735 100644 --- a/code/lib/core-server/src/utils/copy-all-static-files.ts +++ b/code/lib/core-server/src/utils/copy-all-static-files.ts @@ -1,6 +1,6 @@ import chalk from 'chalk'; import fs from 'fs-extra'; -import path from 'path'; +import { join, relative } from 'path'; import { logger } from '@storybook/node-logger'; import { getDirectoryFromWorkingDir } from '@storybook/core-common'; import { parseStaticDir } from './server-statics'; @@ -11,11 +11,19 @@ export async function copyAllStaticFiles(staticDirs: any[] | undefined, outputDi staticDirs.map(async (dir) => { try { const { staticDir, staticPath, targetDir } = await parseStaticDir(dir); - const targetPath = path.join(outputDir, targetDir); - logger.info(chalk`=> Copying static files: {cyan ${staticDir}} => {cyan ${targetDir}}`); + const targetPath = join(outputDir, targetDir); + + // we copy prebuild static files from node_modules/@storybook/manager & preview + if (!staticDir.includes('node_modules')) { + logger.info( + chalk`=> Copying static files: {cyan ${print(staticDir)}} => {cyan ${print( + targetDir + )}}` + ); + } // Storybook's own files should not be overwritten, so we skip such files if we find them - const skipPaths = ['index.html', 'iframe.html'].map((f) => path.join(targetPath, f)); + const skipPaths = ['index.html', 'iframe.html'].map((f) => join(targetPath, f)); await fs.copy(staticPath, targetPath, { dereference: true, preserveTimestamps: true, @@ -49,9 +57,13 @@ export async function copyAllStaticFilesRelativeToMain( }) ); - const targetPath = path.join(outputDir, to); - const skipPaths = ['index.html', 'iframe.html'].map((f) => path.join(targetPath, f)); - logger.info(chalk`=> Copying static files: {cyan ${from}} at {cyan ${targetPath}}`); + const targetPath = join(outputDir, to); + const skipPaths = ['index.html', 'iframe.html'].map((f) => join(targetPath, f)); + if (!from.includes('node_modules')) { + logger.info( + chalk`=> Copying static files: {cyan ${print(from)}} at {cyan ${print(targetPath)}}` + ); + } await fs.copy(from, targetPath, { dereference: true, preserveTimestamps: true, @@ -59,3 +71,6 @@ export async function copyAllStaticFilesRelativeToMain( }); }, Promise.resolve()); } +function print(p: string): string { + return relative(process.cwd(), p); +} diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts index 74401020256..078ddcb012d 100644 --- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts @@ -10,7 +10,6 @@ export async function getStoryIndexGenerator( buildStoriesJson?: boolean; storyStoreV7?: boolean; argTypeTargetsV7?: boolean; - warnOnLegacyHierarchySeparator?: boolean; }, options: Options, serverChannel: ServerChannel @@ -24,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 386db92d143..2a0b93e1ca4 100644 --- a/code/lib/core-server/src/utils/server-statics.ts +++ b/code/lib/core-server/src/utils/server-statics.ts @@ -1,28 +1,20 @@ import { logger } from '@storybook/node-logger'; import type { Options } from '@storybook/types'; import { getDirectoryFromWorkingDir } from '@storybook/core-common'; -import { ConflictingStaticDirConfigError } from '@storybook/core-events/server-errors'; import chalk from 'chalk'; import type { Router } from 'express'; import express from 'express'; import { pathExists } from 'fs-extra'; import path, { basename, isAbsolute } from 'path'; -import isEqual from 'lodash/isEqual.js'; import { dedent } from 'ts-dedent'; -import { defaultStaticDirs } from './constants'; export async function useStatics(router: Router, options: Options) { const staticDirs = (await options.presets.apply('staticDirs')) ?? []; const faviconPath = await options.presets.apply('favicon'); - if (options.staticDir && !isEqual(staticDirs, defaultStaticDirs)) { - throw new ConflictingStaticDirConfigError(); - } - const statics = [ ...staticDirs.map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)), - ...(options.staticDir || []), ]; if (statics && statics.length > 0) { @@ -77,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 197f630eddf..8d1849e4038 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -4,8 +4,6 @@ import type { Router, Request, Response } from 'express'; import Watchpack from 'watchpack'; import path from 'path'; import debounce from 'lodash/debounce.js'; -// @ts-expect-error -- cannot find declaration file -import { createStoriesMdxIndexer } from '@storybook/addon-docs/preset'; import { STORY_INDEX_INVALIDATED } from '@storybook/core-events'; import { normalizeStoriesEntry } from '@storybook/core-common'; @@ -44,8 +42,7 @@ const getInitializedStoryIndexGenerator = async ( inputNormalizedStories = normalizedStories ) => { const options: StoryIndexGeneratorOptions = { - storyIndexers: [], - indexers: [csfIndexer, createStoriesMdxIndexer(false)], + indexers: [csfIndexer], configDir: workingDir, workingDir, storyStoreV7: true, @@ -239,29 +236,6 @@ describe('useStoriesJson', () => { "title": "nested/Button", "type": "story", }, - "nested-page--docs": { - "id": "nested-page--docs", - "importPath": "./src/nested/Page.stories.mdx", - "name": "docs", - "storiesImports": [], - "tags": [ - "stories-mdx", - "docs", - ], - "title": "nested/Page", - "type": "docs", - }, - "nested-page--story-one": { - "id": "nested-page--story-one", - "importPath": "./src/nested/Page.stories.mdx", - "name": "StoryOne", - "tags": [ - "stories-mdx", - "story", - ], - "title": "nested/Page", - "type": "story", - }, "second-nested-g--story-one": { "id": "second-nested-g--story-one", "importPath": "./src/second-nested/G.stories.ts", diff --git a/code/lib/core-webpack/package.json b/code/lib/core-webpack/package.json index cdc7af4d91d..a3dc0ce82f6 100644 --- a/code/lib/core-webpack/package.json +++ b/code/lib/core-webpack/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/core-webpack", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Storybook framework-agnostic API", "keywords": [ "storybook" diff --git a/code/lib/csf-plugin/package.json b/code/lib/csf-plugin/package.json index c5a678fc825..b3769a22ca3 100644 --- a/code/lib/csf-plugin/package.json +++ b/code/lib/csf-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-plugin", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Enrich CSF files via static analysis", "keywords": [ "storybook" diff --git a/code/lib/csf-tools/package.json b/code/lib/csf-tools/package.json index 9fac705c176..4da5a9f0429 100644 --- a/code/lib/csf-tools/package.json +++ b/code/lib/csf-tools/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/csf-tools", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Parse and manipulate CSF and Storybook config files", "keywords": [ "storybook" diff --git a/code/lib/csf-tools/src/CsfFile.test.ts b/code/lib/csf-tools/src/CsfFile.test.ts index 74b9676f3f9..c503cf79567 100644 --- a/code/lib/csf-tools/src/CsfFile.test.ts +++ b/code/lib/csf-tools/src/CsfFile.test.ts @@ -5,8 +5,8 @@ import yaml from 'js-yaml'; import { loadCsf } from './CsfFile'; expect.addSnapshotSerializer({ - print: (val: any) => yaml.dump(typeof val === 'string' ? val : val.toString()).trimEnd(), - test: (val) => typeof val !== 'string', + print: (val: any) => yaml.dump(val).trimEnd(), + test: (val) => typeof val !== 'string' && !(val instanceof Error), }); const makeTitle = (userTitle?: string) => { @@ -33,7 +33,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: false + __id: foo-bar--a + - id: foo-bar--b + name: B + parameters: + __isArgsStory: true + __id: foo-bar--b + `); }); it('exported const stories', () => { @@ -47,7 +61,19 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __id: foo-bar--a + - id: foo-bar--b + name: B + parameters: + __id: foo-bar--b + `); }); it('underscores', () => { @@ -59,7 +85,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--basic + name: Basic + parameters: + __isArgsStory: false + __id: foo-bar--basic + `); }); it('exclude stories', () => { @@ -72,7 +107,16 @@ describe('CsfFile', () => { export const C = () => {}; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + excludeStories: + - B + - C + stories: + - id: foo-bar--a + name: A + `); }); it('include stories', () => { @@ -84,7 +128,15 @@ describe('CsfFile', () => { export const IncludeA = () => {}; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + includeStories: + - IncludeA + stories: + - id: foo-bar--include-a + name: Include A + `); }); it('storyName annotation', () => { @@ -96,7 +148,13 @@ describe('CsfFile', () => { A.storyName = 'Some story'; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: Some story + `); }); it('no title', () => { @@ -108,7 +166,16 @@ describe('CsfFile', () => { export const B = () => {}; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + component: '''foo''' + title: Default Title + stories: + - id: default-title--a + name: A + - id: default-title--b + name: B + `); }); it('custom component id', () => { @@ -120,7 +187,16 @@ describe('CsfFile', () => { export const B = () => {}; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + id: custom-id + stories: + - id: custom-id--a + name: A + - id: custom-id--b + name: B + `); }); it('custom parameters.__id', () => { @@ -132,7 +208,16 @@ describe('CsfFile', () => { export const CustomParemetersId = { parameters: { __id: 'custom-id' } }; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + id: custom-meta-id + stories: + - id: custom-meta-id--just-custom-meta-id + name: Just Custom Meta Id + - id: custom-id + name: Custom Paremeters Id + `); }); it('typescript', () => { @@ -146,7 +231,15 @@ describe('CsfFile', () => { export const B: StoryFn = () => <>B; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar/baz + stories: + - id: foo-bar-baz--a + name: A + - id: foo-bar-baz--b + name: B + `); }); it('typescript satisfies', () => { @@ -161,7 +254,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: AA + parameters: + __isArgsStory: true + __id: foo-bar--a + - id: foo-bar--b + name: B + parameters: + __isArgsStory: true + __id: foo-bar--b + `); }); it('typescript as', () => { @@ -176,7 +283,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: AA + parameters: + __isArgsStory: true + __id: foo-bar--a + - id: foo-bar--b + name: B + parameters: + __isArgsStory: true + __id: foo-bar--b + `); }); it('typescript meta var', () => { @@ -191,7 +312,15 @@ describe('CsfFile', () => { export const B: StoryFn = () => <>B; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar/baz + stories: + - id: foo-bar-baz--a + name: A + - id: foo-bar-baz--b + name: B + `); }); it('typescript satisfies meta var', () => { @@ -206,7 +335,15 @@ describe('CsfFile', () => { export const B: StoryFn = () => <>B; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar/baz + stories: + - id: foo-bar-baz--a + name: A + - id: foo-bar-baz--b + name: B + `); }); it('component object', () => { @@ -218,7 +355,16 @@ describe('CsfFile', () => { export const B = () => {}; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + component: '{}' + title: Default Title + stories: + - id: default-title--a + name: A + - id: default-title--b + name: B + `); }); it('template bind', () => { @@ -232,7 +378,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: true + __id: foo-bar--a + `); }); it('meta variable', () => { @@ -245,7 +400,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: false + __id: foo-bar--a + `); }); it('docs-only story', () => { @@ -258,7 +422,17 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--page + name: Page + parameters: + __isArgsStory: false + __id: foo-bar--page + docsOnly: true + `); }); it('docs-only story with local vars', () => { @@ -274,7 +448,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - stories-mdx + includeStories: + - __page + stories: + - id: foo-bar--page + name: Page + parameters: + __isArgsStory: false + __id: foo-bar--page + docsOnly: true + `); }); it('title variable', () => { @@ -288,7 +476,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: false + __id: foo-bar--a + - id: foo-bar--b + name: B + parameters: + __isArgsStory: true + __id: foo-bar--b + `); }); it('re-exported stories', () => { @@ -300,7 +502,15 @@ describe('CsfFile', () => { export { B } from './B'; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + - id: foo-bar--b + name: B + `); }); it('named exports order', () => { @@ -314,7 +524,21 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--b + name: B + parameters: + __isArgsStory: true + __id: foo-bar--b + - id: foo-bar--a + name: A + parameters: + __isArgsStory: false + __id: foo-bar--a + `); }); it('as default export', () => { @@ -330,7 +554,15 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __id: foo-bar--a + `); }); it('support for parameter decorators', () => { @@ -353,7 +585,11 @@ describe('CsfFile', () => { title: 'Chip', } `) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: Chip + stories: [] + `); }); }); @@ -429,7 +665,15 @@ describe('CsfFile', () => { export function B() {} ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + - id: foo-bar--b + name: B + `); }); }); @@ -488,7 +732,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: false + __id: foo-bar--a + `); }); it('Object export with args render', () => { @@ -502,7 +755,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: true + __id: foo-bar--a + `); }); it('Object export with default render', () => { @@ -514,7 +776,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + parameters: + __isArgsStory: true + __id: foo-bar--a + `); }); it('Object export with name', () => { @@ -528,7 +799,16 @@ describe('CsfFile', () => { `, true ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: Apple + parameters: + __isArgsStory: true + __id: foo-bar--a + `); }); it('Object export with storyName', () => { @@ -559,7 +839,10 @@ describe('CsfFile', () => { export default { title: 'foo/bar', x: 1, y: 2 }; `; const csf = loadCsf(input, { makeTitle }).parse(); - expect(csf.imports).toMatchInlineSnapshot(`./Button,./Check`); + expect(csf.imports).toMatchInlineSnapshot(` + - ./Button + - ./Check + `); }); // eslint-disable-next-line jest/no-disabled-tests it.skip('dynamic imports', () => { @@ -591,7 +874,17 @@ describe('CsfFile', () => { A.tags = ['Y']; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + `); }); it('csf3', () => { @@ -605,7 +898,17 @@ describe('CsfFile', () => { }; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + `); }); it('variables', () => { @@ -621,7 +924,17 @@ describe('CsfFile', () => { }; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + `); }); it('array error', () => { @@ -662,7 +975,18 @@ describe('CsfFile', () => { A.tags = ['Y']; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + - play-fn + `); }); it('story csf3', () => { @@ -677,7 +1001,18 @@ describe('CsfFile', () => { }; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + - play-fn + `); }); it('meta csf2', () => { @@ -691,7 +1026,18 @@ describe('CsfFile', () => { }; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + - play-fn + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + `); }); it('meta csf3', () => { @@ -703,7 +1049,18 @@ describe('CsfFile', () => { A.tags = ['Y']; ` ) - ).toMatchInlineSnapshot(`'[object Object]'`); + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - X + - play-fn + stories: + - id: foo-bar--a + name: A + tags: + - 'Y' + `); }); }); @@ -730,7 +1087,30 @@ describe('CsfFile', () => { { makeTitle, fileName: 'foo/bar.stories.js' } ).parse(); - expect(indexInputs).toMatchInlineSnapshot(`'[object Object],[object Object]'`); + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + exportName: A + name: A + title: custom foo title + metaId: component-id + tags: + - component-tag + - story-tag + - play-fn + __id: component-id--a + - type: story + importPath: foo/bar.stories.js + exportName: B + name: B + title: custom foo title + metaId: component-id + tags: + - component-tag + - story-tag + - play-fn + __id: component-id--b + `); }); it('supports custom parameters.__id', () => { @@ -749,7 +1129,17 @@ describe('CsfFile', () => { { makeTitle, fileName: 'foo/bar.stories.js' } ).parse(); - expect(indexInputs).toMatchInlineSnapshot(`'[object Object]'`); + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + exportName: A + name: A + title: custom foo title + metaId: component-id + tags: + - component-tag + __id: custom-story-id + `); }); it('removes duplicate tags', () => { @@ -767,7 +1157,20 @@ describe('CsfFile', () => { { makeTitle, fileName: 'foo/bar.stories.js' } ).parse(); - expect(indexInputs).toMatchInlineSnapshot(`'[object Object]'`); + expect(indexInputs).toMatchInlineSnapshot(` + - type: story + importPath: foo/bar.stories.js + exportName: A + name: A + title: custom foo title + tags: + - component-tag + - component-tag-dup + - inherit-tag-dup + - story-tag + - story-tag-dup + __id: custom-foo-title--a + `); }); it('throws if getting indexInputs without filename option', () => { @@ -786,13 +1189,9 @@ describe('CsfFile', () => { ).parse(); expect(() => csf.indexInputs).toThrowErrorMatchingInlineSnapshot(` - >- - Error: Cannot automatically create index inputs with CsfFile.indexInputs - because the CsfFile instance was created without a the fileName option. - - Either add the fileName option when creating the CsfFile instance, or create - the index inputs manually. -`); + [Error: Cannot automatically create index inputs with CsfFile.indexInputs because the CsfFile instance was created without a the fileName option. + Either add the fileName option when creating the CsfFile instance, or create the index inputs manually.] + `); }); }); }); diff --git a/code/lib/csf-tools/src/enrichCsf.test.ts b/code/lib/csf-tools/src/enrichCsf.test.ts index d9871e93522..562cacd8c04 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 7490f1d7218..035f1c5e108 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 72e43ab4585..34021666c62 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 08ebaf5cb46..ec832b109e8 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 b406cdfe20d..a2ebf3bd843 100644 --- a/code/ui/components/package.json +++ b/code/ui/components/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/components", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Core Storybook Components", "keywords": [ "storybook" @@ -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/Button/Button.tsx b/code/ui/components/src/components/Button/Button.tsx index 5045f0621ed..37b64a78649 100644 --- a/code/ui/components/src/components/Button/Button.tsx +++ b/code/ui/components/src/components/Button/Button.tsx @@ -15,23 +15,23 @@ export interface ButtonProps extends ButtonHTMLAttributes { active?: boolean; animation?: 'none' | 'rotate360' | 'glow' | 'jiggle'; - /** @deprecated Use {@link asChild} instead */ + /** @deprecated Use {@link asChild} instead. This will be removed in Storybook 9.0 */ isLink?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ primary?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ secondary?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ tertiary?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ gray?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ inForm?: boolean; - /** @deprecated Use {@link size} instead */ + /** @deprecated Use {@link size} instead. This will be removed in Storybook 9.0 */ small?: boolean; - /** @deprecated Use {@link variant} instead */ + /** @deprecated Use {@link variant} instead. This will be removed in Storybook 9.0 */ outline?: boolean; - /** @deprecated Add your icon as a child directly */ + /** @deprecated Add your icon as a child directly. This will be removed in Storybook 9.0 */ containsIcon?: boolean; } diff --git a/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx b/code/ui/components/src/components/tooltip/WithTooltip.stories.tsx index 76eab8d7c11..8ccb459095a 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 bece4bc6427..e76c898d6dd 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/components/src/components/typography/typography.stories.mdx b/code/ui/components/src/components/typography/typography.mdx similarity index 100% rename from code/ui/components/src/components/typography/typography.stories.mdx rename to code/ui/components/src/components/typography/typography.mdx diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json index 46ceaa8859b..92ba5532c00 100644 --- a/code/ui/manager/package.json +++ b/code/ui/manager/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/manager", - "version": "8.0.0-alpha.4", + "version": "8.0.0-alpha.8", "description": "Core Storybook UI", "keywords": [ "storybook" diff --git a/code/ui/manager/src/components/layout/Layout.stories.tsx b/code/ui/manager/src/components/layout/Layout.stories.tsx index 593e83dea5a..aea6f4a1a5f 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 624542b2f68..0a639c9db64 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 b958ebe2e1b..34e08e7c02e 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/components/sidebar/Menu.stories.tsx b/code/ui/manager/src/components/sidebar/Menu.stories.tsx index e04811e76bf..ebc234f75ef 100644 --- a/code/ui/manager/src/components/sidebar/Menu.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Menu.stories.tsx @@ -8,7 +8,7 @@ import { styled } from '@storybook/theming'; import { screen, userEvent, within } from '@storybook/testing-library'; import type { State } from '@storybook/manager-api'; import { LinkIcon } from '@storybook/icons'; -import { SidebarMenu, ToolbarMenu } from './Menu'; +import { SidebarMenu } from './Menu'; import { useMenu } from '../../container/Menu'; import { LayoutProvider } from '../layout/LayoutProvider'; @@ -34,11 +34,10 @@ export const Items: Story = { }; export const Real: Story = { - render: () => , -}; - -export const Toolbar = { - render: () => , + args: { + isHighlighted: true, + }, + render: (args) => , }; const DoubleThemeRenderingHack = styled.div({ diff --git a/code/ui/manager/src/components/sidebar/Menu.tsx b/code/ui/manager/src/components/sidebar/Menu.tsx index 95290acbc09..9dadfa2b156 100644 --- a/code/ui/manager/src/components/sidebar/Menu.tsx +++ b/code/ui/manager/src/components/sidebar/Menu.tsx @@ -5,7 +5,7 @@ import { styled } from '@storybook/theming'; import { transparentize } from 'polished'; import type { Button, TooltipLinkListLink } from '@storybook/components'; import { WithTooltip, TooltipLinkList, Icons, IconButton } from '@storybook/components'; -import { CloseIcon, CogIcon, MenuIcon } from '@storybook/icons'; +import { CloseIcon, CogIcon } from '@storybook/icons'; import { useLayout } from '../layout/LayoutProvider'; export type MenuList = ComponentProps['links']; @@ -22,48 +22,42 @@ const Icon = styled(Icons)(sharedStyles, ({ theme }) => ({ color: theme.color.secondary, })); -export const SidebarIconButton: FC< - ComponentProps & { highlighted: boolean; active: boolean } -> = styled(IconButton)< - ComponentProps & { - highlighted: boolean; - active: boolean; - } ->(({ highlighted, active, theme }) => ({ - position: 'relative', - overflow: 'visible', - color: theme.textMutedColor, - marginTop: 0, - zIndex: 1, - - ...(highlighted && { - '&:before, &:after': { - content: '""', - position: 'absolute', - top: 6, - right: 6, - width: 5, - height: 5, - zIndex: 2, - borderRadius: '50%', - background: theme.background.app, - border: `1px solid ${theme.background.app}`, - boxShadow: `0 0 0 2px ${theme.background.app}`, - }, - '&:after': { - background: theme.color.positive, - border: `1px solid rgba(0, 0, 0, 0.1)`, - boxShadow: `0 0 0 2px ${theme.background.app}`, - }, - - '&:hover:after, &:focus-visible:after': { - boxShadow: `0 0 0 2px ${transparentize(0.88, theme.color.secondary)}`, - }, - }), - ...(active && { - color: theme.color.secondary, - }), -})); +export const SidebarIconButton: FC & { highlighted: boolean }> = + styled(IconButton)< + ComponentProps & { + highlighted: boolean; + } + >(({ highlighted, theme }) => ({ + position: 'relative', + overflow: 'visible', + marginTop: 0, + zIndex: 1, + + ...(highlighted && { + '&:before, &:after': { + content: '""', + position: 'absolute', + top: 6, + right: 6, + width: 5, + height: 5, + zIndex: 2, + borderRadius: '50%', + background: theme.background.app, + border: `1px solid ${theme.background.app}`, + boxShadow: `0 0 0 2px ${theme.background.app}`, + }, + '&:after': { + background: theme.color.positive, + border: `1px solid rgba(0, 0, 0, 0.1)`, + boxShadow: `0 0 0 2px ${theme.background.app}`, + }, + + '&:hover:after, &:focus-visible:after': { + boxShadow: `0 0 0 2px ${transparentize(0.88, theme.color.secondary)}`, + }, + }), + })); const MenuButtonGroup = styled.div({ display: 'flex', @@ -134,13 +128,13 @@ export const SidebarMenu: FC = ({ menu, isHighlighted, onClick > - setMobileMenuOpen(false)} > - + ); } @@ -163,33 +157,3 @@ export const SidebarMenu: FC = ({ menu, isHighlighted, onClick
); }; - -export const ToolbarMenu: FC<{ - menu: MenuList; -}> = ({ menu }) => { - return ( - } - > - - - - - ); -}; - -// We should not have to reset the margin-top here -// TODO: remove this once we have a the new IconButton component -const CloseIconButton = styled(IconButton)({ - marginTop: 0, -}); diff --git a/code/ui/manager/src/container/Menu.tsx b/code/ui/manager/src/container/Menu.tsx index 9646acf2cdd..2c8fb64c01a 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/container/Sidebar.tsx b/code/ui/manager/src/container/Sidebar.tsx index 9041badfd5d..7eec79fc83c 100755 --- a/code/ui/manager/src/container/Sidebar.tsx +++ b/code/ui/manager/src/container/Sidebar.tsx @@ -1,7 +1,8 @@ import React, { useMemo } from 'react'; import type { Combo, StoriesHash } from '@storybook/manager-api'; -import { types, Consumer } from '@storybook/manager-api'; +import { Consumer } from '@storybook/manager-api'; +import { Addon_TypesEnum } from '@storybook/types'; import type { SidebarProps as SidebarComponentProps } from '../components/sidebar/Sidebar'; import { Sidebar as SidebarComponent } from '../components/sidebar/Sidebar'; @@ -41,9 +42,12 @@ const Sidebar = React.memo(function Sideber({ onMenuClick }: SidebarProps) { const whatsNewNotificationsEnabled = state.whatsNewData?.status === 'SUCCESS' && !state.disableWhatsNewNotifications; - const items = api.getElements(types.experimental_SIDEBAR_BOTTOM); - const bottom = useMemo(() => Object.values(items), [items]); - const top = useMemo(() => Object.values(api.getElements(types.experimental_SIDEBAR_TOP)), []); + const bottomItems = api.getElements(Addon_TypesEnum.experimental_SIDEBAR_BOTTOM); + const topItems = api.getElements(Addon_TypesEnum.experimental_SIDEBAR_TOP); + // eslint-disable-next-line react-hooks/exhaustive-deps + const bottom = useMemo(() => Object.values(bottomItems), [...Object.values(bottomItems)]); + // eslint-disable-next-line react-hooks/exhaustive-deps + const top = useMemo(() => Object.values(topItems), [...Object.values(topItems)]); return { title: name, diff --git a/code/ui/manager/src/globals/exports.ts b/code/ui/manager/src/globals/exports.ts index adeceb1a1e8..1da23ad426d 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/About.tsx b/code/ui/manager/src/settings/About.tsx index 797af9dcc73..658bc92030b 100644 --- a/code/ui/manager/src/settings/About.tsx +++ b/code/ui/manager/src/settings/About.tsx @@ -2,15 +2,19 @@ import type { FC } from 'react'; import React from 'react'; import { styled } from '@storybook/theming'; -import { Button as BaseButton, Link, StorybookIcon } from '@storybook/components'; +import { Button, Link, StorybookLogo } from '@storybook/components'; import { DocumentIcon, GithubIcon } from '@storybook/icons'; import { UpgradeBlock } from '../components/upgrade/UpgradeBlock'; -const Header = styled.header(({ theme }) => ({ +const Container = styled.div({ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + marginTop: 40, +}); + +const Header = styled.header({ marginBottom: 32, - fontSize: theme.typography.size.l2, - color: theme.base === 'light' ? theme.color.darkest : theme.color.lightest, - fontWeight: theme.typography.weight.bold, alignItems: 'center', display: 'flex', @@ -19,14 +23,6 @@ const Header = styled.header(({ theme }) => ({ width: 'auto', marginRight: 8, }, -})); - -const Container = styled.div({ - display: `flex`, - alignItems: 'center', - justifyContent: 'center', - height: 'calc(100% - 40px)', - flexDirection: 'column', }); const Footer = styled.div(({ theme }) => ({ @@ -39,18 +35,14 @@ const Footer = styled.div(({ theme }) => ({ fontSize: theme.typography.size.s2, })); -const SquareButton = styled(BaseButton)(({ theme }) => ({ - '&&': { - borderRadius: 4, - fontSize: '13px', - lineHeight: '14px', - color: theme.base === 'light' ? theme.color.darker : theme.color.lightest, - padding: '9px 12px', - svg: { - marginRight: 6, - }, - }, -})); +const Actions = styled.div({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + marginBottom: 24, + marginTop: 24, + gap: 16, +}); const StyledLink = styled(Link as any)(({ theme }) => ({ '&&': { @@ -65,28 +57,25 @@ const StyledLink = styled(Link as any)(({ theme }) => ({ const AboutScreen: FC<{ onNavigateToWhatsNew?: () => void }> = ({ onNavigateToWhatsNew }) => { return ( -
- Storybook +
-