diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md
index 30572cbb308c..02449fd291ce 100644
--- a/CHANGELOG.prerelease.md
+++ b/CHANGELOG.prerelease.md
@@ -1,3 +1,28 @@
+## 8.0.0-beta.6
+
+- Addon-onboarding: Move onboarding to monorepo - [#26176](https://github.com/storybookjs/storybook/pull/26176), thanks [@ndelangen](https://github.com/ndelangen)!
+- Angular: Fix Storybook startup after init - [#26213](https://github.com/storybookjs/storybook/pull/26213), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
+- Angular: Use dedicated tsconfig for compodocs - [#26214](https://github.com/storybookjs/storybook/pull/26214), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
+- Automigration: Fix overflow bug by using a better link - [#26203](https://github.com/storybookjs/storybook/pull/26203), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
+- Automigration: Run the `mdx-to-csf` codemod during automigration - [#26201](https://github.com/storybookjs/storybook/pull/26201), thanks [@ndelangen](https://github.com/ndelangen)!
+- Automigrations: Create addon-postcss automigration - [#26228](https://github.com/storybookjs/storybook/pull/26228), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
+- Automigrations: Enhance experience for upgrades from Storybook 6 to 8 - [#26067](https://github.com/storybookjs/storybook/pull/26067), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
+- Automigrations: General fixes for automigrations, blockers and codemods - [#26210](https://github.com/storybookjs/storybook/pull/26210), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
+- Blocks: Fix `Stories` block rendering duplicate stories when globals are changed - [#26110](https://github.com/storybookjs/storybook/pull/26110), thanks [@JReinhold](https://github.com/JReinhold)!
+- Builder Vite: Pass on errors about not having a real stats object in smoke tests - [#26195](https://github.com/storybookjs/storybook/pull/26195), thanks [@tmeasday](https://github.com/tmeasday)!
+- CLI: Rename `--webpack-stats-json` to `--stats-json` as it works in Vite - [#26128](https://github.com/storybookjs/storybook/pull/26128), thanks [@tmeasday](https://github.com/tmeasday)!
+- Codemods: Enhance mdx-to-csf codemod - [#26164](https://github.com/storybookjs/storybook/pull/26164), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
+- Core: Export preview symbols for embedding - [#26224](https://github.com/storybookjs/storybook/pull/26224), thanks [@tmeasday](https://github.com/tmeasday)!
+- Docs: Don't show how to setup controls for empty argTypes in doc pages - [#26200](https://github.com/storybookjs/storybook/pull/26200), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
+- Docs: Fix function prop rendering as `noRefCheck` in storybook docs - [#26104](https://github.com/storybookjs/storybook/pull/26104), thanks [@thisisanto](https://github.com/thisisanto)!
+- Doctor: Add dynamic check for incompatible Storybook packages - [#26219](https://github.com/storybookjs/storybook/pull/26219), thanks [@yannbf](https://github.com/yannbf)!
+- Maintenance: Remove deprecation of `manager-api`'s `types` export - [#26202](https://github.com/storybookjs/storybook/pull/26202), thanks [@ndelangen](https://github.com/ndelangen)!
+- Revert "Revert: "Angular: Reduce the warnings from `ts-loader` via stricter list of `includes`"" - [#26226](https://github.com/storybookjs/storybook/pull/26226), thanks [@ndelangen](https://github.com/ndelangen)!
+- Revert: "Angular: Reduce the warnings from `ts-loader` via stricter list of `includes`" - [#26208](https://github.com/storybookjs/storybook/pull/26208), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
+- UI: Improve sidebar performance when switching stories - [#26184](https://github.com/storybookjs/storybook/pull/26184), thanks [@literalpie](https://github.com/literalpie)!
+- UI: Update theme switcher to use toggle button for two themes - [#25682](https://github.com/storybookjs/storybook/pull/25682), thanks [@ivoilic](https://github.com/ivoilic)!
+- Vue: Improve types for array, union and intersection when using vue-docgen-api - [#26177](https://github.com/storybookjs/storybook/pull/26177), thanks [@larsrickert](https://github.com/larsrickert)!
+
## 8.0.0-beta.5
- Addon-controls: Dont show "setup controls" if control is disabled or a function - [#26120](https://github.com/storybookjs/storybook/pull/26120), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
diff --git a/MIGRATION.md b/MIGRATION.md
index 77d77356d46e..202962c2cf7f 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -15,6 +15,7 @@
- [For Solid:](#for-solid)
- [For Qwik:](#for-qwik)
- [TurboSnap Vite plugin is no longer needed](#turbosnap-vite-plugin-is-no-longer-needed)
+ - [`--webpack-stats-json` option renamed `--stats-json`](#--webpack-stats-json-option-renamed---stats-json)
- [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)
@@ -551,6 +552,11 @@ At least in build mode, `builder-vite` now supports the `--webpack-stats-json` f
This means https://github.com/IanVS/vite-plugin-turbosnap is no longer necessary, and duplicative, and the plugin will automatically be removed if found.
+### `--webpack-stats-json` option renamed `--stats-json`
+
+Now that both Vite and Webpack support the `preview-stats.json` file, the flag has been renamed. The old flag will continue to work.
+
+
### 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,
diff --git a/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts b/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts
new file mode 100644
index 000000000000..c77284a296f3
--- /dev/null
+++ b/code/addons/docs/template/stories/docs2/multiple-csf-files-a.stories.ts
@@ -0,0 +1,23 @@
+import { global as globalThis } from '@storybook/global';
+
+export default {
+ title: 'Multiple CSF Files Same Title',
+ component: globalThis.Components.Html,
+ tags: ['autodocs'],
+ args: {
+ content: '
paragraph
',
+ },
+ parameters: {
+ chromatic: { disable: true },
+ },
+};
+
+export const DefaultA = {};
+
+export const SpanContent = {
+ args: { content: 'span ' },
+};
+
+export const CodeContent = {
+ args: { content: 'code
' },
+};
diff --git a/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts b/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts
new file mode 100644
index 000000000000..955c04af9f9e
--- /dev/null
+++ b/code/addons/docs/template/stories/docs2/multiple-csf-files-b.stories.ts
@@ -0,0 +1,23 @@
+import { global as globalThis } from '@storybook/global';
+
+export default {
+ title: 'Multiple CSF Files Same Title',
+ component: globalThis.Components.Html,
+ tags: ['autodocs'],
+ args: {
+ content: 'paragraph
',
+ },
+ parameters: {
+ chromatic: { disable: true },
+ },
+};
+
+export const DefaultB = {};
+
+export const H1Content = {
+ args: { content: 'heading 1 ' },
+};
+
+export const H2Content = {
+ args: { content: 'heading 2 ' },
+};
diff --git a/code/addons/onboarding/CHANGELOG.md b/code/addons/onboarding/CHANGELOG.md
new file mode 100644
index 000000000000..16a9c57bea11
--- /dev/null
+++ b/code/addons/onboarding/CHANGELOG.md
@@ -0,0 +1,686 @@
+# v1.0.11 (Tue Jan 23 2024)
+
+#### 🐛 Bug Fix
+
+- Fix z-index bug by adding a wrapper [#82](https://github.com/storybookjs/addon-onboarding/pull/82) ([@ndelangen](https://github.com/ndelangen))
+- Make selectors Storybook 8 compatible [#81](https://github.com/storybookjs/addon-onboarding/pull/81) ([@yannbf](https://github.com/yannbf))
+- UI: Fix z-index in modal elements [#78](https://github.com/storybookjs/addon-onboarding/pull/78) ([@cdedreuille](https://github.com/cdedreuille) [@yannbf](https://github.com/yannbf))
+
+#### Authors: 3
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Norbert de Langen ([@ndelangen](https://github.com/ndelangen))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v1.0.10 (Mon Dec 11 2023)
+
+#### 🐛 Bug Fix
+
+- Fix Yarn remove command in README [#80](https://github.com/storybookjs/addon-onboarding/pull/80) ([@githrdw](https://github.com/githrdw))
+
+#### Authors: 1
+
+- [@githrdw](https://github.com/githrdw)
+
+---
+
+# v1.0.9 (Fri Dec 01 2023)
+
+#### 🐛 Bug Fix
+
+- update telemetry version [#79](https://github.com/storybookjs/addon-onboarding/pull/79) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v1.0.8 (Thu Jul 20 2023)
+
+#### ⚠️ Pushed to `main`
+
+- Create CODEOWNERS ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v1.0.7 (Tue Jul 11 2023)
+
+#### 🐛 Bug Fix
+
+- Replace chevron icon on Configure page to avoid @storybook/components usage [#77](https://github.com/storybookjs/addon-onboarding/pull/77) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v1.0.6 (Mon Jul 10 2023)
+
+#### 🐛 Bug Fix
+
+- Fix language detection [#76](https://github.com/storybookjs/addon-onboarding/pull/76) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v1.0.5 (Mon Jul 10 2023)
+
+#### 🐛 Bug Fix
+
+- Remove nextjs-specific code [#75](https://github.com/storybookjs/addon-onboarding/pull/75) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v1.0.4 (Thu Jul 06 2023)
+
+#### 🐛 Bug Fix
+
+- Update Configure page design [#74](https://github.com/storybookjs/addon-onboarding/pull/74) ([@JReinhold](https://github.com/JReinhold) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Jeppe Reinhold ([@JReinhold](https://github.com/JReinhold))
+
+---
+
+# v1.0.3 (Fri Jun 23 2023)
+
+#### 🐛 Bug Fix
+
+- Display the button story filename in tooltip [#73](https://github.com/storybookjs/addon-onboarding/pull/73) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v1.0.2 (Thu Jun 22 2023)
+
+#### 🐛 Bug Fix
+
+- Fix package.json version extraction [#72](https://github.com/storybookjs/addon-onboarding/pull/72) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v1.0.1 (Thu Jun 22 2023)
+
+#### 🐛 Bug Fix
+
+- Fix build assets [#71](https://github.com/storybookjs/addon-onboarding/pull/71) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v1.0.0 (Thu Jun 22 2023)
+
+#### 💥 Breaking Change
+
+- Release 1.0.0 [#70](https://github.com/storybookjs/addon-onboarding/pull/70) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### 🐛 Bug Fix
+
+- Fix text for Javascript Projects [#68](https://github.com/storybookjs/addon-onboarding/pull/68) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.43 (Thu Jun 22 2023)
+
+#### 🐛 Bug Fix
+
+- Fix react joyride in yarn pnp mode [#69](https://github.com/storybookjs/addon-onboarding/pull/69) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- fix links in configure.mdx [#67](https://github.com/storybookjs/addon-onboarding/pull/67) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 2
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.42 (Wed Jun 21 2023)
+
+#### 🐛 Bug Fix
+
+- Fix nextjs code steps [#66](https://github.com/storybookjs/addon-onboarding/pull/66) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.41 (Wed Jun 21 2023)
+
+#### 🐛 Bug Fix
+
+- Minor improvements [#65](https://github.com/storybookjs/addon-onboarding/pull/65) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.40 (Tue Jun 20 2023)
+
+#### 🐛 Bug Fix
+
+- Fix preset.js entry point [#63](https://github.com/storybookjs/addon-onboarding/pull/63) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.39 (Tue Jun 20 2023)
+
+#### 🐛 Bug Fix
+
+- Fix comment in code section [#64](https://github.com/storybookjs/addon-onboarding/pull/64) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.38 (Mon Jun 19 2023)
+
+#### 🐛 Bug Fix
+
+- Pass addon version in telemetry event [#62](https://github.com/storybookjs/addon-onboarding/pull/62) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.37 (Mon Jun 19 2023)
+
+#### 🐛 Bug Fix
+
+- UI Fixes [#61](https://github.com/storybookjs/addon-onboarding/pull/61) ([@yannbf](https://github.com/yannbf) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.36 (Fri Jun 16 2023)
+
+#### 🐛 Bug Fix
+
+- Improve dark mode [#58](https://github.com/storybookjs/addon-onboarding/pull/58) ([@cdedreuille](https://github.com/cdedreuille) [@yannbf](https://github.com/yannbf))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.35 (Fri Jun 16 2023)
+
+#### 🐛 Bug Fix
+
+- Add dark mode styles to tooltip [#54](https://github.com/storybookjs/addon-onboarding/pull/54) ([@yannbf](https://github.com/yannbf))
+- UI fixes [#56](https://github.com/storybookjs/addon-onboarding/pull/56) ([@yannbf](https://github.com/yannbf))
+- Fix watch mode [#55](https://github.com/storybookjs/addon-onboarding/pull/55) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.34 (Wed Jun 14 2023)
+
+#### 🐛 Bug Fix
+
+- Fix configure page's layout [#53](https://github.com/storybookjs/addon-onboarding/pull/53) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 1
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+
+---
+
+# v0.0.33 (Wed Jun 14 2023)
+
+#### 🐛 Bug Fix
+
+- Improve story detection [#52](https://github.com/storybookjs/addon-onboarding/pull/52) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.32 (Wed Jun 14 2023)
+
+#### 🐛 Bug Fix
+
+- Fixes on syntax highlighter [#51](https://github.com/storybookjs/addon-onboarding/pull/51) ([@cdedreuille](https://github.com/cdedreuille) [@yannbf](https://github.com/yannbf))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.31 (Wed Jun 14 2023)
+
+#### 🐛 Bug Fix
+
+- Improve instructions [#50](https://github.com/storybookjs/addon-onboarding/pull/50) ([@shilman](https://github.com/shilman))
+
+#### Authors: 1
+
+- Michael Shilman ([@shilman](https://github.com/shilman))
+
+---
+
+# v0.0.30 (Tue Jun 13 2023)
+
+#### 🐛 Bug Fix
+
+- Add core server events for telemetry [#40](https://github.com/storybookjs/addon-onboarding/pull/40) ([@valentinpalkovic](https://github.com/valentinpalkovic) [@yannbf](https://github.com/yannbf))
+
+#### Authors: 2
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.29 (Tue Jun 13 2023)
+
+#### ⚠️ Pushed to `main`
+
+- cleanup dependencies ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.28 (Mon Jun 12 2023)
+
+#### 🐛 Bug Fix
+
+- Feat/overall improvements [#49](https://github.com/storybookjs/addon-onboarding/pull/49) ([@yannbf](https://github.com/yannbf) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.27 (Fri Jun 09 2023)
+
+#### 🐛 Bug Fix
+
+- Add previous button to write stories modal [#48](https://github.com/storybookjs/addon-onboarding/pull/48) ([@yannbf](https://github.com/yannbf) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.26 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Remove animation modal [#47](https://github.com/storybookjs/addon-onboarding/pull/47) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 1
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+
+---
+
+# v0.0.25 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Improve addon bootstrapping [#46](https://github.com/storybookjs/addon-onboarding/pull/46) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.24 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Use the correct event to detect args change [#45](https://github.com/storybookjs/addon-onboarding/pull/45) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.23 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Various improvements [#44](https://github.com/storybookjs/addon-onboarding/pull/44) ([@yannbf](https://github.com/yannbf) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.22 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Improve confetti colors and shapes [#43](https://github.com/storybookjs/addon-onboarding/pull/43) ([@yannbf](https://github.com/yannbf) [@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.21 (Thu Jun 08 2023)
+
+#### 🐛 Bug Fix
+
+- Improve modal animation [#42](https://github.com/storybookjs/addon-onboarding/pull/42) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 1
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+
+---
+
+# v0.0.20 (Wed Jun 07 2023)
+
+#### 🐛 Bug Fix
+
+- Change code for Javascript projects [#41](https://github.com/storybookjs/addon-onboarding/pull/41) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.19 (Wed Jun 07 2023)
+
+#### 🐛 Bug Fix
+
+- Improve write a story modal [#38](https://github.com/storybookjs/addon-onboarding/pull/38) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 1
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+
+---
+
+# v0.0.18 (Wed Jun 07 2023)
+
+#### 🐛 Bug Fix
+
+- Implement "How to write your Story" flow [#33](https://github.com/storybookjs/addon-onboarding/pull/33) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.17 (Wed Jun 07 2023)
+
+#### 🐛 Bug Fix
+
+- use onboarding parameter in conjunction with onboarding path [#37](https://github.com/storybookjs/addon-onboarding/pull/37) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.16 (Tue Jun 06 2023)
+
+#### 🐛 Bug Fix
+
+- Improve guided tour [#35](https://github.com/storybookjs/addon-onboarding/pull/35) ([@cdedreuille](https://github.com/cdedreuille) [@yannbf](https://github.com/yannbf))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.15 (Tue Jun 06 2023)
+
+#### 🐛 Bug Fix
+
+- Trigger via `/onboarding` path instead of query parameter [#21](https://github.com/storybookjs/addon-onboarding/pull/21) ([@yannbf](https://github.com/yannbf))
+- Welcome modal animation [#34](https://github.com/storybookjs/addon-onboarding/pull/34) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 2
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.14 (Mon Jun 05 2023)
+
+#### 🐛 Bug Fix
+
+- Modal improvements [#22](https://github.com/storybookjs/addon-onboarding/pull/22) ([@cdedreuille](https://github.com/cdedreuille))
+
+#### Authors: 1
+
+- Charles de Dreuille ([@cdedreuille](https://github.com/cdedreuille))
+
+---
+
+# v0.0.13 (Mon Jun 05 2023)
+
+#### 🐛 Bug Fix
+
+- Implement syntax highlighter [#20](https://github.com/storybookjs/addon-onboarding/pull/20) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.12 (Thu Jun 01 2023)
+
+#### 🐛 Bug Fix
+
+- Implemented bare minimum list component [#19](https://github.com/storybookjs/addon-onboarding/pull/19) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.11 (Thu Jun 01 2023)
+
+#### 🐛 Bug Fix
+
+- Add guided tour example [#5](https://github.com/storybookjs/addon-onboarding/pull/5) ([@yannbf](https://github.com/yannbf))
+
+#### Authors: 1
+
+- Yann Braga ([@yannbf](https://github.com/yannbf))
+
+---
+
+# v0.0.10 (Tue May 30 2023)
+
+#### ⚠️ Pushed to `main`
+
+- Rename confett to Confetti ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Rename confetti to confett ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.9 (Tue May 30 2023)
+
+#### 🐛 Bug Fix
+
+- Implement bare minimum modal component [#18](https://github.com/storybookjs/addon-onboarding/pull/18) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.8 (Tue May 30 2023)
+
+#### 🐛 Bug Fix
+
+- Implement bare minimum confetti component [#7](https://github.com/storybookjs/addon-onboarding/pull/7) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.7 (Thu May 25 2023)
+
+#### 🐛 Bug Fix
+
+- Init Storybook Theme Provider [#6](https://github.com/storybookjs/addon-onboarding/pull/6) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.6 (Tue May 23 2023)
+
+#### 🐛 Bug Fix
+
+- Add minimum React Application [#3](https://github.com/storybookjs/addon-onboarding/pull/3) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.5 (Tue May 23 2023)
+
+#### ⚠️ Pushed to `main`
+
+- Add clean package.json ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.4 (Tue May 23 2023)
+
+#### ⚠️ Pushed to `main`
+
+- Cleanup ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.3 (Tue May 23 2023)
+
+#### 🐛 Bug Fix
+
+- Setup Chromatic [#2](https://github.com/storybookjs/addon-onboarding/pull/2) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.2 (Tue May 23 2023)
+
+#### 🐛 Bug Fix
+
+- Remove unnecessary files [#1](https://github.com/storybookjs/addon-onboarding/pull/1) ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+---
+
+# v0.0.1 (Tue May 23 2023)
+
+#### ⚠️ Pushed to `main`
+
+- Fix manager.ts and add export {} ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Add auto.config.js ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Init Addon Onboarding ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- project setup ([@valentinpalkovic](https://github.com/valentinpalkovic))
+- Initial commit ([@valentinpalkovic](https://github.com/valentinpalkovic))
+
+#### Authors: 1
+
+- Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic))
diff --git a/code/addons/onboarding/README.md b/code/addons/onboarding/README.md
new file mode 100644
index 000000000000..257dd6a05627
--- /dev/null
+++ b/code/addons/onboarding/README.md
@@ -0,0 +1,50 @@
+# Storybook Addon Onboarding
+
+This addon provides a guided tour in some of Storybook's features, helping you get to know about the basics of Storybook and learn how to write stories!
+
+![](./.github/assets/onboarding-intro.png)
+
+## Triggering the onboarding
+
+This addon comes installed by default in Storybook projects and should trigger automatically.
+If you want to retrigger the addon, you should make sure that your Storybook still contains the example stories that come when initializing Storybook, and you can then navigate to http://localhost:6006/?path=/onboarding after running Storybook.
+
+## Uninstalling
+
+This addon serves to provide you a guided experience on the basics of Storybook. Once you are done, the addon is therefore not needed anymore and will not get activated (unless triggered manually), so you can freely remove it. Here's how to do so:
+
+### 1. Remove the dependency
+
+yarn:
+
+```zsh
+yarn remove @storybook/addon-onboarding
+```
+
+npm:
+
+```zsh
+npm uninstall -D @storybook/addon-onboarding
+```
+
+pnpm:
+
+```zsh
+pnpm remove -D @storybook/addon-onboarding
+```
+
+### 2. Remove the addon in your `.storybook/main.js` file
+
+```diff
+const config = {
+ stories: [
+ "../stories/**/*.stories.mdx",
+ "../stories/**/*.stories.@(js|jsx|ts|tsx)",
+ ],
+ addons: [
+ "@storybook/addon-essentials",
+- "@storybook/addon-onboarding"
+ ],
+};
+export default config;
+```
diff --git a/code/addons/onboarding/package.json b/code/addons/onboarding/package.json
new file mode 100644
index 000000000000..ee3f943427d6
--- /dev/null
+++ b/code/addons/onboarding/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "@storybook/addon-onboarding",
+ "version": "8.0.0-beta.5",
+ "description": "Storybook Addon Onboarding - Introduces a new onboarding experience",
+ "keywords": [
+ "storybook-addons",
+ "addon-onboarding"
+ ],
+ "homepage": "https://github.com/storybookjs/storybook/tree/next/code/addons/onboarding",
+ "bugs": {
+ "url": "https://github.com/storybookjs/storybook/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/storybookjs/storybook.git",
+ "directory": "code/addons/onboarding"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/storybook"
+ },
+ "license": "MIT",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "node": "./dist/index.js",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./manager": "./dist/manager.js",
+ "./preset": "./dist/preset.js",
+ "./package.json": "./package.json"
+ },
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "types": "dist/index.d.ts",
+ "files": [
+ "dist/**/*",
+ "README.md",
+ "*.js",
+ "*.d.ts",
+ "!src/**/*"
+ ],
+ "scripts": {
+ "check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts",
+ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
+ },
+ "devDependencies": {
+ "@radix-ui/react-dialog": "^1.0.5",
+ "@storybook/channels": "workspace:*",
+ "@storybook/components": "workspace:*",
+ "@storybook/core-events": "workspace:*",
+ "@storybook/icons": "^1.2.5",
+ "@storybook/manager-api": "workspace:*",
+ "@storybook/react": "workspace:*",
+ "@storybook/telemetry": "workspace:*",
+ "@storybook/test": "workspace:*",
+ "@storybook/testing-library": "next",
+ "@storybook/theming": "workspace:*",
+ "@storybook/types": "workspace:*",
+ "framer-motion": "^11.0.3",
+ "react": "^18.2.0",
+ "react-confetti": "^6.1.0",
+ "react-dom": "^18.2.0",
+ "react-joyride": "^2.7.2",
+ "react-use-measure": "^2.1.1",
+ "typescript": "^5.3.2"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "bundler": {
+ "exportEntries": [
+ "./src/index.ts"
+ ],
+ "managerEntries": [
+ "./src/manager.tsx"
+ ],
+ "nodeEntries": [
+ "./src/preset.ts"
+ ]
+ }
+}
diff --git a/code/addons/onboarding/preset.js b/code/addons/onboarding/preset.js
new file mode 100644
index 000000000000..2c7ea670f4a6
--- /dev/null
+++ b/code/addons/onboarding/preset.js
@@ -0,0 +1,8 @@
+function managerEntries(entry = []) {
+ return [...entry, require.resolve('./dist/manager.mjs')];
+}
+
+module.exports = {
+ managerEntries,
+ ...require('./dist/preset'),
+};
diff --git a/code/addons/onboarding/project.json b/code/addons/onboarding/project.json
new file mode 100644
index 000000000000..1c9bf19c6931
--- /dev/null
+++ b/code/addons/onboarding/project.json
@@ -0,0 +1,6 @@
+{
+ "name": "@storybook/addon-onboarding",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "implicitDependencies": [],
+ "type": "library"
+}
diff --git a/code/addons/onboarding/src/App.tsx b/code/addons/onboarding/src/App.tsx
new file mode 100644
index 000000000000..e561c55cea1c
--- /dev/null
+++ b/code/addons/onboarding/src/App.tsx
@@ -0,0 +1,155 @@
+import React, { useCallback, useEffect, useState } from 'react';
+import { ThemeProvider, convert } from '@storybook/theming';
+import { addons, type API } from '@storybook/manager-api';
+
+import { GuidedTour } from './features/GuidedTour/GuidedTour';
+import { WelcomeModal } from './features/WelcomeModal/WelcomeModal';
+import { WriteStoriesModal } from './features/WriteStoriesModal/WriteStoriesModal';
+import { Confetti } from './components/Confetti/Confetti';
+import { STORYBOOK_ADDON_ONBOARDING_CHANNEL } from './constants';
+import { useGetProject } from './features/WriteStoriesModal/hooks/useGetProject';
+
+type Step =
+ | '1:Welcome'
+ | '2:StorybookTour'
+ | '3:WriteYourStory'
+ | '4:VisitNewStory'
+ | '5:ConfigureYourProject';
+
+const theme = convert();
+
+export default function App({ api }: { api: API }) {
+ const [enabled, setEnabled] = useState(true);
+ const [showConfetti, setShowConfetti] = useState(false);
+ const [step, setStep] = useState('1:Welcome');
+ const { data: codeSnippets } = useGetProject();
+
+ const skipOnboarding = useCallback(() => {
+ // remove onboarding query parameter from current url
+ const url = new URL(window.location.href);
+ // @ts-expect-error (not strict)
+ const path = decodeURIComponent(url.searchParams.get('path'));
+ url.search = `?path=${path}&onboarding=false`;
+ history.replaceState({}, '', url.href);
+ api.setQueryParams({ onboarding: 'false' });
+ setEnabled(false);
+ }, [setEnabled, api]);
+
+ useEffect(() => {
+ api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
+ step: '1:Welcome',
+ type: 'telemetry',
+ });
+ }, []);
+
+ useEffect(() => {
+ if (step !== '1:Welcome') {
+ api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
+ step,
+ type: 'telemetry',
+ });
+ }
+ }, [api, step]);
+
+ useEffect(() => {
+ let stepTimeout: number;
+ if (step === '4:VisitNewStory') {
+ setShowConfetti(true);
+ stepTimeout = window.setTimeout(() => {
+ setStep('5:ConfigureYourProject');
+ }, 2000);
+ }
+
+ return () => {
+ clearTimeout(stepTimeout);
+ };
+ }, [step]);
+
+ useEffect(() => {
+ const storyId = api.getCurrentStoryData()?.id;
+ api.setQueryParams({ onboarding: 'true' });
+ // make sure the initial state is set correctly:
+ // 1. Selected story is primary button
+ // 2. The addon panel is opened, in the bottom and the controls tab is selected
+ if (storyId !== 'example-button--primary') {
+ try {
+ api.selectStory('example-button--primary', undefined, {
+ ref: undefined,
+ });
+ } catch (e) {
+ //
+ }
+ }
+ }, []);
+
+ if (!enabled) {
+ return null;
+ }
+
+ return (
+
+ {enabled && showConfetti && (
+ {
+ confetti?.reset();
+ setShowConfetti(false);
+ }}
+ />
+ )}
+ {enabled && step === '1:Welcome' && (
+ {
+ setStep('2:StorybookTour');
+ }}
+ skipOnboarding={() => {
+ skipOnboarding();
+
+ api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
+ step: 'X:SkippedOnboarding',
+ where: 'WelcomeModal',
+ type: 'telemetry',
+ });
+ }}
+ />
+ )}
+ {enabled && (step === '2:StorybookTour' || step === '5:ConfigureYourProject') && (
+ {
+ setStep('3:WriteYourStory');
+ }}
+ codeSnippets={codeSnippets || undefined}
+ onLastTourDone={() => {
+ try {
+ api.selectStory('configure-your-project--docs');
+ } catch (e) {
+ //
+ }
+ api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
+ step: '6:FinishedOnboarding',
+ type: 'telemetry',
+ });
+ skipOnboarding();
+ }}
+ />
+ )}
+ {enabled && step === '3:WriteYourStory' && codeSnippets && (
+ {
+ api.selectStory('example-button--warning');
+
+ setStep('4:VisitNewStory');
+ }}
+ skipOnboarding={skipOnboarding}
+ />
+ )}
+
+ );
+}
diff --git a/code/addons/onboarding/src/components/Button/Button.stories.tsx b/code/addons/onboarding/src/components/Button/Button.stories.tsx
new file mode 100644
index 000000000000..cf8a824713fb
--- /dev/null
+++ b/code/addons/onboarding/src/components/Button/Button.stories.tsx
@@ -0,0 +1,15 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { Button } from './Button';
+
+const meta: Meta = {
+ title: 'Components/Button',
+ component: Button,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: { children: 'Button' },
+};
diff --git a/code/addons/onboarding/src/components/Button/Button.tsx b/code/addons/onboarding/src/components/Button/Button.tsx
new file mode 100644
index 000000000000..622359c4e31a
--- /dev/null
+++ b/code/addons/onboarding/src/components/Button/Button.tsx
@@ -0,0 +1,76 @@
+import type { ComponentProps } from 'react';
+import React, { forwardRef } from 'react';
+import { styled } from '@storybook/theming';
+
+export interface ButtonProps extends ComponentProps<'button'> {
+ children: string;
+ onClick?: (e: React.MouseEvent) => void;
+ variant?: 'primary' | 'secondary' | 'outline';
+}
+
+const StyledButton = styled.button<{ variant: ButtonProps['variant'] }>`
+ all: unset;
+ box-sizing: border-box;
+ border: 0;
+ border-radius: 0.25rem;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0 0.75rem;
+ background: ${({ theme, variant }) => {
+ if (variant === 'primary') return theme.color.secondary;
+ if (variant === 'secondary') return theme.color.lighter;
+ if (variant === 'outline') return 'transparent';
+ return theme.color.secondary;
+ }};
+ color: ${({ theme, variant }) => {
+ if (variant === 'primary') return theme.color.lightest;
+ if (variant === 'secondary') return theme.darkest;
+ if (variant === 'outline') return theme.darkest;
+ return theme.color.lightest;
+ }};
+ box-shadow: ${({ variant }) => {
+ if (variant === 'primary') return 'none';
+ if (variant === 'secondary') return '#D9E8F2 0 0 0 1px inset';
+ if (variant === 'outline') return '#D9E8F2 0 0 0 1px inset';
+ return 'none';
+ }};
+ height: 32px;
+ font-size: 0.8125rem;
+ font-weight: 700;
+ font-family: ${({ theme }) => theme.typography.fonts.base};
+ transition: background-color, box-shadow, opacity;
+ transition-duration: 0.16s;
+ transition-timing-function: ease-in-out;
+ text-decoration: none;
+
+ &:hover {
+ background-color: ${({ variant }) => {
+ if (variant === 'primary') return '#0b94eb';
+ if (variant === 'secondary') return '#eef4f9';
+ if (variant === 'outline') return 'transparent';
+ return '#0b94eb';
+ }};
+ }
+
+ &:focus {
+ box-shadow: ${({ variant }) => {
+ if (variant === 'primary') return 'inset 0 0 0 1px rgba(0, 0, 0, 0.2)';
+ if (variant === 'secondary') return 'inset 0 0 0 1px #0b94eb';
+ if (variant === 'outline') return 'inset 0 0 0 1px #0b94eb';
+ return 'inset 0 0 0 2px rgba(0, 0, 0, 0.1)';
+ }};
+ }
+`;
+
+export const Button = forwardRef(function Button(
+ { children, onClick, variant = 'primary', ...rest },
+ ref
+) {
+ return (
+
+ {children}
+
+ );
+});
diff --git a/code/addons/onboarding/src/components/Confetti/Confetti.stories.tsx b/code/addons/onboarding/src/components/Confetti/Confetti.stories.tsx
new file mode 100644
index 000000000000..80b01c7e31d8
--- /dev/null
+++ b/code/addons/onboarding/src/components/Confetti/Confetti.stories.tsx
@@ -0,0 +1,61 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { Confetti } from './Confetti';
+import React from 'react';
+
+const meta: Meta = {
+ component: Confetti,
+ parameters: {
+ chromatic: { disableSnapshot: true },
+ },
+ decorators: [
+ (StoryFn) => (
+
+ I am clickable
+
+
+ ),
+ ],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ recycle: true,
+ numberOfPieces: 200,
+ top: undefined,
+ left: undefined,
+ width: undefined,
+ height: undefined,
+ friction: 0.99,
+ wind: 0,
+ gravity: 0.1,
+ initialVelocityX: 4,
+ initialVelocityY: 10,
+ tweenDuration: 5000,
+ },
+};
+
+export const OneTimeConfetti: Story = {
+ args: {
+ ...Default.args,
+ numberOfPieces: 800,
+ recycle: false,
+ tweenDuration: 20000,
+ onConfettiComplete: (confetti) => {
+ confetti?.reset();
+ },
+ },
+};
+
+export const Positioned: Story = {
+ args: {
+ ...Default.args,
+ top: 100,
+ left: 300,
+ width: 300,
+ height: 250,
+ },
+};
diff --git a/code/addons/onboarding/src/components/Confetti/Confetti.tsx b/code/addons/onboarding/src/components/Confetti/Confetti.tsx
new file mode 100644
index 000000000000..5f0658dc462b
--- /dev/null
+++ b/code/addons/onboarding/src/components/Confetti/Confetti.tsx
@@ -0,0 +1,129 @@
+import { ReactConfetti } from 'react-confetti/src/ReactConfetti';
+import React, { useEffect } from 'react';
+import { styled } from '@storybook/theming';
+import { createPortal } from 'react-dom';
+import { useState } from 'react';
+
+interface ConfettiProps extends Omit, 'drawShape'> {
+ top?: number;
+ left?: number;
+ width?: number;
+ height?: number;
+ numberOfPieces?: number;
+ recycle?: boolean;
+ colors?: string[];
+}
+
+const Wrapper = styled.div<{
+ width: number;
+ height: number;
+ top: number;
+ left: number;
+}>(({ width, height, left, top }) => ({
+ width: `${width}px`,
+ height: `${height}px`,
+ left: `${left}px`,
+ top: `${top}px`,
+ position: 'relative',
+ overflow: 'hidden',
+}));
+
+export function Confetti({
+ top = 0,
+ left = 0,
+ width = window.innerWidth,
+ height = window.innerHeight,
+ colors = ['#CA90FF', '#FC521F', '#66BF3C', '#FF4785', '#FFAE00', '#1EA7FD'],
+ ...confettiProps
+}: ConfettiProps): React.ReactPortal {
+ const [confettiContainer] = useState(() => {
+ const container = document.createElement('div');
+ container.setAttribute('id', 'confetti-container');
+ container.setAttribute(
+ 'style',
+ 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 9999;'
+ );
+
+ return container;
+ });
+
+ useEffect(() => {
+ document.body.appendChild(confettiContainer);
+
+ return () => {
+ document.body.removeChild(confettiContainer);
+ };
+ }, []);
+
+ return createPortal(
+
+
+ ,
+ confettiContainer
+ );
+}
+
+enum ParticleShape {
+ Circle = 1,
+ Square = 2,
+ TShape = 3,
+ LShape = 4,
+ Triangle = 5,
+ QuarterCircle = 6,
+}
+
+function getRandomInt(min: number, max: number) {
+ return Math.floor(Math.random() * (max - min)) + min;
+}
+
+function draw(this: any, context: CanvasRenderingContext2D) {
+ this.shape = this.shape || getRandomInt(1, 6);
+
+ switch (this.shape) {
+ case ParticleShape.Square: {
+ const cornerRadius = 2;
+ const width = this.w / 2;
+ const height = this.h / 2;
+
+ context.moveTo(-width + cornerRadius, -height);
+ context.lineTo(width - cornerRadius, -height);
+ context.arcTo(width, -height, width, -height + cornerRadius, cornerRadius);
+ context.lineTo(width, height - cornerRadius);
+ context.arcTo(width, height, width - cornerRadius, height, cornerRadius);
+ context.lineTo(-width + cornerRadius, height);
+ context.arcTo(-width, height, -width, height - cornerRadius, cornerRadius);
+ context.lineTo(-width, -height + cornerRadius);
+ context.arcTo(-width, -height, -width + cornerRadius, -height, cornerRadius);
+
+ break;
+ }
+ case ParticleShape.TShape: {
+ context.rect(-4, -4, 8, 16);
+ context.rect(-12, -4, 24, 8);
+ break;
+ }
+ case ParticleShape.LShape: {
+ context.rect(-4, -4, 8, 16);
+ context.rect(-4, -4, 24, 8);
+ break;
+ }
+ case ParticleShape.Circle: {
+ context.arc(0, 0, this.radius, 0, 2 * Math.PI);
+ break;
+ }
+ case ParticleShape.Triangle: {
+ context.moveTo(16, 4);
+ context.lineTo(4, 24);
+ context.lineTo(24, 24);
+ break;
+ }
+ case ParticleShape.QuarterCircle: {
+ context.arc(4, -4, 4, -Math.PI / 2, 0);
+ context.lineTo(4, 0);
+ break;
+ }
+ }
+
+ context.closePath();
+ context.fill();
+}
diff --git a/code/addons/onboarding/src/components/List/List.stories.tsx b/code/addons/onboarding/src/components/List/List.stories.tsx
new file mode 100644
index 000000000000..380fd07ca4cc
--- /dev/null
+++ b/code/addons/onboarding/src/components/List/List.stories.tsx
@@ -0,0 +1,49 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { userEvent, waitFor, within } from '@storybook/testing-library';
+import { expect } from '@storybook/test';
+
+import { List } from './List';
+import { ListItem } from './ListItem/ListItem';
+
+const meta: Meta = {
+ component: List,
+};
+
+export default meta;
+
+export const Default: StoryObj = {
+ render: () => {
+ const [workingIndex, setWorkingIndex] = useState(1);
+
+ return (
+ <>
+
+ = 1} index={1}>
+ Hello World
+
+ = 2} index={2}>
+ Bonjour le monde
+
+ = 3} index={3}>
+ 你好, 世界
+
+
+
+ setWorkingIndex(workingIndex + 1)}>
+ Complete
+
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByText('Complete');
+
+ await expect(canvas.getAllByLabelText('complete')).toHaveLength(1);
+
+ await userEvent.click(button);
+
+ await waitFor(() => expect(canvas.getAllByLabelText('complete')).toHaveLength(2));
+ },
+};
diff --git a/code/addons/onboarding/src/components/List/List.styled.tsx b/code/addons/onboarding/src/components/List/List.styled.tsx
new file mode 100644
index 000000000000..79d91e96dc41
--- /dev/null
+++ b/code/addons/onboarding/src/components/List/List.styled.tsx
@@ -0,0 +1,9 @@
+import { styled } from '@storybook/theming';
+
+export const ListWrapper = styled.ul(() => ({
+ display: 'flex',
+ flexDirection: 'column',
+ rowGap: 16,
+ padding: 0,
+ margin: 0,
+}));
diff --git a/code/addons/onboarding/src/components/List/List.tsx b/code/addons/onboarding/src/components/List/List.tsx
new file mode 100644
index 000000000000..0b9640f7d909
--- /dev/null
+++ b/code/addons/onboarding/src/components/List/List.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { ListWrapper } from './List.styled';
+
+interface ListProps {
+ children: React.ReactNode;
+}
+
+export const List = ({ children }: ListProps) => {
+ return {children} ;
+};
diff --git a/code/addons/onboarding/src/components/List/ListItem/ListItem.styled.tsx b/code/addons/onboarding/src/components/List/ListItem/ListItem.styled.tsx
new file mode 100644
index 000000000000..cf37aed81893
--- /dev/null
+++ b/code/addons/onboarding/src/components/List/ListItem/ListItem.styled.tsx
@@ -0,0 +1,33 @@
+import { styled } from '@storybook/theming';
+
+export const ListItemWrapper = styled.li(() => ({
+ display: 'flex',
+ alignItems: 'flex-start',
+ columnGap: 12,
+}));
+
+export const ListItemContentWrapper = styled.div`
+ font-family: ${({ theme }) => theme.typography.fonts.base};
+ color: ${({ theme }) => theme.color.darker};
+ font-size: 13px;
+ line-height: 18px;
+ margin-top: 2px;
+`;
+
+export const ListItemIndexWrapper = styled.div<{ isCompleted: boolean }>(
+ ({ isCompleted, theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ border: `1px solid ${!isCompleted ? theme.color.medium : 'transparent'}`,
+ width: 20,
+ height: 20,
+ flexShrink: 0,
+ borderRadius: '50%',
+ backgroundColor: isCompleted ? theme.color.green : 'white',
+ fontFamily: theme.typography.fonts.base,
+ fontSize: 10,
+ fontWeight: 600,
+ color: theme.color.dark,
+ })
+);
diff --git a/code/addons/onboarding/src/components/List/ListItem/ListItem.tsx b/code/addons/onboarding/src/components/List/ListItem/ListItem.tsx
new file mode 100644
index 000000000000..e8a9945c0bbe
--- /dev/null
+++ b/code/addons/onboarding/src/components/List/ListItem/ListItem.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { ListItemContentWrapper, ListItemIndexWrapper, ListItemWrapper } from './ListItem.styled';
+import { CheckIcon } from '@storybook/icons';
+
+interface ListItemProps {
+ children: React.ReactNode;
+ index: number;
+ isCompleted?: boolean;
+}
+
+export const ListItem = ({ children, index, isCompleted }: ListItemProps) => {
+ return (
+
+
+ {isCompleted ? : index}
+
+ {children}
+
+ );
+};
diff --git a/code/addons/onboarding/src/components/Modal/Modal.stories.tsx b/code/addons/onboarding/src/components/Modal/Modal.stories.tsx
new file mode 100644
index 000000000000..51d19c49b4f5
--- /dev/null
+++ b/code/addons/onboarding/src/components/Modal/Modal.stories.tsx
@@ -0,0 +1,136 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { userEvent, within } from '@storybook/testing-library';
+import { expect } from '@storybook/test';
+
+import { Modal } from './Modal';
+
+const meta: Meta = {
+ component: Modal,
+ decorators: [(storyFn) => {storyFn()}
],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ width: undefined,
+ height: undefined,
+ },
+ render: (props) => {
+ const [isOpen, setOpen] = useState(false);
+
+ return (
+ <>
+
+ {({ Close }) => (
+
+
Hello world!
+
setOpen(false)}>Close
+
+ )}
+
+ setOpen(true)}>Open modal
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByText('Open modal');
+ await userEvent.click(button);
+ await expect(canvas.findByText('Hello world!')).resolves.toBeInTheDocument();
+ },
+};
+
+export const FixedWidth: Story = {
+ args: {
+ ...Default.args,
+ width: 1024,
+ },
+ render: (props) => {
+ const [isOpen, setOpen] = useState(false);
+
+ return (
+ <>
+
+ {({ Close }) => (
+
+
Hello world!
+
setOpen(false)}>Close
+
+ )}
+
+ setOpen(true)}>Open modal
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByText('Open modal');
+ await userEvent.click(button);
+ await expect(canvas.findByText('Hello world!')).resolves.toBeInTheDocument();
+ },
+};
+
+export const FixedHeight: Story = {
+ args: {
+ ...Default.args,
+ height: 430,
+ },
+ render: (props) => {
+ const [isOpen, setOpen] = useState(false);
+
+ return (
+ <>
+
+ {({ Close }) => (
+
+
Hello world!
+
setOpen(false)}>Close
+
+ )}
+
+ setOpen(true)}>Open modal
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByText('Open modal');
+ await userEvent.click(button);
+ await expect(canvas.findByText('Hello world!')).resolves.toBeInTheDocument();
+ },
+};
+
+export const FixedWidthAndHeight: Story = {
+ args: {
+ ...Default.args,
+ width: 1024,
+ height: 430,
+ },
+ render: (props) => {
+ const [isOpen, setOpen] = useState(false);
+
+ return (
+ <>
+
+ {({ Close }) => (
+
+
Hello world!
+
setOpen(false)}>Close
+
+ )}
+
+ setOpen(true)}>Open modal
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByText('Open modal');
+ await userEvent.click(button);
+ await expect(canvas.findByText('Hello world!')).resolves.toBeInTheDocument();
+ },
+};
diff --git a/code/addons/onboarding/src/components/Modal/Modal.styled.tsx b/code/addons/onboarding/src/components/Modal/Modal.styled.tsx
new file mode 100644
index 000000000000..42cf6537dd36
--- /dev/null
+++ b/code/addons/onboarding/src/components/Modal/Modal.styled.tsx
@@ -0,0 +1,50 @@
+import { css, styled } from '@storybook/theming';
+import * as Dialog from '@radix-ui/react-dialog';
+import React from 'react';
+
+export const StyledOverlay = styled.div`
+ background-color: rgba(27, 28, 29, 0.48);
+ position: fixed;
+ inset: 0px;
+ width: 100%;
+ height: 100%;
+ z-index: 10;
+`;
+
+export const StyledContent = styled.div<{
+ width: number;
+ height: number;
+}>(
+ ({ width, height }) => css`
+ background-color: white;
+ border-radius: 6px;
+ box-shadow: rgba(14, 18, 22, 0.35) 0px 10px 38px -10px;
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: ${width ?? 740}px;
+ height: ${height ? `${height}px` : 'auto'};
+ max-width: calc(100% - 40px);
+ max-height: 85vh;
+ overflow: hidden;
+ z-index: 11;
+
+ &:focus-visible {
+ outline: none;
+ }
+ `
+);
+
+export const ContentWrapper = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps & React.ComponentProps
+>(function ContentWrapper({ width, height, children, ...contentProps }, ref) {
+ return (
+
+
+ {children}
+
+
+ );
+});
diff --git a/code/addons/onboarding/src/components/Modal/Modal.tsx b/code/addons/onboarding/src/components/Modal/Modal.tsx
new file mode 100644
index 000000000000..4c8bd9bec66d
--- /dev/null
+++ b/code/addons/onboarding/src/components/Modal/Modal.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import * as Dialog from '@radix-ui/react-dialog';
+import { ContentWrapper, StyledOverlay } from './Modal.styled';
+
+type ContentProps = React.ComponentProps;
+
+interface ModalProps extends Omit, 'children'> {
+ width?: number;
+ height?: number;
+ children: (props: {
+ Title: typeof Dialog.Title;
+ Description: typeof Dialog.Description;
+ Close: typeof Dialog.Close;
+ }) => React.ReactNode;
+ onEscapeKeyDown?: ContentProps['onEscapeKeyDown'];
+ onInteractOutside?: ContentProps['onInteractOutside'];
+}
+
+export const initial = { opacity: 0 };
+export const animate = { opacity: 1, transition: { duration: 0.3 } };
+export const exit = { opacity: 0, transition: { duration: 0.3 } };
+
+export function Modal({
+ children,
+ width,
+ height,
+ onEscapeKeyDown,
+ onInteractOutside = (ev) => ev.preventDefault(),
+ ...rootProps
+}: ModalProps) {
+ return (
+
+
+
+
+
+
+ {children({
+ Title: Dialog.Title,
+ Description: Dialog.Description,
+ Close: Dialog.Close,
+ })}
+
+
+
+ );
+}
diff --git a/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
new file mode 100644
index 000000000000..67b31843dc45
--- /dev/null
+++ b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.stories.tsx
@@ -0,0 +1,44 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { PulsatingEffect } from './PulsatingEffect';
+import React from 'react';
+import { within } from '@storybook/testing-library';
+import { expect } from '@storybook/test';
+
+const meta: Meta = {
+ component: PulsatingEffect,
+ parameters: {
+ layout: 'centered',
+ chromatic: {
+ disableSnapshot: true,
+ },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: () => (
+ <>
+
+
+ I should be pulsating
+
+ >
+ ),
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const button = canvas.getByRole('button');
+ await expect(button).toHaveStyle(
+ 'animation: 3s ease-in-out 0s infinite normal none running pulsate'
+ );
+ },
+};
diff --git a/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.tsx b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.tsx
new file mode 100644
index 000000000000..23cb853d7856
--- /dev/null
+++ b/code/addons/onboarding/src/components/PulsatingEffect/PulsatingEffect.tsx
@@ -0,0 +1,49 @@
+import { useEffect } from 'react';
+
+export function PulsatingEffect({
+ targetSelector,
+}: {
+ targetSelector: string;
+}): JSX.Element | null {
+ useEffect(() => {
+ const element = document.querySelector(targetSelector);
+
+ if (element) {
+ element.style.animation = 'pulsate 3s infinite';
+ element.style.transformOrigin = 'center';
+ element.style.animationTimingFunction = 'ease-in-out';
+
+ const keyframes = `
+ @keyframes pulsate {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(2, 156, 253, 0.7), 0 0 0 0 rgba(2, 156, 253, 0.4);
+ }
+ 50% {
+ box-shadow: 0 0 0 20px rgba(2, 156, 253, 0), 0 0 0 40px rgba(2, 156, 253, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(2, 156, 253, 0), 0 0 0 0 rgba(2, 156, 253, 0);
+ }
+ }
+ `;
+ const style = document.createElement('style');
+ style.id = 'sb-onboarding-pulsating-effect';
+ style.innerHTML = keyframes;
+ document.head.appendChild(style);
+ }
+
+ return () => {
+ const styleElement = document.querySelector('#sb-onboarding-pulsating-effect');
+
+ if (styleElement) {
+ styleElement.remove();
+ }
+
+ if (element) {
+ element.style.animation = 'auto';
+ }
+ };
+ }, [targetSelector]);
+
+ return null;
+}
diff --git a/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.styled.tsx b/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.styled.tsx
new file mode 100644
index 000000000000..57a22c42d5d8
--- /dev/null
+++ b/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.styled.tsx
@@ -0,0 +1,9 @@
+import { styled } from '@storybook/theming';
+import { motion } from 'framer-motion';
+
+export const SnippetWrapper = styled(motion.div)`
+ position: relative;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ min-height: 57px;
+`;
diff --git a/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.tsx b/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.tsx
new file mode 100644
index 000000000000..82f5c94bbeea
--- /dev/null
+++ b/code/addons/onboarding/src/components/SyntaxHighlighter/Snippet/Snippet.tsx
@@ -0,0 +1,75 @@
+import { motion } from 'framer-motion';
+import { Fragment, forwardRef } from 'react';
+import { SnippetWrapper } from './Snippet.styled';
+import React from 'react';
+import { SyntaxHighlighter as StorybookSyntaxHighlighter } from '@storybook/components';
+
+interface Props {
+ content: { snippet: string; toggle?: boolean }[];
+ active: boolean;
+ open?: boolean;
+}
+
+const wrapperVariants = {
+ default: {
+ filter: 'grayscale(1)',
+ opacity: 0.5,
+ },
+ active: {
+ filter: 'grayscale(0)',
+ opacity: 1,
+ },
+};
+
+export const Snippet = forwardRef(function Snippet(
+ { active, content, open },
+ ref
+) {
+ const customStyle = {
+ fontSize: '0.8125rem',
+ lineHeight: '1.1875rem',
+ };
+
+ return (
+
+ {content.map(({ toggle, snippet }, i) => (
+
+ {toggle === undefined && (
+
+ {snippet}
+
+ )}
+
+ {toggle && !open && (
+
+ {` // ...`}
+
+ )}
+
+ {toggle && open && (
+
+
+ {snippet}
+
+
+ )}
+
+ ))}
+
+ );
+});
diff --git a/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.stories.tsx b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.stories.tsx
new file mode 100644
index 000000000000..5d29d4a42c22
--- /dev/null
+++ b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.stories.tsx
@@ -0,0 +1,84 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { SyntaxHighlighter } from './SyntaxHighlighter';
+import React from 'react';
+import type { CodeSnippets } from '../../features/WriteStoriesModal/code/types';
+
+const meta: Meta = {
+ component: SyntaxHighlighter,
+ parameters: {
+ chromatic: { delay: 300 },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+const data: CodeSnippets['code'] = [
+ [
+ {
+ snippet: `import type { Meta, StoryObj } from '@storybook/react';
+
+import { Button } from './Button';`,
+ },
+ ],
+ [
+ {
+ snippet: `const meta: Meta = {
+ title: 'Example/Button',
+ component: Button,
+ // ...
+};
+
+export default meta;`,
+ },
+ ],
+ [
+ { snippet: `export const Primary: Story = {` },
+ {
+ snippet: `args: {
+ primary: true,
+ label: 'Click',
+ background: 'red'
+ }`,
+ toggle: true,
+ },
+ { snippet: `};` },
+ ],
+ [
+ {
+ snippet: `// Copy the code below
+
+export const Warning: Story = {
+ args: {
+ primary: true,
+ label: 'Delete now',
+ backgroundColor: 'red',
+ }
+};`,
+ },
+ ],
+];
+
+export const Default: Story = {
+ render: (args) => {
+ const [activeStep, setActiveStep] = React.useState(0);
+
+ return (
+
+
+ setActiveStep(0)}>Reset
+ setActiveStep((step) => (activeStep > 0 ? step - 1 : 0))}>
+ Previous
+
+ setActiveStep((step) => step + 1)}>Next
+
+ );
+ },
+ args: {
+ data: data,
+ activeStep: 0,
+ width: 480,
+ filename: 'Button.stories.tsx',
+ },
+};
diff --git a/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.styled.tsx b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.styled.tsx
new file mode 100644
index 000000000000..29beed376ed0
--- /dev/null
+++ b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.styled.tsx
@@ -0,0 +1,54 @@
+import { styled } from '@storybook/theming';
+import { motion } from 'framer-motion';
+
+export const Code = styled(motion.div)`
+ position: relative;
+ z-index: 2;
+`;
+
+export const SnippetWrapperFirst = styled(motion.div)`
+ position: relative;
+ padding-top: 10px;
+ padding-bottom: 10px;
+`;
+
+export const SnippetWrapper = styled(motion.div)`
+ position: relative;
+ padding-top: 12px;
+ padding-bottom: 12px;
+`;
+
+export const Container = styled.div<{ width: number }>`
+ position: relative;
+ box-sizing: border-box;
+ background: #171c23;
+ width: ${({ width }) => width}px;
+ height: 100%;
+ overflow: hidden;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 4px;
+ border-left: ${({ theme }) => (theme.base === 'dark' ? 1 : 0)}px solid #fff2;
+ border-bottom: ${({ theme }) => (theme.base === 'dark' ? 1 : 0)}px solid #fff2;
+ border-top: ${({ theme }) => (theme.base === 'dark' ? 1 : 0)}px solid #fff2;
+ border-radius: 6px 0 0 6px;
+ overflow: hidden;
+
+ && {
+ pre {
+ background: transparent !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ }
+ }
+`;
+
+export const Backdrop = styled(motion.div)`
+ background: #143046;
+ position: absolute;
+ z-index: 1;
+ left: 0;
+ top: 44px;
+ width: 100%;
+ height: 81px;
+`;
diff --git a/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.tsx b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.tsx
new file mode 100644
index 000000000000..a1038465b926
--- /dev/null
+++ b/code/addons/onboarding/src/components/SyntaxHighlighter/SyntaxHighlighter.tsx
@@ -0,0 +1,117 @@
+import React, { createRef, useCallback, useLayoutEffect, useMemo, useState } from 'react';
+import { Backdrop, Code, Container, SnippetWrapperFirst } from './SyntaxHighlighter.styled';
+import { Snippet } from './Snippet/Snippet';
+import { ThemeProvider, ensure, themes } from '@storybook/theming';
+import { SyntaxHighlighter as StorybookSyntaxHighlighter } from '@storybook/components';
+
+type SyntaxHighlighterProps = {
+ data: { snippet: string; toggle?: boolean }[][];
+ activeStep: number;
+ width: number;
+ filename: string;
+};
+
+type StepsProps = {
+ yPos: number;
+ backdropHeight: number;
+ index: number;
+ open: boolean;
+};
+
+export const SyntaxHighlighter = ({
+ activeStep,
+ data,
+ width,
+ filename,
+}: SyntaxHighlighterProps) => {
+ const [steps, setSteps] = useState([]);
+
+ const refs = useMemo(() => data.map(() => createRef()), [data]);
+
+ const getYPos = (idx: number) => {
+ let yPos = 0;
+ for (let i = 0; i < idx; i++) {
+ yPos -= refs[i].current!.getBoundingClientRect().height;
+ }
+ return yPos;
+ };
+
+ const setNewSteps = useCallback(() => {
+ const newSteps = data.flatMap((content, i) => {
+ const backdropHeight = refs[i].current!.getBoundingClientRect().height;
+ const finalSteps = [
+ {
+ yPos: getYPos(i),
+ backdropHeight,
+ index: i,
+ open: false,
+ },
+ ];
+
+ if (content.length > 1) {
+ finalSteps.push({
+ yPos: getYPos(i),
+ backdropHeight,
+ index: i,
+ open: true,
+ });
+ }
+
+ return finalSteps;
+ });
+
+ setSteps(newSteps);
+ }, [data]);
+
+ useLayoutEffect(() => {
+ // Call setNewSteps every time height of the refs elements changes
+ const resizeObserver = new ResizeObserver(() => {
+ setNewSteps();
+ });
+
+ refs.forEach((ref) => {
+ resizeObserver.observe(ref.current!);
+ });
+
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }, []);
+
+ const customStyle = {
+ fontSize: '0.8125rem',
+ lineHeight: '1.1875rem',
+ };
+
+ return (
+
+
+
+
+
+ {'// ' + filename}
+
+
+ {data.map((content, idx: number) => (
+ idx ? true : steps[activeStep]?.open ?? false}
+ content={content}
+ />
+ ))}
+
+
+
+
+ );
+};
diff --git a/code/addons/onboarding/src/constants.ts b/code/addons/onboarding/src/constants.ts
new file mode 100644
index 000000000000..f81e55b4cf93
--- /dev/null
+++ b/code/addons/onboarding/src/constants.ts
@@ -0,0 +1 @@
+export const STORYBOOK_ADDON_ONBOARDING_CHANNEL = 'STORYBOOK_ADDON_ONBOARDING_CHANNEL';
diff --git a/code/addons/onboarding/src/features/GuidedTour/GuidedTour.styled.tsx b/code/addons/onboarding/src/features/GuidedTour/GuidedTour.styled.tsx
new file mode 100644
index 000000000000..5f666ab74aae
--- /dev/null
+++ b/code/addons/onboarding/src/features/GuidedTour/GuidedTour.styled.tsx
@@ -0,0 +1,20 @@
+import { type Theme } from '@storybook/theming';
+
+export const getStyles = (theme: Theme) => ({
+ border: 0,
+ borderRadius: '0.25rem',
+ cursor: 'pointer',
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: '0 0.75rem',
+ background: theme.color.secondary,
+ color: '#FFF',
+ height: 32,
+ fontSize: '0.8125rem',
+ fontWeight: 700,
+ fontFamily: theme.typography.fonts.base,
+ transition: 'all 0.16s ease-in-out',
+ textDecoration: 'none',
+ outline: 'none',
+});
diff --git a/code/addons/onboarding/src/features/GuidedTour/GuidedTour.tsx b/code/addons/onboarding/src/features/GuidedTour/GuidedTour.tsx
new file mode 100644
index 000000000000..c4fdcc240a2e
--- /dev/null
+++ b/code/addons/onboarding/src/features/GuidedTour/GuidedTour.tsx
@@ -0,0 +1,207 @@
+import type { ComponentProps } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
+import type { CallBackProps } from 'react-joyride';
+import Joyride, { STATUS } from 'react-joyride';
+import type { API } from '@storybook/manager-api';
+import { UPDATE_STORY_ARGS } from '@storybook/core-events';
+import { useTheme } from '@storybook/theming';
+
+import { PulsatingEffect } from '../../components/PulsatingEffect/PulsatingEffect';
+import { Confetti } from '../../components/Confetti/Confetti';
+import { Tooltip } from './Tooltip';
+import { SpanHighlight } from '../WriteStoriesModal/WriteStoriesModal.styled';
+import type { CodeSnippets } from '../WriteStoriesModal/code/types';
+
+type GuidedTourStep = ComponentProps['step'];
+
+export function GuidedTour({
+ api,
+ isFinalStep,
+ onFirstTourDone,
+ onLastTourDone,
+ codeSnippets,
+}: {
+ api: API;
+ isFinalStep?: boolean;
+ codeSnippets?: CodeSnippets;
+ onFirstTourDone: () => void;
+ onLastTourDone: () => void;
+}) {
+ const [stepIndex, setStepIndex] = useState();
+ const theme = useTheme();
+
+ useEffect(() => {
+ api.once(UPDATE_STORY_ARGS, () => {
+ setStepIndex(3);
+ });
+ }, []);
+
+ const storyPlaygroundElement = useMemo(() => {
+ return (document.querySelector('#root div[role=main]') ||
+ document.querySelector('#storybook-panel-root')) as HTMLElement;
+ }, []);
+
+ const steps: GuidedTourStep[] = isFinalStep
+ ? [
+ {
+ target: '#example-button--warning',
+ title: 'Congratulations!',
+ content: (
+ <>
+ You just created your first story. Continue setting up your project to write stories
+ for your own components.
+ >
+ ),
+ placement: 'right',
+ disableOverlay: true,
+ disableBeacon: true,
+ floaterProps: {
+ disableAnimation: true,
+ },
+ onNextButtonClick() {
+ onLastTourDone();
+ },
+ },
+ ]
+ : [
+ {
+ target: '#storybook-explorer-tree > div',
+ title: 'Storybook is built from stories',
+ content: (
+ <>
+ Storybook stories represent the key states of each of your components.
+
+
+ {codeSnippets?.filename && (
+ <>
+ We automatically added four stories for this Button component in this example
+ file:
+
+ {codeSnippets.filename}
+ >
+ )}
+ >
+ ),
+ placement: 'right',
+ disableBeacon: true,
+ styles: {
+ spotlight: {
+ transform: 'translateY(30px)',
+ },
+ },
+ floaterProps: {
+ disableAnimation: true,
+ },
+ },
+ {
+ target: '#storybook-preview-iframe',
+ title: 'Storybook previews are interactive',
+ content:
+ 'Whenever you modify code or stories, Storybook automatically updates how it previews your components.',
+ placement: 'bottom',
+ styles: {
+ spotlight: {
+ borderRadius: 0,
+ },
+ },
+ },
+ {
+ target: storyPlaygroundElement,
+ title: 'Interactive story playground',
+ content: (
+ <>
+ See how a story renders with different data and state without touching code.
+
+
+ Try it out by pressing this button.
+
+ >
+ ),
+ placement: 'right',
+ spotlightClicks: true,
+ floaterProps: {
+ target: '#control-primary',
+ options: {
+ preventOverflow: {
+ boundariesElement: 'window',
+ },
+ },
+ },
+ hideNextButton: true,
+ },
+ {
+ target: '#control-primary',
+ title: 'Congratulations!',
+ content: (
+ <>
+ You learned how to control your stories interactively. Now let's explore how to write
+ your first story.
+
+ >
+ ),
+ placement: 'right',
+ floaterProps: {
+ options: {
+ preventOverflow: {
+ boundariesElement: 'window',
+ },
+ },
+ },
+ disableOverlay: true,
+ },
+ ];
+
+ return (
+ {
+ if (!isFinalStep && data.status === STATUS.FINISHED) {
+ onFirstTourDone();
+ }
+ }}
+ floaterProps={{
+ options: {
+ offset: {
+ offset: '0, 6',
+ },
+ },
+ styles: {
+ floater: {
+ padding: 0,
+ paddingLeft: 8,
+ paddingTop: 8,
+ filter:
+ theme.base === 'light'
+ ? 'drop-shadow(0px 5px 5px rgba(0,0,0,0.05)) drop-shadow(0 1px 3px rgba(0,0,0,0.1))'
+ : 'drop-shadow(#fff5 0px 0px 0.5px) drop-shadow(#fff5 0px 0px 0.5px)',
+ },
+ },
+ }}
+ tooltipComponent={Tooltip}
+ styles={{
+ overlay: {
+ mixBlendMode: 'unset',
+ backgroundColor: 'none',
+ },
+ spotlight: {
+ backgroundColor: 'none',
+ border: `solid 2px ${theme.color.secondary}`,
+ boxShadow: '0px 0px 0px 9999px rgba(0,0,0,0.4)',
+ },
+ options: {
+ zIndex: 10000,
+ primaryColor: theme.color.secondary,
+ arrowColor: theme.base === 'dark' ? '#292A2C' : theme.color.lightest,
+ },
+ }}
+ />
+ );
+}
diff --git a/code/addons/onboarding/src/features/GuidedTour/Tooltip.tsx b/code/addons/onboarding/src/features/GuidedTour/Tooltip.tsx
new file mode 100644
index 000000000000..c58fcbe1ad7f
--- /dev/null
+++ b/code/addons/onboarding/src/features/GuidedTour/Tooltip.tsx
@@ -0,0 +1,91 @@
+import type { FC } from 'react';
+import React from 'react';
+import { styled } from '@storybook/theming';
+import type { Step, TooltipRenderProps } from 'react-joyride';
+import { Button } from '../../components/Button/Button';
+
+const TooltipBody = styled.div`
+ background: ${({ theme }) => {
+ return theme.base === 'dark' ? '#292A2C' : theme.color.lightest;
+ }};
+ width: 260px;
+ padding: 15px;
+ border-radius: 5px;
+`;
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+
+const TooltipTitle = styled.div`
+ font-size: 13px;
+ line-height: 18px;
+ font-weight: 700;
+ color: ${({ theme }) => theme.color.defaultText};
+`;
+
+const TooltipContent = styled.p`
+ font-size: 13px;
+ line-height: 18px;
+ text-align: start;
+ color: ${({ theme }) => theme.color.defaultText};
+ margin: 0;
+ margin-top: 5px;
+`;
+
+const TooltipFooter = styled.div`
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 15px;
+`;
+
+type TooltipProps = {
+ step: Partial<
+ Pick<
+ // this only seems to happen during the check task, nos in vsCode..
+ // this seems to be 'any' in vsCode because of it?
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore (hide property 'input' circularly references itself in mapped type)
+ Step,
+ | 'title'
+ | 'content'
+ | 'target'
+ | 'placement'
+ | 'disableOverlay'
+ | 'disableBeacon'
+ | 'floaterProps'
+ | 'spotlightClicks'
+ | 'styles'
+ > & {
+ hideNextButton: boolean;
+ onNextButtonClick: () => void;
+ }
+ >;
+ primaryProps: TooltipRenderProps['primaryProps'];
+ tooltipProps: TooltipRenderProps['tooltipProps'];
+};
+
+export const Tooltip: FC = ({ step, primaryProps, tooltipProps }) => {
+ return (
+
+
+ {step.title && {step.title} }
+ {step.content}
+
+ {!step.hideNextButton && (
+
+
+ Next
+
+
+ )}
+
+ );
+};
diff --git a/code/addons/onboarding/src/features/WelcomeModal/StorybookLogo.tsx b/code/addons/onboarding/src/features/WelcomeModal/StorybookLogo.tsx
new file mode 100644
index 000000000000..bcfbba836ab9
--- /dev/null
+++ b/code/addons/onboarding/src/features/WelcomeModal/StorybookLogo.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+export function StorybookLogo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.stories.tsx b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.stories.tsx
new file mode 100644
index 000000000000..ecf153295ee6
--- /dev/null
+++ b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.stories.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { WelcomeModal } from './WelcomeModal';
+
+const meta: Meta = {
+ component: WelcomeModal,
+ decorators: [(storyFn) => {storyFn()}
],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.styled.tsx b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.styled.tsx
new file mode 100644
index 000000000000..503c97e102ef
--- /dev/null
+++ b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.styled.tsx
@@ -0,0 +1,146 @@
+import { ArrowRightIcon } from '@storybook/icons';
+import { keyframes, styled } from '@storybook/theming';
+
+export const ModalContentWrapper = styled.div`
+ border-radius: 5px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ height: 100%;
+ justify-content: space-between;
+`;
+
+export const TopContent = styled.div`
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+`;
+
+export const Title = styled.h1`
+ margin: 0;
+ margin-top: 20px;
+ margin-bottom: 5px;
+ color: ${({ theme }) => theme.color.darkest};
+ font-weight: ${({ theme }) => theme.typography.weight.bold};
+ font-size: ${({ theme }) => theme.typography.size.m1}px;
+ line-height: ${({ theme }) => theme.typography.size.m3}px;
+`;
+
+export const Description = styled.p`
+ margin: 0;
+ margin-bottom: 20px;
+ max-width: 320px;
+ text-align: center;
+ font-size: ${({ theme }) => theme.typography.size.s2}px;
+ font-weight: ${({ theme }) => theme.typography.weight.regular};
+ line-height: ${({ theme }) => theme.typography.size.m1}px;
+ color: ${({ theme }) => theme.color.darker};
+`;
+
+export const SkipButton = styled.button`
+ all: unset;
+ cursor: pointer;
+ font-size: 13px;
+ color: #798186;
+ padding-bottom: 20px;
+
+ &:focus-visible {
+ outline: auto;
+ }
+`;
+
+export const StyledIcon = styled(ArrowRightIcon)`
+ margin-left: 2px;
+ height: 10px;
+`;
+
+export const Background = styled.div`
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+ overflow: hidden;
+`;
+
+export const circle1Anim = keyframes`
+ 0% { transform: translate(0px, 0px) }
+ 50% { transform: translate(-200px, 0px) }
+ 100% { transform: translate(0px, 0px) }
+`;
+
+export const Circle1 = styled.div`
+ position: absolute;
+ width: 1200px;
+ height: 1200px;
+ left: -200px;
+ top: -900px;
+ background: radial-gradient(
+ circle at center,
+ rgba(253, 255, 147, 1) 0%,
+ rgba(253, 255, 147, 0) 70%
+ );
+ animation: ${circle1Anim} 4s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 3;
+`;
+
+export const circle2Anim = keyframes`
+ 0% { transform: translate(0px, 0px) }
+ 50% { transform: translate(400px, 0px) }
+ 100% { transform: translate(0px, 0px) }
+`;
+
+export const Circle2 = styled.div`
+ position: absolute;
+ width: 1200px;
+ height: 1200px;
+ left: -600px;
+ top: -840px;
+ background: radial-gradient(
+ circle at center,
+ rgba(255, 119, 119, 1) 0%,
+ rgba(255, 119, 119, 0) 70%
+ );
+ animation: ${circle2Anim} 6s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 2;
+`;
+
+export const circle3Anim = keyframes`
+ 0% { transform: translate(600px, -40px) }
+ 50% { transform: translate(600px, -200px) }
+ 100% { transform: translate(600px, -40px) }
+`;
+
+export const Circle3 = styled.div`
+ position: absolute;
+ width: 1200px;
+ height: 1200px;
+ left: -600px;
+ top: -840px;
+ background: radial-gradient(
+ circle at center,
+ rgba(119, 255, 247, 0.8) 0%,
+ rgba(119, 255, 247, 0) 70%
+ );
+ animation: ${circle3Anim} 4s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 4;
+`;
+
+export const StyledTitle = styled.h2`
+ color: #000;
+ font-weight: 700;
+ font-size: 20px;
+ line-height: 20px;
+`;
+export const StyledDescription = styled.p`
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 20px;
+ color: #454e54;
+`;
diff --git a/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.tsx b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.tsx
new file mode 100644
index 000000000000..9459529970e9
--- /dev/null
+++ b/code/addons/onboarding/src/features/WelcomeModal/WelcomeModal.tsx
@@ -0,0 +1,56 @@
+import type { FC } from 'react';
+import React from 'react';
+
+import { Button } from '../../components/Button/Button';
+import { Modal } from '../../components/Modal/Modal';
+import { StorybookLogo } from './StorybookLogo';
+import {
+ ModalContentWrapper,
+ SkipButton,
+ StyledIcon,
+ Title,
+ Description,
+ Background,
+ Circle1,
+ Circle2,
+ Circle3,
+ TopContent,
+} from './WelcomeModal.styled';
+
+interface WelcomeModalProps {
+ onProceed: () => void;
+ skipOnboarding: () => void;
+}
+
+export const WelcomeModal: FC = ({ onProceed, skipOnboarding }) => {
+ return (
+
+
+ {({ Close }) => (
+
+
+
+ Welcome to Storybook
+
+ Storybook helps you develop UI components faster. Learn the basics in a few simple
+ steps.
+
+
+ Start your 3 minute tour
+
+
+
+ Skip tour
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
new file mode 100644
index 000000000000..d2284dbd913f
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.stories.tsx
@@ -0,0 +1,83 @@
+import React from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { waitFor, within } from '@storybook/testing-library';
+import { expect, fn } from '@storybook/test';
+import { STORY_INDEX_INVALIDATED, STORY_RENDERED } from '@storybook/core-events';
+import { WriteStoriesModal } from './WriteStoriesModal';
+import typescriptSnippet from './code/typescript';
+
+const getData = fn();
+
+const meta: Meta = {
+ component: WriteStoriesModal,
+ args: {
+ codeSnippets: typescriptSnippet,
+ // @ts-expect-error (bad)
+ api: {
+ getData,
+ },
+ addonsStore: {
+ // @ts-expect-error (bad)
+ getChannel: () => ({
+ once: (type: string, cb: () => void) => {
+ if (type === STORY_RENDERED) {
+ cb();
+ }
+ },
+ on: (type: string, cb: () => void) => {
+ if (type === STORY_INDEX_INVALIDATED) {
+ storyIndexInvalidatedCb = cb;
+ }
+ },
+ off: () => {},
+ }),
+ },
+ },
+ decorators: [
+ (storyFn, context) => {
+ (context.args.api.getData as typeof getData)
+ // do not respond to the first call, this would only return the data correctly if the story already exists
+ // which is not the case in this story, it only makes sense in the real scenario
+ .mockReturnValueOnce(null)
+ .mockReturnValueOnce({ some: 'data' });
+ return {storyFn()}
;
+ },
+ ],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+let storyIndexInvalidatedCb: () => void;
+
+export const Default: Story = {};
+
+export const DefaultPlayed: Story = {
+ args: {
+ ...Default.args,
+ },
+ play: async ({ canvasElement, step }) => {
+ const canvas = within(canvasElement.parentElement!);
+ const importsText = await canvas.findByText('Imports');
+ await step('Wait for modal to be visible', async () => {
+ const modal = await canvas.findByRole('dialog');
+ await waitFor(async () => expect(modal).toBeVisible());
+ });
+ await expect(importsText).toBeVisible();
+ await canvas.getByRole('button', { name: /Next/i }).click();
+ const metaText = await canvas.findAllByText('Meta');
+ await expect(metaText?.[0]).toBeVisible();
+ await canvas.getByRole('button', { name: /Next/i }).click();
+ const storyText = await canvas.findAllByText('Story');
+ await expect(storyText?.[0]).toBeVisible();
+ await canvas.getByRole('button', { name: /Next/i }).click();
+ const argsText = await canvas.findAllByText('Args');
+ await expect(argsText?.[0]).toBeVisible();
+ await canvas.getByRole('button', { name: /Next/i }).click();
+ (await canvas.findByRole('button', { name: /Copy code/i })).click();
+ storyIndexInvalidatedCb();
+ await waitFor(() => expect(canvas.getAllByLabelText('complete')).toHaveLength(3));
+ },
+};
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.styled.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.styled.tsx
new file mode 100644
index 000000000000..b47dba7c7c39
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.styled.tsx
@@ -0,0 +1,170 @@
+import { keyframes, styled } from '@storybook/theming';
+
+export const ModalContent = styled.div`
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ max-height: 85vh;
+
+ &:focus-visible {
+ outline: none;
+ }
+`;
+
+export const Main = styled.div`
+ position: relative;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ font-family: ${({ theme }) => theme.typography.fonts.base};
+`;
+
+export const Header = styled.div`
+ box-sizing: border-box;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 15px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ height: 44px;
+`;
+
+export const ModalTitle = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 13px;
+ line-height: 18px;
+ font-weight: bold;
+ color: ${({ theme }) => theme.color.darkest};
+`;
+
+export const Content = styled.div`
+ font-size: 13px;
+ line-height: 18px;
+ padding: 15px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ justify-content: space-between;
+ color: ${({ theme }) => theme.color.darker};
+
+ h3 {
+ font-size: 13px;
+ line-height: 18px;
+ font-weight: bold;
+ padding: 0;
+ margin: 0;
+ }
+`;
+
+export const SpanHighlight = styled.span`
+ display: inline-flex;
+ border-radius: 3px;
+ padding: 0 5px;
+ margin-bottom: -2px;
+ opacity: 0.8;
+ font-family: ${({ theme }) => theme.typography.fonts.mono};
+ font-size: 11px;
+ border: 1px solid #ecf4f9;
+ color: ${({ theme }) => theme.color.darkest};
+ background-color: #f7fafc;
+ box-sizing: border-box;
+ line-height: 17px;
+`;
+
+export const Image = styled.img`
+ max-width: 100%;
+ margin-top: 1em;
+`;
+
+export const Background = styled.div`
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -1;
+ overflow: hidden;
+`;
+
+export const circle1Anim = keyframes`
+ 0% { transform: translate(0px, 0px) }
+ 50% { transform: translate(120px, 0px) }
+ 100% { transform: translate(0px, 0px) }
+`;
+
+export const Circle1 = styled.div`
+ position: absolute;
+ width: 350px;
+ height: 350px;
+ left: -160px;
+ top: -260px;
+ background: radial-gradient(
+ circle at center,
+ rgba(255, 119, 119, 1) 0%,
+ rgba(255, 119, 119, 0) 70%
+ );
+ animation: ${circle1Anim} 8s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 2;
+`;
+
+export const circle2Anim = keyframes`
+ 0% { transform: translate(0px, 0px) }
+ 33% { transform: translate(-64px, 0px) }
+ 66% { transform: translate(120px, 0px) }
+ 100% { transform: translate(0px, 0px) }
+`;
+
+export const Circle2 = styled.div`
+ position: absolute;
+ width: 350px;
+ height: 350px;
+ left: -54px;
+ top: -250px;
+ background: radial-gradient(
+ circle at center,
+ rgba(253, 255, 147, 1) 0%,
+ rgba(253, 255, 147, 0) 70%
+ );
+ animation: ${circle2Anim} 12s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 3;
+`;
+
+export const circle3Anim = keyframes`
+ 0% { transform: translate(0px, 0px) }
+ 50% { transform: translate(-120px, 0px) }
+ 100% { transform: translate(0px, 0px) }
+`;
+
+export const Circle3 = styled.div`
+ position: absolute;
+ width: 350px;
+ height: 350px;
+ left: 150px;
+ top: -220px;
+ background: radial-gradient(
+ circle at center,
+ rgba(119, 255, 247, 0.8) 0%,
+ rgba(119, 255, 247, 0) 70%
+ );
+ animation: ${circle3Anim} 4s linear infinite;
+ animation-timing-function: ease-in-out;
+ z-index: 4;
+`;
+
+export const ButtonsWrapper = styled.div`
+ box-sizing: border-box;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin-top: 4px;
+`;
+
+export const Step2Text = styled.div`
+ margin-bottom: 4px;
+`;
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.tsx
new file mode 100644
index 000000000000..b456b02240c9
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/WriteStoriesModal.tsx
@@ -0,0 +1,291 @@
+import React, { useCallback, useState, type FC } from 'react';
+import { Button } from '../../components/Button/Button';
+import { Modal } from '../../components/Modal/Modal';
+import useMeasure from 'react-use-measure';
+import {
+ Background,
+ ButtonsWrapper,
+ Circle1,
+ Circle2,
+ Circle3,
+ Content,
+ Header,
+ Image,
+ Main,
+ ModalContent,
+ ModalTitle,
+ SpanHighlight,
+ Step2Text,
+} from './WriteStoriesModal.styled';
+import { SyntaxHighlighter } from '../../components/SyntaxHighlighter/SyntaxHighlighter';
+import { List } from '../../components/List/List';
+import { ListItem } from '../../components/List/ListItem/ListItem';
+import { useGetButtonPath } from './hooks/useGetButtonPath';
+import { useGetWarningButtonStatus } from './hooks/useGetWarningButtonStatus';
+import { useGetBackdropBoundary } from './hooks/useGetBackdropBoundary';
+import titleSidebarImg from './assets/01-title-sidebar.png';
+import storyNameSidebarImg from './assets/02-story-name-sidebar.png';
+import argsImg from './assets/03-args.png';
+import type { API, AddonStore } from '@storybook/manager-api';
+import { STORYBOOK_ADDON_ONBOARDING_CHANNEL } from '../../constants';
+import { useTheme } from '@storybook/theming';
+import type { CodeSnippets } from './code/types';
+import { BookmarkHollowIcon, CrossIcon } from '@storybook/icons';
+
+// TODO: Add warning if backdropBoundary && !warningButtonStatus?.data is not true.
+// backdropBoundary && !warningButtonStatus?.data
+
+interface WriteStoriesModalProps {
+ onFinish: () => void;
+ api: API;
+ addonsStore: AddonStore;
+ codeSnippets: CodeSnippets;
+ skipOnboarding: () => void;
+}
+
+export const WriteStoriesModal: FC = ({
+ onFinish,
+ api,
+ addonsStore,
+ skipOnboarding,
+ codeSnippets,
+}) => {
+ const [step, setStep] = useState<'imports' | 'meta' | 'story' | 'args' | 'customStory'>(
+ 'imports'
+ );
+ const theme = useTheme();
+
+ const stepIndex = {
+ imports: 0,
+ meta: 1,
+ story: 2,
+ args: 3,
+ customStory: 4,
+ };
+
+ const [isWarningStoryCopied, setWarningStoryCopied] = useState(false);
+
+ const [clipboardButtonRef, clipboardButtonBounds] = useMeasure();
+
+ const buttonPath = useGetButtonPath();
+ const warningButtonStatus = useGetWarningButtonStatus(step === 'customStory', api, addonsStore);
+ const backdropBoundary = useGetBackdropBoundary(
+ 'syntax-highlighter-backdrop',
+ step === 'customStory'
+ );
+
+ const isJavascript = codeSnippets?.language === 'javascript';
+
+ const copyWarningStory = () => {
+ const warningContent = codeSnippets.code[3][0].snippet;
+ navigator.clipboard.writeText(warningContent.replace('// Copy the code below', ''));
+ setWarningStoryCopied(true);
+ };
+
+ const onModalClose = useCallback(() => {
+ api.emit(STORYBOOK_ADDON_ONBOARDING_CHANNEL, {
+ step: 'X:SkippedOnboarding',
+ where: `HowToWriteAStoryModal:${step}`,
+ type: 'telemetry',
+ });
+ }, [api, step]);
+
+ return (
+
+ {({ Title, Description, Close }) => (
+
+ {codeSnippets ? (
+
+ ) : null}
+ {step === 'customStory' && backdropBoundary && !warningButtonStatus?.data && (
+ copyWarningStory()}
+ style={{
+ position: 'absolute',
+ opacity: clipboardButtonBounds.width ? 1 : 0,
+ top: backdropBoundary.top + backdropBoundary.height - 45,
+ left:
+ backdropBoundary.left +
+ backdropBoundary.width -
+ (clipboardButtonBounds.width ?? 0) -
+ 10,
+ zIndex: 1000,
+ }}
+ >
+ {isWarningStoryCopied ? 'Copied to clipboard' : 'Copy code'}
+
+ )}
+
+
+
+
+
+ How to write a story
+
+
+
+
+
+
+
+
+ {step === 'imports' && (
+ <>
+
+
Imports
+ {isJavascript ? (
+
Import a component. In this case, the Button component.
+ ) : (
+ <>
+
+ First, import Meta and{' '}
+ StoryObj for type safety and
+ autocompletion in TypeScript stories.
+
+
Next, import a component. In this case, the Button component.
+ >
+ )}
+
+ {
+ setStep('meta');
+ }}
+ >
+ Next
+
+ >
+ )}
+ {step === 'meta' && (
+ <>
+
+
Meta
+
+ The default export, Meta, contains metadata about this component's stories.
+ The title field (optional) controls where stories appear in the sidebar.
+
+
+
+
+ setStep('imports')}>
+ Previous
+
+ setStep('story')}>Next
+
+ >
+ )}
+ {step === 'story' && (
+ <>
+
+
Story
+
+ Each named export is a story. Its contents specify how the story is rendered
+ in addition to other configuration options.
+
+
+
+
+ setStep('meta')}>
+ Previous
+
+ setStep('args')}>Next
+
+ >
+ )}
+ {step === 'args' && (
+ <>
+
+
Args
+
+ Args are inputs that are passed to the component, which Storybook uses to
+ render the component in different states. In React, args = props. They also
+ specify the initial control values for the story.
+
+
+
+
+ setStep('story')}>
+ Previous
+
+ setStep('customStory')}>Next
+
+ >
+ )}
+ {step === 'customStory' &&
+ (!warningButtonStatus?.error ? (
+ <>
+
+
Create your first story
+
+ Now it's your turn. See how easy it is to create your first story by
+ following these steps below.
+
+
+
+ Copy the Warning story.
+
+
+
+ Open the Button story in your current working directory.
+
+ {buttonPath?.data && (
+ // Replace '/' by '/' to properly break line
+
+ {buttonPath.data.replaceAll('/', '/').replaceAll('\\', '\\')}
+
+ )}
+
+
+ Paste it at the bottom of the file and save.
+
+
+
+
+ setStep('args')}>
+ Previous
+
+ {warningButtonStatus?.data ? (
+ onFinish()}>Go to story
+ ) : null}
+
+ >
+ ) : null)}
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/assets/01-title-sidebar.png b/code/addons/onboarding/src/features/WriteStoriesModal/assets/01-title-sidebar.png
new file mode 100644
index 000000000000..064d9995bf1d
Binary files /dev/null and b/code/addons/onboarding/src/features/WriteStoriesModal/assets/01-title-sidebar.png differ
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/assets/02-story-name-sidebar.png b/code/addons/onboarding/src/features/WriteStoriesModal/assets/02-story-name-sidebar.png
new file mode 100644
index 000000000000..1d562959c156
Binary files /dev/null and b/code/addons/onboarding/src/features/WriteStoriesModal/assets/02-story-name-sidebar.png differ
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/assets/03-args.png b/code/addons/onboarding/src/features/WriteStoriesModal/assets/03-args.png
new file mode 100644
index 000000000000..3312453658ae
Binary files /dev/null and b/code/addons/onboarding/src/features/WriteStoriesModal/assets/03-args.png differ
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/code/javascript.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/code/javascript.tsx
new file mode 100644
index 000000000000..2a91a7972e14
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/code/javascript.tsx
@@ -0,0 +1,48 @@
+import type { CodeSnippets } from './types';
+
+const data: CodeSnippets = {
+ filename: 'Button.stories.js',
+ language: 'typescript',
+ code: [
+ [
+ {
+ snippet: `import { Button } from './Button';`,
+ },
+ ],
+ [
+ {
+ snippet: `export default {
+ title: 'Example/Button',
+ component: Button,
+ // ...
+ };`,
+ },
+ ],
+ [
+ { snippet: `export const Primary = {` },
+ {
+ snippet: `args: {
+ primary: true,
+ label: 'Click',
+ background: 'red'
+ }`,
+ toggle: true,
+ },
+ { snippet: `};` },
+ ],
+ [
+ {
+ snippet: `// Copy the code below
+export const Warning = {
+ args: {
+ primary: true,
+ label: 'Delete now',
+ backgroundColor: 'red',
+ }
+};`,
+ },
+ ],
+ ],
+};
+
+export default data;
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/code/types.ts b/code/addons/onboarding/src/features/WriteStoriesModal/code/types.ts
new file mode 100644
index 000000000000..130b4428f390
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/code/types.ts
@@ -0,0 +1,6 @@
+export type CodeSnippets = {
+ framework?: string;
+ language: 'javascript' | 'typescript';
+ filename: string;
+ code: { snippet: string; toggle?: boolean }[][];
+};
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/code/typescript.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/code/typescript.tsx
new file mode 100644
index 000000000000..45a578d8c6bd
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/code/typescript.tsx
@@ -0,0 +1,56 @@
+import type { CodeSnippets } from './types';
+
+const data: CodeSnippets = {
+ filename: 'Button.stories.ts',
+ language: 'typescript',
+ code: [
+ [
+ {
+ snippet: `import type { Meta, StoryObj } from '@storybook/react';
+
+ import { Button } from './Button';`,
+ },
+ ],
+ [
+ {
+ snippet: `const meta: Meta = {
+ title: 'Example/Button',
+ component: Button,
+ // ...
+ };
+
+ export default meta;`,
+ },
+ ],
+ [
+ {
+ snippet: `type Story = StoryObj;
+
+ export const Primary: Story = {`,
+ },
+ {
+ snippet: `args: {
+ primary: true,
+ label: 'Click',
+ background: 'red'
+ }`,
+ toggle: true,
+ },
+ { snippet: `};` },
+ ],
+ [
+ {
+ snippet: `// Copy the code below
+ export const Warning: Story = {
+ args: {
+ primary: true,
+ label: 'Delete now',
+ backgroundColor: 'red',
+ }
+ };`,
+ },
+ ],
+ ],
+};
+
+export default data;
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetBackdropBoundary.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetBackdropBoundary.tsx
new file mode 100644
index 000000000000..b4a5a5fdd8ab
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetBackdropBoundary.tsx
@@ -0,0 +1,37 @@
+import { useEffect, useState } from 'react';
+
+// get an element and return its boundary. It accepts a classname as argument
+export const useGetBackdropBoundary = (className: string, active: boolean) => {
+ const [boundary, setBoundary] = useState<{
+ top: number;
+ left: number;
+ height: number;
+ width: number;
+ } | null>(null);
+
+ const element = document.querySelector(`.${className}`) as HTMLElement;
+
+ // setBoundary if element changes sized. use resize observer
+ useEffect(() => {
+ if (active) {
+ const resizeObserver = new ResizeObserver(() => {
+ if (element) {
+ setBoundary({
+ top: element.offsetTop,
+ left: element.offsetLeft,
+ height: element.offsetHeight,
+ width: element.offsetWidth,
+ });
+ }
+ });
+
+ resizeObserver.observe(element);
+
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }
+ }, [className, active]);
+
+ return boundary;
+};
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetButtonPath.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetButtonPath.tsx
new file mode 100644
index 000000000000..9521d3d74b20
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetButtonPath.tsx
@@ -0,0 +1,28 @@
+import { useEffect, useState } from 'react';
+import type { Response } from '../../../types/response';
+
+export function useGetButtonPath() {
+ const [buttonPath, setButtonPath] = useState>(null);
+
+ useEffect(() => {
+ const getButtonPath = async () => {
+ try {
+ const response = await fetch('/index.json');
+ const json = await response.json();
+ const buttonPathInner = json.entries['example-button--primary'].importPath;
+ setButtonPath({
+ data: buttonPathInner,
+ error: null,
+ });
+ } catch (e) {
+ setButtonPath({
+ data: null,
+ error: e,
+ });
+ }
+ };
+ getButtonPath();
+ }, []);
+
+ return buttonPath;
+}
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetProject.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetProject.tsx
new file mode 100644
index 000000000000..43ecdd89a174
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetProject.tsx
@@ -0,0 +1,43 @@
+import { useEffect, useState } from 'react';
+
+import dataJavascript from '../code/javascript';
+import dataTypescript from '../code/typescript';
+import type { CodeSnippets } from '../code/types';
+
+type Project = {
+ language: 'javascript' | 'typescript';
+ framework: {
+ name: string;
+ };
+};
+
+export function useGetProject() {
+ const [project, setProject] = useState<{
+ data: CodeSnippets | null;
+ error: Error | null;
+ }>({ data: null, error: null });
+
+ useEffect(() => {
+ const getProject = async () => {
+ try {
+ const response = await fetch('/project.json');
+ const projectInner = (await response.json()) as Project;
+ const data = projectInner?.language === 'javascript' ? dataJavascript : dataTypescript;
+
+ setProject({
+ data,
+ error: null,
+ });
+ } catch (e) {
+ setProject({
+ data: null,
+ // @ts-expect-error (bad)
+ error: e,
+ });
+ }
+ };
+ getProject();
+ }, []);
+
+ return project;
+}
diff --git a/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetWarningButtonStatus.tsx b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetWarningButtonStatus.tsx
new file mode 100644
index 000000000000..8a26cca0d252
--- /dev/null
+++ b/code/addons/onboarding/src/features/WriteStoriesModal/hooks/useGetWarningButtonStatus.tsx
@@ -0,0 +1,39 @@
+import { useState, useEffect } from 'react';
+import type { Response } from '../../../types/response';
+import type { API, AddonStore } from '@storybook/manager-api';
+import { STORY_INDEX_INVALIDATED, STORY_RENDERED } from '@storybook/core-events';
+
+export const useGetWarningButtonStatus = (active: boolean, api: API, addonsStore: AddonStore) => {
+ const [status, setStatus] = useState>(null);
+
+ useEffect(() => {
+ if (active) {
+ const getWarningButtonStatus = () => {
+ addonsStore.getChannel().once(STORY_RENDERED, () => {
+ const out = api.getData('example-button--warning');
+
+ if (out) {
+ setStatus({ data: true, error: null });
+ } else {
+ setStatus({ data: false, error: null });
+ }
+ });
+ };
+
+ const addonStoreChannel: ReturnType = addonsStore.getChannel();
+
+ // If the story already exists, we don't need to listen to any events
+ if (api.getData('example-button--warning')) {
+ setStatus({ data: true, error: null });
+ } else {
+ addonStoreChannel.on(STORY_INDEX_INVALIDATED, getWarningButtonStatus);
+ }
+
+ return () => {
+ addonStoreChannel.off(STORY_INDEX_INVALIDATED, getWarningButtonStatus);
+ };
+ }
+ }, [active]);
+
+ return status;
+};
diff --git a/code/addons/onboarding/src/helpers/textContentMatcher.tsx b/code/addons/onboarding/src/helpers/textContentMatcher.tsx
new file mode 100644
index 000000000000..c1f6dcc6e0c6
--- /dev/null
+++ b/code/addons/onboarding/src/helpers/textContentMatcher.tsx
@@ -0,0 +1,30 @@
+/**
+ * Getting the deepest element that contain string / match regex even when it split between multiple elements
+ *
+ * @example
+ * For:
+ *
+ * Hello World
+ *
+ *
+ * screen.getByText('Hello World') // ❌ Fail
+ * screen.getByText(textContentMatcher('Hello World')) // ✅ pass
+ */
+export function textContentMatcher(textMatch: string | RegExp) {
+ const hasText =
+ typeof textMatch === 'string'
+ ? (node: Element) => node.textContent === textMatch
+ : (node: Element) => textMatch.test(node.textContent!);
+
+ const matcher = (_content: string, node: Element) => {
+ if (!hasText(node)) {
+ return false;
+ }
+
+ return Array.from(node?.children || []).every((child) => !hasText(child));
+ };
+
+ matcher.toString = () => `textContentMatcher(${textMatch})`;
+
+ return matcher;
+}
diff --git a/code/addons/onboarding/src/index.ts b/code/addons/onboarding/src/index.ts
new file mode 100644
index 000000000000..dafa948eda6c
--- /dev/null
+++ b/code/addons/onboarding/src/index.ts
@@ -0,0 +1,2 @@
+// make it work with --isolatedModules
+export default {};
diff --git a/code/addons/onboarding/src/manager.tsx b/code/addons/onboarding/src/manager.tsx
new file mode 100644
index 000000000000..e19c452724b8
--- /dev/null
+++ b/code/addons/onboarding/src/manager.tsx
@@ -0,0 +1,52 @@
+import ReactDOM from 'react-dom';
+import React, { lazy, Suspense } from 'react';
+import { addons } from '@storybook/manager-api';
+import { STORY_SPECIFIED } from '@storybook/core-events';
+
+const App = lazy(() => import('./App'));
+
+// The addon is enabled only when:
+// 1. The onboarding query parameter is present
+// 2. The example button stories are present
+addons.register('@storybook/addon-onboarding', async (api) => {
+ const urlState = api.getUrlState();
+ const isOnboarding =
+ urlState.path === '/onboarding' || urlState.queryParams.onboarding === 'true';
+
+ api.once(STORY_SPECIFIED, () => {
+ const hasButtonStories =
+ !!api.getData('example-button--primary') ||
+ !!document.getElementById('example-button--primary');
+
+ if (!hasButtonStories) {
+ console.warn(
+ `[@storybook/addon-onboarding] It seems like you have finished the onboarding experience in Storybook! Therefore this addon is not necessary anymore and will not be loaded. You are free to remove it from your project. More info: https://github.com/storybookjs/storybook/tree/next/code/addons/onboarding#uninstalling`
+ );
+ return;
+ }
+
+ if (!isOnboarding || window.innerWidth < 730) {
+ return;
+ }
+
+ api.togglePanel(true);
+ api.togglePanelPosition('bottom');
+ api.setSelectedPanel('addon-controls');
+
+ // Add a new DOM element to document.body, where we will bootstrap our React app
+ const domNode = document.createElement('div');
+
+ domNode.id = 'storybook-addon-onboarding';
+ // Append the new DOM element to document.body
+ document.body.appendChild(domNode);
+
+ // Render the React app
+ // eslint-disable-next-line react/no-deprecated
+ ReactDOM.render(
+ Loading...}>
+
+ ,
+ domNode
+ );
+ });
+});
diff --git a/code/addons/onboarding/src/preset.ts b/code/addons/onboarding/src/preset.ts
new file mode 100644
index 000000000000..126a1dbda96d
--- /dev/null
+++ b/code/addons/onboarding/src/preset.ts
@@ -0,0 +1,36 @@
+import type { CoreConfig, Options } from '@storybook/types';
+import type { Channel } from '@storybook/channels';
+import { STORYBOOK_ADDON_ONBOARDING_CHANNEL } from './constants';
+import { telemetry } from '@storybook/telemetry';
+import fs from 'fs';
+
+type Event = {
+ type: 'telemetry';
+ step: string;
+ payload?: any;
+};
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export const experimental_serverChannel = async (channel: Channel, options: Options) => {
+ const { disableTelemetry } = await options.presets.apply('core', {});
+
+ if (!disableTelemetry) {
+ const packageJsonPath = require.resolve('@storybook/addon-onboarding/package.json');
+
+ const { version: addonVersion } = JSON.parse(
+ fs.readFileSync(packageJsonPath, { encoding: 'utf-8' })
+ );
+
+ channel.on(STORYBOOK_ADDON_ONBOARDING_CHANNEL, ({ type, ...event }: Event) => {
+ if (type === 'telemetry') {
+ // @ts-expect-error (bad string)
+ telemetry('addon-onboarding', {
+ ...event,
+ addonVersion,
+ });
+ }
+ });
+ }
+
+ return channel;
+};
diff --git a/code/addons/onboarding/src/types.d.ts b/code/addons/onboarding/src/types.d.ts
new file mode 100644
index 000000000000..dd84df40a488
--- /dev/null
+++ b/code/addons/onboarding/src/types.d.ts
@@ -0,0 +1,4 @@
+declare module '*.png' {
+ const content: any;
+ export default content;
+}
diff --git a/code/addons/onboarding/src/types/response.ts b/code/addons/onboarding/src/types/response.ts
new file mode 100644
index 000000000000..56814973274f
--- /dev/null
+++ b/code/addons/onboarding/src/types/response.ts
@@ -0,0 +1,10 @@
+export type Response =
+ | null
+ | {
+ data: T;
+ error: null;
+ }
+ | {
+ data: null;
+ error: any;
+ };
diff --git a/code/addons/onboarding/tsconfig.json b/code/addons/onboarding/tsconfig.json
new file mode 100644
index 000000000000..a4429176e35f
--- /dev/null
+++ b/code/addons/onboarding/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "strict": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/code/addons/onboarding/vitest.config.ts b/code/addons/onboarding/vitest.config.ts
new file mode 100644
index 000000000000..622642938f21
--- /dev/null
+++ b/code/addons/onboarding/vitest.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig, mergeConfig } from 'vitest/config';
+import { sep, posix } from 'path';
+import { vitestCommonConfig } from '../../vitest.workspace';
+
+export default mergeConfig(
+ vitestCommonConfig,
+ defineConfig({
+ test: {
+ environment: 'jsdom',
+ name: __dirname.split(sep).slice(-2).join(posix.sep),
+ },
+ })
+);
diff --git a/code/addons/themes/src/theme-switcher.tsx b/code/addons/themes/src/theme-switcher.tsx
index 7e366fe8359e..ef27f0d25769 100644
--- a/code/addons/themes/src/theme-switcher.tsx
+++ b/code/addons/themes/src/theme-switcher.tsx
@@ -15,10 +15,10 @@ import {
const IconButtonLabel = styled.div(({ theme }) => ({
fontSize: theme.typography.size.s2 - 1,
- marginLeft: 10,
}));
const hasMultipleThemes = (themesList: ThemeAddonState['themesList']) => themesList.length > 1;
+const hasTwoThemes = (themesList: ThemeAddonState['themesList']) => themesList.length === 2;
export const ThemeSwitcher = () => {
const { themeOverride } = useParameter(
@@ -52,33 +52,55 @@ export const ThemeSwitcher = () => {
return themeName && <>{`${themeName} theme`}>;
}, [themeOverride, themeDefault, selected]);
- return hasMultipleThemes(themesList) ? (
-
- {
- return (
- ({
- id: theme,
- title: theme,
- active: selected === theme,
- onClick: () => {
- updateGlobals({ theme });
- onHide();
- },
- }))}
- />
- );
+ if (hasTwoThemes(themesList)) {
+ const currentTheme = selected || themeDefault;
+ const alternateTheme = themesList.find((theme) => theme !== currentTheme);
+ return (
+ {
+ updateGlobals({ theme: alternateTheme });
}}
>
-
-
- {label && {label} }
-
-
-
- ) : null;
+
+ {label && {label} }
+
+ );
+ }
+
+ if (hasMultipleThemes(themesList)) {
+ return (
+
+ {
+ return (
+ ({
+ id: theme,
+ title: theme,
+ active: selected === theme,
+ onClick: () => {
+ updateGlobals({ theme });
+ onHide();
+ },
+ }))}
+ />
+ );
+ }}
+ >
+
+
+ {label && {label} }
+
+
+
+ );
+ }
+
+ return null;
};
diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json
index 24e6750ab3df..7b69fdf20063 100644
--- a/code/builders/builder-vite/package.json
+++ b/code/builders/builder-vite/package.json
@@ -46,6 +46,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-common": "workspace:*",
+ "@storybook/core-events": "workspace:*",
"@storybook/csf-plugin": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/preview": "workspace:*",
diff --git a/code/builders/builder-vite/src/build.ts b/code/builders/builder-vite/src/build.ts
index ff8f46c8bed3..67ce2c42942d 100644
--- a/code/builders/builder-vite/src/build.ts
+++ b/code/builders/builder-vite/src/build.ts
@@ -44,7 +44,7 @@ export async function build(options: Options) {
finalConfig.plugins && (await hasVitePlugins(finalConfig.plugins, [turbosnapPluginName]));
if (hasTurbosnapPlugin) {
logger.warn(dedent`Found '${turbosnapPluginName}' which is now included by default in Storybook 8.
- Removing from your plugins list. Ensure you pass \`--webpack-stats-json\` to generate stats.
+ Removing from your plugins list. Ensure you pass \`--stats-json\` to generate stats.
For more information, see https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#turbosnap-vite-plugin-is-no-longer-needed`);
diff --git a/code/builders/builder-vite/src/index.ts b/code/builders/builder-vite/src/index.ts
index 9cdac7233d67..3c0b7591d2cf 100644
--- a/code/builders/builder-vite/src/index.ts
+++ b/code/builders/builder-vite/src/index.ts
@@ -5,6 +5,7 @@ import type { RequestHandler } from 'express';
import type { ViteDevServer } from 'vite';
import express from 'express';
import { dirname, join, parse } from 'path';
+import { NoStatsForViteDevError } from '@storybook/core-events/server-errors';
import type { Options } from '@storybook/types';
import { transformIframeHtml } from './transform-iframe-html';
import { createViteServer } from './vite-server';
@@ -68,7 +69,11 @@ export const start: ViteBuilder['start'] = async ({
return {
bail,
- stats: { toJson: () => null },
+ stats: {
+ toJson: () => {
+ throw new NoStatsForViteDevError();
+ },
+ },
totalTime: process.hrtime(startTime),
};
};
diff --git a/code/e2e-tests/addon-docs.spec.ts b/code/e2e-tests/addon-docs.spec.ts
index 4ae3da33e99e..72470acb62ab 100644
--- a/code/e2e-tests/addon-docs.spec.ts
+++ b/code/e2e-tests/addon-docs.spec.ts
@@ -210,4 +210,19 @@ test.describe('addon-docs', () => {
await expect(componentReactVersion).toHaveText(expectedReactVersion);
await expect(componentReactDomVersion).toHaveText(expectedReactVersion);
});
+
+ test('should have stories from multiple CSF files in autodocs', async ({ page }) => {
+ const sbPage = new SbPage(page);
+ await sbPage.navigateToStory('/addons/docs/multiple-csf-files-same-title', 'docs');
+ const root = sbPage.previewRoot();
+
+ const storyHeadings = root.locator('.sb-anchor > h3');
+ await expect(await storyHeadings.count()).toBe(6);
+ await expect(storyHeadings.nth(0)).toHaveText('Default A');
+ await expect(storyHeadings.nth(1)).toHaveText('Span Content');
+ await expect(storyHeadings.nth(2)).toHaveText('Code Content');
+ await expect(storyHeadings.nth(3)).toHaveText('Default B');
+ await expect(storyHeadings.nth(4)).toHaveText('H 1 Content');
+ await expect(storyHeadings.nth(5)).toHaveText('H 2 Content');
+ });
});
diff --git a/code/frameworks/angular/src/builders/build-storybook/index.spec.ts b/code/frameworks/angular/src/builders/build-storybook/index.spec.ts
index 64b10c2c2ca3..3c8960195611 100644
--- a/code/frameworks/angular/src/builders/build-storybook/index.spec.ts
+++ b/code/frameworks/angular/src/builders/build-storybook/index.spec.ts
@@ -104,7 +104,7 @@ describe.skip('Build Storybook Builder', () => {
packageJson: expect.any(Object),
mode: 'static',
tsConfig: './storybook/tsconfig.ts',
- webpackStatsJson: false,
+ statsJson: false,
})
);
});
@@ -133,7 +133,7 @@ describe.skip('Build Storybook Builder', () => {
packageJson: expect.any(Object),
mode: 'static',
tsConfig: 'path/to/tsConfig.json',
- webpackStatsJson: false,
+ statsJson: false,
})
);
});
@@ -142,7 +142,7 @@ describe.skip('Build Storybook Builder', () => {
const run = await architect.scheduleBuilder('@storybook/angular:build-storybook', {
tsConfig: 'path/to/tsConfig.json',
compodoc: false,
- webpackStatsJson: true,
+ statsJson: true,
});
const output = await run.result;
@@ -162,7 +162,7 @@ describe.skip('Build Storybook Builder', () => {
packageJson: expect.any(Object),
mode: 'static',
tsConfig: 'path/to/tsConfig.json',
- webpackStatsJson: true,
+ statsJson: true,
})
);
});
@@ -212,7 +212,7 @@ describe.skip('Build Storybook Builder', () => {
packageJson: expect.any(Object),
mode: 'static',
tsConfig: './storybook/tsconfig.ts',
- webpackStatsJson: false,
+ statsJson: false,
})
);
});
@@ -242,7 +242,7 @@ describe.skip('Build Storybook Builder', () => {
packageJson: expect.any(Object),
mode: 'static',
tsConfig: 'path/to/tsConfig.json',
- webpackStatsJson: false,
+ statsJson: false,
})
);
});
diff --git a/code/frameworks/angular/src/builders/build-storybook/index.ts b/code/frameworks/angular/src/builders/build-storybook/index.ts
index 81d6b2689c2a..25f7faeb5268 100644
--- a/code/frameworks/angular/src/builders/build-storybook/index.ts
+++ b/code/frameworks/angular/src/builders/build-storybook/index.ts
@@ -51,6 +51,7 @@ export type StorybookBuilderOptions = JsonObject & {
| 'quiet'
| 'test'
| 'webpackStatsJson'
+ | 'statsJson'
| 'disableTelemetry'
| 'debugWebpack'
| 'previewUrl'
@@ -66,10 +67,12 @@ const commandBuilder: BuilderHandlerFn = (
): BuilderOutputLike => {
const builder = from(setup(options, context)).pipe(
switchMap(({ tsConfig }) => {
+ const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir });
const runCompodoc$ = options.compodoc
- ? runCompodoc({ compodocArgs: options.compodocArgs, tsconfig: tsConfig }, context).pipe(
- mapTo({ tsConfig })
- )
+ ? runCompodoc(
+ { compodocArgs: options.compodocArgs, tsconfig: docTSConfig ?? tsConfig },
+ context
+ ).pipe(mapTo({ tsConfig }))
: of({});
return runCompodoc$.pipe(mapTo({ tsConfig }));
@@ -93,6 +96,7 @@ const commandBuilder: BuilderHandlerFn = (
quiet,
enableProdMode = true,
webpackStatsJson,
+ statsJson,
debugWebpack,
disableTelemetry,
assets,
@@ -120,6 +124,7 @@ const commandBuilder: BuilderHandlerFn = (
},
tsConfig,
webpackStatsJson,
+ statsJson,
debugWebpack,
previewUrl,
};
diff --git a/code/frameworks/angular/src/builders/start-storybook/index.ts b/code/frameworks/angular/src/builders/start-storybook/index.ts
index cad69e51131f..2ecbb63c8a0f 100644
--- a/code/frameworks/angular/src/builders/start-storybook/index.ts
+++ b/code/frameworks/angular/src/builders/start-storybook/index.ts
@@ -57,6 +57,7 @@ export type StorybookBuilderOptions = JsonObject & {
| 'docs'
| 'debugWebpack'
| 'webpackStatsJson'
+ | 'statsJson'
| 'loglevel'
| 'previewUrl'
>;
@@ -66,11 +67,13 @@ export type StorybookBuilderOutput = JsonObject & BuilderOutput & {};
const commandBuilder: BuilderHandlerFn = (options, context) => {
const builder = from(setup(options, context)).pipe(
switchMap(({ tsConfig }) => {
+ const docTSConfig = findUpSync('tsconfig.doc.json', { cwd: options.configDir });
+
const runCompodoc$ = options.compodoc
? runCompodoc(
{
compodocArgs: [...options.compodocArgs, ...(options.quiet ? ['--silent'] : [])],
- tsconfig: tsConfig,
+ tsconfig: docTSConfig ?? tsConfig,
},
context
).pipe(mapTo({ tsConfig }))
@@ -112,6 +115,7 @@ const commandBuilder: BuilderHandlerFn = (options, cont
debugWebpack,
loglevel,
webpackStatsJson,
+ statsJson,
previewUrl,
sourceMap = false,
} = options;
@@ -143,8 +147,9 @@ const commandBuilder: BuilderHandlerFn = (options, cont
initialPath,
open,
debugWebpack,
- loglevel,
webpackStatsJson,
+ statsJson,
+ loglevel,
previewUrl,
};
diff --git a/code/frameworks/vue3-vite/src/types.ts b/code/frameworks/vue3-vite/src/types.ts
index c6ee52bf144a..4b77d28e7a94 100644
--- a/code/frameworks/vue3-vite/src/types.ts
+++ b/code/frameworks/vue3-vite/src/types.ts
@@ -1,9 +1,16 @@
import type { BuilderOptions, StorybookConfigVite } from '@storybook/builder-vite';
import type { StorybookConfig as StorybookConfigBase } from '@storybook/types';
+import type { ComponentMeta } from 'vue-component-meta';
+import type { ComponentDoc } from 'vue-docgen-api';
type FrameworkName = '@storybook/vue3-vite';
type BuilderName = '@storybook/builder-vite';
+/**
+ * Available docgen plugins for vue.
+ */
+export type VueDocgenPlugin = 'vue-docgen-api' | 'vue-component-meta';
+
export type FrameworkOptions = {
builder?: BuilderOptions;
/**
@@ -14,7 +21,7 @@ export type FrameworkOptions = {
* "vue-component-meta" will become the new default in the future and "vue-docgen-api" will be removed.
* @default "vue-docgen-api"
*/
- docgen?: 'vue-docgen-api' | 'vue-component-meta';
+ docgen?: VueDocgenPlugin;
};
type StorybookConfigFramework = {
@@ -43,3 +50,37 @@ export type StorybookConfig = Omit<
> &
StorybookConfigVite &
StorybookConfigFramework;
+
+/**
+ * Gets the type of a single array element.
+ */
+type ArrayElement = T extends readonly (infer A)[] ? A : never;
+
+/**
+ * Type of "__docgenInfo" depending on the used docgenPlugin.
+ */
+export type VueDocgenInfo = T extends 'vue-component-meta'
+ ? ComponentMeta
+ : ComponentDoc;
+
+/**
+ * Single prop/event/slot/exposed entry of "__docgenInfo" depending on the used docgenPlugin.
+ *
+ * @example
+ * ```ts
+ * type PropInfo = VueDocgenInfoEntry<"vue-component-meta", "props">;
+ * ```
+ */
+export type VueDocgenInfoEntry<
+ T extends VueDocgenPlugin,
+ TKey extends 'props' | 'events' | 'slots' | 'exposed' | 'expose' =
+ | 'props'
+ | 'events'
+ | 'slots'
+ | 'exposed'
+ | 'expose',
+> = ArrayElement<
+ T extends 'vue-component-meta'
+ ? VueDocgenInfo<'vue-component-meta'>[Exclude]
+ : VueDocgenInfo<'vue-docgen-api'>[Exclude]
+>;
diff --git a/code/lib/cli/src/autoblock/block-dependencies-versions.ts b/code/lib/cli/src/autoblock/block-dependencies-versions.ts
index fb052e60c62e..45db54bac794 100644
--- a/code/lib/cli/src/autoblock/block-dependencies-versions.ts
+++ b/code/lib/cli/src/autoblock/block-dependencies-versions.ts
@@ -1,6 +1,7 @@
import { createBlocker } from './types';
import { dedent } from 'ts-dedent';
import { lt } from 'semver';
+import chalk from 'chalk';
const minimalVersionsMap = {
'@angular/core': '15.0.0',
@@ -9,6 +10,7 @@ const minimalVersionsMap = {
preact: '10.0.0',
svelte: '4.0.0',
vue: '3.0.0',
+ vite: '4.0.0',
};
type Result = {
@@ -47,9 +49,11 @@ export const blocker = createBlocker({
switch (data.packageName) {
case 'react-scripts':
return dedent`
- Support react-script < 5.0.0 has been removed.
+ Support for react-script < 5.0.0 has been removed.
Please see the migration guide for more information:
- https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#create-react-app-dropped-cra4-support
+ ${chalk.yellow(
+ 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#create-react-app-dropped-cra4-support'
+ )}
Upgrade to the latest version of react-scripts.
`;
@@ -57,7 +61,7 @@ export const blocker = createBlocker({
return dedent`
Support for Vue 2 has been removed.
Please see the migration guide for more information:
- https://v3-migration.vuejs.org/
+ ${chalk.yellow('https://v3-migration.vuejs.org/')}
Please upgrade to the latest version of Vue.
`;
@@ -65,7 +69,7 @@ export const blocker = createBlocker({
return dedent`
Support for Angular < 15 has been removed.
Please see the migration guide for more information:
- https://angular.io/guide/update-to-version-15
+ ${chalk.yellow('https://angular.io/guide/update-to-version-15')}
Please upgrade to the latest version of Angular.
`;
@@ -73,14 +77,16 @@ export const blocker = createBlocker({
return dedent`
Support for Next.js < 13.5 has been removed.
Please see the migration guide for more information:
- https://nextjs.org/docs/pages/building-your-application/upgrading/version-13
+ ${chalk.yellow(
+ 'https://nextjs.org/docs/pages/building-your-application/upgrading/version-13'
+ )}
Please upgrade to the latest version of Next.js.
`;
default:
return dedent`
Support for ${data.packageName} version < ${data.minimumVersion} has been removed.
- Storybook 8 needs minimum version of ${data.minimumVersion}, but you had version ${data.installedVersion}.
+ Since version 8, Storybook needs a minimum version of ${data.minimumVersion}, but you have version ${data.installedVersion}.
Please update this dependency.
`;
diff --git a/code/lib/cli/src/autoblock/block-node-version.ts b/code/lib/cli/src/autoblock/block-node-version.ts
index 8d67cdd51018..49c9744b08f0 100644
--- a/code/lib/cli/src/autoblock/block-node-version.ts
+++ b/code/lib/cli/src/autoblock/block-node-version.ts
@@ -1,6 +1,7 @@
import { createBlocker } from './types';
import { dedent } from 'ts-dedent';
import { lt } from 'semver';
+import chalk from 'chalk';
export const blocker = createBlocker({
id: 'minimumNode16',
@@ -16,7 +17,7 @@ export const blocker = createBlocker({
We've detected you're using Node.js v${data.nodeVersion}.
Storybook needs Node.js 18 or higher.
- https://nodejs.org/en/download
+ ${chalk.yellow('https://nodejs.org/en/download')}
`;
},
});
diff --git a/code/lib/cli/src/autoblock/block-stories-mdx.ts b/code/lib/cli/src/autoblock/block-stories-mdx.ts
deleted file mode 100644
index 3c1fadeda351..000000000000
--- a/code/lib/cli/src/autoblock/block-stories-mdx.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createBlocker } from './types';
-import { dedent } from 'ts-dedent';
-import { glob } from 'glob';
-
-export const blocker = createBlocker({
- id: 'storiesMdxUsage',
- async check() {
- const files = await glob('**/*.stories.mdx', { cwd: process.cwd() });
- if (files.length === 0) {
- return false;
- }
- return { files };
- },
- log(options, data) {
- return dedent`
- Support for *.stories.mdx files has been removed.
- Please see the migration guide for more information:
- https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-storiesmdx-csf-in-mdx-format-and-mdx1-support
-
- Storybook will also require you to use MDX 3.0.0 or later.
- Check the migration guide for more information:
- https://mdxjs.com/blog/v3/
-
- Found ${data.files.length} stories.mdx ${
- data.files.length === 1 ? 'file' : 'files'
- }, these must be migrated.
-
- Manually run the migration script to convert your stories.mdx files to CSF format documented here:
- https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf
- `;
- },
-});
diff --git a/code/lib/cli/src/autoblock/block-storystorev6.ts b/code/lib/cli/src/autoblock/block-storystorev6.ts
index cd9eaffb6a7a..d284584c9fbc 100644
--- a/code/lib/cli/src/autoblock/block-storystorev6.ts
+++ b/code/lib/cli/src/autoblock/block-storystorev6.ts
@@ -1,6 +1,7 @@
import { createBlocker } from './types';
import { dedent } from 'ts-dedent';
import type { StorybookConfigRaw } from '@storybook/types';
+import chalk from 'chalk';
export const blocker = createBlocker({
id: 'storyStoreV7removal',
@@ -19,14 +20,16 @@ export const blocker = createBlocker({
StoryStoreV7 feature must be removed from your Storybook configuration.
This feature was removed in Storybook 8.0.0.
Please see the migration guide for more information:
- https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated
+ ${chalk.yellow(
+ 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storystorev6-and-storiesof-is-deprecated'
+ )}
- In your Storybook configuration file you have this code:
+ In your Storybook configuration we found storyStoryV7 feature defined. For instance:
export default = {
- features: {
- storyStoreV7: false, <--- remove this line
- },
+ features: {
+ ${chalk.cyan(`storyStoreV7: false`)}, <--- ${chalk.bold('remove this line')}
+ },
};
You need to remove the storyStoreV7 property.
diff --git a/code/lib/cli/src/autoblock/index.test.ts b/code/lib/cli/src/autoblock/index.test.ts
index 18be67a4e6d8..d6b5ea1a4a23 100644
--- a/code/lib/cli/src/autoblock/index.test.ts
+++ b/code/lib/cli/src/autoblock/index.test.ts
@@ -74,12 +74,13 @@ test('1 fail', async () => {
]);
expect(result).toBe('alwaysFail');
- expect(logger.plain).toHaveBeenCalledWith(expect.stringContaining('Oh no..'));
- expect(stripAnsi(logger.plain.mock.calls[1][0])).toMatchInlineSnapshot(`
- "Blocking your upgrade because of the following issues:
+ expect(stripAnsi(logger.plain.mock.calls[0][0])).toMatchInlineSnapshot(`
+ "Storybook has found potential blockers in your project that need to be resolved before upgrading:
Always fail
+ ─────────────────────────────────────────────────
+
Fix the above issues and try running the upgrade command again."
`);
});
@@ -90,13 +91,17 @@ test('multiple fails', async () => {
Promise.resolve({ blocker: blockers.alwaysFail }),
Promise.resolve({ blocker: blockers.alwaysFail2 }),
]);
- expect(stripAnsi(logger.plain.mock.calls[1][0])).toMatchInlineSnapshot(`
- "Blocking your upgrade because of the following issues:
+ expect(stripAnsi(logger.plain.mock.calls[0][0])).toMatchInlineSnapshot(`
+ "Storybook has found potential blockers in your project that need to be resolved before upgrading:
Always fail
+ ─────────────────────────────────────────────────
+
Always fail 2
+ ─────────────────────────────────────────────────
+
Fix the above issues and try running the upgrade command again."
`);
diff --git a/code/lib/cli/src/autoblock/index.ts b/code/lib/cli/src/autoblock/index.ts
index a6c45a2318ba..6e71266926f6 100644
--- a/code/lib/cli/src/autoblock/index.ts
+++ b/code/lib/cli/src/autoblock/index.ts
@@ -8,13 +8,14 @@ const excludesFalse = (x: T | false): x is T => x !== false;
const blockers: () => BlockerModule[] = () => [
// add/remove blockers here
import('./block-storystorev6'),
- import('./block-stories-mdx'),
import('./block-dependencies-versions'),
import('./block-node-version'),
];
type BlockerModule = Promise<{ blocker: Blocker }>;
+const segmentDivider = '\n\n─────────────────────────────────────────────────\n\n';
+
export const autoblock = async (
options: AutoblockOptions,
list: BlockerModule[] = blockers()
@@ -45,18 +46,18 @@ export const autoblock = async (
if (faults.length > 0) {
const messages = {
- welcome: `Blocking your upgrade because of the following issues:`,
+ welcome: `Storybook has found potential blockers in your project that need to be resolved before upgrading:`,
reminder: chalk.yellow('Fix the above issues and try running the upgrade command again.'),
};
const borderColor = '#FC521F';
- logger.plain('Oh no..');
logger.plain(
boxen(
[messages.welcome]
- .concat(faults.map((i) => i.log))
- .concat([messages.reminder])
- .join('\n\n'),
+ .concat(['\n\n'])
+ .concat([faults.map((i) => i.log).join(segmentDivider)])
+ .concat([segmentDivider, messages.reminder])
+ .join(''),
{ borderStyle: 'round', padding: 1, borderColor }
)
);
diff --git a/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap b/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap
deleted file mode 100644
index 0c9ea612b977..000000000000
--- a/code/lib/cli/src/automigrate/fixes/__snapshots__/angular-builders.test.ts.snap
+++ /dev/null
@@ -1,6 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`is not Nx project > angular builders > Angular < 15.0.0 > should throw an Error 1`] = `
-[Error: ❌ Your project uses Angular < 15.0.0. Storybook 8.0 for Angular requires Angular 15.0.0 or higher.
-Please upgrade your Angular version to at least version 15.0.0 to use Storybook 8.0 in your project.]
-`;
diff --git a/code/lib/cli/src/automigrate/fixes/addon-postcss.test.ts b/code/lib/cli/src/automigrate/fixes/addon-postcss.test.ts
new file mode 100644
index 000000000000..f9b77cab4110
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/addon-postcss.test.ts
@@ -0,0 +1,42 @@
+import { addonPostCSS } from './addon-postcss';
+import type { StorybookConfig } from '@storybook/types';
+import type { JsPackageManager } from '@storybook/core-common';
+import { expect, describe, it } from 'vitest';
+
+const checkAddonPostCSS = async ({
+ packageManager,
+ mainConfig = {},
+ storybookVersion = '7.0.0',
+}: {
+ packageManager?: Partial;
+ mainConfig?: Partial;
+ storybookVersion?: string;
+}) => {
+ return addonPostCSS.check({
+ packageManager: packageManager as any,
+ storybookVersion,
+ mainConfig: mainConfig as any,
+ });
+};
+
+describe('check function', () => {
+ it('should return { hasAddonPostcss: true } if @storybook/addon-postcss is found', async () => {
+ await expect(
+ checkAddonPostCSS({
+ mainConfig: {
+ addons: ['@storybook/addon-postcss'],
+ },
+ })
+ ).resolves.toEqual({ hasAddonPostcss: true });
+ });
+
+ it('should return null if @storybook/addon-postcss is not found', async () => {
+ await expect(
+ checkAddonPostCSS({
+ mainConfig: {
+ addons: [],
+ },
+ })
+ ).resolves.toBeNull();
+ });
+});
diff --git a/code/lib/cli/src/automigrate/fixes/addon-postcss.ts b/code/lib/cli/src/automigrate/fixes/addon-postcss.ts
new file mode 100644
index 000000000000..7978e545a1c5
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/addon-postcss.ts
@@ -0,0 +1,41 @@
+import chalk from 'chalk';
+import { dedent } from 'ts-dedent';
+import type { Fix } from '../types';
+import { getAddonNames } from '../helpers/mainConfigFile';
+
+interface AddonPostcssRunOptions {
+ hasAddonPostcss: boolean;
+}
+
+export const addonPostCSS: Fix = {
+ id: 'addon-postcss',
+
+ versionRange: ['<7', '>=7'],
+
+ promptType: 'notification',
+
+ async check({ mainConfig }) {
+ const addons = getAddonNames(mainConfig);
+ const hasAddonPostcss = !!addons.find((addon) => addon.includes('@storybook/addon-postcss'));
+
+ if (!hasAddonPostcss) {
+ return null;
+ }
+
+ return { hasAddonPostcss: true };
+ },
+
+ prompt() {
+ return dedent`
+ ${chalk.bold(
+ 'Attention'
+ )}: We've detected that you're using the following package which are incompatible with Storybook 8 and beyond:
+
+ - ${chalk.cyan(`@storybook/addon-postcss`)}
+
+ Please migrate to ${chalk.cyan(
+ `@storybook/addon-styling-webpack`
+ )} once you're done upgrading.
+ `;
+ },
+};
diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts
index d57fbd28a10c..3f72411be011 100644
--- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts
+++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts
@@ -12,6 +12,8 @@ export const angularBuildersMultiproject: Fix=7'],
+
async check({ packageManager, mainConfig }) {
// Skip in case of NX
const angularVersion = await packageManager.getPackageVersion('@angular/core');
diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts
index 2b9623e26d93..e3825b0dae65 100644
--- a/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/angular-builders.test.ts
@@ -71,24 +71,6 @@ describe('is not Nx project', () => {
});
});
- describe('Angular < 15.0.0', () => {
- const packageManager = {
- getPackageVersion: (packageName: string) => {
- if (packageName === '@angular/core') {
- return Promise.resolve('14.0.0');
- }
-
- return null;
- },
- } as Partial;
-
- it('should throw an Error', async () => {
- await expect(
- checkAngularBuilders({ packageManager, mainConfig: { framework: '@storybook/angular' } })
- ).rejects.toThrowErrorMatchingSnapshot();
- });
- });
-
describe('Angular >= 16.0.0', () => {
const packageManager = {
getPackageVersion: (packageName) => {
diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders.ts b/code/lib/cli/src/automigrate/fixes/angular-builders.ts
index b0c40a0f6991..ac2f2af99d2e 100644
--- a/code/lib/cli/src/automigrate/fixes/angular-builders.ts
+++ b/code/lib/cli/src/automigrate/fixes/angular-builders.ts
@@ -1,5 +1,4 @@
import { dedent } from 'ts-dedent';
-import semver from 'semver';
import type { StorybookConfig } from '@storybook/types';
import chalk from 'chalk';
import prompts from 'prompts';
@@ -17,6 +16,8 @@ interface AngularBuildersRunOptions {
export const angularBuilders: Fix = {
id: 'angular-builders',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, mainConfig }) {
const angularVersion = await packageManager.getPackageVersion('@angular/core');
@@ -31,13 +32,6 @@ export const angularBuilders: Fix = {
return null;
}
- if (semver.lt(angularVersion, '15.0.0')) {
- throw new Error(dedent`
- ❌ Your project uses Angular < 15.0.0. Storybook 8.0 for Angular requires Angular 15.0.0 or higher.
- Please upgrade your Angular version to at least version 15.0.0 to use Storybook 8.0 in your project.
- `);
- }
-
const angularJSON = new AngularJSON();
const { hasStorybookBuilder } = angularJSON;
diff --git a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts
index f84c3f7ee778..e5c2aceda7c2 100644
--- a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts
+++ b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts
@@ -16,6 +16,8 @@ interface AutodocsTrueFrameworkRunOptions {
export const autodocsTrue: Fix = {
id: 'autodocsTrue',
+ versionRange: ['<7', '>=7'],
+
async check({ mainConfig }) {
const { docs } = mainConfig;
diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts
deleted file mode 100644
index 175e69c23b14..000000000000
--- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import chalk from 'chalk';
-import dedent from 'ts-dedent';
-import semver from 'semver';
-import type { StoriesEntry } from '@storybook/types';
-import { updateMainConfig } from '../helpers/mainConfigFile';
-import type { Fix } from '../types';
-
-const logger = console;
-
-export interface BareMdxStoriesGlobRunOptions {
- existingStoriesEntries: StoriesEntry[];
- nextStoriesEntries: StoriesEntry[];
-}
-
-const getNextGlob = (glob: string) => {
- // '../src/**/*.stories.@(mdx|js|jsx|ts|tsx)' -> '../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'
- const extGlobsRegex = new RegExp(/(.*\.)(stories\.@.*)(\|mdx|mdx\|)(.*)$/i);
- if (glob.match(extGlobsRegex)) {
- return glob.replace(extGlobsRegex, '$1@(mdx|$2$4)');
- }
-
- // '../src/**/*.stories.*' -> '../src/**/*.@(mdx|stories.*)'
- const allStoriesExtensionsRegex = new RegExp(/(.*\.)(stories\.\*)$/i);
- if (glob.match(allStoriesExtensionsRegex)) {
- return glob.replace(allStoriesExtensionsRegex, '$1@(mdx|$2)');
- }
-
- // '../src/**/*.stories.mdx' -> '../src/**/*.mdx'
- return glob.replaceAll('.stories.mdx', '.mdx');
-};
-
-export const bareMdxStoriesGlob: Fix = {
- id: 'bare-mdx-stories-glob',
- async check({ storybookVersion, mainConfig }) {
- if (!semver.gte(storybookVersion, '7.0.0')) {
- return null;
- }
-
- const existingStoriesEntries = mainConfig.stories as StoriesEntry[];
-
- if (!existingStoriesEntries) {
- throw new Error(dedent`
- ❌ Unable to determine Storybook stories globs in ${chalk.blue(
- mainConfig
- )}, skipping ${chalk.cyan(this.id)} fix.
-
- In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx.
-
- We were unable to automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
- We suggest you make this change manually.
-
- To learn more about this change, see: ${chalk.yellow(
- 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files'
- )}
- `);
- }
-
- const nextStoriesEntries = existingStoriesEntries.map((entry) => {
- const isSpecifier = typeof entry !== 'string';
- const glob = isSpecifier ? entry.files : entry;
-
- if (!glob) {
- // storySpecifier without the 'files' property. Just add the existing to the next list
- return entry;
- }
-
- const nextGlob = getNextGlob(glob);
- return isSpecifier ? { ...entry, files: nextGlob } : nextGlob;
- });
-
- // bails if there are no changes
- if (
- existingStoriesEntries.length === nextStoriesEntries.length &&
- existingStoriesEntries.every((entry, index) => {
- const nextEntry = nextStoriesEntries[index];
- if (typeof entry === 'string') {
- return entry === nextEntry;
- }
- if (typeof nextEntry === 'string') {
- return false;
- }
- return entry.files === nextEntry.files;
- })
- ) {
- return null;
- }
-
- return { existingStoriesEntries, nextStoriesEntries };
- },
-
- prompt({ existingStoriesEntries, nextStoriesEntries }) {
- const prettyExistingStoriesEntries = existingStoriesEntries
- .map((entry) => JSON.stringify(entry, null, 2))
- .join('\n');
- const prettyNextStoriesEntries = nextStoriesEntries
- .map((entry) => JSON.stringify(entry, null, 2))
- .join('\n');
- return dedent`
- We've detected your project has one or more globs in your 'stories' config that matches .stories.mdx files:
- ${chalk.cyan(prettyExistingStoriesEntries)}
-
- In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx.
-
- We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
- That would result in the following 'stories' config:
- ${chalk.cyan(prettyNextStoriesEntries)}
-
- To learn more about this change, see: ${chalk.yellow(
- 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files'
- )}
- `;
- },
-
- async run({ dryRun, mainConfigPath, result: { nextStoriesEntries } }) {
- logger.info(dedent`✅ Setting 'stories' config:
- ${JSON.stringify(nextStoriesEntries, null, 2)}`);
-
- if (!dryRun) {
- await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => {
- main.setFieldValue(['stories'], nextStoriesEntries);
- });
- }
- },
-};
diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.ts
index 8662bb6cc21b..b38cf9ccb677 100644
--- a/code/lib/cli/src/automigrate/fixes/builder-vite.ts
+++ b/code/lib/cli/src/automigrate/fixes/builder-vite.ts
@@ -27,6 +27,8 @@ interface BuilderViteOptions {
export const builderVite: Fix = {
id: 'builder-vite',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, mainConfig }) {
const packageJson = await packageManager.retrievePackageJson();
const builder = mainConfig.core?.builder;
diff --git a/code/lib/cli/src/automigrate/fixes/cra5.ts b/code/lib/cli/src/automigrate/fixes/cra5.ts
index d3786cd2d00f..468fbe90947b 100644
--- a/code/lib/cli/src/automigrate/fixes/cra5.ts
+++ b/code/lib/cli/src/automigrate/fixes/cra5.ts
@@ -20,6 +20,8 @@ interface CRA5RunOptions {
export const cra5: Fix = {
id: 'cra5',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, mainConfig, storybookVersion }) {
const craVersion = await packageManager.getPackageVersion('react-scripts');
diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts
index 25e5dfd03204..de81e08008d8 100644
--- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts
+++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts
@@ -26,6 +26,8 @@ interface EslintPluginRunOptions {
export const eslintPlugin: Fix = {
id: 'eslintPlugin',
+ versionRange: ['<8', '>=7'],
+
async check({ packageManager }) {
const { hasEslint, isStorybookPluginInstalled } = await extractEslintInfo(packageManager);
diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts
deleted file mode 100644
index d23f369da214..000000000000
--- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.test.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { describe, afterEach, it, expect, vi } from 'vitest';
-
-import type { StorybookConfig } from '@storybook/types';
-import { incompatibleAddons } from './incompatible-addons';
-import type { JsPackageManager } from '@storybook/core-common';
-
-const check = async ({
- packageManager,
- main: mainConfig = {},
- storybookVersion = '7.0.0',
-}: {
- packageManager: Partial;
- main?: Partial & Record;
- storybookVersion?: string;
-}) => {
- return incompatibleAddons.check({
- packageManager: packageManager as any,
- configDir: '',
- mainConfig: mainConfig as any,
- storybookVersion,
- });
-};
-
-describe('incompatible-addons fix', () => {
- afterEach(() => {
- vi.restoreAllMocks();
- });
-
- it('should show incompatible addons registered in main.js', async () => {
- await expect(
- check({
- packageManager: {
- getPackageVersion(packageName, basePath) {
- switch (packageName) {
- case '@storybook/addon-essentials':
- return Promise.resolve('7.0.0');
- case '@storybook/addon-info':
- return Promise.resolve('5.3.21');
- default:
- return Promise.resolve(null);
- }
- },
- getAllDependencies: async () => ({}),
- },
- main: { addons: ['@storybook/essentials', '@storybook/addon-info'] },
- })
- ).resolves.toEqual({
- incompatibleAddonList: [
- {
- name: '@storybook/addon-info',
- version: '5.3.21',
- },
- ],
- });
- });
-
- it('should show incompatible addons from package.json', async () => {
- await expect(
- check({
- packageManager: {
- getPackageVersion(packageName, basePath) {
- switch (packageName) {
- case '@storybook/addon-essentials':
- return Promise.resolve('7.0.0');
- case '@storybook/addon-info':
- return Promise.resolve('5.3.21');
- default:
- return Promise.resolve(null);
- }
- },
- getAllDependencies: async () => ({
- '@storybook/addon-essentials': '7.0.0',
- '@storybook/addon-info': '5.3.21',
- }),
- },
- main: { addons: [] },
- })
- ).resolves.toEqual({
- incompatibleAddonList: [
- {
- name: '@storybook/addon-info',
- version: '5.3.21',
- },
- ],
- });
- });
-
- it('no-op when there are no incompatible addons', async () => {
- await expect(
- check({
- packageManager: {
- getPackageVersion(packageName, basePath) {
- switch (packageName) {
- case '@storybook/addon-essentials':
- return Promise.resolve('7.0.0');
- default:
- return Promise.resolve(null);
- }
- },
- getAllDependencies: async () => ({}),
- },
- main: { addons: ['@storybook/essentials'] },
- })
- ).resolves.toBeNull();
- });
-});
diff --git a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts b/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts
deleted file mode 100644
index 469383834cc1..000000000000
--- a/code/lib/cli/src/automigrate/fixes/incompatible-addons.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import chalk from 'chalk';
-import dedent from 'ts-dedent';
-import type { Fix } from '../types';
-import { getIncompatibleAddons } from '../../doctor/getIncompatibleAddons';
-
-interface IncompatibleAddonsOptions {
- incompatibleAddonList: { name: string; version: string }[];
-}
-
-export const incompatibleAddons: Fix = {
- id: 'incompatible-addons',
- promptType: 'manual',
-
- async check({ mainConfig, packageManager }) {
- const incompatibleAddonList = await getIncompatibleAddons(mainConfig, packageManager);
-
- return incompatibleAddonList.length > 0 ? { incompatibleAddonList } : null;
- },
- prompt({ incompatibleAddonList }) {
- return dedent`
- ${chalk.bold(
- 'Attention'
- )}: We've detected that you're using the following addons in versions which are known to be incompatible with Storybook 7:
-
- ${incompatibleAddonList
- .map(({ name, version }) => `- ${chalk.cyan(`${name}@${version}`)}`)
- .join('\n')}
-
- Please be aware they might not work in Storybook 7. Reach out to their maintainers for updates and check the following Github issue for more information:
- ${chalk.yellow('https://github.com/storybookjs/storybook/issues/20529')}
- `;
- },
-};
diff --git a/code/lib/cli/src/automigrate/fixes/index.ts b/code/lib/cli/src/automigrate/fixes/index.ts
index 68b642586f2b..91ba9a27927f 100644
--- a/code/lib/cli/src/automigrate/fixes/index.ts
+++ b/code/lib/cli/src/automigrate/fixes/index.ts
@@ -2,7 +2,6 @@ import type { Fix } from '../types';
import { cra5 } from './cra5';
import { webpack5 } from './webpack5';
-import { vite4 } from './vite4';
import { vue3 } from './vue3';
import { mdxgfm } from './mdx-gfm';
import { removeLegacyMDX1 } from './remove-legacymdx1';
@@ -13,18 +12,19 @@ import { sbScripts } from './sb-scripts';
import { sbBinary } from './sb-binary';
import { newFrameworks } from './new-frameworks';
import { removedGlobalClientAPIs } from './remove-global-client-apis';
-import { mdx1to2 } from './mdx-1-to-2';
import { autodocsTrue } from './autodocs-true';
import { angularBuilders } from './angular-builders';
-import { incompatibleAddons } from './incompatible-addons';
import { angularBuildersMultiproject } from './angular-builders-multiproject';
import { wrapRequire } from './wrap-require';
import { reactDocgen } from './react-docgen';
+import { mdxToCSF } from './mdx-to-csf';
import { removeReactDependency } from './prompt-remove-react';
import { storyshotsMigration } from './storyshots-migration';
import { removeArgtypesRegex } from './remove-argtypes-regex';
import { webpack5CompilerSetup } from './webpack5-compiler-setup';
import { removeJestTestingLibrary } from './remove-jest-testing-library';
+import { mdx1to3 } from './mdx-1-to-3';
+import { addonPostCSS } from './addon-postcss';
export * from '../types';
@@ -33,18 +33,17 @@ export const allFixes: Fix[] = [
cra5,
webpack5,
vue3,
- vite4,
+ addonPostCSS,
viteConfigFile,
eslintPlugin,
builderVite,
sbBinary,
sbScripts,
- incompatibleAddons,
- removeArgtypesRegex,
removeJestTestingLibrary,
+ removeArgtypesRegex,
removedGlobalClientAPIs,
- mdx1to2,
mdxgfm,
+ mdxToCSF,
autodocsTrue,
angularBuildersMultiproject,
angularBuilders,
@@ -54,6 +53,7 @@ export const allFixes: Fix[] = [
removeReactDependency,
removeLegacyMDX1,
webpack5CompilerSetup,
+ mdx1to3,
];
export const initFixes: Fix[] = [eslintPlugin];
diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts
similarity index 96%
rename from code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts
rename to code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts
index c471d478fdc6..320e4447de7d 100644
--- a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.test.ts
@@ -1,7 +1,7 @@
import { it, expect } from 'vitest';
import { dedent } from 'ts-dedent';
-import { fixMdxStyleTags, fixMdxComments } from './mdx-1-to-2';
+import { fixMdxStyleTags, fixMdxComments } from './mdx-1-to-3';
it('fixMdxStyleTags fixes badly-formatted style blocks', () => {
expect(
diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts
similarity index 85%
rename from code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts
rename to code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts
index 108dde3c5aa3..3d81d0934447 100644
--- a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts
+++ b/code/lib/cli/src/automigrate/fixes/mdx-1-to-3.ts
@@ -31,7 +31,7 @@ export const fixMdxComments = (mdx: string) => {
const logger = console;
-interface Mdx1to2Options {
+interface Mdx1to3Options {
storiesMdxFiles: string[];
}
@@ -40,10 +40,12 @@ interface Mdx1to2Options {
*
* If so:
* - Assume they might be MDX1
- * - Offer to help migrate to MDX2
+ * - Offer to help migrate to MDX3
*/
-export const mdx1to2: Fix = {
- id: 'mdx1to2',
+export const mdx1to3: Fix = {
+ id: 'mdx1to3',
+
+ versionRange: ['<7.0.0', '>=8.0.0-alpha.0'],
async check() {
const storiesMdxFiles = await globby('./!(node_modules)**/*.(story|stories).mdx');
@@ -54,8 +56,8 @@ export const mdx1to2: Fix = {
return dedent`
We've found ${chalk.yellow(storiesMdxFiles.length)} '.stories.mdx' files in your project.
- Storybook has upgraded to MDX2 (https://mdxjs.com/blog/v2/), which contains breaking changes from MDX1.
- We can try to automatically upgrade your MDX files to MDX2 format using some common patterns.
+ Storybook has upgraded to MDX3 (https://mdxjs.com/blog/v3/). MDX3 itself doesn't contain disruptive breaking changes, whereas the transition from MDX1 to MDX2 was a significant change.
+ We can try to automatically upgrade your MDX files to MDX3 format using some common patterns.
After this install completes, and before you start Storybook, we strongly recommend reading the MDX2 section
of the 7.0 migration guide. It contains useful tools for detecting and fixing any remaining issues.
diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts
index 7fc07b63349e..98bcaa736b87 100644
--- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts
+++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts
@@ -1,5 +1,4 @@
import { dedent } from 'ts-dedent';
-import semver from 'semver';
import { join } from 'path';
import slash from 'slash';
import glob from 'globby';
@@ -19,11 +18,9 @@ interface Options {
export const mdxgfm: Fix = {
id: 'github-flavored-markdown-mdx',
- async check({ configDir, mainConfig, storybookVersion }) {
- if (!semver.gte(storybookVersion, '7.0.0')) {
- return null;
- }
+ versionRange: ['<7', '>=7'],
+ async check({ configDir, mainConfig }) {
const hasMDXFiles = await mainConfig?.stories?.reduce(async (acc, item) => {
const val = await acc;
@@ -83,11 +80,11 @@ export const mdxgfm: Fix = {
return dedent`
In MDX1 you had the option of using GitHub flavored markdown.
- Storybook 8.0 uses MDX3 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
- We recommend you follow the guide on the link above, however we can add a temporary storybook addon that helps make this migration easier.
+ We recommend that you follow the guide in the link above; however, we can add a temporary Storybook addon to help make this migration easier.
We'll install the addon and add it to your storybook config.
`;
},
diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts b/code/lib/cli/src/automigrate/fixes/mdx-to-csf.test.ts
similarity index 89%
rename from code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts
rename to code/lib/cli/src/automigrate/fixes/mdx-to-csf.test.ts
index 27154a2a63a2..226ac6976363 100644
--- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/mdx-to-csf.test.ts
@@ -4,8 +4,8 @@ import type { StorybookConfigRaw } from '@storybook/types';
import type { PackageJson } from '@storybook/core-common';
import { ansiRegex } from '../helpers/cleanLog';
import { makePackageManager } from '../helpers/testing-helpers';
-import type { BareMdxStoriesGlobRunOptions } from './bare-mdx-stories-glob';
-import { bareMdxStoriesGlob } from './bare-mdx-stories-glob';
+import type { BareMdxStoriesGlobRunOptions } from './mdx-to-csf';
+import { mdxToCSF } from './mdx-to-csf';
const checkBareMdxStoriesGlob = async ({
packageJson,
@@ -16,7 +16,7 @@ const checkBareMdxStoriesGlob = async ({
main?: Partial & Record;
storybookVersion?: string;
}) => {
- return bareMdxStoriesGlob.check({
+ return mdxToCSF.check({
mainConfig: mainConfig as StorybookConfigRaw,
packageManager: makePackageManager(packageJson),
storybookVersion,
@@ -29,16 +29,6 @@ describe('bare-mdx fix', () => {
});
describe('should no-op', () => {
- it('in SB < v7.0.0', async () => {
- const packageJson = {
- dependencies: { '@storybook/react': '^6.2.0' },
- };
- const main = { stories: ['../**/*.stories.mdx'] };
- await expect(
- checkBareMdxStoriesGlob({ packageJson, main, storybookVersion: '6.5.0' })
- ).resolves.toBeFalsy();
- });
-
describe('in SB >= v7.0.0', () => {
it('without main', async () => {
const packageJson = {
@@ -141,7 +131,7 @@ describe('bare-mdx fix', () => {
);
it('prompts', () => {
- const result = bareMdxStoriesGlob.prompt({
+ const result = mdxToCSF.prompt({
existingStoriesEntries: [
'../src/**/*.stories.@(js|jsx|mdx|ts|tsx)',
{ directory: '../src/**', files: '*.stories.mdx' },
@@ -161,7 +151,9 @@ describe('bare-mdx fix', () => {
"files": "*.stories.mdx"
}
- In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx.
+ In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. Since Storybook 8, we have removed the support of story definition in MDX files entirely. Therefore '.stories.mdx' files aren't supported anymore.
+
+ Now, since Storybook 8.0, we have removed support for .stories.mdx files.
We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
That would result in the following 'stories' config:
@@ -172,7 +164,9 @@ describe('bare-mdx fix', () => {
"files": "*.mdx"
}
- To learn more about this change, see: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files"
+ Additionally, we will run the 'mdx-to-csf' codemod for you, which tries to transform '*.stories.mdx' files to '*.stories.js' and '*.mdx' files.
+
+ To learn more about this change, see: https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf"
`);
});
});
diff --git a/code/lib/cli/src/automigrate/fixes/mdx-to-csf.ts b/code/lib/cli/src/automigrate/fixes/mdx-to-csf.ts
new file mode 100644
index 000000000000..72dc4ddbde00
--- /dev/null
+++ b/code/lib/cli/src/automigrate/fixes/mdx-to-csf.ts
@@ -0,0 +1,164 @@
+import chalk from 'chalk';
+import dedent from 'ts-dedent';
+import type { StoriesEntry } from '@storybook/types';
+import { updateMainConfig } from '../helpers/mainConfigFile';
+import type { Fix } from '../types';
+import { runCodemod } from '@storybook/codemod';
+import { prompt } from 'prompts';
+import { glob } from 'glob';
+
+const logger = console;
+
+export interface BareMdxStoriesGlobRunOptions {
+ existingStoriesEntries: StoriesEntry[];
+ nextStoriesEntries: StoriesEntry[];
+ files: string[];
+}
+
+const getNextGlob = (globString: string) => {
+ // '../src/**/*.stories.@(mdx|js|jsx|ts|tsx)' -> '../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'
+ const extGlobsRegex = new RegExp(/(.*\.)(stories\.@.*)(\|mdx|mdx\|)(.*)$/i);
+ if (globString.match(extGlobsRegex)) {
+ return globString.replace(extGlobsRegex, '$1@(mdx|$2$4)');
+ }
+
+ // '../src/**/*.stories.*' -> '../src/**/*.@(mdx|stories.*)'
+ const allStoriesExtensionsRegex = new RegExp(/(.*\.)(stories\.\*)$/i);
+ if (globString.match(allStoriesExtensionsRegex)) {
+ return globString.replace(allStoriesExtensionsRegex, '$1@(mdx|$2)');
+ }
+
+ // '../src/**/*.stories.mdx' -> '../src/**/*.mdx'
+ return globString.replaceAll('.stories.mdx', '.mdx');
+};
+
+export const mdxToCSF: Fix = {
+ id: 'mdx-to-csf',
+ versionRange: ['<7', '>=7'],
+ async check({ mainConfig }) {
+ const existingStoriesEntries = mainConfig.stories as StoriesEntry[];
+
+ if (!existingStoriesEntries) {
+ throw new Error(dedent`
+ ❌ Unable to determine Storybook stories globs in ${chalk.blue(
+ mainConfig
+ )}, skipping ${chalk.cyan(this.id)} fix.
+
+ In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx.
+
+ Now, since Storybook 8.0, we have removed support for .stories.mdx files.
+
+ We were unable to automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
+ We suggest you make this change manually.
+ To learn more about this change, see: ${chalk.yellow(
+ 'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mdx-docs-files'
+ )}
+ `);
+ }
+
+ const files: string[] = [];
+
+ const nextStoriesEntries = await Promise.all(
+ existingStoriesEntries.map(async (entry) => {
+ const isSpecifier = typeof entry !== 'string';
+ const globString = isSpecifier ? entry.files : entry;
+
+ if (!globString) {
+ // storySpecifier without the 'files' property. Just add the existing to the next list
+ return entry;
+ }
+
+ files.push(...(await glob(globString)).filter((file) => file.endsWith('.stories.mdx')));
+
+ const nextGlob = getNextGlob(globString);
+ return isSpecifier ? { ...entry, files: nextGlob } : nextGlob;
+ })
+ );
+
+ const resultFromMainConfig = checkMainConfigStories(existingStoriesEntries, nextStoriesEntries);
+
+ if ((nextStoriesEntries && resultFromMainConfig) || files.length > 0) {
+ return { existingStoriesEntries, nextStoriesEntries, files };
+ }
+
+ // bails if there are no changes, no files to migrate, or if the nextStoriesEntries is empty
+ return null;
+ },
+
+ prompt({ existingStoriesEntries, nextStoriesEntries }) {
+ const prettyExistingStoriesEntries = existingStoriesEntries
+ .map((entry) => JSON.stringify(entry, null, 2))
+ .join('\n');
+ const prettyNextStoriesEntries = nextStoriesEntries
+ .map((entry) => JSON.stringify(entry, null, 2))
+ .join('\n');
+ return dedent`
+ We've detected your project has one or more globs in your 'stories' config that matches .stories.mdx files:
+ ${chalk.cyan(prettyExistingStoriesEntries)}
+
+ In Storybook 7, we have deprecated defining stories in MDX files, and consequently have changed the suffix to simply .mdx. Since Storybook 8, we have removed the support of story definition in MDX files entirely. Therefore '.stories.mdx' files aren't supported anymore.
+
+ Now, since Storybook 8.0, we have removed support for .stories.mdx files.
+
+ We can automatically migrate your 'stories' config to include any .mdx file instead of just .stories.mdx.
+ That would result in the following 'stories' config:
+ ${chalk.cyan(prettyNextStoriesEntries)}
+
+ Additionally, we will run the 'mdx-to-csf' codemod for you, which tries to transform '*.stories.mdx' files to '*.stories.js' and '*.mdx' files.
+
+ To learn more about this change, see: ${chalk.yellow(
+ 'https://storybook.js.org/docs/migration-guide#storiesmdx-to-mdxcsf'
+ )}
+ `;
+ },
+
+ async run({ dryRun, mainConfigPath, result: { nextStoriesEntries } }) {
+ logger.info(dedent`✅ Setting 'stories' config:
+ ${JSON.stringify(nextStoriesEntries, null, 2)}`);
+
+ if (!dryRun) {
+ const { glob: globString } = await prompt({
+ type: 'text',
+ name: 'glob',
+ message: 'Please enter the glob for your MDX stories',
+ initial: './src/**/*.stories.mdx',
+ });
+
+ if (globString) {
+ await runCodemod('mdx-to-csf', { glob: globString, dryRun, logger });
+ }
+
+ await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => {
+ main.setFieldValue(['stories'], nextStoriesEntries);
+ });
+
+ logger.info(dedent`
+ The migration successfully updated your 'stories' config to include any .mdx file instead of just .stories.mdx.
+
+ It also ran the 'mdx-to-csf' codemod to convert your MDX stories to CSF format.
+ This codemod is not perfect however, so you may need to manually fix any issues it couldn't handle.
+ `);
+ }
+ },
+};
+function checkMainConfigStories(
+ existingStoriesEntries: StoriesEntry[],
+ nextStoriesEntries: StoriesEntry[]
+) {
+ if (
+ existingStoriesEntries.length === nextStoriesEntries.length &&
+ existingStoriesEntries.every((entry, index) => {
+ const nextEntry = nextStoriesEntries[index];
+ if (typeof entry === 'string') {
+ return entry === nextEntry;
+ }
+ if (typeof nextEntry === 'string') {
+ return false;
+ }
+ return entry.files === nextEntry.files;
+ })
+ ) {
+ return null;
+ }
+ return true;
+}
diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts
index 4cc2669b91ae..96099855df73 100644
--- a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts
@@ -57,19 +57,6 @@ const getPackageManager = (packages: Record) => {
describe('new-frameworks fix', () => {
describe('should no-op', () => {
- it('in sb < 7', async () => {
- const packageManager = getPackageManager({
- '@storybook/vue': '6.2.0',
- });
-
- await expect(
- checkNewFrameworks({
- packageManager,
- storybookVersion: '6.2.0',
- })
- ).resolves.toBeFalsy();
- });
-
it('in sb 7 with correct structure already', async () => {
const packageManager = getPackageManager({
'@storybook/angular': '7.0.0',
diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts
index f335d654050b..97ace48c8ee9 100644
--- a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts
+++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts
@@ -58,18 +58,9 @@ interface NewFrameworkRunOptions {
export const newFrameworks: Fix = {
id: 'new-frameworks',
- async check({
- configDir,
- packageManager,
- storybookVersion,
- mainConfig,
- mainConfigPath,
- rendererPackage,
- }) {
- if (!semver.gte(storybookVersion, '7.0.0')) {
- return null;
- }
+ versionRange: ['<7', '>=7'],
+ async check({ configDir, packageManager, mainConfig, mainConfigPath, rendererPackage }) {
if (typeof configDir === 'undefined') {
return null;
}
@@ -219,7 +210,7 @@ export const newFrameworks: Fix = {
newFrameworkPackage
)}, but we detected that you are using Vite ${chalk.bold(
viteVersion
- )}, which is unsupported in ${chalk.bold(
+ )}, which is unsupported since ${chalk.bold(
'Storybook 7.0'
)}. Please upgrade Vite to ${chalk.bold('3.0.0 or higher')} and rerun this migration.
`);
@@ -406,7 +397,7 @@ export const newFrameworks: Fix = {
}
return dedent`
- We've detected your project is not fully setup with Storybook's 7 new framework format.
+ We've detected your project is not fully setup with the new framework format, which was introduced in Storybook 7.
Storybook 7 introduced the concept of frameworks, which abstracts configuration for renderers (e.g. React, Vue), builders (e.g. Webpack, Vite) and defaults to make integrations easier.
diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts
index 7d33c0fe90aa..baf9922b5f0f 100644
--- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts
@@ -9,15 +9,12 @@ const check = async ({
main: mainConfig,
storybookVersion = '8.0.0',
}: {
- packageManagerContent: Pick<
- Partial>>,
- 'dependencies' | 'devDependencies' | 'peerDependencies'
- >;
+ packageManagerContent: Partial>>;
main: Partial & Record;
storybookVersion?: string;
}) => {
const packageManager = {
- retrievePackageJson: async () => packageManagerContent,
+ getAllDependencies: async () => packageManagerContent,
} as JsPackageManager;
return removeReactDependency.check({
@@ -31,21 +28,6 @@ const check = async ({
vi.mock('glob', () => ({ glob: vi.fn(() => []) }));
describe('early exits', () => {
- it('cancel if storybookVersion < 8', async () => {
- await expect(
- check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
- main: {
- stories: [],
- framework: '@storybook/vue-vite',
- },
- storybookVersion: '7.0.0',
- })
- ).resolves.toBeFalsy();
- });
-
it('cancel if no react deps', async () => {
await expect(
check({
@@ -61,9 +43,7 @@ describe('early exits', () => {
it('cancel if react renderer', async () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: [],
framework: '@storybook/react-vite',
@@ -73,9 +53,7 @@ describe('early exits', () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: [],
framework: '@storybook/nextjs',
@@ -85,9 +63,7 @@ describe('early exits', () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: [],
framework: { name: '@storybook/react-webpack5' },
@@ -101,9 +77,7 @@ describe('prompts', () => {
it('simple', async () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: ['*.stories.ts'],
addons: [],
@@ -115,9 +89,7 @@ describe('prompts', () => {
it('detects addon docs', async () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: ['*.stories.ts'],
addons: ['@storybook/addon-docs'],
@@ -129,9 +101,7 @@ describe('prompts', () => {
it('detects addon essentials', async () => {
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: ['*.stories.ts'],
addons: ['@storybook/addon-docs', '@storybook/addon-essentials'],
@@ -145,9 +115,7 @@ describe('prompts', () => {
glob.mockImplementationOnce(() => ['*.stories.mdx']);
await expect(
check({
- packageManagerContent: {
- dependencies: { react: '16.0.0' },
- },
+ packageManagerContent: { react: '16.0.0' },
main: {
stories: ['*.stories.ts'],
addons: ['@storybook/addon-docs', '@storybook/addon-essentials'],
diff --git a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts
index a7586a03ff15..65463ec015ea 100644
--- a/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts
+++ b/code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts
@@ -1,31 +1,28 @@
import dedent from 'ts-dedent';
-import semver from 'semver';
import { getFrameworkPackageName } from '../helpers/mainConfigFile';
import type { Fix } from '../types';
+// This fix is only relevant for projects using Storybook 8.0.0-alpha.4 or later
+const minimumStorybookVersion = '8.0.0-alpha.4';
+
export const removeReactDependency: Fix<{}> = {
id: 'remove-react-dependency',
promptType: 'manual',
+ versionRange: [`^7 || <${minimumStorybookVersion}`, `>=${minimumStorybookVersion}`],
+
async check({ packageManager, mainConfig, storybookVersion }) {
// when the user is using the react renderer, we should not prompt them to remove react
const frameworkPackageName = getFrameworkPackageName(mainConfig);
+
if (frameworkPackageName?.includes('react') || frameworkPackageName?.includes('nextjs')) {
return null;
}
// if the user has no dependency on react, we can skip this fix
- const packageJson = await packageManager.retrievePackageJson();
- if (
- !packageJson?.dependencies?.['react'] &&
- !packageJson?.peerDependencies?.['react'] &&
- !packageJson?.devDependencies?.['react']
- ) {
- return null;
- }
+ const { react } = await packageManager.getAllDependencies();
- // do not prompt to remove react for older versions of storybook
- if (!semver.gte(storybookVersion, '8.0.0')) {
+ if (!react) {
return null;
}
@@ -36,7 +33,7 @@ export const removeReactDependency: Fix<{}> = {
We detected that your project has a dependency for "react" that it might not need.
Nothing breaks by having it, you can safely ignore this message, if you wish.
- Storybook asked you to add "react" as a direct dependency in the past.
+ Storybook asked you to add "react" as a direct dependency in the past when upgrading from Storybook 6 to 7.
However, since version 8.0, Storybook no longer requires you to provide "react" as a dependency.
Some community addons might still wrongfully list "react" and "react-dom" as required peer dependencies, but since Storybook 7.6 it should not be needed in the majority of cases.
diff --git a/code/lib/cli/src/automigrate/fixes/react-docgen.ts b/code/lib/cli/src/automigrate/fixes/react-docgen.ts
index e21c73db3577..ef89a24915ff 100644
--- a/code/lib/cli/src/automigrate/fixes/react-docgen.ts
+++ b/code/lib/cli/src/automigrate/fixes/react-docgen.ts
@@ -13,6 +13,8 @@ interface Options {
export const reactDocgen: Fix = {
id: 'react-docgen',
+ versionRange: ['<8.0.0-alpha.1', '>=8.0.0-alpha.1'],
+
async check({ mainConfig }) {
// @ts-expect-error assume react
const { reactDocgenTypescriptOptions } = mainConfig.typescript || {};
@@ -25,7 +27,7 @@ export const reactDocgen: Fix = {
You have "typescript.reactDocgenTypescriptOptions" configured in your main.js,
but "typescript.reactDocgen" is unset.
- In Storybook 8.0, we changed the default React docgen analysis from
+ Since Storybook 8.0, we changed the default React docgen analysis from
"react-docgen-typescript" to "react-docgen". We recommend "react-docgen"
for most projects, since it is dramatically faster. However, it doesn't
handle all TypeScript constructs, and may generate different results
diff --git a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts
index 6a517fc0be0c..78dd25049b27 100644
--- a/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts
+++ b/code/lib/cli/src/automigrate/fixes/remove-argtypes-regex.ts
@@ -9,6 +9,7 @@ import chalk from 'chalk';
export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPath: string }> = {
id: 'remove-argtypes-regex',
promptType: 'manual',
+ versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'],
async check({ previewConfigPath }) {
if (!previewConfigPath) return null;
@@ -33,49 +34,25 @@ export const removeArgtypesRegex: Fix<{ argTypesRegex: NodePath; previewConfigPa
return argTypesRegex ? { argTypesRegex, previewConfigPath } : null;
},
prompt({ argTypesRegex, previewConfigPath }) {
- const snippet = dedent`
- import { fn } from '@storybook/test';
- export default {
- args: { onClick: fn() }, // will log to the action panel when clicked
- };`;
-
- // @ts-expect-error File is not yet exposed, see https://github.com/babel/babel/issues/11350#issuecomment-644118606
- const file: BabelFile = new babel.File(
- { file: 'story.tsx' },
- { code: snippet, ast: babelParse(snippet) }
- );
-
- let formattedSnippet;
- file.path.traverse({
- Identifier: (path) => {
- if (path.node.name === 'fn') {
- formattedSnippet = path.buildCodeFrameError(``).message;
- }
- },
- });
-
return dedent`
${chalk.bold('Attention')}: We've detected that you're using argTypesRegex:
${argTypesRegex.buildCodeFrameError(`${previewConfigPath}`).message}
- In Storybook 8, we recommend removing this regex.
- Assign explicit spies with the ${chalk.cyan('fn')} function instead:
- ${formattedSnippet}
-
- The above pattern is needed when using spies in the play function, ${chalk.bold(
- 'even'
- )} if you keep using argTypesRegex.
- Implicit spies (based on a combination of argTypesRegex and docgen) is not supported in Storybook 8.
+ In Storybook you can write so-called play functions, which are used to render your stories interactively.
+ Mocking action args in play functions was done implicitly by analyzing the argTypesRegex.
+
+ Since Storybook 8, implicit action args mocking isn't supported anymore.
- Use the following command to check for spy usages in your play functions:
- ${chalk.cyan(
- 'npx storybook migrate find-implicit-spies --glob="**/*.stories.@(js|jsx|ts|tsx)"'
- )}
+ Use the following command to check for mocked action usages in your play functions:
+ ${chalk.cyan(
+ 'npx storybook migrate find-implicit-spies --glob="**/*.stories.@(js|jsx|ts|tsx)"'
+ )}
- Make sure to assign an explicit ${chalk.cyan('fn')} to your args for those usages.
-
- For more information please visit our migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function
+ And follow the documentation to migrate your play functions:
+ ${chalk.yellow(
+ 'https://storybook.js.org/docs/8.0/essentials/actions#via-storybooktest-fn-spy-function'
+ )}
`;
},
};
diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts
index 2f25e5277096..64de15f9a4b4 100644
--- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts
+++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts
@@ -21,6 +21,8 @@ export const removedGlobalClientAPIs: Fix = {
id: 'removedglobalclientapis',
promptType: 'manual',
+ versionRange: ['<7', '>=7'],
+
async check({ previewConfigPath }) {
if (previewConfigPath) {
const contents = await readFile(previewConfigPath, 'utf8');
diff --git a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts
index 60a2c2a97a35..468992c259e1 100644
--- a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts
@@ -33,7 +33,7 @@ it('should prompt to install the test package and run the codemod', async () =>
main: { addons: ['@storybook/essentials', '@storybook/addon-info'] },
});
- await expect(options).toMatchInlineSnapshot(`
+ expect(options).toMatchInlineSnapshot(`
{
"incompatiblePackages": [
"@storybook/jest",
@@ -51,14 +51,13 @@ it('should prompt to install the test package and run the codemod', async () =>
});
expect(await removeJestTestingLibrary.prompt(options!)).toMatchInlineSnapshot(`
- Attention: We've detected that you're using the following packages which are known to be incompatible with Storybook 8:
+ Attention: We've detected that you're using the following packages which are known to be incompatible since Storybook 8:
- @storybook/jest
- @storybook/testing-library
- Install the replacement for those packages: @storybook/test
+ We will uninstall them for you and install @storybook/test instead.
- And run the following codemod:
- npx storybook migrate migrate-to-test-package --glob="**/*.stories.@(js|jsx|ts|tsx)"
+ Also, we can help you migrate your stories to use the new package.
`);
});
diff --git a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts
index ba238dd2740a..3eed2c36ab70 100644
--- a/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts
+++ b/code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.ts
@@ -1,11 +1,17 @@
import chalk from 'chalk';
import dedent from 'ts-dedent';
import type { Fix } from '../types';
+import { getStorybookVersionSpecifier } from '../../helpers';
+import { runCodemod } from '@storybook/codemod';
+import prompts from 'prompts';
+
+const logger = console;
export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> = {
id: 'remove-jest-testing-library',
- promptType: 'manual',
- async check({ mainConfig, packageManager }) {
+ versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'],
+ promptType: 'auto',
+ async check({ packageManager }) {
const deps = await packageManager.getAllDependencies();
const incompatiblePackages = Object.keys(deps).filter(
@@ -17,16 +23,40 @@ export const removeJestTestingLibrary: Fix<{ incompatiblePackages: string[] }> =
return dedent`
${chalk.bold(
'Attention'
- )}: We've detected that you're using the following packages which are known to be incompatible with Storybook 8:
+ )}: We've detected that you're using the following packages which are known to be incompatible since Storybook 8:
${incompatiblePackages.map((name) => `- ${chalk.cyan(`${name}`)}`).join('\n')}
- Install the replacement for those packages: ${chalk.cyan('@storybook/test')}
-
- And run the following codemod:
- ${chalk.cyan(
- 'npx storybook migrate migrate-to-test-package --glob="**/*.stories.@(js|jsx|ts|tsx)"'
- )}
+ We will uninstall them for you and install ${chalk.cyan('@storybook/test')} instead.
+
+ Also, we can help you migrate your stories to use the new package.
`;
},
+ async run({ packageManager, dryRun }) {
+ if (!dryRun) {
+ const packageJson = await packageManager.retrievePackageJson();
+
+ await packageManager.removeDependencies({ skipInstall: true, packageJson }, [
+ '@storybook/jest',
+ '@storybook/testing-library',
+ ]);
+
+ const versionToInstall = getStorybookVersionSpecifier(packageJson);
+
+ await packageManager.addDependencies({ packageJson }, [
+ `@storybook/test@${versionToInstall}`,
+ ]);
+
+ const { glob: globString } = await prompts({
+ type: 'text',
+ name: 'glob',
+ message: 'Please enter the glob for your stories to migrate to @storybook/test',
+ initial: './src/**/*.stories.*',
+ });
+
+ if (globString) {
+ await runCodemod('migrate-to-test-package', { glob: globString, dryRun, logger });
+ }
+ }
+ },
};
diff --git a/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts b/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts
index 57f90e8403af..e934c09b7f9c 100644
--- a/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts
+++ b/code/lib/cli/src/automigrate/fixes/remove-legacymdx1.ts
@@ -17,13 +17,14 @@ interface RemoveLegacyMDX1Options {
* If so, prompt them to upgrade to delete it.
*/
export const removeLegacyMDX1: Fix = {
- id: 'builder-vite',
+ id: 'remove-legacy-mdx1',
+ versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'],
async check({ mainConfig }) {
- if (mainConfig.features) {
+ if (mainConfig.features && Object.hasOwn(mainConfig.features, 'legacyMdx1')) {
//
return {
- hasFeature: !!Object.hasOwn(mainConfig.features, 'legacyMdx1'),
+ hasFeature: true,
};
}
diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts
index b1f06e162c30..bb5250179a89 100644
--- a/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts
+++ b/code/lib/cli/src/automigrate/fixes/sb-binary.test.ts
@@ -17,31 +17,6 @@ const checkStorybookBinary = async ({
};
describe('storybook-binary fix', () => {
- describe('sb < 7.0', () => {
- describe('does nothing', () => {
- const packageManager = {
- getPackageVersion: (packageName) => {
- switch (packageName) {
- case '@storybook/react':
- return Promise.resolve('6.2.0');
- default:
- return null;
- }
- },
- retrievePackageJson: () => Promise.resolve({}),
- } as Partial;
-
- it('should no-op', async () => {
- await expect(
- checkStorybookBinary({
- packageManager,
- storybookVersion: '6.2.0',
- })
- ).resolves.toBeFalsy();
- });
- });
- });
-
describe('sb >= 7.0', () => {
it('should no-op in NX projects', async () => {
const packageManager = {
diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.ts
index 4c84cf987d04..53b583b8a58f 100644
--- a/code/lib/cli/src/automigrate/fixes/sb-binary.ts
+++ b/code/lib/cli/src/automigrate/fixes/sb-binary.ts
@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { dedent } from 'ts-dedent';
-import semver from 'semver';
import type { Fix } from '../types';
import { getStorybookVersionSpecifier } from '../../helpers';
import type { PackageJsonWithDepsAndDevDeps } from '@storybook/core-common';
@@ -24,6 +23,8 @@ const logger = console;
export const sbBinary: Fix = {
id: 'storybook-binary',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, storybookVersion }) {
const packageJson = await packageManager.retrievePackageJson();
@@ -32,7 +33,7 @@ export const sbBinary: Fix = {
const storybookBinaryVersion = await packageManager.getPackageVersion('storybook');
// Nx provides their own binary, so we don't need to do anything
- if (nrwlStorybookVersion || semver.lt(storybookVersion, '7.0.0')) {
+ if (nrwlStorybookVersion) {
return null;
}
diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts
index 7ff111920e27..aad1796b9a38 100644
--- a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts
+++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts
@@ -78,6 +78,8 @@ export const getStorybookScripts = (allScripts: NonNullable = {
id: 'sb-scripts',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, storybookVersion }) {
const packageJson = await packageManager.retrievePackageJson();
const { scripts = {} } = packageJson;
diff --git a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts
index 6d132037c3e0..deba1b9df901 100644
--- a/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts
+++ b/code/lib/cli/src/automigrate/fixes/storyshots-migration.ts
@@ -3,7 +3,8 @@ import dedent from 'ts-dedent';
import type { Fix } from '../types';
export const storyshotsMigration: Fix = {
- id: 'storyshots-migration',
+ id: 'storyshots',
+ versionRange: ['<8.0.0-alpha.0', '>=8.0.0-alpha.0'],
promptType: 'manual',
async check({ mainConfig, packageManager }) {
diff --git a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
index 899c3807ed3a..6325b71ad062 100644
--- a/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
+++ b/code/lib/cli/src/automigrate/fixes/vite-config-file.ts
@@ -13,6 +13,8 @@ interface ViteConfigFileRunOptions {
export const viteConfigFile = {
id: 'viteConfigFile',
+ versionRange: ['<8.0.0-beta.3', '>=8.0.0-beta.3'],
+
async check({ mainConfig, packageManager }) {
let isViteConfigFileFound = !!(await findUp([
'vite.config.js',
@@ -94,7 +96,7 @@ export const viteConfigFile = {
prompt({ existed, plugins }) {
if (existed) {
return dedent`
- Storybook 8.0.0 no longer ships with a Vite config build-in.
+ Since version 8.0.0, Storybook no longer ships with a Vite config build-in.
We've detected you do have a Vite config, but you may be missing the following plugins in it.
${plugins.map((plugin) => ` - ${plugin}`).join('\n')}
@@ -108,7 +110,7 @@ export const viteConfigFile = {
`;
}
return dedent`
- Storybook 8.0.0 no longer ships with a Vite config build-in.
+ Since version 8.0.0, Storybook no longer ships with a Vite config build-in.
Please add a vite.config.js file to your project root.
You can find more information on how to do this here:
diff --git a/code/lib/cli/src/automigrate/fixes/vite4.ts b/code/lib/cli/src/automigrate/fixes/vite4.ts
deleted file mode 100644
index f85cc4d57cef..000000000000
--- a/code/lib/cli/src/automigrate/fixes/vite4.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import chalk from 'chalk';
-import { dedent } from 'ts-dedent';
-import semver from 'semver';
-import type { Fix } from '../types';
-
-const logger = console;
-
-interface Vite4RunOptions {
- viteVersion: string | null;
-}
-
-export const vite4 = {
- id: 'vite4',
-
- async check({ packageManager }) {
- const viteVersion = await packageManager.getPackageVersion('vite');
-
- if (!viteVersion || semver.gt(viteVersion, '4.0.0')) {
- return null;
- }
-
- return { viteVersion };
- },
-
- prompt({ viteVersion: viteVersion }) {
- const viteFormatted = chalk.cyan(`${viteVersion}`);
-
- return dedent`
- We've detected your version of Vite is outdated (${viteFormatted}).
-
- Storybook 8.0.0 will require Vite 4.0.0 or later.
- Do you want us to upgrade Vite for you?
- `;
- },
-
- async run({ packageManager, dryRun }) {
- const deps = [`vite`];
- logger.info(`✅ Adding dependencies: ${deps}`);
- if (!dryRun) {
- await packageManager.addDependencies({ installAsDevDependencies: true }, deps);
- }
- },
-} satisfies Fix;
diff --git a/code/lib/cli/src/automigrate/fixes/vue3.ts b/code/lib/cli/src/automigrate/fixes/vue3.ts
index 84bde42d6196..f641b30373de 100644
--- a/code/lib/cli/src/automigrate/fixes/vue3.ts
+++ b/code/lib/cli/src/automigrate/fixes/vue3.ts
@@ -19,6 +19,8 @@ interface Vue3RunOptions {
export const vue3: Fix = {
id: 'vue3',
+ versionRange: ['<7', '>=7'],
+
async check({ packageManager, mainConfig, storybookVersion }) {
const vueVersion = await packageManager.getPackageVersion('vue');
diff --git a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts
index a22f8a55fdd0..d899ee5df3dd 100644
--- a/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts
+++ b/code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.ts
@@ -28,7 +28,7 @@ type Options = {
export const webpack5CompilerSetup = {
id: 'webpack5-compiler-setup',
-
+ versionRange: ['<8.0.0-alpha.9', '>=8.0.0-alpha.9'],
promptType(result) {
return result.isNextJs && !result.shouldRemoveSWCFlag ? 'notification' : 'auto';
},
diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.ts b/code/lib/cli/src/automigrate/fixes/webpack5.ts
index 8b54ee2d5a8c..a2072be41eb9 100644
--- a/code/lib/cli/src/automigrate/fixes/webpack5.ts
+++ b/code/lib/cli/src/automigrate/fixes/webpack5.ts
@@ -25,7 +25,9 @@ interface Webpack5RunOptions {
export const webpack5 = {
id: 'webpack5',
- async check({ configDir, packageManager, mainConfig, storybookVersion }) {
+ versionRange: ['<7', '>=7'],
+
+ async check({ packageManager, mainConfig, storybookVersion }) {
const webpackVersion = await packageManager.getPackageVersion('webpack');
if (
diff --git a/code/lib/cli/src/automigrate/fixes/wrap-require.ts b/code/lib/cli/src/automigrate/fixes/wrap-require.ts
index 3651eb2c57a8..e241f05858ab 100644
--- a/code/lib/cli/src/automigrate/fixes/wrap-require.ts
+++ b/code/lib/cli/src/automigrate/fixes/wrap-require.ts
@@ -22,6 +22,8 @@ interface WrapRequireRunOptions {
export const wrapRequire: Fix = {
id: 'wrap-require',
+ versionRange: ['<7.2.0-rc.0', '>=7.2.0-rc.0'],
+
async check({ packageManager, storybookVersion, mainConfigPath }) {
const isStorybookInMonorepo = await packageManager.isStorybookInMonorepo();
const isPnp = await detectPnp();
diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
index 85de99111c87..f43e84370852 100644
--- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
+++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.test.ts
@@ -108,7 +108,7 @@ describe('getMigrationSummary', () => {
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
- Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/migration-guides/7.0
+ Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/docs/8.0/migration-guide
And reach out on Discord if you need help: https://discord.gg/storybook"
`);
});
@@ -128,7 +128,7 @@ describe('getMigrationSummary', () => {
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
- Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/migration-guides/7.0
+ Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/docs/8.0/migration-guide
And reach out on Discord if you need help: https://discord.gg/storybook
─────────────────────────────────────────────────
@@ -152,12 +152,12 @@ describe('getMigrationSummary', () => {
- You can find more information for a given dependency by running yarn why
+ Please try de-duplicating these dependencies by running yarn dedupe
- Please try de-duplicating these dependencies by running yarn dedupe"
+ You can find more information for a given dependency by running yarn why "
`);
});
@@ -176,7 +176,7 @@ describe('getMigrationSummary', () => {
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
- Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/migration-guides/7.0
+ Please check the changelog and migration guide for manual migrations and more information: https://storybook.js.org/docs/8.0/migration-guide
And reach out on Discord if you need help: https://discord.gg/storybook"
`);
});
diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
index 1dcd0a0658c7..aa0503865749 100644
--- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
+++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts
@@ -70,7 +70,7 @@ export function getMigrationSummary({
The automigrations try to migrate common patterns in your project, but might not contain everything needed to migrate to the latest version of Storybook.
Please check the changelog and migration guide for manual migrations and more information: ${chalk.yellow(
- 'https://storybook.js.org/migration-guides/7.0'
+ 'https://storybook.js.org/docs/8.0/migration-guide'
)}
And reach out on Discord if you need help: ${chalk.yellow('https://discord.gg/storybook')}
`);
diff --git a/code/lib/cli/src/automigrate/index.test.ts b/code/lib/cli/src/automigrate/index.test.ts
new file mode 100644
index 000000000000..78a098e9c8d7
--- /dev/null
+++ b/code/lib/cli/src/automigrate/index.test.ts
@@ -0,0 +1,157 @@
+import { vi, it, expect, describe, beforeEach } from 'vitest';
+import { runFixes } from './index';
+import type { Fix } from './types';
+import type { JsPackageManager, PackageJsonWithDepsAndDevDeps } from '@storybook/core-common';
+import { afterEach } from 'node:test';
+
+const check1 = vi.fn();
+const run1 = vi.fn();
+const retrievePackageJson = vi.fn();
+const getPackageVersion = vi.fn();
+const prompt1Message = 'prompt1Message';
+
+vi.spyOn(console, 'error').mockImplementation(console.log);
+
+const fixes: Fix[] = [
+ {
+ id: 'fix-1',
+
+ versionRange: ['<7', '>=7'],
+
+ async check(config) {
+ return check1(config);
+ },
+
+ prompt() {
+ return prompt1Message;
+ },
+
+ async run(result) {
+ run1(result);
+ },
+ },
+];
+
+const coreCommonMock = vi.hoisted(() => {
+ return {
+ loadMainConfig: vi.fn(),
+ };
+});
+
+vi.mock('@storybook/core-common', async (importOriginal) => ({
+ ...(await importOriginal()),
+ loadMainConfig: coreCommonMock.loadMainConfig,
+}));
+
+const promptMocks = vi.hoisted(() => {
+ return {
+ default: vi.fn(),
+ };
+});
+
+vi.mock('prompts', () => {
+ return {
+ default: promptMocks.default,
+ };
+});
+
+class PackageManager implements Partial {
+ public async retrievePackageJson(): Promise {
+ return retrievePackageJson();
+ }
+
+ getPackageVersion(packageName: string, basePath?: string | undefined): Promise {
+ return getPackageVersion(packageName, basePath);
+ }
+}
+
+const packageManager = new PackageManager() as any as JsPackageManager;
+
+const dryRun = false;
+const yes = true;
+const rendererPackage = 'storybook';
+const skipInstall = false;
+const configDir = '/path/to/config';
+const mainConfigPath = '/path/to/mainConfig';
+const beforeVersion = '6.5.15';
+const isUpgrade = true;
+
+const runFixWrapper = async ({
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ beforeVersion,
+ storybookVersion,
+}: {
+ beforeVersion: string;
+ storybookVersion: string;
+}) => {
+ return runFixes({
+ fixes,
+ dryRun,
+ yes,
+ rendererPackage,
+ skipInstall,
+ configDir,
+ packageManager: packageManager,
+ mainConfigPath,
+ storybookVersion,
+ beforeVersion,
+ isUpgrade,
+ });
+};
+
+describe('runFixes', () => {
+ beforeEach(() => {
+ retrievePackageJson.mockResolvedValue({
+ depedencies: [],
+ devDepedencies: [],
+ });
+ getPackageVersion.mockImplementation((packageName) => {
+ return beforeVersion;
+ });
+ check1.mockResolvedValue({ some: 'result' });
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('should be unnecessary to run fix-1 from SB 6.5.15 to 6.5.16', async () => {
+ const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '6.5.16' });
+
+ // Assertions
+ expect(fixResults).toEqual({
+ 'fix-1': 'unnecessary',
+ });
+ expect(run1).not.toHaveBeenCalled();
+ });
+
+ it('should be necessary to run fix-1 from SB 6.5.15 to 7.0.0', async () => {
+ promptMocks.default.mockResolvedValue({ shouldContinue: true });
+
+ const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '7.0.0' });
+
+ expect(fixResults).toEqual({
+ 'fix-1': 'succeeded',
+ });
+ expect(run1).toHaveBeenCalledWith({
+ dryRun,
+ mainConfigPath,
+ packageManager,
+ result: {
+ some: 'result',
+ },
+ skipInstall,
+ });
+ });
+
+ it('should fail if an error is thrown', async () => {
+ check1.mockRejectedValue(new Error('check1 error'));
+
+ const { fixResults } = await runFixWrapper({ beforeVersion, storybookVersion: '7.0.0' });
+
+ expect(fixResults).toEqual({
+ 'fix-1': 'check_failed',
+ });
+ expect(run1).not.toHaveBeenCalled();
+ });
+});
diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts
index 1e78d57863ce..b0864a7071c6 100644
--- a/code/lib/cli/src/automigrate/index.ts
+++ b/code/lib/cli/src/automigrate/index.ts
@@ -5,6 +5,7 @@ import { createWriteStream, move, remove } from 'fs-extra';
import tempy from 'tempy';
import { join } from 'path';
import invariant from 'tiny-invariant';
+import semver from 'semver';
import {
JsPackageManagerFactory,
@@ -26,6 +27,7 @@ import { FixStatus, allFixes } from './fixes';
import { cleanLog } from './helpers/cleanLog';
import { getMigrationSummary } from './helpers/getMigrationSummary';
import { getStorybookData } from './helpers/mainConfigFile';
+import { doctor } from '../doctor';
const logger = console;
const LOG_FILE_NAME = 'migration-storybook.log';
@@ -82,7 +84,17 @@ export const doAutomigrate = async (options: AutofixOptionsFromCLI) => {
throw new Error('Could not determine main config path');
}
- return automigrate({ ...options, packageManager, storybookVersion, mainConfigPath, configDir });
+ await automigrate({
+ ...options,
+ packageManager,
+ storybookVersion,
+ beforeVersion: storybookVersion,
+ mainConfigPath,
+ configDir,
+ isUpgrade: false,
+ });
+
+ await doctor({ configDir, packageManager: options.packageManager });
};
export const automigrate = async ({
@@ -95,6 +107,7 @@ export const automigrate = async ({
configDir,
mainConfigPath,
storybookVersion,
+ beforeVersion,
renderer: rendererPackage,
skipInstall,
hideMigrationSummary = false,
@@ -128,6 +141,7 @@ export const automigrate = async ({
configDir,
mainConfigPath,
storybookVersion,
+ beforeVersion,
dryRun,
yes,
});
@@ -171,6 +185,8 @@ export async function runFixes({
packageManager,
mainConfigPath,
storybookVersion,
+ beforeVersion,
+ isUpgrade,
}: {
fixes: Fix[];
yes?: boolean;
@@ -181,6 +197,8 @@ export async function runFixes({
packageManager: JsPackageManager;
mainConfigPath: string;
storybookVersion: string;
+ beforeVersion: string;
+ isUpgrade?: boolean;
}): Promise<{
preCheckFailure?: PreCheckFailure;
fixResults: Record;
@@ -199,15 +217,22 @@ export async function runFixes({
packageManager,
});
- result = await f.check({
- packageManager,
- configDir,
- rendererPackage,
- mainConfig,
- storybookVersion,
- previewConfigPath,
- mainConfigPath,
- });
+ if (
+ (isUpgrade &&
+ semver.satisfies(beforeVersion, f.versionRange[0], { includePrerelease: true }) &&
+ semver.satisfies(storybookVersion, f.versionRange[1], { includePrerelease: true })) ||
+ !isUpgrade
+ ) {
+ result = await f.check({
+ packageManager,
+ configDir,
+ rendererPackage,
+ mainConfig,
+ storybookVersion,
+ previewConfigPath,
+ mainConfigPath,
+ });
+ }
} catch (error) {
logger.info(`⚠️ failed to check fix ${chalk.bold(f.id)}`);
if (error instanceof Error) {
diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts
index 510cdcda24c1..36b4bac18c47 100644
--- a/code/lib/cli/src/automigrate/types.ts
+++ b/code/lib/cli/src/automigrate/types.ts
@@ -23,13 +23,19 @@ export interface RunOptions {
* promptType defines how the user will be prompted to apply an automigration fix
* - auto: the fix will be applied automatically
* - manual: the user will be prompted to apply the fix
- * - notification: the user will be notified about the some changes. A fix isn't required
+ * - notification: the user will be notified about some changes. A fix isn't required, though
*/
export type Prompt = 'auto' | 'manual' | 'notification';
export interface Fix {
id: string;
promptType?: Prompt | ((result: ResultType) => Promise | Prompt);
+ /**
+ * The from/to version range of Storybook that this fix applies to. The strings are semver ranges.
+ * The versionRange will only be checked if the automigration is part of an upgrade.
+ * If the automigration is not part of an upgrade but rather called via `automigrate` CLI, the check function should handle the version check.
+ */
+ versionRange: [from: string, to: string];
check: (options: CheckOptions) => Promise;
prompt: (result: ResultType) => string;
run?: (options: RunOptions) => Promise;
@@ -46,7 +52,15 @@ export enum PreCheckFailure {
export interface AutofixOptions extends Omit {
packageManager: JsPackageManager;
mainConfigPath: string;
+ /**
+ * The version of Storybook before the migration.
+ */
+ beforeVersion: string;
storybookVersion: string;
+ /**
+ * Whether the migration is part of an upgrade.
+ */
+ isUpgrade: boolean;
}
export interface AutofixOptionsFromCLI {
fixId?: FixId;
diff --git a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts
index d76c01d9ee97..272313300728 100644
--- a/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts
+++ b/code/lib/cli/src/doctor/getDuplicatedDepsWarnings.ts
@@ -101,15 +101,15 @@ export function getDuplicatedDepsWarnings(
messages.push(
'\n',
- `You can find more information for a given dependency by running ${chalk.cyan(
- `${installationMetadata.infoCommand} `
+ `Please try de-duplicating these dependencies by running ${chalk.cyan(
+ `${installationMetadata.dedupeCommand}`
)}`
);
messages.push(
'\n',
- `Please try de-duplicating these dependencies by running ${chalk.cyan(
- `${installationMetadata.dedupeCommand}`
+ `You can find more information for a given dependency by running ${chalk.cyan(
+ `${installationMetadata.infoCommand} `
)}`
);
diff --git a/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.test.ts b/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.test.ts
new file mode 100644
index 000000000000..5d5a2d8a6e87
--- /dev/null
+++ b/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.test.ts
@@ -0,0 +1,158 @@
+import { describe, it, expect, vi } from 'vitest';
+import type { AnalysedPackage } from './getIncompatibleStorybookPackages';
+import {
+ getIncompatibleStorybookPackages,
+ getIncompatiblePackagesSummary,
+ checkPackageCompatibility,
+} from './getIncompatibleStorybookPackages';
+import type { JsPackageManager } from '@storybook/core-common';
+
+vi.mock('chalk', () => {
+ return {
+ default: {
+ yellow: (str: string) => str,
+ cyan: (str: string) => str,
+ bold: (str: string) => str,
+ },
+ };
+});
+
+vi.mock('./utils', () => ({
+ getPackageJsonPath: vi.fn(() => Promise.resolve('package.json')),
+ getPackageJsonOfDependency: vi.fn(() => Promise.resolve({})),
+ PackageJsonNotFoundError: Error,
+}));
+
+const packageManagerMock = {
+ getAllDependencies: () =>
+ Promise.resolve({
+ '@storybook/addon-essentials': '7.0.0',
+ }),
+ latestVersion: vi.fn(() => Promise.resolve('8.0.0')),
+ getPackageJSON: vi.fn(() => Promise.resolve('8.0.0')),
+} as any as JsPackageManager;
+
+describe('checkPackageCompatibility', () => {
+ it('returns that a package is incompatible', async () => {
+ const packageName = 'my-storybook-package';
+ vi.mocked(packageManagerMock.getPackageJSON).mockResolvedValueOnce({
+ name: packageName,
+ version: '1.0.0',
+ dependencies: {
+ '@storybook/core-common': '7.0.0',
+ },
+ });
+ const result = await checkPackageCompatibility(packageName, {
+ currentStorybookVersion: '8.0.0',
+ packageManager: packageManagerMock as JsPackageManager,
+ });
+ expect(result).toEqual(
+ expect.objectContaining({
+ packageName: 'my-storybook-package',
+ packageVersion: '1.0.0',
+ hasIncompatibleDependencies: true,
+ })
+ );
+ });
+
+ it('returns that a package is compatible', async () => {
+ const packageName = 'my-storybook-package';
+ vi.mocked(packageManagerMock.getPackageJSON).mockResolvedValueOnce({
+ name: packageName,
+ version: '1.0.0',
+ dependencies: {
+ '@storybook/core-common': '8.0.0',
+ },
+ });
+ const result = await checkPackageCompatibility(packageName, {
+ currentStorybookVersion: '8.0.0',
+ packageManager: packageManagerMock as JsPackageManager,
+ });
+ expect(result).toEqual(
+ expect.objectContaining({
+ packageName: 'my-storybook-package',
+ packageVersion: '1.0.0',
+ hasIncompatibleDependencies: false,
+ })
+ );
+ });
+
+ it('returns that a package is incompatible and because it is core, can be upgraded', async () => {
+ const packageName = '@storybook/addon-essentials';
+
+ vi.mocked(packageManagerMock.getPackageJSON).mockResolvedValueOnce({
+ name: packageName,
+ version: '7.0.0',
+ dependencies: {
+ '@storybook/core-common': '7.0.0',
+ },
+ });
+
+ const result = await checkPackageCompatibility(packageName, {
+ currentStorybookVersion: '8.0.0',
+ packageManager: packageManagerMock,
+ });
+
+ expect(result).toEqual(
+ expect.objectContaining({
+ packageName: '@storybook/addon-essentials',
+ packageVersion: '7.0.0',
+ hasIncompatibleDependencies: true,
+ availableUpdate: '8.0.0',
+ })
+ );
+ });
+});
+
+describe('getIncompatibleStorybookPackages', () => {
+ it('returns an array of incompatible packages', async () => {
+ vi.mocked(packageManagerMock.getPackageJSON).mockResolvedValueOnce({
+ name: '@storybook/addon-essentials',
+ version: '7.0.0',
+ dependencies: {
+ '@storybook/core-common': '7.0.0',
+ },
+ });
+
+ const result = await getIncompatibleStorybookPackages({
+ currentStorybookVersion: '8.0.0',
+ packageManager: packageManagerMock as JsPackageManager,
+ });
+
+ expect(result).toEqual([
+ expect.objectContaining({
+ packageName: '@storybook/addon-essentials',
+ hasIncompatibleDependencies: true,
+ }),
+ ]);
+ });
+});
+
+describe('getIncompatiblePackagesSummary', () => {
+ it('generates a summary message for incompatible packages', () => {
+ const analysedPackages: AnalysedPackage[] = [
+ {
+ packageName: 'storybook-react',
+ packageVersion: '1.0.0',
+ hasIncompatibleDependencies: true,
+ },
+ {
+ packageName: '@storybook/addon-essentials',
+ packageVersion: '7.0.0',
+ hasIncompatibleDependencies: true,
+ availableUpdate: '8.0.0',
+ },
+ ];
+ const summary = getIncompatiblePackagesSummary(analysedPackages, '8.0.0');
+ expect(summary).toMatchInlineSnapshot(`
+ "The following packages are incompatible with Storybook 8.0.0 as they depend on different major versions of Storybook packages:
+ - storybook-react@1.0.0
+ - @storybook/addon-essentials@7.0.0 (8.0.0 available!)
+
+
+ Please consider updating your packages or contacting the maintainers for compatibility details.
+ For more on Storybook 8 compatibility, see the linked GitHub issue:
+ https://github.com/storybookjs/storybook/issues/26031"
+ `);
+ });
+});
diff --git a/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.ts b/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.ts
new file mode 100644
index 000000000000..d481fae93c82
--- /dev/null
+++ b/code/lib/cli/src/doctor/getIncompatibleStorybookPackages.ts
@@ -0,0 +1,135 @@
+/* eslint-disable local-rules/no-uncategorized-errors */
+import chalk from 'chalk';
+import semver from 'semver';
+import type { JsPackageManager } from '@storybook/core-common';
+import { JsPackageManagerFactory, versions as storybookCorePackages } from '@storybook/core-common';
+
+export type AnalysedPackage = {
+ packageName: string;
+ packageVersion?: string;
+ homepage?: string;
+ hasIncompatibleDependencies?: boolean;
+ availableUpdate?: string;
+};
+
+type Context = {
+ currentStorybookVersion: string;
+ packageManager: JsPackageManager;
+ skipUpgradeCheck?: boolean;
+ skipErrors?: boolean;
+};
+
+const isPackageIncompatible = (installedVersion: string, currentStorybookVersion: string) => {
+ const storybookVersion = semver.coerce(currentStorybookVersion);
+ const packageVersion = semver.coerce(installedVersion);
+ return storybookVersion?.major !== packageVersion?.major;
+};
+
+export const checkPackageCompatibility = async (dependency: string, context: Context) => {
+ const { currentStorybookVersion, skipErrors, packageManager } = context;
+ try {
+ const dependencyPackageJson = await packageManager.getPackageJSON(dependency);
+ if (dependencyPackageJson === null) {
+ return { packageName: dependency };
+ }
+
+ const {
+ version: packageVersion,
+ name = dependency,
+ dependencies,
+ peerDependencies,
+ homepage,
+ } = dependencyPackageJson;
+
+ const hasIncompatibleDependencies = !!Object.entries({
+ ...dependencies,
+ ...peerDependencies,
+ })
+ .filter(([dep]) => storybookCorePackages[dep as keyof typeof storybookCorePackages])
+ .find(([, version]) => {
+ // prevent issues with "tag" based versions e.g. "latest" or "next" instead of actual numbers
+ return (
+ version &&
+ semver.validRange(version) &&
+ isPackageIncompatible(version, currentStorybookVersion)
+ );
+ });
+
+ const isCorePackage = storybookCorePackages[name as keyof typeof storybookCorePackages];
+
+ let availableUpdate;
+
+ // For now, we notify about updates only for core packages (which will match the currently installed storybook version)
+ // In the future, we can use packageManager.latestVersion(name, constraint) for all packages
+ if (isCorePackage && semver.gt(currentStorybookVersion, packageVersion!)) {
+ availableUpdate = currentStorybookVersion;
+ }
+
+ return {
+ packageName: name,
+ packageVersion,
+ homepage,
+ hasIncompatibleDependencies,
+ availableUpdate,
+ };
+ } catch (err) {
+ if (!skipErrors) {
+ console.log(`Error checking compatibility for ${dependency}, please report an issue:\n`, err);
+ }
+ return { packageName: dependency };
+ }
+};
+
+export const getIncompatibleStorybookPackages = async (
+ context: Omit & Partial>
+): Promise => {
+ const packageManager = context.packageManager ?? JsPackageManagerFactory.getPackageManager();
+
+ const allDeps = await packageManager.getAllDependencies();
+ const storybookLikeDeps = Object.keys(allDeps).filter((dep) => dep.includes('storybook'));
+
+ if (storybookLikeDeps.length === 0) {
+ throw new Error('No Storybook dependencies found in the package.json');
+ }
+
+ return Promise.all(
+ storybookLikeDeps.map((dep) => checkPackageCompatibility(dep, { ...context, packageManager }))
+ );
+};
+
+export const getIncompatiblePackagesSummary = (
+ dependencyAnalysis: AnalysedPackage[],
+ currentStorybookVersion: string
+) => {
+ const summaryMessage: string[] = [];
+
+ const incompatiblePackages = dependencyAnalysis.filter(
+ (dep) => dep.hasIncompatibleDependencies
+ ) as AnalysedPackage[];
+
+ if (incompatiblePackages.length > 0) {
+ summaryMessage.push(
+ `The following packages are incompatible with Storybook ${chalk.bold(
+ currentStorybookVersion
+ )} as they depend on different major versions of Storybook packages:`
+ );
+ incompatiblePackages.forEach(
+ ({ packageName: addonName, packageVersion: addonVersion, homepage, availableUpdate }) => {
+ const packageDescription = `${chalk.cyan(addonName)}@${chalk.cyan(addonVersion)}`;
+ const updateMessage = availableUpdate ? ` (${availableUpdate} available!)` : '';
+ const packageRepo = homepage ? `\n Repo: ${chalk.yellow(homepage)}` : '';
+
+ summaryMessage.push(`- ${packageDescription}${updateMessage}${packageRepo}`);
+ }
+ );
+
+ summaryMessage.push(
+ '\n',
+ 'Please consider updating your packages or contacting the maintainers for compatibility details.',
+ 'For more on Storybook 8 compatibility, see the linked GitHub issue:',
+ chalk.yellow('https://github.com/storybookjs/storybook/issues/26031')
+ );
+ }
+
+ return summaryMessage.join('\n');
+};
diff --git a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts
index 18f0008c537f..f8c9e0874a0f 100644
--- a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts
+++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts
@@ -75,7 +75,7 @@ export function getMismatchingVersionsWarnings(
if (filteredDependencies.length > 0) {
const packageJsonSuffix = '(in your package.json)';
messages.push(
- `Based on your lockfile, these dependencies should be upgraded:`,
+ `Based on your lockfile, these dependencies should be aligned:`,
filteredDependencies
.map(
([name, dep]) =>
diff --git a/code/lib/cli/src/doctor/index.ts b/code/lib/cli/src/doctor/index.ts
index 9aaeede8bc0f..69884c2f6755 100644
--- a/code/lib/cli/src/doctor/index.ts
+++ b/code/lib/cli/src/doctor/index.ts
@@ -9,10 +9,13 @@ import { JsPackageManagerFactory } from '@storybook/core-common';
import type { PackageManagerName } from '@storybook/core-common';
import { getStorybookData } from '../automigrate/helpers/mainConfigFile';
import { cleanLog } from '../automigrate/helpers/cleanLog';
-import { incompatibleAddons } from '../automigrate/fixes/incompatible-addons';
-import { getDuplicatedDepsWarnings } from './getDuplicatedDepsWarnings';
-import { getIncompatibleAddons } from './getIncompatibleAddons';
import { getMismatchingVersionsWarnings } from './getMismatchingVersionsWarning';
+import {
+ getIncompatiblePackagesSummary,
+ getIncompatibleStorybookPackages,
+} from './getIncompatibleStorybookPackages';
+import { getDuplicatedDepsWarnings } from './getDuplicatedDepsWarnings';
+import { isPrerelease } from './utils';
const logger = console;
const LOG_FILE_NAME = 'doctor-storybook.log';
@@ -50,9 +53,21 @@ export const doctor = async ({
packageManager: pkgMgr,
}: DoctorOptions = {}) => {
augmentLogsToFile();
- const diagnosticMessages: string[] = [];
- logger.info('🩺 checking the health of your Storybook..');
+ let foundIssues = false;
+ const logDiagnostic = (title: string, message: string) => {
+ foundIssues = true;
+ logger.info(
+ boxen(message, {
+ borderStyle: 'round',
+ padding: 1,
+ title,
+ borderColor: '#F1618C',
+ })
+ );
+ };
+
+ logger.info('🩺 The doctor is checking the health of your Storybook..');
const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr });
let storybookVersion;
@@ -89,9 +104,17 @@ export const doctor = async ({
throw new Error('mainConfig is undefined');
}
- const incompatibleAddonList = await getIncompatibleAddons(mainConfig);
- if (incompatibleAddonList.length > 0) {
- diagnosticMessages.push(incompatibleAddons.prompt({ incompatibleAddonList }));
+ const allDependencies = (await packageManager.getAllDependencies()) as Record;
+
+ const incompatibleStorybookPackagesList = await getIncompatibleStorybookPackages({
+ currentStorybookVersion: storybookVersion,
+ });
+ const incompatiblePackagesMessage = getIncompatiblePackagesSummary(
+ incompatibleStorybookPackagesList,
+ storybookVersion
+ );
+ if (incompatiblePackagesMessage) {
+ logDiagnostic('Incompatible packages found', incompatiblePackagesMessage);
}
const installationMetadata = await packageManager.findInstallations([
@@ -99,39 +122,44 @@ export const doctor = async ({
'storybook',
]);
- const allDependencies = (await packageManager.getAllDependencies()) as Record;
- const mismatchingVersionMessage = getMismatchingVersionsWarnings(
- installationMetadata,
- allDependencies
- );
- if (mismatchingVersionMessage) {
- diagnosticMessages.push(mismatchingVersionMessage);
- } else {
- const list = installationMetadata
- ? getDuplicatedDepsWarnings(installationMetadata)
- : getDuplicatedDepsWarnings();
- if (list) {
- diagnosticMessages.push(list?.join('\n'));
+ // If we found incompatible packages, we let the users fix that first
+ // If they run doctor again and there are still issues, we show the other warnings
+ if (!incompatiblePackagesMessage) {
+ const mismatchingVersionMessage = getMismatchingVersionsWarnings(
+ installationMetadata,
+ allDependencies
+ );
+ if (mismatchingVersionMessage) {
+ logDiagnostic('Diagnostics', [mismatchingVersionMessage].join('\n\n-------\n\n'));
+ } else {
+ const list = installationMetadata
+ ? getDuplicatedDepsWarnings(installationMetadata)
+ : getDuplicatedDepsWarnings();
+ if (Array.isArray(list) && list.length > 0) {
+ logDiagnostic('Duplicated dependencies found', list?.join('\n'));
+ }
}
}
+
+ const doctorCommand = isPrerelease(storybookVersion)
+ ? 'npx storybook@next doctor'
+ : 'npx storybook@latest doctor';
+
+ const commandMessage = `You can always recheck the health of your project by running:\n${chalk.cyan(
+ doctorCommand
+ )}`;
logger.info();
- const finalMessages = diagnosticMessages.filter(Boolean);
+ if (foundIssues) {
+ logger.info(commandMessage);
+ logger.info();
- if (finalMessages.length > 0) {
- finalMessages.push(`You can find the full logs in ${chalk.cyan(LOG_FILE_PATH)}`);
+ logger.info(`Full logs are available in ${chalk.cyan(LOG_FILE_PATH)}`);
- logger.info(
- boxen(finalMessages.join('\n\n-------\n\n'), {
- borderStyle: 'round',
- padding: 1,
- title: 'Diagnostics',
- borderColor: 'red',
- })
- );
await move(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME), { overwrite: true });
} else {
- logger.info('🥳 Your Storybook project looks good!');
+ logger.info(`🥳 Your Storybook project looks good!`);
+ logger.info(commandMessage);
await remove(TEMP_LOG_FILE_PATH);
}
logger.info();
diff --git a/code/lib/cli/src/doctor/utils.ts b/code/lib/cli/src/doctor/utils.ts
new file mode 100644
index 000000000000..65963febaa2f
--- /dev/null
+++ b/code/lib/cli/src/doctor/utils.ts
@@ -0,0 +1,3 @@
+export const isPrerelease = (version: string) => {
+ return version.includes('-');
+};
diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts
index b4e02c322b46..53c6259dc35b 100644
--- a/code/lib/cli/src/generate.ts
+++ b/code/lib/cli/src/generate.ts
@@ -224,7 +224,11 @@ command('dev')
.option('--quiet', 'Suppress verbose build output')
.option('--no-version-updates', 'Suppress update check', true)
.option('--debug-webpack', 'Display final webpack configurations for debugging purposes')
- .option('--webpack-stats-json [directory]', 'Write Webpack Stats JSON to disk')
+ .option(
+ '--webpack-stats-json [directory]',
+ 'Write Webpack stats JSON to disk (synonym for `--stats-json`)'
+ )
+ .option('--stats-json [directory]', 'Write stats JSON to disk')
.option(
'--preview-url ',
'Disables the default storybook preview and lets your use your own'
@@ -263,7 +267,11 @@ command('build')
.option('--quiet', 'Suppress verbose build output')
.option('--loglevel ', 'Control level of logging during build')
.option('--debug-webpack', 'Display final webpack configurations for debugging purposes')
- .option('--webpack-stats-json [directory]', 'Write Webpack Stats JSON to disk')
+ .option(
+ '--webpack-stats-json [directory]',
+ 'Write Webpack stats JSON to disk (synonym for `--stats-json`)'
+ )
+ .option('--stats-json [directory]', 'Write stats JSON to disk')
.option(
'--preview-url ',
'Disables the default storybook preview and lets your use your own'
diff --git a/code/lib/cli/src/generators/ANGULAR/helpers.ts b/code/lib/cli/src/generators/ANGULAR/helpers.ts
index b2394797909c..33b79bb7339c 100644
--- a/code/lib/cli/src/generators/ANGULAR/helpers.ts
+++ b/code/lib/cli/src/generators/ANGULAR/helpers.ts
@@ -3,6 +3,8 @@ import { join } from 'path';
import prompts from 'prompts';
import dedent from 'ts-dedent';
import { MissingAngularJsonError } from '@storybook/core-events/server-errors';
+import boxen from 'boxen';
+import { logger } from '@storybook/node-logger';
export const ANGULAR_JSON_PATH = 'angular.json';
@@ -13,6 +15,18 @@ export const compoDocPreviewPrefix = dedent`
`.trimStart();
export const promptForCompoDocs = async (): Promise => {
+ logger.plain(
+ // Create a text which explains the user why compodoc is necessary
+ boxen(
+ dedent`
+ Compodoc is a great tool to generate documentation for your Angular projects.
+ Storybook can use the documentation generated by Compodoc to extract argument definitions
+ and JSDOC comments to display them in the Storybook UI. We highly recommend using Compodoc for
+ your Angular projects to get the best experience out of Storybook.
+ `,
+ { title: 'Compodoc', borderStyle: 'round', padding: 1, borderColor: '#F1618C' }
+ )
+ );
const { useCompoDoc } = await prompts({
type: 'confirm',
name: 'useCompoDoc',
diff --git a/code/lib/cli/src/generators/ANGULAR/index.ts b/code/lib/cli/src/generators/ANGULAR/index.ts
index cedc43def3b4..6a770ef1922c 100644
--- a/code/lib/cli/src/generators/ANGULAR/index.ts
+++ b/code/lib/cli/src/generators/ANGULAR/index.ts
@@ -1,5 +1,5 @@
import { join } from 'path';
-import { paddedLog } from '@storybook/core-common';
+import { commandLog } from '@storybook/core-common';
import { baseGenerator } from '../baseGenerator';
import type { Generator } from '../types';
import { CoreBuilder } from '../../project_types';
@@ -31,7 +31,7 @@ const generator: Generator<{ projectName: string }> = async (
}
const angularProjectName = await angularJSON.getProjectName();
- paddedLog(`Adding Storybook support to your "${angularProjectName}" project`);
+ commandLog(`Adding Storybook support to your "${angularProjectName}" project`);
const angularProject = angularJSON.getProjectSettingsByName(angularProjectName);
diff --git a/code/lib/cli/src/generators/NEXTJS/index.ts b/code/lib/cli/src/generators/NEXTJS/index.ts
index fe6672b669a7..e29cb4f6a751 100644
--- a/code/lib/cli/src/generators/NEXTJS/index.ts
+++ b/code/lib/cli/src/generators/NEXTJS/index.ts
@@ -15,7 +15,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => {
'react',
{
staticDir,
- extraAddons: ['@storybook/addon-onboarding@^1.0.0'],
+ extraAddons: [`@storybook/addon-onboarding`],
webpackCompiler: ({ builder }) => undefined,
},
'nextjs'
diff --git a/code/lib/cli/src/generators/REACT/index.ts b/code/lib/cli/src/generators/REACT/index.ts
index 967ed7b5531b..267f15b8427a 100644
--- a/code/lib/cli/src/generators/REACT/index.ts
+++ b/code/lib/cli/src/generators/REACT/index.ts
@@ -11,7 +11,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => {
await baseGenerator(packageManager, npmOptions, options, 'react', {
extraPackages,
webpackCompiler: ({ builder }) => (builder === CoreBuilder.Webpack5 ? 'swc' : undefined),
- extraAddons: ['@storybook/addon-onboarding@^1.0.0'],
+ extraAddons: [`@storybook/addon-onboarding`],
});
};
diff --git a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts
index 97a8c906d526..af74a75861ca 100644
--- a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts
+++ b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts
@@ -2,7 +2,6 @@ import path from 'path';
import fs from 'fs';
import semver from 'semver';
import dedent from 'ts-dedent';
-import { versions } from '@storybook/core-common';
import { baseGenerator } from '../baseGenerator';
import type { Generator } from '../types';
@@ -47,11 +46,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => {
// Miscellaneous dependency to add to be sure Storybook + CRA is working fine with Yarn PnP mode
extraPackages.push('prop-types');
- const version = versions['@storybook/preset-create-react-app'];
- const extraAddons = [
- `@storybook/preset-create-react-app@${version}`,
- '@storybook/addon-onboarding',
- ];
+ const extraAddons = [`@storybook/preset-create-react-app`, `@storybook/addon-onboarding`];
await baseGenerator(
packageManager,
diff --git a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts
index a6f0293248f4..7274e40bf40f 100644
--- a/code/lib/cli/src/generators/WEBPACK_REACT/index.ts
+++ b/code/lib/cli/src/generators/WEBPACK_REACT/index.ts
@@ -4,7 +4,7 @@ import type { Generator } from '../types';
const generator: Generator = async (packageManager, npmOptions, options) => {
await baseGenerator(packageManager, npmOptions, options, 'react', {
- extraAddons: ['@storybook/addon-onboarding@^1.0.0'],
+ extraAddons: [`@storybook/addon-onboarding`],
webpackCompiler: ({ builder }) => (builder === CoreBuilder.Webpack5 ? 'swc' : undefined),
});
};
diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts
index 3d647f923a43..b5504f2f751e 100644
--- a/code/lib/cli/src/initiate.ts
+++ b/code/lib/cli/src/initiate.ts
@@ -434,7 +434,8 @@ export async function initiate(options: CommandOptions, pkg: PackageJson): Promi
const flags = [];
// npm needs extra -- to pass flags to the command
- if (packageManager.type === 'npm') {
+ // in the case of Angular, we are calling `ng run` which doesn't need the extra `--`
+ if (packageManager.type === 'npm' && projectType !== ProjectType.ANGULAR) {
flags.push('--');
}
diff --git a/code/lib/cli/src/migrate.ts b/code/lib/cli/src/migrate.ts
index 5e38507afd61..15d2169120e7 100644
--- a/code/lib/cli/src/migrate.ts
+++ b/code/lib/cli/src/migrate.ts
@@ -1,6 +1,6 @@
import { listCodemods, runCodemod } from '@storybook/codemod';
import { runFixes } from './automigrate';
-import { bareMdxStoriesGlob } from './automigrate/fixes/bare-mdx-stories-glob';
+import { mdxToCSF } from './automigrate/fixes/mdx-to-csf';
import {
JsPackageManagerFactory,
getStorybookInfo,
@@ -36,14 +36,17 @@ export async function migrate(migration: any, { glob, dryRun, list, rename, pars
}
await runFixes({
- fixes: [bareMdxStoriesGlob],
+ fixes: [mdxToCSF],
configDir,
mainConfigPath,
packageManager,
storybookVersion,
+ beforeVersion: storybookVersion,
+ isUpgrade: false,
});
await addStorybookBlocksPackage();
}
+
await runCodemod(migration, { glob, dryRun, logger, rename, parser });
} else {
throw new Error('Migrate: please specify a migration name or --list');
diff --git a/code/lib/cli/src/upgrade.test.ts b/code/lib/cli/src/upgrade.test.ts
index 149951460724..14bafd7cf5b9 100644
--- a/code/lib/cli/src/upgrade.test.ts
+++ b/code/lib/cli/src/upgrade.test.ts
@@ -1,12 +1,11 @@
import { describe, it, expect, vi } from 'vitest';
+import * as sbcc from '@storybook/core-common';
import {
UpgradeStorybookToLowerVersionError,
UpgradeStorybookToSameVersionError,
} from '@storybook/core-events/server-errors';
import { doUpgrade, getStorybookVersion } from './upgrade';
-import type * as sbcc from '@storybook/core-common';
-
const findInstallationsMock = vi.fn>();
vi.mock('@storybook/telemetry');
@@ -66,7 +65,7 @@ describe('Upgrade errors', () => {
});
await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToLowerVersionError);
- expect(findInstallationsMock).toHaveBeenCalledWith(['storybook', '@storybook/cli']);
+ expect(findInstallationsMock).toHaveBeenCalledWith(Object.keys(sbcc.versions));
});
it('should throw an error when upgrading to the same version number', async () => {
findInstallationsMock.mockResolvedValue({
@@ -83,6 +82,6 @@ describe('Upgrade errors', () => {
});
await expect(doUpgrade({} as any)).rejects.toThrowError(UpgradeStorybookToSameVersionError);
- expect(findInstallationsMock).toHaveBeenCalledWith(['storybook', '@storybook/cli']);
+ expect(findInstallationsMock).toHaveBeenCalledWith(Object.keys(sbcc.versions));
});
});
diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts
index 5a19c85502bf..179c7d806a2f 100644
--- a/code/lib/cli/src/upgrade.ts
+++ b/code/lib/cli/src/upgrade.ts
@@ -17,7 +17,6 @@ import {
isCorePackage,
versions,
getStorybookInfo,
- getCoercedStorybookVersion,
loadMainConfig,
JsPackageManagerFactory,
} from '@storybook/core-common';
@@ -41,15 +40,12 @@ export const getStorybookVersion = (line: string) => {
};
const getInstalledStorybookVersion = async (packageManager: JsPackageManager) => {
- const installations = await packageManager.findInstallations(['storybook', '@storybook/cli']);
+ const installations = await packageManager.findInstallations(Object.keys(versions));
if (!installations) {
return;
}
- const cliVersion = installations.dependencies['@storybook/cli']?.[0].version;
- if (cliVersion) {
- return cliVersion;
- }
- return installations.dependencies['storybook']?.[0].version;
+
+ return Object.entries(installations.dependencies)[0]?.[1]?.[0].version;
};
const deprecatedPackages = [
@@ -145,11 +141,10 @@ export const doUpgrade = async ({
throw new UpgradeStorybookToSameVersionError({ beforeVersion });
}
- const [latestVersion, packageJson, storybookVersion] = await Promise.all([
+ const [latestVersion, packageJson] = await Promise.all([
//
packageManager.latestVersion('@storybook/cli'),
packageManager.retrievePackageJson(),
- getCoercedStorybookVersion(packageManager),
]);
const isOutdated = lt(currentVersion, latestVersion);
@@ -192,7 +187,7 @@ export const doUpgrade = async ({
const mainConfig = await loadMainConfig({ configDir });
// GUARDS
- if (!storybookVersion) {
+ if (!beforeVersion) {
throw new UpgradeStorybookUnknownCurrentVersionError();
}
@@ -256,7 +251,7 @@ export const doUpgrade = async ({
}
// AUTOMIGRATIONS
- if (!skipCheck && !results && mainConfigPath && storybookVersion) {
+ if (!skipCheck && !results && mainConfigPath) {
checkVersionConsistency();
results = await automigrate({
dryRun,
@@ -264,7 +259,9 @@ export const doUpgrade = async ({
packageManager,
configDir,
mainConfigPath,
- storybookVersion,
+ beforeVersion,
+ storybookVersion: currentVersion,
+ isUpgrade: true,
});
}
diff --git a/code/lib/cli/templates/angular/application/template-csf/.storybook/tsconfig.doc.json b/code/lib/cli/templates/angular/application/template-csf/.storybook/tsconfig.doc.json
new file mode 100644
index 000000000000..22e282bd5db9
--- /dev/null
+++ b/code/lib/cli/templates/angular/application/template-csf/.storybook/tsconfig.doc.json
@@ -0,0 +1,10 @@
+// This tsconfig is used by Compodoc to generate the documentation for the project.
+// If Compodoc is not used, this file can be deleted.
+{
+ "extends": "./tsconfig.json",
+ // Exclude all files that are not needed for documentation generation.
+ "exclude": ["../src/test.ts", "../src/**/*.spec.ts", "../src/**/*.stories.ts"],
+ // Please make sure to include all files from which Compodoc should generate documentation.
+ "include": ["../src/**/*"],
+ "files": ["./typings.d.ts"]
+}
diff --git a/code/lib/codemod/src/index.ts b/code/lib/codemod/src/index.ts
index 0bc3f4d1b3db..b55c790adf83 100644
--- a/code/lib/codemod/src/index.ts
+++ b/code/lib/codemod/src/index.ts
@@ -30,7 +30,16 @@ async function renameFile(file: any, from: any, to: any, { logger }: any) {
return renameAsync(file, newFile);
}
-export async function runCodemod(codemod: any, { glob, logger, dryRun, rename, parser }: any) {
+export async function runCodemod(
+ codemod: any,
+ {
+ glob,
+ logger,
+ dryRun,
+ rename,
+ parser,
+ }: { glob: any; logger: any; dryRun?: any; rename?: any; parser?: any }
+) {
const codemods = listCodemods();
if (!codemods.includes(codemod)) {
throw new Error(`Unknown codemod ${codemod}. Run --list for options.`);
@@ -87,17 +96,17 @@ export async function runCodemod(codemod: any, { glob, logger, dryRun, rename, p
shell: true,
}
);
- if (result.status === 1) {
+
+ if (codemod === 'mdx-to-csf' && result.status === 1) {
+ logger.log(
+ 'The codemod was not able to transform the files mentioned above. We have renamed the files to .mdx.broken. Please check the files and rename them back to .mdx after you have either manually transformed them to mdx + csf or fixed the issues so that the codemod can transform them.'
+ );
+ } else if (result.status === 1) {
logger.log('Skipped renaming because of errors.');
return;
}
}
- if (!renameParts && codemod === 'mdx-to-csf') {
- renameParts = ['.stories.mdx', '.mdx'];
- rename = '.stories.mdx:.mdx;';
- }
-
if (renameParts) {
const [from, to] = renameParts;
logger.log(`=> Renaming ${rename}: ${files.length} files`);
diff --git a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
index 1be65c569edc..36db68148a31 100644
--- a/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
+++ b/code/lib/codemod/src/transforms/__tests__/mdx-to-csf.test.ts
@@ -598,6 +598,50 @@ it('story child is identifier', async () => {
`);
});
+it('should replace ArgsTable by Controls', async () => {
+ const input = dedent`
+ import { ArgsTable } from '@storybook/addon-docs/blocks';
+ import { Button } from './button';
+
+ Dummy Code
+
+
+ `;
+
+ const mdx = await jscodeshift({ source: input, path: 'Foobar.stories.mdx' });
+
+ expect(mdx).toMatchInlineSnapshot(`
+ import { Controls } from '@storybook/blocks';
+ import { Button } from './button';
+
+ Dummy Code
+
+
+ `);
+});
+
+it('should not create stories.js file if there are no components', async () => {
+ const input = dedent`
+ import { Meta } from '@storybook/addon-docs';
+
+
+
+ # Welcome to Storybook
+ `;
+
+ const mdx = await jscodeshift({ source: input, path: 'Foobar.stories.mdx' });
+
+ expect(fs.writeFileSync).not.toHaveBeenCalled();
+
+ expect(mdx).toMatchInlineSnapshot(`
+ import { Meta } from '@storybook/blocks';
+
+
+
+ # Welcome to Storybook
+ `);
+});
+
it('nameToValidExport', () => {
expect(nameToValidExport('1 starts with digit')).toMatchInlineSnapshot(`$1StartsWithDigit`);
expect(nameToValidExport('name')).toMatchInlineSnapshot(`Name`);
diff --git a/code/lib/codemod/src/transforms/mdx-to-csf.ts b/code/lib/codemod/src/transforms/mdx-to-csf.ts
index 9c657c822e04..765331e40b2e 100644
--- a/code/lib/codemod/src/transforms/mdx-to-csf.ts
+++ b/code/lib/codemod/src/transforms/mdx-to-csf.ts
@@ -24,6 +24,9 @@ import type { MdxFlowExpression } from 'mdast-util-mdx-expression';
const mdxProcessor = remark().use(remarkMdx) as ReturnType;
+const renameList: { original: string; baseName: string }[] = [];
+const brokenList: { original: string; baseName: string }[] = [];
+
export default async function jscodeshift(info: FileInfo) {
const parsed = path.parse(info.path);
@@ -37,18 +40,37 @@ export default async function jscodeshift(info: FileInfo) {
baseName += '_';
}
- const result = await transform(info, path.basename(baseName));
+ try {
+ const { csf, mdx } = await transform(info, path.basename(baseName));
+
+ if (csf != null) {
+ fs.writeFileSync(`${baseName}.stories.js`, csf);
+ }
- const [mdx, csf] = result;
+ renameList.push({ original: info.path, baseName });
- if (csf != null) {
- fs.writeFileSync(`${baseName}.stories.js`, csf);
+ return mdx;
+ } catch (e) {
+ brokenList.push({ original: info.path, baseName });
+ throw e;
}
-
- return mdx;
}
-export async function transform(info: FileInfo, baseName: string): Promise<[string, string]> {
+// The JSCodeshift CLI doesn't return a list of files that were transformed or skipped.
+// This is a workaround to rename the files after the transformation, which we can remove after we switch from jscodeshift to another solution.
+process.on('exit', () => {
+ renameList.forEach((file) => {
+ fs.renameSync(file.original, `${file.baseName}.mdx`);
+ });
+ brokenList.forEach((file) => {
+ fs.renameSync(file.original, `${file.original}.broken`);
+ });
+});
+
+export async function transform(
+ info: FileInfo,
+ baseName: string
+): Promise<{ mdx: string; csf: string | null }> {
const root = mdxProcessor.parse(info.source);
const storyNamespaceName = nameToValidExport(`${baseName}Stories`);
@@ -74,25 +96,42 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri
node.value = node.value
.replaceAll('@storybook/addon-docs/blocks', '@storybook/blocks')
.replaceAll('@storybook/addon-docs', '@storybook/blocks');
+
+ if (node.value.includes('@storybook/blocks')) {
+ // @ts-ignore
+ const file: BabelFile = new babel.File(
+ { filename: 'info.path' },
+ { code: node.value, ast: babelParse(node.value) }
+ );
+
+ file.path.traverse({
+ ImportDeclaration(path) {
+ if (path.node.source.value === '@storybook/blocks') {
+ path.get('specifiers').forEach((specifier) => {
+ if (specifier.isImportSpecifier()) {
+ const imported = specifier.get('imported');
+ if (imported.isIdentifier() && imported.node.name === 'ArgsTable') {
+ imported.node.name = 'Controls';
+ }
+ }
+ });
+ }
+ },
+ });
+
+ node.value = recast.print(file.ast).code;
+ }
});
const file = getEsmAst(root);
visit(root, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => {
if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
- if (is(node, { name: 'Meta' })) {
- metaAttributes.push(...node.attributes);
- node.attributes = [
- {
- type: 'mdxJsxAttribute',
- name: 'of',
- value: {
- type: 'mdxJsxAttributeValueExpression',
- value: storyNamespaceName,
- },
- },
- ];
+ if (is(node, { name: 'ArgsTable' })) {
+ node.name = 'Controls';
+ node.attributes = [];
}
+
if (is(node, { name: 'Story' })) {
const nameAttribute = node.attributes.find(
(it) => it.type === 'mdxJsxAttribute' && it.name === 'name'
@@ -167,21 +206,6 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri
return undefined;
});
- const metaProperties = metaAttributes.flatMap((attribute) => {
- if (attribute.type === 'mdxJsxAttribute') {
- if (typeof attribute.value === 'string') {
- return [t.objectProperty(t.identifier(attribute.name), t.stringLiteral(attribute.value))];
- }
- return [
- t.objectProperty(
- t.identifier(attribute.name),
- babelParseExpression(attribute.value?.value ?? '') as any as t.Expression
- ),
- ];
- }
- return [];
- });
-
file.path.traverse({
// remove mdx imports from csf
ImportDeclaration(path) {
@@ -196,11 +220,49 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri
},
});
- if (storiesMap.size === 0 && metaAttributes.length === 0) {
+ if (storiesMap.size === 0) {
// A CSF file must have at least one story, so skip migrating if this is the case.
- return [mdxProcessor.stringify(root), ''];
+ return {
+ csf: null,
+ mdx: mdxProcessor.stringify(root),
+ };
}
+ // Rewrites the Meta tag to use the new story namespace
+ visit(root, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node, index, parent) => {
+ if (
+ (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
+ is(node, { name: 'Meta' })
+ ) {
+ metaAttributes.push(...node.attributes);
+ node.attributes = [
+ {
+ type: 'mdxJsxAttribute',
+ name: 'of',
+ value: {
+ type: 'mdxJsxAttributeValueExpression',
+ value: storyNamespaceName,
+ },
+ },
+ ];
+ }
+ });
+
+ const metaProperties = metaAttributes.flatMap((attribute) => {
+ if (attribute.type === 'mdxJsxAttribute') {
+ if (typeof attribute.value === 'string') {
+ return [t.objectProperty(t.identifier(attribute.name), t.stringLiteral(attribute.value))];
+ }
+ return [
+ t.objectProperty(
+ t.identifier(attribute.name),
+ babelParseExpression(attribute.value?.value ?? '') as any as t.Expression
+ ),
+ ];
+ }
+ return [];
+ });
+
addStoriesImport(root, baseName, storyNamespaceName);
const newStatements: t.Statement[] = [
@@ -297,7 +359,10 @@ export async function transform(info: FileInfo, baseName: string): Promise<[stri
filepath: path,
});
- return [newMdx, output];
+ return {
+ csf: output,
+ mdx: newMdx,
+ };
}
function getEsmAst(root: ReturnType) {
diff --git a/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts
index bd5a4372bccf..1445b1be2b87 100644
--- a/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts
+++ b/code/lib/core-common/src/js-package-manager/NPMProxy.test.ts
@@ -375,7 +375,7 @@ describe('NPM Proxy', () => {
}
`);
- const installations = await npmProxy.findInstallations();
+ const installations = await npmProxy.findInstallations(['@storybook/*']);
expect(installations).toMatchInlineSnapshot(`
{
diff --git a/code/lib/core-common/src/js-package-manager/NPMProxy.ts b/code/lib/core-common/src/js-package-manager/NPMProxy.ts
index 62d8be2fec5c..8e996861f32a 100644
--- a/code/lib/core-common/src/js-package-manager/NPMProxy.ts
+++ b/code/lib/core-common/src/js-package-manager/NPMProxy.ts
@@ -130,7 +130,7 @@ export class NPMProxy extends JsPackageManager {
});
}
- public async findInstallations() {
+ public async findInstallations(pattern: string[]) {
const exec = async ({ depth }: { depth: number }) => {
const pipeToNull = platform() === 'win32' ? '2>NUL' : '2>/dev/null';
return this.executeCommand({
@@ -146,7 +146,7 @@ export class NPMProxy extends JsPackageManager {
const commandResult = await exec({ depth: 99 });
const parsedOutput = JSON.parse(commandResult);
- return this.mapDependencies(parsedOutput);
+ return this.mapDependencies(parsedOutput, pattern);
} catch (e) {
// when --depth is higher than 0, npm can return a non-zero exit code
// in case the user's project has peer dependency issues. So we try again with no depth
@@ -154,7 +154,7 @@ export class NPMProxy extends JsPackageManager {
const commandResult = await exec({ depth: 0 });
const parsedOutput = JSON.parse(commandResult);
- return this.mapDependencies(parsedOutput);
+ return this.mapDependencies(parsedOutput, pattern);
} catch (err) {
logger.warn(`An issue occurred while trying to find dependencies metadata using npm.`);
return undefined;
@@ -245,13 +245,21 @@ export class NPMProxy extends JsPackageManager {
}
}
- protected mapDependencies(input: NpmListOutput): InstallationMetadata {
+ /**
+ *
+ * @param input The output of `npm ls --json`
+ * @param pattern A list of package names to filter the result. * can be used as a placeholder
+ */
+ protected mapDependencies(input: NpmListOutput, pattern: string[]): InstallationMetadata {
const acc: Record = {};
const existingVersions: Record = {};
const duplicatedDependencies: Record = {};
const recurse = ([name, packageInfo]: [string, NpmDependency]): void => {
- if (!name || !name.includes('storybook')) return;
+ // transform pattern into regex where `*` is replaced with `.*`
+ if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) {
+ return;
+ }
const value = {
version: packageInfo.version,
diff --git a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts
index 7e7f0279e226..a2f8baf6206b 100644
--- a/code/lib/core-common/src/js-package-manager/PNPMProxy.ts
+++ b/code/lib/core-common/src/js-package-manager/PNPMProxy.ts
@@ -107,7 +107,7 @@ export class PNPMProxy extends JsPackageManager {
try {
const parsedOutput = JSON.parse(commandResult);
- return this.mapDependencies(parsedOutput);
+ return this.mapDependencies(parsedOutput, pattern);
} catch (e) {
return undefined;
}
@@ -241,7 +241,7 @@ export class PNPMProxy extends JsPackageManager {
}
}
- protected mapDependencies(input: PnpmListOutput): InstallationMetadata {
+ protected mapDependencies(input: PnpmListOutput, pattern: string[]): InstallationMetadata {
const acc: Record = {};
const existingVersions: Record = {};
const duplicatedDependencies: Record = {};
@@ -252,7 +252,10 @@ export class PNPMProxy extends JsPackageManager {
}, {} as PnpmDependencies);
const recurse = ([name, packageInfo]: [string, PnpmDependency]): void => {
- if (!name || !name.includes('storybook')) return;
+ // transform pattern into regex where `*` is replaced with `.*`
+ if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) {
+ return;
+ }
const value = {
version: packageInfo.version,
diff --git a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts
index 2018e8f8ddf4..5980b26accc4 100644
--- a/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts
+++ b/code/lib/core-common/src/js-package-manager/Yarn1Proxy.ts
@@ -92,7 +92,7 @@ export class Yarn1Proxy extends JsPackageManager {
try {
const parsedOutput = JSON.parse(commandResult);
- return this.mapDependencies(parsedOutput);
+ return this.mapDependencies(parsedOutput, pattern);
} catch (e) {
return undefined;
}
@@ -179,7 +179,7 @@ export class Yarn1Proxy extends JsPackageManager {
}
}
- protected mapDependencies(input: Yarn1ListOutput): InstallationMetadata {
+ protected mapDependencies(input: Yarn1ListOutput, pattern: string[]): InstallationMetadata {
if (input.type === 'tree') {
const { trees } = input.data;
const acc: Record = {};
@@ -189,7 +189,10 @@ export class Yarn1Proxy extends JsPackageManager {
const recurse = (tree: (typeof trees)[0]) => {
const { children } = tree;
const { name, value } = parsePackageData(tree.name);
- if (!name || !name.includes('storybook')) return;
+ if (!name || !pattern.some((p) => new RegExp(`^${p.replace(/\*/g, '.*')}$`).test(name))) {
+ return;
+ }
+
if (!existingVersions[name]?.includes(value.version)) {
if (acc[name]) {
acc[name].push(value);
diff --git a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts
index 4f76868d395c..09f535e2dfa5 100644
--- a/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts
+++ b/code/lib/core-common/src/js-package-manager/Yarn2Proxy.ts
@@ -106,20 +106,14 @@ export class Yarn2Proxy extends JsPackageManager {
public async findInstallations(pattern: string[]) {
const commandResult = await this.executeCommand({
command: 'yarn',
- args: [
- 'info',
- '--name-only',
- '--recursive',
- pattern.map((p) => `"${p}"`).join(' '),
- `"${pattern}"`,
- ],
+ args: ['info', '--name-only', '--recursive', ...pattern],
env: {
FORCE_COLOR: 'false',
},
});
try {
- return this.mapDependencies(commandResult);
+ return this.mapDependencies(commandResult, pattern);
} catch (e) {
return undefined;
}
@@ -252,14 +246,17 @@ export class Yarn2Proxy extends JsPackageManager {
}
}
- protected mapDependencies(input: string): InstallationMetadata {
+ protected mapDependencies(input: string, pattern: string[]): InstallationMetadata {
const lines = input.split('\n');
const acc: Record = {};
const existingVersions: Record = {};
const duplicatedDependencies: Record = {};
lines.forEach((packageName) => {
- if (!packageName || !packageName.includes('storybook')) {
+ if (
+ !packageName ||
+ !pattern.some((p) => new RegExp(`${p.replace(/\*/g, '.*')}`).test(packageName))
+ ) {
return;
}
diff --git a/code/lib/core-common/src/versions.ts b/code/lib/core-common/src/versions.ts
index 3dabeec3ea21..fbec778916ad 100644
--- a/code/lib/core-common/src/versions.ts
+++ b/code/lib/core-common/src/versions.ts
@@ -12,6 +12,7 @@ export default {
'@storybook/addon-links': '8.0.0-beta.5',
'@storybook/addon-mdx-gfm': '8.0.0-beta.5',
'@storybook/addon-measure': '8.0.0-beta.5',
+ '@storybook/addon-onboarding': '8.0.0-beta.5',
'@storybook/addon-outline': '8.0.0-beta.5',
'@storybook/addon-storysource': '8.0.0-beta.5',
'@storybook/addon-themes': '8.0.0-beta.5',
diff --git a/code/lib/core-events/src/errors/server-errors.ts b/code/lib/core-events/src/errors/server-errors.ts
index a9a9e758d70e..c8eccef6eb2a 100644
--- a/code/lib/core-events/src/errors/server-errors.ts
+++ b/code/lib/core-events/src/errors/server-errors.ts
@@ -576,3 +576,17 @@ export class UpgradeStorybookUnknownCurrentVersionError extends StorybookError {
`;
}
}
+
+export class NoStatsForViteDevError extends StorybookError {
+ readonly category = Category.BUILDER_VITE;
+
+ readonly code = 1;
+
+ template() {
+ return dedent`
+ Unable to write preview stats as the Vite builder does not support stats in dev mode.
+
+ Please remove the \`--stats-json\` flag when running in dev mode.
+ `;
+ }
+}
diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts
index 582d2328086e..de47bcd7a575 100644
--- a/code/lib/core-server/src/build-dev.ts
+++ b/code/lib/core-server/src/build-dev.ts
@@ -17,7 +17,7 @@ import { join, relative, resolve } from 'path';
import { deprecate } from '@storybook/node-logger';
import { dedent } from 'ts-dedent';
import { readFile } from 'fs-extra';
-import { MissingBuilderError } from '@storybook/core-events/server-errors';
+import { MissingBuilderError, NoStatsForViteDevError } from '@storybook/core-events/server-errors';
import { storybookDevServer } from './dev-server';
import { outputStats } from './utils/output-stats';
import { outputStartupInformation } from './utils/output-startup-information';
@@ -89,7 +89,7 @@ export async function buildDevStandalone(
frameworkName = frameworkName || 'custom';
try {
- await warnOnIncompatibleAddons(config);
+ await warnOnIncompatibleAddons(packageJson.version);
} catch (e) {
console.warn('Storybook failed to check addon compatibility', e);
}
@@ -182,15 +182,26 @@ export async function buildDevStandalone(
const previewStats = previewResult?.stats;
const managerStats = managerResult?.stats;
- if (options.webpackStatsJson) {
- const target = options.webpackStatsJson === true ? options.outputDir : options.webpackStatsJson;
+ const statsOption = options.webpackStatsJson || options.statsJson;
+ if (statsOption) {
+ const target = statsOption === true ? options.outputDir : statsOption;
+
await outputStats(target, previewStats);
}
if (options.smokeTest) {
const warnings: Error[] = [];
warnings.push(...(managerStats?.toJson()?.warnings || []));
- warnings.push(...(previewStats?.toJson()?.warnings || []));
+ try {
+ warnings.push(...(previewStats?.toJson()?.warnings || []));
+ } catch (err) {
+ if (err instanceof NoStatsForViteDevError) {
+ // pass, the Vite builder has no warnings in the stats object anyway,
+ // but no stats at all in dev mode
+ } else {
+ throw err;
+ }
+ }
const problems = warnings
.filter((warning) => !warning.message.includes(`export 'useInsertionEffect'`))
diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts
index 1c2ce486189a..f1f842eaea28 100644
--- a/code/lib/core-server/src/build-static.ts
+++ b/code/lib/core-server/src/build-static.ts
@@ -180,9 +180,9 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
.then(async (previewStats) => {
logger.trace({ message: '=> Preview built', time: process.hrtime(startTime) });
- if (options.webpackStatsJson) {
- const target =
- options.webpackStatsJson === true ? options.outputDir : options.webpackStatsJson;
+ const statsOption = options.webpackStatsJson || options.statsJson;
+ if (statsOption) {
+ const target = statsOption === true ? options.outputDir : statsOption;
await outputStats(target, previewStats);
}
})
diff --git a/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts b/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts
index fd4aaac39286..6d7359a21edb 100644
--- a/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts
+++ b/code/lib/core-server/src/utils/warnOnIncompatibleAddons.ts
@@ -1,25 +1,22 @@
-import type { StorybookConfig } from '@storybook/types';
import { logger } from '@storybook/node-logger';
-import chalk from 'chalk';
-import dedent from 'ts-dedent';
+import {
+ getIncompatibleStorybookPackages,
+ getIncompatiblePackagesSummary,
+} from '../../../cli/src/doctor/getIncompatibleStorybookPackages';
-import { getIncompatibleAddons } from '../../../cli/src/doctor/getIncompatibleAddons';
+export const warnOnIncompatibleAddons = async (currentStorybookVersion: string) => {
+ const incompatiblePackagesList = await getIncompatibleStorybookPackages({
+ skipUpgradeCheck: true,
+ skipErrors: true,
+ currentStorybookVersion,
+ });
-export const warnOnIncompatibleAddons = async (config: StorybookConfig) => {
- const incompatibleAddons = await getIncompatibleAddons(config);
+ const incompatiblePackagesMessage = await getIncompatiblePackagesSummary(
+ incompatiblePackagesList,
+ currentStorybookVersion
+ );
- if (incompatibleAddons.length > 0) {
- logger.warn(dedent`
- ${chalk.bold(
- chalk.red('Attention')
- )}: We've detected that you're using the following addons in versions which are known to be incompatible with Storybook 7:
-
- ${incompatibleAddons
- .map(({ name, version }) => `- ${chalk.cyan(`${name}@${version}`)}`)
- .join('\n')}
-
- Please be aware they might not work in Storybook 7. Reach out to their maintainers for updates and check the following Github issue for more information:
- ${chalk.yellow('https://github.com/storybookjs/storybook/issues/20529')}
- `);
+ if (incompatiblePackagesMessage) {
+ logger.warn(incompatiblePackagesMessage);
}
};
diff --git a/code/lib/docs-tools/src/argTypes/docgen/utils/docgenInfo.ts b/code/lib/docs-tools/src/argTypes/docgen/utils/docgenInfo.ts
index 70084ed8ed91..6d1a7005ca0c 100644
--- a/code/lib/docs-tools/src/argTypes/docgen/utils/docgenInfo.ts
+++ b/code/lib/docs-tools/src/argTypes/docgen/utils/docgenInfo.ts
@@ -3,7 +3,9 @@
import type { Component } from '../../types';
import { str } from './string';
-export function hasDocgen(component: Component): boolean {
+export function hasDocgen(
+ component: Component
+): component is object & { __docgenInfo: T } {
return !!component.__docgenInfo;
}
diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx
index 24e288782adc..fbb8b74a6420 100644
--- a/code/lib/manager-api/src/index.tsx
+++ b/code/lib/manager-api/src/index.tsx
@@ -517,11 +517,8 @@ export function useArgTypes(): ArgTypes {
export { addons } from './lib/addons';
-/**
- * We need to rename this so it's not compiled to a straight re-export
- * Our globalization plugin can't handle an import and export of the same name in different lines
- * @deprecated
- */
+// We need to rename this so it's not compiled to a straight re-export
+// Our globalization plugin can't handle an import and export of the same name in different lines
const typesX = types;
export { typesX as types };
diff --git a/code/lib/preview-api/src/index.ts b/code/lib/preview-api/src/index.ts
index c2fcc5bf9640..e47cdaa0a0dd 100644
--- a/code/lib/preview-api/src/index.ts
+++ b/code/lib/preview-api/src/index.ts
@@ -68,4 +68,5 @@ export type { PropDescriptor } from './store';
* STORIES API
*/
export { StoryStore } from './store';
-export { Preview, PreviewWithSelection, PreviewWeb } from './preview-web';
+export { Preview, PreviewWeb, PreviewWithSelection, UrlStore, WebView } from './preview-web';
+export type { SelectionStore, View } from './preview-web';
diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts
index 22a3aacc8a41..5321f4bc697f 100644
--- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts
+++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.test.ts
@@ -33,6 +33,52 @@ describe('referenceCSFFile', () => {
});
});
+describe('attachCSFFile', () => {
+ const firstCsfParts = csfFileParts('first-meta--first-story', 'first-meta');
+ const secondCsfParts = csfFileParts('second-meta--second-story', 'second-meta');
+ const store = {
+ componentStoriesFromCSFFile: ({ csfFile }: { csfFile: CSFFile }) =>
+ csfFile === firstCsfParts.csfFile ? [firstCsfParts.story] : [secondCsfParts.story],
+ } as unknown as StoryStore;
+
+ it('attaches multiple CSF files', () => {
+ // Arrange - create a context with both CSF files
+ const context = new DocsContext(channel, store, renderStoryToElement, [
+ firstCsfParts.csfFile,
+ secondCsfParts.csfFile,
+ ]);
+
+ // Act - attach the first CSF file
+ context.attachCSFFile(firstCsfParts.csfFile);
+
+ // Assert - the first story is now the primary story and the only component story
+ expect(context.storyById()).toEqual(firstCsfParts.story);
+ expect(context.componentStories()).toEqual([firstCsfParts.story]);
+
+ // Assert - stories from both CSF files are available
+ expect(context.componentStoriesFromCSFFile(firstCsfParts.csfFile)).toEqual([
+ firstCsfParts.story,
+ ]);
+ expect(context.componentStoriesFromCSFFile(secondCsfParts.csfFile)).toEqual([
+ secondCsfParts.story,
+ ]);
+
+ // Act - attach the second CSF file
+ context.attachCSFFile(secondCsfParts.csfFile);
+
+ // Assert - the first story is still the primary story but both stories are available
+ expect(context.storyById()).toEqual(firstCsfParts.story);
+ expect(context.componentStories()).toEqual([firstCsfParts.story, secondCsfParts.story]);
+
+ // Act - attach the second CSF file again
+ context.attachCSFFile(secondCsfParts.csfFile);
+
+ // Assert - still only two stories are available
+ expect(context.storyById()).toEqual(firstCsfParts.story);
+ expect(context.componentStories()).toEqual([firstCsfParts.story, secondCsfParts.story]);
+ });
+});
+
describe('resolveOf', () => {
const { story, csfFile, storyExport, metaExport, moduleExports, component } = csfFileParts();
diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts
index da41240756d5..b22ad4afe444 100644
--- a/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts
+++ b/code/lib/preview-api/src/modules/preview-web/docs-context/DocsContext.ts
@@ -27,7 +27,7 @@ export class DocsContext implements DocsContextProps
private nameToStoryId: Map;
- private attachedCSFFile?: CSFFile;
+ private attachedCSFFiles: Set>;
private primaryStory?: PreparedStory;
@@ -38,11 +38,12 @@ export class DocsContext implements DocsContextProps
/** The CSF files known (via the index) to be refererenced by this docs file */
csfFiles: CSFFile[]
) {
+ this.componentStoriesValue = [];
this.storyIdToCSFFile = new Map();
this.exportToStory = new Map();
this.exportsToCSFFile = new Map();
this.nameToStoryId = new Map();
- this.componentStoriesValue = [];
+ this.attachedCSFFiles = new Set();
csfFiles.forEach((csfFile, index) => {
this.referenceCSFFile(csfFile);
@@ -71,10 +72,15 @@ export class DocsContext implements DocsContextProps
if (!this.exportsToCSFFile.has(csfFile.moduleExports)) {
throw new Error('Cannot attach a CSF file that has not been referenced');
}
+ if (this.attachedCSFFiles.has(csfFile)) {
+ // this CSF file is already attached, don't do anything
+ return;
+ }
- this.attachedCSFFile = csfFile;
+ this.attachedCSFFiles.add(csfFile);
const stories = this.store.componentStoriesFromCSFFile({ csfFile });
+
stories.forEach((story) => {
this.nameToStoryId.set(story.name, story.id);
this.componentStoriesValue.push(story);
@@ -115,15 +121,18 @@ export class DocsContext implements DocsContextProps
return { type: 'story', story: this.primaryStory } as TResolvedExport;
}
- if (!this.attachedCSFFile)
+ if (this.attachedCSFFiles.size === 0)
throw new Error(
`No CSF file attached to this docs file, did you forget to use ?`
);
- if (moduleExportType === 'meta')
- return { type: 'meta', csfFile: this.attachedCSFFile } as TResolvedExport;
+ const firstAttachedCSFFile = Array.from(this.attachedCSFFiles)[0];
+
+ if (moduleExportType === 'meta') {
+ return { type: 'meta', csfFile: firstAttachedCSFFile } as TResolvedExport;
+ }
- const { component } = this.attachedCSFFile.meta;
+ const { component } = firstAttachedCSFFile.meta;
if (!component)
throw new Error(
`Attached CSF file does not defined a component, did you forget to export one?`
diff --git a/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts b/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts
index 4be2710c8f10..5de026e902b8 100644
--- a/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts
+++ b/code/lib/preview-api/src/modules/preview-web/docs-context/test-utils.ts
@@ -1,6 +1,6 @@
import type { CSFFile, PreparedStory } from '@storybook/types';
-export function csfFileParts() {
+export function csfFileParts(storyId = 'meta--story', metaId = 'meta') {
// These compose the raw exports of the CSF file
const component = {};
const metaExport = { component };
@@ -9,13 +9,13 @@ export function csfFileParts() {
// This is the prepared story + CSF file after SB has processed them
const storyAnnotations = {
- id: 'meta--story',
+ id: storyId,
moduleExport: storyExport,
} as CSFFile['stories'][string];
- const story = { id: 'meta--story', moduleExport: storyExport } as PreparedStory;
- const meta = { id: 'meta', title: 'Meta', component, moduleExports } as CSFFile['meta'];
+ const story = { id: storyId, moduleExport: storyExport } as PreparedStory;
+ const meta = { id: metaId, title: 'Meta', component, moduleExports } as CSFFile['meta'];
const csfFile = {
- stories: { 'meta--story': storyAnnotations },
+ stories: { [storyId]: storyAnnotations },
meta,
moduleExports,
} as CSFFile;
diff --git a/code/lib/types/src/modules/addons.ts b/code/lib/types/src/modules/addons.ts
index fe4fa8551cfa..c1325e3f07ec 100644
--- a/code/lib/types/src/modules/addons.ts
+++ b/code/lib/types/src/modules/addons.ts
@@ -358,7 +358,12 @@ export interface Addon_BaseType {
* This is called as a function, so if you want to use hooks,
* your function needs to return a JSX.Element within which components are rendered
*/
- render: (renderOptions: Partial) => ReactElement | null;
+ render: (props: Partial) => ReturnType>>;
+ // TODO: for Storybook 9 I'd like to change this to be:
+ // render: FC>;
+ // This would bring it in line with how every other addon is set up.
+ // We'd need to change how the render function is called in the manager:
+ // https://github.com/storybookjs/storybook/blob/4e6fc0dde0842841d99cb3cf5148ca293a950301/code/ui/manager/src/components/preview/Preview.tsx#L105
/**
* @unstable
*/
diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts
index 380508211121..c4aeeacbcf1f 100644
--- a/code/lib/types/src/modules/core-common.ts
+++ b/code/lib/types/src/modules/core-common.ts
@@ -186,6 +186,7 @@ export interface CLIOptions {
test?: boolean;
debugWebpack?: boolean;
webpackStatsJson?: string | boolean;
+ statsJson?: string | boolean;
outputDir?: string;
}
diff --git a/code/package.json b/code/package.json
index 2feca7f1b1e3..db4035002a0b 100644
--- a/code/package.json
+++ b/code/package.json
@@ -78,6 +78,7 @@
"resolutions": {
"@playwright/test": "1.36.0",
"@storybook/theming": "workspace:*",
+ "@types/node": "^18.0.0",
"@vitest/expect@1.1.3": "patch:@vitest/expect@npm%3A1.1.3#~/.yarn/patches/@vitest-expect-npm-1.1.3-2062bf533f.patch",
"esbuild": "^0.18.0",
"playwright": "1.36.0",
@@ -100,6 +101,7 @@
"@storybook/addon-links": "workspace:*",
"@storybook/addon-mdx-gfm": "workspace:*",
"@storybook/addon-measure": "workspace:*",
+ "@storybook/addon-onboarding": "workspace:*",
"@storybook/addon-outline": "workspace:*",
"@storybook/addon-storysource": "workspace:*",
"@storybook/addon-toolbars": "workspace:*",
@@ -292,5 +294,6 @@
"Dependency Upgrades"
]
]
- }
+ },
+ "deferredNextVersion": "8.0.0-beta.6"
}
diff --git a/code/renderers/react/src/docs/jsxDecorator.tsx b/code/renderers/react/src/docs/jsxDecorator.tsx
index e43702daf02f..3395a3860557 100644
--- a/code/renderers/react/src/docs/jsxDecorator.tsx
+++ b/code/renderers/react/src/docs/jsxDecorator.tsx
@@ -130,7 +130,7 @@ export const renderJsx = (code: React.ReactElement, options: JSXOptions) => {
return string;
}).join('\n');
- return result.replace(/function\s+noRefCheck\(\)\s+\{\}/g, '() => {}');
+ return result.replace(/function\s+noRefCheck\(\)\s*\{\}/g, '() => {}');
};
const defaultOpts = {
diff --git a/code/renderers/svelte/template/components/Html.svelte b/code/renderers/svelte/template/components/Html.svelte
index e341acf73280..bc155de5d4b1 100644
--- a/code/renderers/svelte/template/components/Html.svelte
+++ b/code/renderers/svelte/template/components/Html.svelte
@@ -5,4 +5,4 @@
export let content = '';
-{@html content}>
+{@html content}
diff --git a/code/renderers/vue3/src/docs/__snapshots__/extractArgTypes.test.ts.snap b/code/renderers/vue3/src/docs/__snapshots__/extractArgTypes.test.ts.snap
index b4a5c410991a..cbdcb9f51226 100644
--- a/code/renderers/vue3/src/docs/__snapshots__/extractArgTypes.test.ts.snap
+++ b/code/renderers/vue3/src/docs/__snapshots__/extractArgTypes.test.ts.snap
@@ -1,277 +1,1042 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-exports[`extractArgTypes > should extract events for Vue component 1`] = `
+exports[`extractArgTypes (vue-docgen-api) > should extract events for Vue component 1`] = `
{
"bar": {
"control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
+ "disabled": true,
},
"description": "",
"name": "bar",
"table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
+ "category": "events",
"type": {
"summary": "[value: { year: number; title?: any; }]",
},
},
"type": {
- "name": [
- {
- "kind": "object",
- "schema": {
- "title": {
- "declarations": [
- {
- "file": "/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue",
- "range": [
- 269,
- 280,
- ],
- },
- ],
- "description": "",
- "global": false,
- "name": "title",
- "required": false,
- "schema": "any",
- "tags": [],
- "type": "any",
- },
- "year": {
- "declarations": [
- {
- "file": "/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue",
- "range": [
- 255,
- 268,
- ],
- },
- ],
- "description": "",
- "global": false,
- "name": "year",
- "required": true,
- "schema": "number",
- "tags": [],
- "type": "number",
- },
- },
- "type": "{ year: number; title?: any; }",
- },
- ],
- "required": false,
+ "name": "other",
+ "value": "[value: { year: number; title?: any; }]",
},
},
"baz": {
"control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
+ "disabled": true,
},
"description": "",
"name": "baz",
"table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
+ "category": "events",
"type": {
"summary": "[]",
},
},
"type": {
- "name": [],
- "required": false,
+ "name": "other",
+ "value": "[]",
},
},
"foo": {
"control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
+ "disabled": true,
},
"description": "",
"name": "foo",
"table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
+ "category": "events",
"type": {
"summary": "[data?: { foo: string; }]",
},
},
"type": {
- "name": [
- {
- "kind": "enum",
- "schema": [
- "undefined",
- {
- "kind": "object",
- "schema": {
- "foo": {
- "declarations": [
- {
- "file": "/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue",
- "range": [
- 207,
- 218,
- ],
- },
- ],
- "description": "",
- "global": false,
- "name": "foo",
- "required": true,
- "schema": "string",
- "tags": [],
- "type": "string",
- },
- },
- "type": "{ foo: string; }",
- },
- ],
- "type": "{ foo: string; } | undefined",
- },
- ],
+ "name": "other",
+ "value": "[data?: { foo: string; } | undefined]",
+ },
+ },
+}
+`;
+
+exports[`extractArgTypes (vue-docgen-api) > should extract events for component 1`] = `
+{
+ "bar": {
+ "control": {
+ "disabled": true,
+ },
+ "description": "Test description bar",
+ "name": "bar",
+ "table": {
+ "category": "events",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "{ year: number; title?: any }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "{ year: number; title?: any }",
+ },
+ },
+ "baz": {
+ "control": {
+ "disabled": true,
+ },
+ "description": "Test description baz",
+ "name": "baz",
+ "table": {
+ "category": "events",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": undefined,
+ },
+ "type": {
+ "name": "other",
"required": false,
+ "value": "",
},
},
}
`;
-exports[`extractArgTypes > should extract slots type for Vue component 1`] = `
+exports[`extractArgTypes (vue-docgen-api) > should extract expose for component 1`] = `
{
- "default": {
+ "count": {
+ "control": {
+ "disabled": true,
+ },
+ "description": "a count number",
+ "name": "count",
+ "table": {
+ "category": "expose",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": undefined,
+ },
+ "type": {
+ "name": "other",
+ "value": "",
+ },
+ },
+ "label": {
+ "control": {
+ "disabled": true,
+ },
+ "description": "a label string",
+ "name": "label",
+ "table": {
+ "category": "expose",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": undefined,
+ },
+ "type": {
+ "name": "other",
+ "value": "",
+ },
+ },
+}
+`;
+
+exports[`extractArgTypes (vue-docgen-api) > should extract props for component 1`] = `
+{
+ "array": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description required array object",
+ "name": "array",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "MyNestedProps[]",
+ },
+ },
+ "type": {
+ "name": "array",
+ "required": true,
+ "value": {
+ "name": "object",
+ "required": false,
+ "value": {
+ "nestedProp": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ },
+ },
+ },
+ "arrayOptional": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description optional array object",
+ "name": "arrayOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "MyNestedProps[]",
+ },
+ },
+ "type": {
+ "name": "array",
+ "required": false,
+ "value": {
+ "name": "object",
+ "required": false,
+ "value": {
+ "nestedProp": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ },
+ },
+ },
+ "bar": {
"control": {
"disabled": false,
},
"defaultValue": {
- "summary": undefined,
+ "summary": "1",
},
- "description": "",
- "name": "default",
+ "description": "description bar is optional number",
+ "name": "bar",
"table": {
"category": "props",
"defaultValue": {
- "summary": undefined,
+ "summary": "1",
},
- "jsDocTags": [],
"type": {
- "summary": "{ num: number; }",
+ "summary": "number",
},
},
"type": {
- "name": "object",
+ "name": "number",
"required": false,
+ },
+ },
+ "baz": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description baz is required boolean",
+ "name": "baz",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "boolean",
+ },
+ },
+ "type": {
+ "name": "boolean",
+ "required": true,
+ },
+ },
+ "enumValue": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description enum value",
+ "name": "enumValue",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "MyEnum",
+ },
+ },
+ "type": {
+ "name": "enum",
+ "required": true,
+ "value": [
+ "MyEnum.Small",
+ "MyEnum.Medium",
+ "MyEnum.Large",
+ ],
+ },
+ },
+ "foo": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "@default: "rounded" @since: v1.0.0 @see: https://vuejs.org/ @deprecated: v1.1.0 string foo",
+ "name": "foo",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "string",
+ },
+ },
+ "type": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ "inlined": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "",
+ "name": "inlined",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "{ foo: string; }",
+ },
+ },
+ "type": {
+ "name": "object",
+ "required": true,
"value": {
- "num": {
- "name": "number",
+ "foo": {
+ "name": "string",
"required": true,
},
},
},
},
- "named": {
+ "literalFromContext": {
"control": {
"disabled": false,
},
- "defaultValue": {
- "summary": undefined,
+ "defaultValue": undefined,
+ "description": "description literal type alias that require context",
+ "name": "literalFromContext",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": ""Uncategorized" | "Content" | "Interaction" | "Display" | "Forms" | "Addons"",
+ },
},
- "description": "",
- "name": "named",
+ "type": {
+ "name": "enum",
+ "required": true,
+ "value": [
+ "Uncategorized",
+ "Content",
+ "Interaction",
+ "Display",
+ "Forms",
+ "Addons",
+ ],
+ },
+ },
+ "nested": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description nested is required nested object",
+ "name": "nested",
"table": {
"category": "props",
- "defaultValue": {
- "summary": undefined,
+ "defaultValue": undefined,
+ "type": {
+ "summary": "MyNestedProps",
+ },
+ },
+ "type": {
+ "name": "object",
+ "required": true,
+ "value": {
+ "nestedProp": {
+ "name": "string",
+ "required": true,
+ },
},
- "jsDocTags": [],
+ },
+ },
+ "nestedIntersection": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description required nested object with intersection",
+ "name": "nestedIntersection",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
"type": {
- "summary": "{ str: string; }",
+ "summary": "MyNestedProps & { additionalProp: string; }",
},
},
"type": {
"name": "object",
- "required": false,
+ "required": true,
"value": {
- "str": {
+ "additionalProp": {
+ "name": "string",
+ "required": true,
+ },
+ "nestedProp": {
"name": "string",
"required": true,
},
},
},
},
- "no-bind": {
+ "nestedOptional": {
"control": {
"disabled": false,
},
- "defaultValue": {
- "summary": undefined,
+ "defaultValue": undefined,
+ "description": "description optional nested object",
+ "name": "nestedOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "MyNestedProps | MyIgnoredNestedProps",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": false,
+ "value": [
+ {
+ "name": "object",
+ "required": false,
+ "value": {
+ "nestedProp": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ },
+ {
+ "name": "object",
+ "required": false,
+ "value": {
+ "nestedProp": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ },
+ ],
+ },
+ },
+ "recursive": {
+ "control": {
+ "disabled": false,
},
+ "defaultValue": undefined,
"description": "",
- "name": "no-bind",
+ "name": "recursive",
"table": {
"category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
+ "defaultValue": undefined,
"type": {
- "summary": "{}",
+ "summary": "MyNestedRecursiveProps",
},
},
"type": {
"name": "object",
"required": false,
- "value": {},
+ "value": {
+ "recursive": {
+ "name": "other",
+ "required": true,
+ "value": "MyNestedRecursiveProps",
+ },
+ },
},
},
- "vbind": {
+ "stringArray": {
"control": {
"disabled": false,
},
"defaultValue": {
- "summary": undefined,
+ "summary": "["foo", "bar"]",
},
- "description": "",
- "name": "vbind",
+ "description": "description stringArray is string array",
+ "name": "stringArray",
"table": {
"category": "props",
"defaultValue": {
- "summary": undefined,
+ "summary": "["foo", "bar"]",
},
- "jsDocTags": [],
"type": {
- "summary": "{ num: number; str: string; }",
+ "summary": "string[]",
},
},
"type": {
- "name": "object",
+ "name": "array",
"required": false,
"value": {
- "num": {
- "name": "number",
- "required": true,
- },
- "str": {
+ "name": "string",
+ "required": false,
+ },
+ },
+ },
+ "union": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description union is required union type",
+ "name": "union",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "string | number",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": true,
+ "value": [
+ {
"name": "string",
- "required": true,
+ "required": false,
},
+ {
+ "name": "number",
+ "required": false,
+ },
+ ],
+ },
+ },
+ "unionOptional": {
+ "control": {
+ "disabled": false,
+ },
+ "defaultValue": undefined,
+ "description": "description unionOptional is optional union type",
+ "name": "unionOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": undefined,
+ "type": {
+ "summary": "string | number | boolean",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": false,
+ "value": [
+ {
+ "name": "string",
+ "required": false,
+ },
+ {
+ "name": "number",
+ "required": false,
+ },
+ {
+ "name": "boolean",
+ "required": false,
+ },
+ ],
+ },
+ },
+}
+`;
+
+exports[`extractArgTypes (vue-docgen-api) > should extract props for component 2`] = `
+{
+ "array": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description required array object",
+ "name": "array",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedProps[]",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "Array([object Object])",
+ },
+ },
+ "arrayOptional": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description optional array object",
+ "name": "arrayOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedProps[]",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "Array([object Object])",
+ },
+ },
+ "bar": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description bar is optional number",
+ "name": "bar",
+ "table": {
+ "category": "props",
+ "defaultValue": {
+ "summary": "1",
+ },
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "number",
+ },
+ },
+ "type": {
+ "name": "number",
+ "required": false,
+ },
+ },
+ "baz": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description baz is required boolean",
+ "name": "baz",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "boolean",
+ },
+ },
+ "type": {
+ "name": "boolean",
+ "required": true,
+ },
+ },
+ "enumValue": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description enum value",
+ "name": "enumValue",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyEnum",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "MyEnum",
+ },
+ },
+ "foo": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "string foo",
+ "name": "foo",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "string",
+ },
+ },
+ "type": {
+ "name": "string",
+ "required": true,
+ },
+ },
+ "inlined": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "inlined",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "{ foo: string }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "{ foo: string }",
+ },
+ },
+ "literalFromContext": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description literal type alias that require context",
+ "name": "literalFromContext",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyCategories",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "MyCategories",
+ },
+ },
+ "nested": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description nested is required nested object",
+ "name": "nested",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedProps",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "MyNestedProps",
+ },
+ },
+ "nestedIntersection": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description required nested object with intersection",
+ "name": "nestedIntersection",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedProps & {
+ /**
+ * description required additional property
+ */
+ additionalProp: string;
+}",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": true,
+ "value": "intersection([object Object],[object Object])",
+ },
+ },
+ "nestedOptional": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description optional nested object",
+ "name": "nestedOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedProps | MyIgnoredNestedProps",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": false,
+ "value": [
+ {
+ "name": "other",
+ "value": "MyNestedProps",
+ },
+ {
+ "name": "other",
+ "value": "MyIgnoredNestedProps",
+ },
+ ],
+ },
+ },
+ "recursive": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "recursive",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "MyNestedRecursiveProps",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "MyNestedRecursiveProps",
+ },
+ },
+ "stringArray": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description stringArray is string array",
+ "name": "stringArray",
+ "table": {
+ "category": "props",
+ "defaultValue": {
+ "summary": "() => ['foo', 'bar']",
+ },
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "string[]",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "Array([object Object])",
+ },
+ },
+ "union": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description union is required union type",
+ "name": "union",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "string | number",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": true,
+ "value": [
+ {
+ "name": "string",
+ },
+ {
+ "name": "number",
+ },
+ ],
+ },
+ },
+ "unionOptional": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "description unionOptional is optional union type",
+ "name": "unionOptional",
+ "table": {
+ "category": "props",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "string | number | boolean",
+ },
+ },
+ "type": {
+ "name": "union",
+ "required": false,
+ "value": [
+ {
+ "name": "string",
+ },
+ {
+ "name": "number",
+ },
+ {
+ "name": "boolean",
+ },
+ ],
+ },
+ },
+}
+`;
+
+exports[`extractArgTypes (vue-docgen-api) > should extract slots for component 1`] = `
+{
+ "default": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "default",
+ "table": {
+ "category": "slots",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "{ num: unknown }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "{ num: unknown }",
+ },
+ },
+ "named": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "named",
+ "table": {
+ "category": "slots",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "{ str: unknown }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "{ str: unknown }",
+ },
+ },
+ "no-bind": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "no-bind",
+ "table": {
+ "category": "slots",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": undefined,
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "",
+ },
+ },
+ "vbind": {
+ "control": {
+ "disabled": false,
+ },
+ "description": undefined,
+ "name": "vbind",
+ "table": {
+ "category": "slots",
+ "defaultValue": null,
+ "jsDocTags": undefined,
+ "type": {
+ "summary": "{ num: unknown; str: unknown }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "required": false,
+ "value": "{ num: unknown; str: unknown }",
+ },
+ },
+}
+`;
+
+exports[`extractArgTypes (vue-docgen-api) > should extract slots type for Vue component 1`] = `
+{
+ "default": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "",
+ "name": "default",
+ "table": {
+ "category": "slots",
+ "type": {
+ "summary": "{ num: number; }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "value": "{ num: number; }",
+ },
+ },
+ "named": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "",
+ "name": "named",
+ "table": {
+ "category": "slots",
+ "type": {
+ "summary": "{ str: string; }",
},
},
+ "type": {
+ "name": "other",
+ "value": "{ str: string; }",
+ },
+ },
+ "no-bind": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "",
+ "name": "no-bind",
+ "table": {
+ "category": "slots",
+ "type": {
+ "summary": "{}",
+ },
+ },
+ "type": {
+ "name": "other",
+ "value": "{}",
+ },
+ },
+ "vbind": {
+ "control": {
+ "disabled": false,
+ },
+ "description": "",
+ "name": "vbind",
+ "table": {
+ "category": "slots",
+ "type": {
+ "summary": "{ num: number; str: string; }",
+ },
+ },
+ "type": {
+ "name": "other",
+ "value": "{ num: number; str: string; }",
+ },
},
}
`;
diff --git a/code/renderers/vue3/src/docs/extractArgTypes.test.ts b/code/renderers/vue3/src/docs/extractArgTypes.test.ts
index 2c978af54dd0..053266882e87 100644
--- a/code/renderers/vue3/src/docs/extractArgTypes.test.ts
+++ b/code/renderers/vue3/src/docs/extractArgTypes.test.ts
@@ -9,519 +9,49 @@ import {
referenceTypeEvents,
referenceTypeProps,
templateSlots,
+ vueDocgenMocks,
} from './tests-meta-components/meta-components';
-vitest.mock('@storybook/docs-tools');
+vitest.mock('@storybook/docs-tools', async (importOriginal) => {
+ const module: Record = await importOriginal();
+ return {
+ ...module,
+ extractComponentProps: vi.fn(),
+ hasDocgen: vi.fn(),
+ };
+});
-describe('extractArgTypes', () => {
+describe('extractArgTypes (vue-docgen-api)', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('should return null if component does not contain docs', () => {
- (hasDocgen as Mock).mockReturnValueOnce(false);
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(false);
(extractComponentProps as Mock).mockReturnValueOnce([] as any);
expect(extractArgTypes({} as any)).toBeNull();
});
- it('should extract arg types for component', () => {
+ it('should extract props for component', () => {
const component = referenceTypeProps;
- (hasDocgen as Mock).mockReturnValueOnce(true);
- (extractComponentProps as Mock).mockReturnValue(mockExtractComponentPropsReturn);
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'props' ? mockExtractComponentPropsReturn : [];
+ });
const argTypes = extractArgTypes(component);
- expect(argTypes).toMatchInlineSnapshot(`
- {
- "array": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description required array object",
- "name": "array",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedProps[]",
- },
- },
- "type": {
- "name": "array",
- "required": true,
- "value": {
- "name": "object",
- "required": false,
- "value": {
- "nestedProp": {
- "name": "string",
- "required": true,
- },
- },
- },
- },
- },
- "arrayOptional": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description optional array object",
- "name": "arrayOptional",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedProps[]",
- },
- },
- "type": {
- "name": "array",
- "required": false,
- "value": {
- "name": "object",
- "required": false,
- "value": {
- "nestedProp": {
- "name": "string",
- "required": true,
- },
- },
- },
- },
- },
- "bar": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": "1",
- },
- "description": "description bar is optional number",
- "name": "bar",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": "1",
- },
- "jsDocTags": [],
- "type": {
- "summary": "number",
- },
- },
- "type": {
- "name": "number",
- "required": false,
- },
- },
- "baz": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description baz is required boolean",
- "name": "baz",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "boolean",
- },
- },
- "type": {
- "name": "boolean",
- "required": true,
- },
- },
- "enumValue": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description enum value",
- "name": "enumValue",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyEnum",
- },
- },
- "type": {
- "name": "enum",
- "required": true,
- "value": [
- "MyEnum.Small",
- "MyEnum.Medium",
- "MyEnum.Large",
- ],
- },
- },
- "foo": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "@default: "rounded" @since: v1.0.0 @see: https://vuejs.org/ @deprecated: v1.1.0 string foo",
- "name": "foo",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [
- {
- "name": "default",
- "text": ""rounded"",
- },
- {
- "name": "since",
- "text": "v1.0.0",
- },
- {
- "name": "see",
- "text": "https://vuejs.org/",
- },
- {
- "name": "deprecated",
- "text": "v1.1.0",
- },
- ],
- "type": {
- "summary": "string",
- },
- },
- "type": {
- "name": "string",
- "required": true,
- },
- },
- "inlined": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "",
- "name": "inlined",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "{ foo: string; }",
- },
- },
- "type": {
- "name": "object",
- "required": true,
- "value": {
- "foo": {
- "name": "string",
- "required": true,
- },
- },
- },
- },
- "literalFromContext": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description literal type alias that require context",
- "name": "literalFromContext",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": ""Uncategorized" | "Content" | "Interaction" | "Display" | "Forms" | "Addons"",
- },
- },
- "type": {
- "name": "enum",
- "required": true,
- "value": [
- "Uncategorized",
- "Content",
- "Interaction",
- "Display",
- "Forms",
- "Addons",
- ],
- },
- },
- "nested": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description nested is required nested object",
- "name": "nested",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedProps",
- },
- },
- "type": {
- "name": "object",
- "required": true,
- "value": {
- "nestedProp": {
- "name": "string",
- "required": true,
- },
- },
- },
- },
- "nestedIntersection": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description required nested object with intersection",
- "name": "nestedIntersection",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedProps & { additionalProp: string; }",
- },
- },
- "type": {
- "name": "object",
- "required": true,
- "value": {
- "additionalProp": {
- "name": "string",
- "required": true,
- },
- "nestedProp": {
- "name": "string",
- "required": true,
- },
- },
- },
- },
- "nestedOptional": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description optional nested object",
- "name": "nestedOptional",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedProps | MyIgnoredNestedProps",
- },
- },
- "type": {
- "name": {
- "kind": "enum",
- "schema": [
- "undefined",
- {
- "kind": "object",
- "schema": {
- "nestedProp": {
- "declarations": [],
- "description": "nested prop documentation",
- "global": false,
- "name": "nestedProp",
- "required": true,
- "schema": "string",
- "tags": [],
- "type": "string",
- },
- },
- "type": "MyNestedProps",
- },
- {
- "kind": "object",
- "schema": {
- "nestedProp": {
- "declarations": [],
- "description": "",
- "global": false,
- "name": "nestedProp",
- "required": true,
- "schema": "string",
- "tags": [],
- "type": "string",
- },
- },
- "type": "MyIgnoredNestedProps",
- },
- ],
- "type": "MyNestedProps | MyIgnoredNestedProps | undefined",
- },
- "required": false,
- },
- },
- "recursive": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "",
- "name": "recursive",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "MyNestedRecursiveProps",
- },
- },
- "type": {
- "name": "object",
- "required": false,
- "value": {
- "recursive": {
- "name": "MyNestedRecursiveProps",
- "required": true,
- },
- },
- },
- },
- "stringArray": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": "["foo", "bar"]",
- },
- "description": "description stringArray is string array",
- "name": "stringArray",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": "["foo", "bar"]",
- },
- "jsDocTags": [],
- "type": {
- "summary": "string[]",
- },
- },
- "type": {
- "name": "array",
- "required": false,
- "value": {
- "name": "string",
- "required": false,
- },
- },
- },
- "union": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description union is required union type",
- "name": "union",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "string | number",
- },
- },
- "type": {
- "name": "string",
- "required": true,
- },
- },
- "unionOptional": {
- "control": {
- "disabled": false,
- },
- "defaultValue": {
- "summary": undefined,
- },
- "description": "description unionOptional is optional union type",
- "name": "unionOptional",
- "table": {
- "category": "props",
- "defaultValue": {
- "summary": undefined,
- },
- "jsDocTags": [],
- "type": {
- "summary": "string | number | boolean",
- },
- },
- "type": {
- "name": "string",
- "required": false,
- },
- },
- }
- `);
+ expect(argTypes).toMatchSnapshot();
});
it('should extract events for Vue component', () => {
const component = referenceTypeEvents;
- (hasDocgen as Mock).mockReturnValueOnce(true);
- (extractComponentProps as Mock).mockReturnValue(mockExtractComponentEventsReturn);
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'events' ? mockExtractComponentEventsReturn : [];
+ });
const argTypes = extractArgTypes(component);
@@ -530,8 +60,68 @@ describe('extractArgTypes', () => {
it('should extract slots type for Vue component', () => {
const component = templateSlots;
- (hasDocgen as Mock).mockReturnValueOnce(true);
- (extractComponentProps as Mock).mockReturnValue(mockExtractComponentSlotsReturn);
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'slots' ? mockExtractComponentSlotsReturn : [];
+ });
+
+ const argTypes = extractArgTypes(component);
+
+ expect(argTypes).toMatchSnapshot();
+ });
+});
+
+describe('extractArgTypes (vue-docgen-api)', () => {
+ beforeEach(() => {
+ vi.resetAllMocks();
+ });
+
+ it('should extract props for component', async () => {
+ const component = vueDocgenMocks.props.component;
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'props' ? vueDocgenMocks.props.extractedProps : [];
+ });
+
+ const argTypes = extractArgTypes(component);
+
+ expect(argTypes).toMatchSnapshot();
+ });
+
+ it('should extract events for component', async () => {
+ const component = vueDocgenMocks.events.component;
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'events' ? vueDocgenMocks.events.extractedProps : [];
+ });
+
+ const argTypes = extractArgTypes(component);
+
+ expect(argTypes).toMatchSnapshot();
+ });
+
+ it('should extract slots for component', async () => {
+ const component = vueDocgenMocks.slots.component;
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'slots' ? vueDocgenMocks.slots.extractedProps : [];
+ });
+
+ const argTypes = extractArgTypes(component);
+
+ expect(argTypes).toMatchSnapshot();
+ });
+
+ it('should extract expose for component', async () => {
+ const component = vueDocgenMocks.expose.component;
+ (hasDocgen as unknown as Mock).mockReturnValueOnce(true);
+
+ (extractComponentProps as Mock).mockImplementation((_, section) => {
+ return section === 'expose' ? vueDocgenMocks.expose.extractedProps : [];
+ });
const argTypes = extractArgTypes(component);
diff --git a/code/renderers/vue3/src/docs/extractArgTypes.ts b/code/renderers/vue3/src/docs/extractArgTypes.ts
index 5a535d40599c..151476491c95 100644
--- a/code/renderers/vue3/src/docs/extractArgTypes.ts
+++ b/code/renderers/vue3/src/docs/extractArgTypes.ts
@@ -1,60 +1,51 @@
-import type { SBType, StrictArgTypes } from '@storybook/types';
-
+import type { ExtractedProp } from '@storybook/docs-tools';
import {
+ convert,
extractComponentProps,
- convert as genericConvert,
hasDocgen,
type ArgTypesExtractor,
- type DocgenInfo,
} from '@storybook/docs-tools';
+import type { SBType, StrictArgTypes, StrictInputType } from '@storybook/types';
+import type { VueDocgenInfo, VueDocgenInfoEntry, VueDocgenPlugin } from '@storybook/vue3-vite';
-type Schema = { kind: string; schema: [] | object; type?: string } | string;
-type MetaDocgenInfo = DocgenInfo & {
- type: string | { name: string; value: string[] };
- default: string;
- global: boolean;
- name: string;
- schema?: Schema;
- tags?: { name: string; text: string }[];
-};
+type PropertyMetaSchema = VueDocgenInfoEntry<'vue-component-meta', 'props'>['schema'];
// "exposed" is used by the vue-component-meta plugin while "expose" is used by vue-docgen-api
const ARG_TYPE_SECTIONS = ['props', 'events', 'slots', 'exposed', 'expose'] as const;
export const extractArgTypes: ArgTypesExtractor = (component) => {
- if (!hasDocgen(component)) {
+ if (!hasDocgen>(component)) {
return null;
}
+ const usedDocgenPlugin: VueDocgenPlugin =
+ // eslint-disable-next-line no-underscore-dangle
+ 'exposed' in component.__docgenInfo ? 'vue-component-meta' : 'vue-docgen-api';
+
const argTypes: StrictArgTypes = {};
ARG_TYPE_SECTIONS.forEach((section) => {
const props = extractComponentProps(component, section);
props.forEach((extractedProp) => {
- const docgenInfo = extractedProp.docgenInfo as MetaDocgenInfo;
+ let argType: StrictInputType | undefined;
- if (argTypes[docgenInfo.name] || docgenInfo.global) {
- return; // skip duplicate and global props
+ // use the corresponding extractor whether vue-docgen-api or vue-component-meta
+ // was used for the docinfo
+ if (usedDocgenPlugin === 'vue-docgen-api') {
+ const docgenInfo = extractedProp.docgenInfo as VueDocgenInfoEntry<'vue-docgen-api'>;
+ argType = extractFromVueDocgenApi(docgenInfo, section, extractedProp);
+ } else {
+ const docgenInfo =
+ extractedProp.docgenInfo as unknown as VueDocgenInfoEntry<'vue-component-meta'>;
+ argType = extractFromVueComponentMeta(docgenInfo, section);
}
- const type =
- typeof docgenInfo.type === 'string' ? docgenInfo.type : docgenInfo.type?.name ?? '';
- const sbType = section === 'props' ? convertPropType(docgenInfo) : ({ name: type } as SBType);
+ // skip duplicate and global props
+ if (!argType || argTypes[argType.name]) return;
- const defaultValue = { summary: docgenInfo.default };
-
- argTypes[docgenInfo.name] = {
- name: docgenInfo.name,
- description: formatDescriptionWithTags(docgenInfo.description, docgenInfo.tags),
- defaultValue,
- type: sbType,
- table: {
- type: { summary: type.replace(' | undefined', '') },
- jsDocTags: docgenInfo.tags ?? [],
- defaultValue,
- category: section,
- },
+ argTypes[argType.name] = {
+ ...argType,
control: { disabled: !['props', 'slots'].includes(section) },
};
});
@@ -64,81 +55,289 @@ export const extractArgTypes: ArgTypesExtractor = (component) => {
};
/**
- * Converts the given prop type into a SBType so it can be correctly displayed in the UI (controls etc.).
+ * Extracts the argType for a prop/event/slot/expose generated with "vue-docgen-api".
+ *
+ * @param docgenInfo __docgenInfo from "vue-docgen-api"
+ * @param section Whether the arg is a prop, event, slot or expose
+ * @param extractedProp Extracted prop, needed when "section" is "slots"
*/
-export const convertPropType = (propInfo: MetaDocgenInfo): SBType => {
- const schema = propInfo.schema;
- const required = propInfo.required ?? false;
- const fallbackSbType = { name: schema, required } as SBType;
+export const extractFromVueDocgenApi = (
+ docgenInfo: VueDocgenInfoEntry<'vue-docgen-api'>,
+ section: (typeof ARG_TYPE_SECTIONS)[number],
+ extractedProp?: ExtractedProp
+): StrictInputType => {
+ let type: string | undefined;
+ let sbType: SBType | undefined;
- if (!schema) return genericConvert(propInfo) ?? fallbackSbType;
- if (typeof schema === 'string') return fallbackSbType;
+ if (section === 'events') {
+ const eventInfo = docgenInfo as VueDocgenInfoEntry<'vue-docgen-api', 'events'>;
+ type = eventInfo.type?.names.join();
+ sbType = { name: 'other', value: type ?? '', required: false };
+ }
- // convert enum schemas (e.g. union or enum type to corresponding SBType)
- // so the enum values can be selected via radio/dropdown in the UI
- if (schema.kind === 'enum' && Array.isArray(schema.schema)) {
- // filter out empty or "undefined" for optional props
- const definedValues = schema.schema.filter((item) => item != null && item !== 'undefined');
+ if (section === 'slots') {
+ const slotInfo = docgenInfo as VueDocgenInfoEntry<'vue-docgen-api', 'slots'>;
- if (definedValues.length === 1 && typeof definedValues[0] === 'object') {
- return convertPropType({ schema: definedValues[0] } as MetaDocgenInfo);
- }
+ // extract type of slot bindings/props
+ const slotBindings = slotInfo.bindings
+ ?.filter((binding) => !!binding.name)
+ .map((binding) => {
+ return `${binding.name}: ${binding.type?.name ?? 'unknown'}`;
+ })
+ .join('; ');
- const values = definedValues
- .filter((item: Schema) => typeof item === 'string')
- .map((item: Schema) => (typeof item !== 'string' ? item.schema.toString() : item))
- .map((item: string) => item.replace(/'/g, '"'));
+ type = slotBindings ? `{ ${slotBindings} }` : undefined;
+ sbType = { name: 'other', value: type ?? '', required: false };
+ }
- if (values.length === 0) return fallbackSbType;
+ if (section === 'props') {
+ const propInfo = docgenInfo as VueDocgenInfoEntry<'vue-docgen-api', 'props'>;
- const isBoolean = values.length === 2 && values.includes('true') && values.includes('false');
- if (isBoolean) return { name: 'boolean', required };
+ type = propInfo.type?.name;
+ sbType = extractedProp ? convert(extractedProp.docgenInfo) : { name: 'other', value: type };
- const isLateralUnion =
- values.length > 1 && values.every((item) => item.startsWith('"') && item.endsWith('"'));
- const isEnum =
- !isLateralUnion &&
- values.length > 1 &&
- values.every((item) => typeof item === 'string' && item.includes('.'));
+ // try to get more specific types for array, union and intersection
+ // e.g. "string[]" instead of "Array"
+ if (
+ propInfo.type &&
+ 'elements' in propInfo.type &&
+ Array.isArray(propInfo.type.elements) &&
+ propInfo.type.elements.length > 0
+ ) {
+ const elements = (propInfo.type.elements as { name: string }[]).map((i) => i.name);
- if (isLateralUnion || isEnum) {
- const valuesWithoutQuotes = values.map((item: string) => item.replace(/"/g, ''));
- return { name: 'enum', value: valuesWithoutQuotes, required };
- }
+ if (type === 'Array') {
+ const arrayElements = elements.length === 1 ? elements[0] : `(${elements.join(' | ')})`;
+ type = `${arrayElements}[]`;
+ }
- return { name: values[0], required } as SBType;
+ if (type === 'union') type = elements.join(' | ');
+ else if (type === 'intersection') type = elements.join(' & ');
+ }
}
- // recursively convert object properties to SBType
- if (schema.kind === 'object' && typeof schema.schema === 'object') {
- const schemaObject = schema.schema as Record;
+ const required = 'required' in docgenInfo ? docgenInfo.required ?? false : false;
+
+ return {
+ name: docgenInfo.name,
+ description: docgenInfo.description,
+ type: sbType ? { ...sbType, required } : { name: 'other', value: type ?? '' },
+ table: {
+ type: type ? { summary: type } : undefined,
+ defaultValue: extractedProp?.propDef.defaultValue,
+ jsDocTags: extractedProp?.propDef.jsDocTags,
+ category: section,
+ },
+ };
+};
+
+/**
+ * Extracts the argType for a prop/event/slot/exposed generated with "vue-component-meta".
+
+ * @param docgenInfo __docgenInfo from "vue-component-meta"
+ * @param section Whether the arg is a prop, event, slot or exposed
+ */
+export const extractFromVueComponentMeta = (
+ docgenInfo: VueDocgenInfoEntry<'vue-component-meta'>,
+ section: (typeof ARG_TYPE_SECTIONS)[number]
+): StrictInputType | undefined => {
+ // ignore global props
+ if ('global' in docgenInfo && docgenInfo.global) return;
+
+ const tableType = { summary: docgenInfo.type.replace(' | undefined', '') };
+
+ if (section === 'props') {
+ const propInfo = docgenInfo as VueDocgenInfoEntry<'vue-component-meta', 'props'>;
+ const defaultValue = propInfo.default ? { summary: propInfo.default } : undefined;
return {
- name: 'object',
- required,
- value: Object.entries(schemaObject).reduce>((obj, [key, value]) => {
- obj[key] = convertPropType(value);
- return obj;
- }, {}),
+ name: propInfo.name,
+ description: formatDescriptionWithTags(propInfo.description, propInfo.tags),
+ defaultValue,
+ type: convertVueComponentMetaProp(propInfo),
+ table: {
+ type: tableType,
+ defaultValue,
+ category: section,
+ },
};
- }
-
- if (schema.kind === 'array' && Array.isArray(schema.schema)) {
+ } else {
return {
- name: 'array',
- value: convertPropType({ schema: schema.schema[0] } as MetaDocgenInfo),
- required,
+ name: docgenInfo.name,
+ description: 'description' in docgenInfo ? docgenInfo.description : '',
+ type: { name: 'other', value: docgenInfo.type },
+ table: { type: tableType, category: section },
};
}
+};
+
+/**
+ * Converts the given prop info that was generated with "vue-component-meta" into a SBType.
+ */
+export const convertVueComponentMetaProp = (
+ propInfo: Pick, 'schema' | 'required' | 'type'>
+): SBType => {
+ const schema = propInfo.schema;
+ const required = propInfo.required;
+ const fallbackSbType: SBType = { name: 'other', value: propInfo.type, required };
+
+ const KNOWN_SCHEMAS = ['string', 'number', 'function', 'boolean', 'symbol'] as const;
+ type KnownSchema = (typeof KNOWN_SCHEMAS)[number];
+
+ if (typeof schema === 'string') {
+ if (KNOWN_SCHEMAS.includes(schema as KnownSchema)) {
+ return { name: schema as KnownSchema, required };
+ }
+ return fallbackSbType;
+ }
+
+ switch (schema.kind) {
+ case 'enum': {
+ // filter out empty or "undefined" for optional props
+ let definedSchemas = schema.schema?.filter((item) => item !== 'undefined') ?? [];
- return fallbackSbType;
+ if (isBooleanSchema(definedSchemas)) {
+ return { name: 'boolean', required };
+ }
+
+ if (isLiteralUnionSchema(definedSchemas) || isEnumSchema(definedSchemas)) {
+ // remove quotes from literals
+ const literals = definedSchemas.map((literal) => literal.replace(/"/g, ''));
+ return { name: 'enum', value: literals, required };
+ }
+
+ if (definedSchemas.length === 1) {
+ return convertVueComponentMetaProp({
+ schema: definedSchemas[0],
+ type: propInfo.type,
+ required,
+ });
+ }
+
+ // for union types like "string | number | boolean",
+ // the schema will be "string | number | true | false"
+ // so we need to replace "true | false" with boolean
+ if (
+ definedSchemas.length > 2 &&
+ definedSchemas.includes('true') &&
+ definedSchemas.includes('false')
+ ) {
+ definedSchemas = definedSchemas.filter((i) => i !== 'true' && i !== 'false');
+ definedSchemas.push('boolean');
+ }
+
+ // recursively convert every type of the union
+ return {
+ name: 'union',
+ value: definedSchemas.map((i) => {
+ if (typeof i === 'object') {
+ return convertVueComponentMetaProp({
+ schema: i,
+ type: i.type,
+ required: false,
+ });
+ } else {
+ return convertVueComponentMetaProp({ schema: i, type: i, required: false });
+ }
+ }),
+ required,
+ };
+ }
+
+ case 'array': {
+ // filter out empty or "undefined" for optional props
+ const definedSchemas = schema.schema?.filter((item) => item !== 'undefined') ?? [];
+ if (definedSchemas.length === 0) return fallbackSbType;
+
+ if (definedSchemas.length === 1) {
+ return {
+ name: 'array',
+ value: convertVueComponentMetaProp({
+ schema: definedSchemas[0],
+ type: propInfo.type,
+ required: false,
+ }),
+ required,
+ };
+ }
+
+ // recursively convert every type of the array
+ // e.g. "(string | number)[]"
+ return {
+ name: 'union',
+ value: definedSchemas.map((i) => {
+ if (typeof i === 'object') {
+ return convertVueComponentMetaProp({
+ schema: i,
+ type: i.type,
+ required: false,
+ });
+ } else {
+ return convertVueComponentMetaProp({ schema: i, type: i, required: false });
+ }
+ }),
+ required,
+ };
+ }
+
+ // recursively/deeply convert all properties of the object
+ case 'object':
+ return {
+ name: 'object',
+ value: Object.entries(schema.schema ?? {}).reduce>(
+ (obj, [propName, propSchema]) => {
+ obj[propName] = convertVueComponentMetaProp(propSchema);
+ return obj;
+ },
+ {}
+ ),
+ required,
+ };
+
+ default:
+ return fallbackSbType;
+ }
};
/**
* Adds the descriptions for the given tags if available.
*/
-const formatDescriptionWithTags = (description: string, tags: MetaDocgenInfo['tags']): string => {
+const formatDescriptionWithTags = (
+ description: string,
+ tags: VueDocgenInfoEntry<'vue-component-meta', 'props'>['tags']
+): string => {
if (!tags?.length || !description) return description ?? '';
const tagDescriptions = tags.map((tag) => `@${tag.name}: ${tag.text}`).join(' ');
return `${tagDescriptions} ${description}`;
};
+
+/**
+ * Checks whether the given schemas are all literal union schemas.
+ *
+ * @example "foo" | "bar" | "baz"
+ */
+const isLiteralUnionSchema = (schemas: PropertyMetaSchema[]): schemas is `"${string}"`[] => {
+ return schemas.every(
+ (schema) => typeof schema === 'string' && schema.startsWith('"') && schema.endsWith('"')
+ );
+};
+
+/**
+ * Checks whether the given schemas are all enums.
+ *
+ * @example [MyEnum.Foo, MyEnum.Bar]
+ */
+const isEnumSchema = (schemas: PropertyMetaSchema[]): schemas is string[] => {
+ return schemas.every((schema) => typeof schema === 'string' && schema.includes('.'));
+};
+
+/**
+ * Checks whether the given schemas are representing a boolean.
+ *
+ * @example [true, false]
+ */
+const isBooleanSchema = (
+ schemas: PropertyMetaSchema[]
+): schemas is ['true', 'false'] | ['false', 'true'] => {
+ return schemas.length === 2 && schemas.includes('true') && schemas.includes('false');
+};
diff --git a/code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts b/code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts
index 085b59a1e9ae..ad10ccc96a52 100644
--- a/code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts
+++ b/code/renderers/vue3/src/docs/tests-meta-components/meta-components.ts
@@ -1,8 +1,10 @@
-export const referenceTypeProps = {
- __name: 'component',
+import { TypeSystem } from '@storybook/docs-tools';
+import type { VueDocgenInfo } from 'frameworks/vue3-vite/src';
+
+type TestComponent = { __docgenInfo: VueDocgenInfo<'vue-component-meta'> };
+
+export const referenceTypeProps: TestComponent = {
__docgenInfo: {
- exportName: 'default',
- displayName: 'component',
type: 1,
props: [
{
@@ -468,380 +470,167 @@ export const referenceTypeProps = {
],
events: [],
slots: [],
- exposed: [
- {
- name: 'foo',
- type: 'string',
- description: 'string foo',
- declarations: [],
- schema: 'string',
+ exposed: [],
+ },
+};
+
+export const mockExtractComponentPropsReturn = [
+ {
+ propDef: {
+ name: 'bar',
+ type: {},
+ required: false,
+ description: 'description bar is optional number',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
},
- {
- name: 'bar',
- type: 'number',
- description: 'description bar is optional number',
- declarations: [],
- schema: 'number',
+ },
+ docgenInfo: {
+ name: 'bar',
+ global: false,
+ description: 'description bar is optional number',
+ tags: [],
+ required: false,
+ type: 'number | undefined',
+ declarations: [],
+ schema: {
+ kind: 'enum',
+ type: 'number | undefined',
+ schema: ['undefined', 'number'],
},
- {
- name: 'baz',
- type: 'boolean',
- description: 'description baz is required boolean',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'boolean',
- schema: ['false', 'true'],
- },
+ default: '1',
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'stringArray',
+ type: {},
+ required: false,
+ description: 'description stringArray is string array',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
},
- {
- name: 'stringArray',
- type: 'string[]',
- description: 'description stringArray is string array',
- declarations: [],
- schema: {
- kind: 'array',
- type: 'string[]',
- schema: ['string'],
- },
+ },
+ docgenInfo: {
+ name: 'stringArray',
+ global: false,
+ description: 'description stringArray is string array',
+ tags: [],
+ required: false,
+ type: 'string[] | undefined',
+ declarations: [],
+ schema: {
+ kind: 'enum',
+ type: 'string[] | undefined',
+ schema: [
+ 'undefined',
+ {
+ kind: 'array',
+ type: 'string[]',
+ schema: ['string'],
+ },
+ ],
},
- {
- name: 'union',
- type: 'string | number',
- description: 'description union is required union type',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'string | number',
- schema: ['string', 'number'],
- },
+ default: '["foo", "bar"]',
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'key',
+ type: {},
+ required: false,
+ description: '',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
},
- {
- name: 'unionOptional',
- type: 'string | number | boolean | undefined',
- description: 'description unionOptional is optional union type',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'string | number | boolean | undefined',
- schema: ['undefined', 'string', 'number', 'false', 'true'],
- },
+ },
+ docgenInfo: {
+ name: 'key',
+ global: true,
+ description: '',
+ tags: [],
+ required: false,
+ type: 'string | number | symbol | undefined',
+ declarations: [],
+ schema: {
+ kind: 'enum',
+ type: 'string | number | symbol | undefined',
+ schema: ['undefined', 'string', 'number', 'symbol'],
},
- {
- name: 'nested',
- type: 'MyNestedProps',
- description: 'description nested is required nested object',
- declarations: [],
- schema: {
- kind: 'object',
- type: 'MyNestedProps',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: 'nested prop documentation',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'ref',
+ type: {},
+ required: false,
+ description: '',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
},
- {
- name: 'nestedIntersection',
- type: 'MyNestedProps & { additionalProp: string; }',
- description: 'description required nested object with intersection',
- declarations: [],
- schema: {
- kind: 'object',
- type: 'MyNestedProps & { additionalProp: string; }',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: 'nested prop documentation',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- additionalProp: {
- name: 'additionalProp',
- global: false,
- description: 'description required additional property',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
+ },
+ docgenInfo: {
+ name: 'ref',
+ global: true,
+ description: '',
+ tags: [],
+ required: false,
+ type: 'VNodeRef | undefined',
+ declarations: [],
+ schema: {
+ kind: 'enum',
+ type: 'VNodeRef | undefined',
+ schema: [
+ 'undefined',
+ 'string',
+ 'Ref',
+ {
+ kind: 'event',
+ type: '(ref: Element | ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase, {}, {}> | null, refs: Record<...>): void',
+ schema: [],
},
- },
+ ],
},
- {
- name: 'nestedOptional',
- type: 'MyNestedProps | MyIgnoredNestedProps | undefined',
- description: 'description optional nested object',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'MyNestedProps | MyIgnoredNestedProps | undefined',
- schema: [
- 'undefined',
- {
- kind: 'object',
- type: 'MyNestedProps',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: 'nested prop documentation',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
- {
- kind: 'object',
- type: 'MyIgnoredNestedProps',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: '',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
- ],
- },
- },
- {
- name: 'array',
- type: 'MyNestedProps[]',
- description: 'description required array object',
- declarations: [],
- schema: {
- kind: 'array',
- type: 'MyNestedProps[]',
- schema: [
- {
- kind: 'object',
- type: 'MyNestedProps',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: 'nested prop documentation',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
- ],
- },
- },
- {
- name: 'arrayOptional',
- type: 'MyNestedProps[] | undefined',
- description: 'description optional array object',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'MyNestedProps[] | undefined',
- schema: [
- 'undefined',
- {
- kind: 'array',
- type: 'MyNestedProps[]',
- schema: [
- {
- kind: 'object',
- type: 'MyNestedProps',
- schema: {
- nestedProp: {
- name: 'nestedProp',
- global: false,
- description: 'nested prop documentation',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
- ],
- },
- ],
- },
- },
- {
- name: 'enumValue',
- type: 'MyEnum',
- description: 'description enum value',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'MyEnum',
- schema: ['MyEnum.Small', 'MyEnum.Medium', 'MyEnum.Large'],
- },
- },
- {
- name: 'literalFromContext',
- type: '"Uncategorized" | "Content" | "Interaction" | "Display" | "Forms" | "Addons"',
- description: 'description literal type alias that require context',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '"Uncategorized" | "Content" | "Interaction" | "Display" | "Forms" | "Addons"',
- schema: [
- '"Uncategorized"',
- '"Content"',
- '"Interaction"',
- '"Display"',
- '"Forms"',
- '"Addons"',
- ],
- },
- },
- {
- name: 'inlined',
- type: '{ foo: string; }',
- description: '',
- declarations: [],
- schema: {
- kind: 'object',
- type: '{ foo: string; }',
- schema: {
- foo: {
- name: 'foo',
- global: false,
- description: '',
- tags: [],
- required: true,
- type: 'string',
- declarations: [],
- schema: 'string',
- },
- },
- },
- },
- {
- name: 'recursive',
- type: 'MyNestedRecursiveProps | undefined',
- description: '',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'MyNestedRecursiveProps | undefined',
- schema: [
- 'undefined',
- {
- kind: 'object',
- type: 'MyNestedRecursiveProps',
- schema: {
- recursive: {
- name: 'recursive',
- global: false,
- description: '',
- tags: [],
- required: true,
- type: 'MyNestedRecursiveProps',
- declarations: [],
- schema: 'MyNestedRecursiveProps',
- },
- },
- },
- ],
- },
- },
- ],
- sourceFiles:
- '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3_vue3-vite-default-ts/component-meta/reference-type-props/component.vue',
- },
-};
-
-export const mockExtractComponentPropsReturn = [
- {
- propDef: {
- name: 'bar',
- type: {},
- required: false,
- description: 'description bar is optional number',
- defaultValue: null,
- sbType: {
- name: 'other',
- },
- },
- docgenInfo: {
- name: 'bar',
- global: false,
- description: 'description bar is optional number',
- tags: [],
- required: false,
- type: 'number | undefined',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'number | undefined',
- schema: ['undefined', 'number'],
- },
- default: '1',
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
- name: 'stringArray',
+ name: 'ref_for',
type: {},
required: false,
- description: 'description stringArray is string array',
+ description: '',
defaultValue: null,
sbType: {
name: 'other',
},
},
docgenInfo: {
- name: 'stringArray',
- global: false,
- description: 'description stringArray is string array',
+ name: 'ref_for',
+ global: true,
+ description: '',
tags: [],
required: false,
- type: 'string[] | undefined',
+ type: 'boolean | undefined',
declarations: [],
schema: {
kind: 'enum',
- type: 'string[] | undefined',
- schema: [
- 'undefined',
- {
- kind: 'array',
- type: 'string[]',
- schema: ['string'],
- },
- ],
+ type: 'boolean | undefined',
+ schema: ['undefined', 'false', 'true'],
},
- default: '["foo", "bar"]',
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
- name: 'key',
+ name: 'ref_key',
type: {},
required: false,
description: '',
@@ -851,24 +640,24 @@ export const mockExtractComponentPropsReturn = [
},
},
docgenInfo: {
- name: 'key',
+ name: 'ref_key',
global: true,
description: '',
tags: [],
required: false,
- type: 'string | number | symbol | undefined',
+ type: 'string | undefined',
declarations: [],
schema: {
kind: 'enum',
- type: 'string | number | symbol | undefined',
- schema: ['undefined', 'string', 'number', 'symbol'],
+ type: 'string | undefined',
+ schema: ['undefined', 'string'],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
- name: 'ref',
+ name: 'class',
type: {},
required: false,
description: '',
@@ -878,33 +667,20 @@ export const mockExtractComponentPropsReturn = [
},
},
docgenInfo: {
- name: 'ref',
+ name: 'class',
global: true,
description: '',
tags: [],
required: false,
- type: 'VNodeRef | undefined',
+ type: 'unknown',
declarations: [],
- schema: {
- kind: 'enum',
- type: 'VNodeRef | undefined',
- schema: [
- 'undefined',
- 'string',
- 'Ref',
- {
- kind: 'event',
- type: '(ref: Element | ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase, {}, {}> | null, refs: Record<...>): void',
- schema: [],
- },
- ],
- },
+ schema: 'unknown',
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
- name: 'ref_for',
+ name: 'style',
type: {},
required: false,
description: '',
@@ -914,100 +690,23 @@ export const mockExtractComponentPropsReturn = [
},
},
docgenInfo: {
- name: 'ref_for',
+ name: 'style',
global: true,
description: '',
tags: [],
required: false,
- type: 'boolean | undefined',
+ type: 'unknown',
declarations: [],
- schema: {
- kind: 'enum',
- type: 'boolean | undefined',
- schema: ['undefined', 'false', 'true'],
- },
+ schema: 'unknown',
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
- name: 'ref_key',
+ name: 'foo',
type: {},
- required: false,
- description: '',
- defaultValue: null,
- sbType: {
- name: 'other',
- },
- },
- docgenInfo: {
- name: 'ref_key',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'string | undefined',
- declarations: [],
- schema: {
- kind: 'enum',
- type: 'string | undefined',
- schema: ['undefined', 'string'],
- },
- },
- typeSystem: 'JavaScript',
- },
- {
- propDef: {
- name: 'class',
- type: {},
- required: false,
- description: '',
- defaultValue: null,
- sbType: {
- name: 'other',
- },
- },
- docgenInfo: {
- name: 'class',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [],
- schema: 'unknown',
- },
- typeSystem: 'JavaScript',
- },
- {
- propDef: {
- name: 'style',
- type: {},
- required: false,
- description: '',
- defaultValue: null,
- sbType: {
- name: 'other',
- },
- },
- docgenInfo: {
- name: 'style',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [],
- schema: 'unknown',
- },
- typeSystem: 'JavaScript',
- },
- {
- propDef: {
- name: 'foo',
- type: {},
- required: true,
- description: 'string foo',
+ required: true,
+ description: 'string foo',
defaultValue: null,
sbType: {
name: 'other',
@@ -1040,7 +739,7 @@ export const mockExtractComponentPropsReturn = [
declarations: [],
schema: 'string',
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1067,7 +766,7 @@ export const mockExtractComponentPropsReturn = [
schema: ['false', 'true'],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1094,7 +793,7 @@ export const mockExtractComponentPropsReturn = [
schema: ['string', 'number'],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1121,7 +820,7 @@ export const mockExtractComponentPropsReturn = [
schema: ['undefined', 'string', 'number', 'false', 'true'],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1159,7 +858,7 @@ export const mockExtractComponentPropsReturn = [
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1207,7 +906,7 @@ export const mockExtractComponentPropsReturn = [
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1268,7 +967,7 @@ export const mockExtractComponentPropsReturn = [
],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1312,7 +1011,7 @@ export const mockExtractComponentPropsReturn = [
],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1363,7 +1062,7 @@ export const mockExtractComponentPropsReturn = [
],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1390,7 +1089,7 @@ export const mockExtractComponentPropsReturn = [
schema: ['MyEnum.Small', 'MyEnum.Medium', 'MyEnum.Large'],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1424,7 +1123,7 @@ export const mockExtractComponentPropsReturn = [
],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1462,7 +1161,7 @@ export const mockExtractComponentPropsReturn = [
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1507,147 +1206,20 @@ export const mockExtractComponentPropsReturn = [
],
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
];
-export const referenceTypeEvents = {
- __name: 'component',
- emits: ['foo', 'bar', 'baz'],
- __hmrId: '3a8b03b5',
- __file:
- '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
+export const referenceTypeEvents: TestComponent = {
__docgenInfo: {
- exportName: 'default',
- displayName: 'component',
- props: [
- {
- name: 'key',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'string | number | symbol | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47082, 47113],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'string | number | symbol | undefined',
- schema: ['undefined', 'string', 'number', 'symbol'],
- },
- },
- {
- name: 'ref',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'VNodeRef | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47118, 47133],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'VNodeRef | undefined',
- schema: [
- 'undefined',
- 'string',
- 'Ref',
- {
- kind: 'event',
- type: '(ref: Element | ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase, {}, {}> | null, refs: Record<...>): void',
- schema: [],
- },
- ],
- },
- },
- {
- name: 'ref_for',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'boolean | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47138, 47156],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'boolean | undefined',
- schema: ['undefined', 'false', 'true'],
- },
- },
- {
- name: 'ref_key',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'string | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47161, 47178],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'string | undefined',
- schema: ['undefined', 'string'],
- },
- },
- {
- name: 'class',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [52888, 52904],
- },
- ],
- schema: 'unknown',
- },
- {
- name: 'style',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [52909, 52925],
- },
- ],
- schema: 'unknown',
- },
- ],
+ type: 1,
+ props: [],
events: [
{
name: 'foo',
type: '[data?: { foo: string; } | undefined]',
signature: '(event: "foo", data?: { foo: string; } | undefined): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [
{
kind: 'enum',
@@ -1665,12 +1237,7 @@ export const referenceTypeEvents = {
tags: [],
required: true,
type: 'string',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [207, 218],
- },
- ],
+ declarations: [],
schema: 'string',
},
},
@@ -1683,12 +1250,7 @@ export const referenceTypeEvents = {
name: 'bar',
type: '[value: { year: number; title?: any; }]',
signature: '(event: "bar", value: { year: number; title?: any; }): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [
{
kind: 'object',
@@ -1701,12 +1263,7 @@ export const referenceTypeEvents = {
tags: [],
required: true,
type: 'number',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [255, 268],
- },
- ],
+ declarations: [],
schema: 'number',
},
title: {
@@ -1716,12 +1273,7 @@ export const referenceTypeEvents = {
tags: [],
required: false,
type: 'any',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [269, 280],
- },
- ],
+ declarations: [],
schema: 'any',
},
},
@@ -1732,140 +1284,12 @@ export const referenceTypeEvents = {
name: 'baz',
type: '[]',
signature: '(event: "baz"): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [],
},
],
slots: [],
- exposed: [
- {
- name: 'onFoo',
- type: '((data?: { foo: string; } | undefined) => any) | undefined',
- description: '',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '((data?: { foo: string; } | undefined) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(data?: { foo: string; } | undefined): any',
- schema: [
- {
- kind: 'enum',
- type: '{ foo: string; } | undefined',
- schema: [
- 'undefined',
- {
- kind: 'object',
- type: '{ foo: string; }',
- schema: {
- foo: {
- name: 'foo',
- global: false,
- description: '',
- tags: [],
- required: true,
- type: 'string',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [207, 218],
- },
- ],
- schema: 'string',
- },
- },
- },
- ],
- },
- ],
- },
- ],
- },
- },
- {
- name: 'onBar',
- type: '((value: { year: number; title?: any; }) => any) | undefined',
- description: '',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '((value: { year: number; title?: any; }) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(value: { year: number; title?: any; }): any',
- schema: [
- {
- kind: 'object',
- type: '{ year: number; title?: any; }',
- schema: {
- year: {
- name: 'year',
- global: false,
- description: '',
- tags: [],
- required: true,
- type: 'number',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [255, 268],
- },
- ],
- schema: 'number',
- },
- title: {
- name: 'title',
- global: false,
- description: '',
- tags: [],
- required: false,
- type: 'any',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [269, 280],
- },
- ],
- schema: 'any',
- },
- },
- },
- ],
- },
- ],
- },
- },
- {
- name: 'onBaz',
- type: '(() => any) | undefined',
- description: '',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '(() => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(): any',
- schema: [],
- },
- ],
- },
- },
- ],
- sourceFiles:
- '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
+ exposed: [],
},
};
@@ -1883,12 +1307,7 @@ export const mockExtractComponentEventsReturn = [
name: 'foo',
type: '[data?: { foo: string; } | undefined]',
signature: '(event: "foo", data?: { foo: string; } | undefined): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [
{
kind: 'enum',
@@ -1906,12 +1325,7 @@ export const mockExtractComponentEventsReturn = [
tags: [],
required: true,
type: 'string',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [207, 218],
- },
- ],
+ declarations: [],
schema: 'string',
},
},
@@ -1920,7 +1334,7 @@ export const mockExtractComponentEventsReturn = [
},
],
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1935,12 +1349,7 @@ export const mockExtractComponentEventsReturn = [
name: 'bar',
type: '[value: { year: number; title?: any; }]',
signature: '(event: "bar", value: { year: number; title?: any; }): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [
{
kind: 'object',
@@ -1953,12 +1362,7 @@ export const mockExtractComponentEventsReturn = [
tags: [],
required: true,
type: 'number',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [255, 268],
- },
- ],
+ declarations: [],
schema: 'number',
},
title: {
@@ -1968,19 +1372,14 @@ export const mockExtractComponentEventsReturn = [
tags: [],
required: false,
type: 'any',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/reference-type-events/component.vue',
- range: [269, 280],
- },
- ],
+ declarations: [],
schema: 'any',
},
},
},
],
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -1995,142 +1394,17 @@ export const mockExtractComponentEventsReturn = [
name: 'baz',
type: '[]',
signature: '(event: "baz"): void',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [4468, 4503],
- },
- ],
+ declarations: [],
schema: [],
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
];
-export const templateSlots = {
- __hmrId: 'c8033161',
- __file:
- '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
+export const templateSlots: TestComponent = {
__docgenInfo: {
- exportName: 'default',
- displayName: 'component',
- props: [
- {
- name: 'key',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'string | number | symbol | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47082, 47113],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'string | number | symbol | undefined',
- schema: ['undefined', 'string', 'number', 'symbol'],
- },
- },
- {
- name: 'ref',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'VNodeRef | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47118, 47133],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'VNodeRef | undefined',
- schema: [
- 'undefined',
- 'string',
- 'Ref',
- {
- kind: 'event',
- type: '(ref: Element | ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase, {}, {}> | null, refs: Record<...>): void',
- schema: [],
- },
- ],
- },
- },
- {
- name: 'ref_for',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'boolean | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47138, 47156],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'boolean | undefined',
- schema: ['undefined', 'false', 'true'],
- },
- },
- {
- name: 'ref_key',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'string | undefined',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [47161, 47178],
- },
- ],
- schema: {
- kind: 'enum',
- type: 'string | undefined',
- schema: ['undefined', 'string'],
- },
- },
- {
- name: 'class',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [52888, 52904],
- },
- ],
- schema: 'unknown',
- },
- {
- name: 'style',
- global: true,
- description: '',
- tags: [],
- required: false,
- type: 'unknown',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [52909, 52925],
- },
- ],
- schema: 'unknown',
- },
- ],
+ type: 1,
+ props: [],
events: [],
slots: [
{
@@ -2204,12 +1478,7 @@ export const templateSlots = {
tags: [],
required: true,
type: 'number',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
- range: [153, 161],
- },
- ],
+ declarations: [],
schema: 'number',
},
str: {
@@ -2219,123 +1488,14 @@ export const templateSlots = {
tags: [],
required: true,
type: 'string',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
- range: [163, 173],
- },
- ],
- schema: 'string',
- },
- },
- },
- },
- ],
- exposed: [
- {
- name: '$slots',
- type: 'Readonly & { "no-bind"?(_: {}): any; default?(_: { num: number; }): any; named?(_: { str: string; }): any; vbind?(_: { num: number; str: string; }): any; }',
- description: '',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/node_modules/@vue/runtime-core/dist/runtime-core.d.ts',
- range: [8406, 8433],
- },
- ],
- schema: {
- kind: 'object',
- type: 'Readonly & { "no-bind"?(_: {}): any; default?(_: { num: number; }): any; named?(_: { str: string; }): any; vbind?(_: { num: number; str: string; }): any; }',
- schema: {
- 'no-bind': {
- name: 'no-bind',
- global: false,
- description: '',
- tags: [],
- required: false,
- type: '((_: {}) => any) | undefined',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '((_: {}) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(_: {}): any',
- schema: [],
- },
- ],
- },
- },
- default: {
- name: 'default',
- global: false,
- description: '',
- tags: [],
- required: false,
- type: '((_: { num: number; }) => any) | undefined',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '((_: { num: number; }) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(_: { num: number; }): any',
- schema: [],
- },
- ],
- },
- },
- named: {
- name: 'named',
- global: false,
- description: '',
- tags: [],
- required: false,
- type: '((_: { str: string; }) => any) | undefined',
- declarations: [],
- schema: {
- kind: 'enum',
- type: '((_: { str: string; }) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(_: { str: string; }): any',
- schema: [],
- },
- ],
- },
- },
- vbind: {
- name: 'vbind',
- global: false,
- description: '',
- tags: [],
- required: false,
- type: '((_: { num: number; str: string; }) => any) | undefined',
declarations: [],
- schema: {
- kind: 'enum',
- type: '((_: { num: number; str: string; }) => any) | undefined',
- schema: [
- 'undefined',
- {
- kind: 'event',
- type: '(_: { num: number; str: string; }): any',
- schema: [],
- },
- ],
- },
+ schema: 'string',
},
},
},
},
],
- sourceFiles:
- '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
+ exposed: [],
},
};
@@ -2361,7 +1521,7 @@ export const mockExtractComponentSlotsReturn = [
schema: {},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -2395,7 +1555,7 @@ export const mockExtractComponentSlotsReturn = [
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -2429,7 +1589,7 @@ export const mockExtractComponentSlotsReturn = [
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
{
propDef: {
@@ -2457,12 +1617,7 @@ export const mockExtractComponentSlotsReturn = [
tags: [],
required: true,
type: 'number',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
- range: [153, 161],
- },
- ],
+ declarations: [],
schema: 'number',
},
str: {
@@ -2472,17 +1627,984 @@ export const mockExtractComponentSlotsReturn = [
tags: [],
required: true,
type: 'string',
- declarations: [
- {
- file: '/storybook/sandbox/vue3-vite-default-ts/src/stories/renderers/vue3/component-meta/template-slots/component.vue',
- range: [163, 173],
- },
- ],
+ declarations: [],
schema: 'string',
},
},
},
},
- typeSystem: 'JavaScript',
+ typeSystem: TypeSystem.JAVASCRIPT,
},
];
+
+export const vueDocgenMocks = {
+ props: {
+ component: {
+ __docgenInfo: {
+ description: '',
+ tags: {},
+ props: [
+ {
+ name: 'foo',
+ description: 'string foo',
+ tags: {
+ default: [
+ {
+ description: '"rounded"',
+ title: 'default',
+ },
+ ],
+ since: [
+ {
+ description: 'v1.0.0',
+ title: 'since',
+ },
+ ],
+ see: [
+ {
+ description: 'https://vuejs.org/',
+ title: 'see',
+ },
+ ],
+ deprecated: [
+ {
+ description: 'v1.1.0',
+ title: 'deprecated',
+ },
+ ],
+ },
+ required: true,
+ type: {
+ name: 'string',
+ },
+ },
+ {
+ name: 'bar',
+ description: 'description bar is optional number',
+ required: false,
+ type: {
+ name: 'number',
+ },
+ defaultValue: {
+ func: false,
+ value: '1',
+ },
+ },
+ {
+ name: 'baz',
+ description: 'description baz is required boolean',
+ required: true,
+ type: {
+ name: 'boolean',
+ },
+ },
+ {
+ name: 'stringArray',
+ description: 'description stringArray is string array',
+ required: false,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'string',
+ },
+ ],
+ },
+ defaultValue: {
+ func: false,
+ value: "() => ['foo', 'bar']",
+ },
+ },
+ {
+ name: 'union',
+ description: 'description union is required union type',
+ required: true,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ ],
+ },
+ },
+ {
+ name: 'unionOptional',
+ description: 'description unionOptional is optional union type',
+ required: false,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ {
+ name: 'boolean',
+ },
+ ],
+ },
+ },
+ {
+ name: 'nested',
+ description: 'description nested is required nested object',
+ required: true,
+ type: {
+ name: 'MyNestedProps',
+ },
+ },
+ {
+ name: 'nestedIntersection',
+ description: 'description required nested object with intersection',
+ required: true,
+ type: {
+ name: 'intersection',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: '{\n /**\n * description required additional property\n */\n additionalProp: string;\n}',
+ },
+ ],
+ },
+ },
+ {
+ name: 'nestedOptional',
+ description: 'description optional nested object',
+ required: false,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: 'MyIgnoredNestedProps',
+ },
+ ],
+ },
+ },
+ {
+ name: 'array',
+ description: 'description required array object',
+ required: true,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ },
+ },
+ {
+ name: 'arrayOptional',
+ description: 'description optional array object',
+ required: false,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ },
+ },
+ {
+ name: 'enumValue',
+ description: 'description enum value',
+ required: true,
+ type: {
+ name: 'MyEnum',
+ },
+ },
+ {
+ name: 'literalFromContext',
+ description: 'description literal type alias that require context',
+ required: true,
+ type: {
+ name: 'MyCategories',
+ },
+ },
+ {
+ name: 'inlined',
+ required: true,
+ type: {
+ name: '{ foo: string }',
+ },
+ },
+ {
+ name: 'recursive',
+ required: false,
+ type: {
+ name: 'MyNestedRecursiveProps',
+ },
+ },
+ ],
+ },
+ },
+ extractedProps: [
+ {
+ propDef: {
+ name: 'foo',
+ type: {
+ summary: 'string',
+ },
+ required: true,
+ description: 'string foo',
+ defaultValue: null,
+ sbType: {
+ name: 'string',
+ },
+ },
+ docgenInfo: {
+ name: 'foo',
+ description: 'string foo',
+ tags: {
+ default: [
+ {
+ description: '"rounded"',
+ title: 'default',
+ },
+ ],
+ since: [
+ {
+ description: 'v1.0.0',
+ title: 'since',
+ },
+ ],
+ see: [
+ {
+ description: 'https://vuejs.org/',
+ title: 'see',
+ },
+ ],
+ deprecated: [
+ {
+ description: 'v1.1.0',
+ title: 'deprecated',
+ },
+ ],
+ },
+ required: true,
+ type: {
+ name: 'string',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'bar',
+ type: {
+ summary: 'number',
+ },
+ required: false,
+ description: 'description bar is optional number',
+ defaultValue: {
+ summary: '1',
+ },
+ sbType: {
+ name: 'number',
+ },
+ },
+ docgenInfo: {
+ name: 'bar',
+ description: 'description bar is optional number',
+ required: false,
+ type: {
+ name: 'number',
+ },
+ defaultValue: {
+ func: false,
+ value: '1',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'baz',
+ type: {
+ summary: 'boolean',
+ },
+ required: true,
+ description: 'description baz is required boolean',
+ defaultValue: null,
+ sbType: {
+ name: 'boolean',
+ },
+ },
+ docgenInfo: {
+ name: 'baz',
+ description: 'description baz is required boolean',
+ required: true,
+ type: {
+ name: 'boolean',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'stringArray',
+ type: {
+ summary: 'Array',
+ },
+ required: false,
+ description: 'description stringArray is string array',
+ defaultValue: {
+ summary: "() => ['foo', 'bar']",
+ },
+ sbType: {
+ name: 'other',
+ value: 'Array([object Object])',
+ },
+ },
+ docgenInfo: {
+ name: 'stringArray',
+ description: 'description stringArray is string array',
+ required: false,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'string',
+ },
+ ],
+ value: [
+ {
+ name: 'string',
+ },
+ ],
+ },
+ defaultValue: {
+ func: false,
+ value: "() => ['foo', 'bar']",
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'union',
+ type: {
+ summary: 'union',
+ },
+ required: true,
+ description: 'description union is required union type',
+ defaultValue: null,
+ sbType: {
+ name: 'union',
+ value: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ ],
+ },
+ },
+ docgenInfo: {
+ name: 'union',
+ description: 'description union is required union type',
+ required: true,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ ],
+ value: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'unionOptional',
+ type: {
+ summary: 'union',
+ },
+ required: false,
+ description: 'description unionOptional is optional union type',
+ defaultValue: null,
+ sbType: {
+ name: 'union',
+ value: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ {
+ name: 'boolean',
+ },
+ ],
+ },
+ },
+ docgenInfo: {
+ name: 'unionOptional',
+ description: 'description unionOptional is optional union type',
+ required: false,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ {
+ name: 'boolean',
+ },
+ ],
+ value: [
+ {
+ name: 'string',
+ },
+ {
+ name: 'number',
+ },
+ {
+ name: 'boolean',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'nested',
+ type: {
+ summary: 'MyNestedProps',
+ },
+ required: true,
+ description: 'description nested is required nested object',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'MyNestedProps',
+ },
+ },
+ docgenInfo: {
+ name: 'nested',
+ description: 'description nested is required nested object',
+ required: true,
+ type: {
+ name: 'MyNestedProps',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'nestedIntersection',
+ type: {
+ summary: 'intersection',
+ },
+ required: true,
+ description: 'description required nested object with intersection',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'intersection([object Object],[object Object])',
+ },
+ },
+ docgenInfo: {
+ name: 'nestedIntersection',
+ description: 'description required nested object with intersection',
+ required: true,
+ type: {
+ name: 'intersection',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: '{\n /**\n * description required additional property\n */\n additionalProp: string;\n}',
+ },
+ ],
+ value: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: '{\n /**\n * description required additional property\n */\n additionalProp: string;\n}',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'nestedOptional',
+ type: {
+ summary: 'union',
+ },
+ required: false,
+ description: 'description optional nested object',
+ defaultValue: null,
+ sbType: {
+ name: 'union',
+ value: [
+ {
+ name: 'other',
+ value: 'MyNestedProps',
+ },
+ {
+ name: 'other',
+ value: 'MyIgnoredNestedProps',
+ },
+ ],
+ },
+ },
+ docgenInfo: {
+ name: 'nestedOptional',
+ description: 'description optional nested object',
+ required: false,
+ type: {
+ name: 'union',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: 'MyIgnoredNestedProps',
+ },
+ ],
+ value: [
+ {
+ name: 'MyNestedProps',
+ },
+ {
+ name: 'MyIgnoredNestedProps',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'array',
+ type: {
+ summary: 'Array',
+ },
+ required: true,
+ description: 'description required array object',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'Array([object Object])',
+ },
+ },
+ docgenInfo: {
+ name: 'array',
+ description: 'description required array object',
+ required: true,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ value: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'arrayOptional',
+ type: {
+ summary: 'Array',
+ },
+ required: false,
+ description: 'description optional array object',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'Array([object Object])',
+ },
+ },
+ docgenInfo: {
+ name: 'arrayOptional',
+ description: 'description optional array object',
+ required: false,
+ type: {
+ name: 'Array',
+ elements: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ value: [
+ {
+ name: 'MyNestedProps',
+ },
+ ],
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'enumValue',
+ type: {
+ summary: 'MyEnum',
+ },
+ required: true,
+ description: 'description enum value',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'MyEnum',
+ },
+ },
+ docgenInfo: {
+ name: 'enumValue',
+ description: 'description enum value',
+ required: true,
+ type: {
+ name: 'MyEnum',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'literalFromContext',
+ type: {
+ summary: 'MyCategories',
+ },
+ required: true,
+ description: 'description literal type alias that require context',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'MyCategories',
+ },
+ },
+ docgenInfo: {
+ name: 'literalFromContext',
+ description: 'description literal type alias that require context',
+ required: true,
+ type: {
+ name: 'MyCategories',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'inlined',
+ type: {
+ summary: '{ foo: string }',
+ },
+ required: true,
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: '{ foo: string }',
+ },
+ },
+ docgenInfo: {
+ name: 'inlined',
+ required: true,
+ type: {
+ name: '{ foo: string }',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'recursive',
+ type: {
+ summary: 'MyNestedRecursiveProps',
+ },
+ required: false,
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ value: 'MyNestedRecursiveProps',
+ },
+ },
+ docgenInfo: {
+ name: 'recursive',
+ required: false,
+ type: {
+ name: 'MyNestedRecursiveProps',
+ },
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ ],
+ },
+ events: {
+ component: {
+ __docgenInfo: {
+ exportName: 'default',
+ displayName: 'component',
+ description: '',
+ tags: {},
+ events: [
+ {
+ name: 'bar',
+ type: {
+ names: ['{ year: number; title?: any }'],
+ },
+ description: 'Test description bar',
+ },
+ {
+ name: 'baz',
+ description: 'Test description baz',
+ },
+ ],
+ },
+ },
+ extractedProps: [
+ {
+ propDef: {
+ name: 'bar',
+ type: {},
+ description: 'Test description bar',
+ defaultValue: null,
+ sbType: {
+ name: 'other',
+ },
+ },
+ docgenInfo: {
+ name: 'bar',
+ type: {
+ names: ['{ year: number; title?: any }'],
+ },
+ description: 'Test description bar',
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ {
+ propDef: {
+ name: 'baz',
+ type: null,
+ description: 'Test description baz',
+ defaultValue: null,
+ sbType: null,
+ },
+ docgenInfo: {
+ name: 'baz',
+ description: 'Test description baz',
+ },
+ typeSystem: TypeSystem.JAVASCRIPT,
+ },
+ ],
+ },
+ slots: {
+ component: {
+ __docgenInfo: {
+ displayName: 'component',
+ exportName: 'default',
+ description: '',
+ tags: {},
+ slots: [
+ {
+ name: 'no-bind',
+ },
+ {
+ name: 'default',
+ scoped: true,
+ bindings: [
+ {
+ name: 'num',
+ title: 'binding',
+ },
+ ],
+ },
+ {
+ name: 'named',
+ scoped: true,
+ bindings: [
+ {
+ name: 'str',
+ title: 'binding',
+ },
+ ],
+ },
+ {
+ name: 'vbind',
+ scoped: true,
+ bindings: [
+ {
+ name: 'num',
+ title: 'binding',
+ },
+ {
+ name: 'str',
+ title: 'binding',
+ },
+ ],
+ },
+ ],
+ },
+ },
+ extractedProps: [
+ {
+ propDef: {
+ name: 'no-bind',
+ type: {
+ summary: 'unknown',
+ },
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'no-bind',
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ {
+ propDef: {
+ name: 'default',
+ type: {
+ summary: 'unknown',
+ },
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'default',
+ scoped: true,
+ bindings: [
+ {
+ name: 'num',
+ title: 'binding',
+ },
+ ],
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ {
+ propDef: {
+ name: 'named',
+ type: {
+ summary: 'unknown',
+ },
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'named',
+ scoped: true,
+ bindings: [
+ {
+ name: 'str',
+ title: 'binding',
+ },
+ ],
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ {
+ propDef: {
+ name: 'vbind',
+ type: {
+ summary: 'unknown',
+ },
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'vbind',
+ scoped: true,
+ bindings: [
+ {
+ name: 'num',
+ title: 'binding',
+ },
+ {
+ name: 'str',
+ title: 'binding',
+ },
+ ],
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ ],
+ },
+ expose: {
+ component: {
+ __docgenInfo: {
+ exportName: 'default',
+ displayName: 'component',
+ description: '',
+ tags: {},
+ expose: [
+ {
+ name: 'label',
+ description: 'a label string',
+ },
+ {
+ name: 'count',
+ description: 'a count number',
+ },
+ ],
+ },
+ },
+ extractedProps: [
+ {
+ propDef: {
+ name: 'label',
+ type: {
+ summary: 'unknown',
+ },
+ description: 'a label string',
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'label',
+ description: 'a label string',
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ {
+ propDef: {
+ name: 'count',
+ type: {
+ summary: 'unknown',
+ },
+ description: 'a count number',
+ defaultValue: null,
+ },
+ docgenInfo: {
+ name: 'count',
+ description: 'a count number',
+ },
+ typeSystem: TypeSystem.UNKNOWN,
+ },
+ ],
+ },
+};
diff --git a/code/ui/blocks/src/blocks/Controls.stories.tsx b/code/ui/blocks/src/blocks/Controls.stories.tsx
index 9d32d9fe12f1..598485dd93a6 100644
--- a/code/ui/blocks/src/blocks/Controls.stories.tsx
+++ b/code/ui/blocks/src/blocks/Controls.stories.tsx
@@ -6,17 +6,19 @@ import * as ExampleStories from '../examples/ControlsParameters.stories';
import * as SubcomponentsExampleStories from '../examples/ControlsWithSubcomponentsParameters.stories';
import { within } from '@storybook/test';
import type { PlayFunctionContext } from '@storybook/csf';
+import * as EmptyArgTypesStories from '../examples/EmptyArgTypes.stories';
-const meta: Meta = {
+const meta = {
component: Controls,
parameters: {
relativeCsfPaths: [
'../examples/ControlsParameters.stories',
+ '../examples/EmptyArgTypes.stories',
'../examples/ControlsWithSubcomponentsParameters.stories',
],
docsStyles: true,
},
-};
+} satisfies Meta;
export default meta;
type Story = StoryObj;
@@ -142,3 +144,12 @@ export const SubcomponentsSortProp: Story = {
sort: 'alpha',
},
};
+
+/**
+ * When a story is defined without any argTypes or args, the Docs UI should not display the control component.
+ */
+export const EmptyArgTypes: Story = {
+ args: {
+ of: EmptyArgTypesStories.Default,
+ },
+};
diff --git a/code/ui/blocks/src/blocks/Controls.tsx b/code/ui/blocks/src/blocks/Controls.tsx
index 2adef888e66a..f47194033f1d 100644
--- a/code/ui/blocks/src/blocks/Controls.tsx
+++ b/code/ui/blocks/src/blocks/Controls.tsx
@@ -59,6 +59,9 @@ export const Controls: FC = (props) => {
const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0;
if (!hasSubcomponents) {
+ if (!(Object.keys(filteredArgTypes).length > 0 || Object.keys(args).length > 0)) {
+ return null;
+ }
return (
I am a story without args or argTypes
,
+ parameters: { chromatic: { disableSnapshot: true } },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+/**
+ * There are no argTypes or args, so this story won't show any controls in the docs page.
+ * In the control addon it will show a UI how to set up controls.
+ */
+export const Default: Story = {};
diff --git a/code/ui/manager/src/components/layout/LayoutProvider.tsx b/code/ui/manager/src/components/layout/LayoutProvider.tsx
index ae088f6358d0..81c342e0546c 100644
--- a/code/ui/manager/src/components/layout/LayoutProvider.tsx
+++ b/code/ui/manager/src/components/layout/LayoutProvider.tsx
@@ -1,5 +1,5 @@
import type { FC, PropsWithChildren } from 'react';
-import React, { createContext, useContext, useState } from 'react';
+import React, { createContext, useContext, useMemo, useState } from 'react';
import { useMediaQuery } from '../hooks/useMedia';
import { BREAKPOINT } from '../../constants';
@@ -32,22 +32,29 @@ export const LayoutProvider: FC = ({ children }) => {
const isDesktop = useMediaQuery(`(min-width: ${BREAKPOINT}px)`);
const isMobile = !isDesktop;
- return (
-
- {children}
-
+ const contextValue = useMemo(
+ () => ({
+ isMobileMenuOpen,
+ setMobileMenuOpen,
+ isMobileAboutOpen,
+ setMobileAboutOpen,
+ isMobilePanelOpen,
+ setMobilePanelOpen,
+ isDesktop,
+ isMobile,
+ }),
+ [
+ isMobileMenuOpen,
+ setMobileMenuOpen,
+ isMobileAboutOpen,
+ setMobileAboutOpen,
+ isMobilePanelOpen,
+ setMobilePanelOpen,
+ isDesktop,
+ isMobile,
+ ]
);
+ return {children} ;
};
export const useLayout = () => useContext(LayoutContext);
diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx
index cbbecc8264a6..0df164a2d35b 100644
--- a/code/ui/manager/src/components/sidebar/Search.tsx
+++ b/code/ui/manager/src/components/sidebar/Search.tsx
@@ -5,7 +5,7 @@ import Downshift from 'downshift';
import type { FuseOptions } from 'fuse.js';
import Fuse from 'fuse.js';
import { global } from '@storybook/global';
-import React, { useMemo, useRef, useState, useCallback } from 'react';
+import React, { useRef, useState, useCallback } from 'react';
import { CloseIcon, SearchIcon } from '@storybook/icons';
import { DEFAULT_REF_ID } from './Sidebar';
import type {
@@ -176,8 +176,8 @@ export const Search = React.memo<{
[api, inputRef, showAllComponents, DEFAULT_REF_ID]
);
- const list: SearchItem[] = useMemo(() => {
- return dataset.entries.reduce((acc, [refId, { index, status }]) => {
+ const makeFuse = useCallback(() => {
+ const list = dataset.entries.reduce((acc, [refId, { index, status }]) => {
const groupStatus = getGroupStatus(index || {}, status);
if (index) {
@@ -196,12 +196,12 @@ export const Search = React.memo<{
}
return acc;
}, []);
+ return new Fuse(list, options);
}, [dataset]);
- const fuse = useMemo(() => new Fuse(list, options), [list]);
-
const getResults = useCallback(
(input: string) => {
+ const fuse = makeFuse();
if (!input) return [];
let results: DownshiftItem[] = [];
@@ -229,7 +229,7 @@ export const Search = React.memo<{
return results;
},
- [allComponents, fuse]
+ [allComponents, makeFuse]
);
const stateReducer = useCallback(
diff --git a/code/ui/manager/src/components/sidebar/Tree.tsx b/code/ui/manager/src/components/sidebar/Tree.tsx
index d1a566bddf4c..42aa27ce694a 100644
--- a/code/ui/manager/src/components/sidebar/Tree.tsx
+++ b/code/ui/manager/src/components/sidebar/Tree.tsx
@@ -481,55 +481,73 @@ export const Tree = React.memo<{
const groupStatus = useMemo(() => getGroupStatus(collapsedData, status), [collapsedData, status]);
- return (
- 0}>
-
- {collapsedItems.map((itemId) => {
- const item = collapsedData[itemId];
- const id = createId(itemId, refId);
-
- if (item.type === 'root') {
- const descendants = expandableDescendants[item.id];
- const isFullyExpanded = descendants.every((d: string) => expanded[d]);
- return (
- // @ts-expect-error (TODO)
-
- );
- }
-
- const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]);
- const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null;
-
+ const treeItems = useMemo(() => {
+ return collapsedItems.map((itemId) => {
+ const item = collapsedData[itemId];
+ const id = createId(itemId, refId);
+
+ if (item.type === 'root') {
+ const descendants = expandableDescendants[item.id];
+ const isFullyExpanded = descendants.every((d: string) => expanded[d]);
return (
- itemId === oid || itemId.startsWith(`${oid}-`))}
- isDisplayed={isDisplayed}
+ isOrphan={false}
+ isDisplayed
isSelected={selectedStoryId === itemId}
isExpanded={!!expanded[itemId]}
setExpanded={setExpanded}
+ isFullyExpanded={isFullyExpanded}
+ expandableDescendants={descendants}
onSelectStoryId={onSelectStoryId}
/>
);
- })}
+ }
+
+ const isDisplayed = !item.parent || ancestry[itemId].every((a: string) => expanded[a]);
+ const color = groupStatus[itemId] ? statusMapping[groupStatus[itemId]][1] : null;
+
+ return (
+ itemId === oid || itemId.startsWith(`${oid}-`))}
+ isDisplayed={isDisplayed}
+ isSelected={selectedStoryId === itemId}
+ isExpanded={!!expanded[itemId]}
+ setExpanded={setExpanded}
+ onSelectStoryId={onSelectStoryId}
+ />
+ );
+ });
+ }, [
+ ancestry,
+ api,
+ collapsedData,
+ collapsedItems,
+ docsMode,
+ expandableDescendants,
+ expanded,
+ groupStatus,
+ onSelectStoryId,
+ orphanIds,
+ refId,
+ selectedStoryId,
+ setExpanded,
+ status,
+ ]);
+ return (
+ 0}>
+
+ {treeItems}
);
});
diff --git a/code/vitest.workspace.ts b/code/vitest.workspace.ts
index 9adcc717f753..56ce2861cea4 100644
--- a/code/vitest.workspace.ts
+++ b/code/vitest.workspace.ts
@@ -27,6 +27,7 @@ export const vitestCommonConfig = defineConfig({
clearMocks: true,
setupFiles: [resolve(__dirname, './vitest-setup.ts')],
globals: true,
+ testTimeout: 10000,
poolOptions: {
threads: {
minThreads: threadCount,
diff --git a/code/yarn.lock b/code/yarn.lock
index 9a8fb5f0771b..c0338dabe0c4 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -444,30 +444,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.12, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.2, @babel/core@npm:^7.3.4, @babel/core@npm:^7.7.5":
- version: 7.23.7
- resolution: "@babel/core@npm:7.23.7"
- dependencies:
- "@ampproject/remapping": "npm:^2.2.0"
- "@babel/code-frame": "npm:^7.23.5"
- "@babel/generator": "npm:^7.23.6"
- "@babel/helper-compilation-targets": "npm:^7.23.6"
- "@babel/helper-module-transforms": "npm:^7.23.3"
- "@babel/helpers": "npm:^7.23.7"
- "@babel/parser": "npm:^7.23.6"
- "@babel/template": "npm:^7.22.15"
- "@babel/traverse": "npm:^7.23.7"
- "@babel/types": "npm:^7.23.6"
- convert-source-map: "npm:^2.0.0"
- debug: "npm:^4.1.0"
- gensync: "npm:^1.0.0-beta.2"
- json5: "npm:^2.2.3"
- semver: "npm:^6.3.1"
- checksum: 38c9934973d384ed83369712978453eac91dc3f22167404dbdb272b64f602e74728a6f37012c53ee57e521b8ae2da60097f050497d9b6a212d28b59cdfb2cd1d
- languageName: node
- linkType: hard
-
-"@babel/core@npm:^7.23.9":
+"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.18.9, @babel/core@npm:^7.20.12, @babel/core@npm:^7.23.0, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.3.4, @babel/core@npm:^7.7.5":
version: 7.23.9
resolution: "@babel/core@npm:7.23.9"
dependencies:
@@ -752,18 +729,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helpers@npm:^7.23.2, @babel/helpers@npm:^7.23.7":
- version: 7.23.8
- resolution: "@babel/helpers@npm:7.23.8"
- dependencies:
- "@babel/template": "npm:^7.22.15"
- "@babel/traverse": "npm:^7.23.7"
- "@babel/types": "npm:^7.23.6"
- checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f
- languageName: node
- linkType: hard
-
-"@babel/helpers@npm:^7.23.9":
+"@babel/helpers@npm:^7.23.2, @babel/helpers@npm:^7.23.9":
version: 7.23.9
resolution: "@babel/helpers@npm:7.23.9"
dependencies:
@@ -785,16 +751,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.11.5, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.5, @babel/parser@npm:^7.23.6, @babel/parser@npm:^7.4.5, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6":
- version: 7.23.6
- resolution: "@babel/parser@npm:7.23.6"
- bin:
- parser: ./bin/babel-parser.js
- checksum: 6f76cd5ccae1fa9bcab3525b0865c6222e9c1d22f87abc69f28c5c7b2c8816a13361f5bd06bddbd5faf903f7320a8feba02545c981468acec45d12a03db7755e
- languageName: node
- linkType: hard
-
-"@babel/parser@npm:^7.23.9":
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.11.5, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.5, @babel/parser@npm:^7.23.6, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.4.5, @babel/parser@npm:^7.6.0, @babel/parser@npm:^7.9.6":
version: 7.23.9
resolution: "@babel/parser@npm:7.23.9"
bin:
@@ -2252,18 +2209,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/template@npm:^7.22.15":
- version: 7.22.15
- resolution: "@babel/template@npm:7.22.15"
- dependencies:
- "@babel/code-frame": "npm:^7.22.13"
- "@babel/parser": "npm:^7.22.15"
- "@babel/types": "npm:^7.22.15"
- checksum: 9312edd37cf1311d738907003f2aa321a88a42ba223c69209abe4d7111db019d321805504f606c7fd75f21c6cf9d24d0a8223104cd21ebd207e241b6c551f454
- languageName: node
- linkType: hard
-
-"@babel/template@npm:^7.23.9":
+"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9":
version: 7.23.9
resolution: "@babel/template@npm:7.23.9"
dependencies:
@@ -2274,25 +2220,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.7, @babel/traverse@npm:^7.4.5":
- version: 7.23.7
- resolution: "@babel/traverse@npm:7.23.7"
- dependencies:
- "@babel/code-frame": "npm:^7.23.5"
- "@babel/generator": "npm:^7.23.6"
- "@babel/helper-environment-visitor": "npm:^7.22.20"
- "@babel/helper-function-name": "npm:^7.23.0"
- "@babel/helper-hoist-variables": "npm:^7.22.5"
- "@babel/helper-split-export-declaration": "npm:^7.22.6"
- "@babel/parser": "npm:^7.23.6"
- "@babel/types": "npm:^7.23.6"
- debug: "npm:^4.3.1"
- globals: "npm:^11.1.0"
- checksum: e32fceb4249beec2bde83968ddffe17444221c1ee5cd18c543a2feaf94e3ca83f2a4dfbc2dcca87cf226e0105973e0fe3717063a21e982a9de9945615ab3f3f5
- languageName: node
- linkType: hard
-
-"@babel/traverse@npm:^7.23.9":
+"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.9, @babel/traverse@npm:^7.4.5":
version: 7.23.9
resolution: "@babel/traverse@npm:7.23.9"
dependencies:
@@ -2310,18 +2238,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/types@npm:^7.0.0, @babel/types@npm:^7.11.5, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.4, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.6.1, @babel/types@npm:^7.7.2, @babel/types@npm:^7.8.3, @babel/types@npm:^7.9.6":
- version: 7.23.6
- resolution: "@babel/types@npm:7.23.6"
- dependencies:
- "@babel/helper-string-parser": "npm:^7.23.4"
- "@babel/helper-validator-identifier": "npm:^7.22.20"
- to-fast-properties: "npm:^2.0.0"
- checksum: 42cefce8a68bd09bb5828b4764aa5586c53c60128ac2ac012e23858e1c179347a4aac9c66fc577994fbf57595227611c5ec8270bf0cfc94ff033bbfac0550b70
- languageName: node
- linkType: hard
-
-"@babel/types@npm:^7.23.9":
+"@babel/types@npm:^7.0.0, @babel/types@npm:^7.11.5, @babel/types@npm:^7.18.9, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.4, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.6.1, @babel/types@npm:^7.7.2, @babel/types@npm:^7.8.3, @babel/types@npm:^7.9.6":
version: 7.23.9
resolution: "@babel/types@npm:7.23.9"
dependencies:
@@ -2543,6 +2460,15 @@ __metadata:
languageName: node
linkType: hard
+"@emotion/is-prop-valid@npm:^0.8.2":
+ version: 0.8.8
+ resolution: "@emotion/is-prop-valid@npm:0.8.8"
+ dependencies:
+ "@emotion/memoize": "npm:0.7.4"
+ checksum: f6be625f067c7fa56a12a4edaf090715616dc4fc7803c87212831f38c969350107b9709b1be54100e53153b18d9fa068eb4bf4f9ac66a37a8edf1bac9b64e279
+ languageName: node
+ linkType: hard
+
"@emotion/is-prop-valid@npm:^1.2.1":
version: 1.2.1
resolution: "@emotion/is-prop-valid@npm:1.2.1"
@@ -2552,6 +2478,13 @@ __metadata:
languageName: node
linkType: hard
+"@emotion/memoize@npm:0.7.4":
+ version: 0.7.4
+ resolution: "@emotion/memoize@npm:0.7.4"
+ checksum: b2376548fc147b43afd1ff005a80a1a025bd7eb4fb759fdb23e96e5ff290ee8ba16628a332848d600fb91c3cdc319eee5395fa33d8875e5d5a8c4ce18cddc18e
+ languageName: node
+ linkType: hard
+
"@emotion/memoize@npm:^0.8.1":
version: 0.8.1
resolution: "@emotion/memoize@npm:0.8.1"
@@ -2905,6 +2838,39 @@ __metadata:
languageName: node
linkType: hard
+"@gilbarbara/deep-equal@npm:^0.1.1":
+ version: 0.1.2
+ resolution: "@gilbarbara/deep-equal@npm:0.1.2"
+ checksum: ef441034a34d3e3a2fdcdd473b1082c4e8a324682c8a35cea100d60b5341fecb7249ae043eecf2ea9bdf736a1fe582b294a347095c3d48a1d9d04d7d6e4ad16a
+ languageName: node
+ linkType: hard
+
+"@gilbarbara/deep-equal@npm:^0.3.1":
+ version: 0.3.1
+ resolution: "@gilbarbara/deep-equal@npm:0.3.1"
+ checksum: 009584aa912f13c59e98b35c3ebf99ff7fc7c5658d5394fd56d58570da61d68cbc6f7ec082ad2ef009ff72873cc1bd8b9b9fda9e39f7e6594fd643a915e0bc94
+ languageName: node
+ linkType: hard
+
+"@gilbarbara/helpers@npm:^0.9.2":
+ version: 0.9.2
+ resolution: "@gilbarbara/helpers@npm:0.9.2"
+ dependencies:
+ "@gilbarbara/types": "npm:^0.2.2"
+ is-lite: "npm:^1.2.1"
+ checksum: 18aa7c59b412d3b76e84aa745595a76cba0d505af3c1bfc109993f1da99655769539296993cc2db5f78b3db260907020895bb6adec4f9ea4486532b9a255c290
+ languageName: node
+ linkType: hard
+
+"@gilbarbara/types@npm:^0.2.2":
+ version: 0.2.2
+ resolution: "@gilbarbara/types@npm:0.2.2"
+ dependencies:
+ type-fest: "npm:^4.1.0"
+ checksum: c998bfb41ff84b5640fcb0cd47d2bdf963c7d36792673edddc469d5a1752a5f7105697e79d34db69062562ae3506283c2474ca14785af652610d8abe17ddbef3
+ languageName: node
+ linkType: hard
+
"@gitbeaker/core@npm:^35.8.1":
version: 35.8.1
resolution: "@gitbeaker/core@npm:35.8.1"
@@ -4315,9 +4281,9 @@ __metadata:
linkType: hard
"@pkgr/core@npm:^0.1.0":
- version: 0.1.0
- resolution: "@pkgr/core@npm:0.1.0"
- checksum: 8f4a0aa6cc1c445fec4f5f12157047e8a05e30b5c03441156f40203d6430f84d15135e8f1a6886e6c9800ff0e4a75d9d3419a43dbcd7490683f2882375a3b99a
+ version: 0.1.1
+ resolution: "@pkgr/core@npm:0.1.1"
+ checksum: 3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8
languageName: node
linkType: hard
@@ -4431,6 +4397,39 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-dialog@npm:^1.0.5":
+ version: 1.0.5
+ resolution: "@radix-ui/react-dialog@npm:1.0.5"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/primitive": "npm:1.0.1"
+ "@radix-ui/react-compose-refs": "npm:1.0.1"
+ "@radix-ui/react-context": "npm:1.0.1"
+ "@radix-ui/react-dismissable-layer": "npm:1.0.5"
+ "@radix-ui/react-focus-guards": "npm:1.0.1"
+ "@radix-ui/react-focus-scope": "npm:1.0.4"
+ "@radix-ui/react-id": "npm:1.0.1"
+ "@radix-ui/react-portal": "npm:1.0.4"
+ "@radix-ui/react-presence": "npm:1.0.1"
+ "@radix-ui/react-primitive": "npm:1.0.3"
+ "@radix-ui/react-slot": "npm:1.0.2"
+ "@radix-ui/react-use-controllable-state": "npm:1.0.1"
+ aria-hidden: "npm:^1.1.1"
+ react-remove-scroll: "npm:2.5.5"
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: c5b3069397379e79857a3203f3ead4d12d87736b59899f02a63e620a07dd1e6704e15523926cdf8e39afe1c945a7ff0f2533c5ea5be1e17c3114820300a51133
+ languageName: node
+ linkType: hard
+
"@radix-ui/react-direction@npm:1.0.1":
version: 1.0.1
resolution: "@radix-ui/react-direction@npm:1.0.1"
@@ -4446,6 +4445,103 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-dismissable-layer@npm:1.0.5":
+ version: 1.0.5
+ resolution: "@radix-ui/react-dismissable-layer@npm:1.0.5"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/primitive": "npm:1.0.1"
+ "@radix-ui/react-compose-refs": "npm:1.0.1"
+ "@radix-ui/react-primitive": "npm:1.0.3"
+ "@radix-ui/react-use-callback-ref": "npm:1.0.1"
+ "@radix-ui/react-use-escape-keydown": "npm:1.0.3"
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: 7e4308867aecfb07b506330c1964d94a52247ab9453725613cd326762aa13e483423c250f107219c131b0449600eb8d1576ce3159c2b96e8c978f75e46062cb2
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-focus-guards@npm:1.0.1":
+ version: 1.0.1
+ resolution: "@radix-ui/react-focus-guards@npm:1.0.1"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: d5fd4e5aa9d9a87c8ad490b3b4992d6f1d9eddf18e56df2a2bcf8744c4332b275d73377fd193df3e6ba0ad9608dc497709beca5c64de2b834d5f5350b3c9a272
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-focus-scope@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@radix-ui/react-focus-scope@npm:1.0.4"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/react-compose-refs": "npm:1.0.1"
+ "@radix-ui/react-primitive": "npm:1.0.3"
+ "@radix-ui/react-use-callback-ref": "npm:1.0.1"
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: 2fce0bafcab4e16cf4ed7560bda40654223f3d0add6b231e1c607433030c14e6249818b444b7b58ee7a6ff6bbf8e192c9c81d22c3a5c88c2daade9d1f881b5be
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-id@npm:1.0.1":
+ version: 1.0.1
+ resolution: "@radix-ui/react-id@npm:1.0.1"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/react-use-layout-effect": "npm:1.0.1"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: e2859ca58bea171c956098ace7ecf615cf9432f58a118b779a14720746b3adcf0351c36c75de131548672d3cd290ca238198acbd33b88dc4706f98312e9317ad
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-portal@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@radix-ui/react-portal@npm:1.0.4"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/react-primitive": "npm:1.0.3"
+ peerDependencies:
+ "@types/react": "*"
+ "@types/react-dom": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ checksum: fed32f8148b833fe852fb5e2f859979ffdf2fb9a9ef46583b9b52915d764ad36ba5c958a64e61d23395628ccc09d678229ee94cd112941e8fe2575021f820c29
+ languageName: node
+ linkType: hard
+
"@radix-ui/react-presence@npm:1.0.1":
version: 1.0.1
resolution: "@radix-ui/react-presence@npm:1.0.1"
@@ -4546,6 +4642,38 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-use-controllable-state@npm:1.0.1":
+ version: 1.0.1
+ resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/react-use-callback-ref": "npm:1.0.1"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 29b069dbf09e48bca321af6272574ad0fc7283174e7d092731a10663fe00c0e6b4bde5e1b5ea67725fe48dcbe8026e7ff0d69d42891c62cbb9ca408498171fbe
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-use-escape-keydown@npm:1.0.3":
+ version: 1.0.3
+ resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3"
+ dependencies:
+ "@babel/runtime": "npm:^7.13.10"
+ "@radix-ui/react-use-callback-ref": "npm:1.0.1"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 3c94c78902dcb40b60083ee2184614f45c95a189178f52d89323b467bd04bcf5fdb1bc4d43debecd7f0b572c3843c7e04edbcb56f40a4b4b43936fb2770fb8ad
+ languageName: node
+ linkType: hard
+
"@radix-ui/react-use-layout-effect@npm:1.0.1":
version: 1.0.1
resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1"
@@ -5075,6 +5203,32 @@ __metadata:
languageName: unknown
linkType: soft
+"@storybook/addon-onboarding@workspace:*, @storybook/addon-onboarding@workspace:addons/onboarding":
+ version: 0.0.0-use.local
+ resolution: "@storybook/addon-onboarding@workspace:addons/onboarding"
+ dependencies:
+ "@radix-ui/react-dialog": "npm:^1.0.5"
+ "@storybook/channels": "workspace:*"
+ "@storybook/components": "workspace:*"
+ "@storybook/core-events": "workspace:*"
+ "@storybook/icons": "npm:^1.2.5"
+ "@storybook/manager-api": "workspace:*"
+ "@storybook/react": "workspace:*"
+ "@storybook/telemetry": "workspace:*"
+ "@storybook/test": "workspace:*"
+ "@storybook/testing-library": "npm:next"
+ "@storybook/theming": "workspace:*"
+ "@storybook/types": "workspace:*"
+ framer-motion: "npm:^11.0.3"
+ react: "npm:^18.2.0"
+ react-confetti: "npm:^6.1.0"
+ react-dom: "npm:^18.2.0"
+ react-joyride: "npm:^2.7.2"
+ react-use-measure: "npm:^2.1.1"
+ typescript: "npm:^5.3.2"
+ languageName: unknown
+ linkType: soft
+
"@storybook/addon-outline@workspace:*, @storybook/addon-outline@workspace:addons/outline":
version: 0.0.0-use.local
resolution: "@storybook/addon-outline@workspace:addons/outline"
@@ -5334,6 +5488,7 @@ __metadata:
"@storybook/channels": "workspace:*"
"@storybook/client-logger": "workspace:*"
"@storybook/core-common": "workspace:*"
+ "@storybook/core-events": "workspace:*"
"@storybook/csf-plugin": "workspace:*"
"@storybook/node-logger": "workspace:*"
"@storybook/preview": "workspace:*"
@@ -6424,6 +6579,7 @@ __metadata:
"@storybook/addon-links": "workspace:*"
"@storybook/addon-mdx-gfm": "workspace:*"
"@storybook/addon-measure": "workspace:*"
+ "@storybook/addon-onboarding": "workspace:*"
"@storybook/addon-outline": "workspace:*"
"@storybook/addon-storysource": "workspace:*"
"@storybook/addon-toolbars": "workspace:*"
@@ -7063,16 +7219,7 @@ __metadata:
languageName: node
linkType: hard
-"@testing-library/user-event@npm:^14.4.0, @testing-library/user-event@npm:^14.4.3":
- version: 14.5.1
- resolution: "@testing-library/user-event@npm:14.5.1"
- peerDependencies:
- "@testing-library/dom": ">=7.21.4"
- checksum: 1e00d6ead23377885b906db6e46e259161a0efb4138f7527481d7435f3c8f65cb7e3eab2900e2ac1886fa6dd03416e773a3a60dea87a9a2086a7127dee315f6f
- languageName: node
- linkType: hard
-
-"@testing-library/user-event@npm:^14.5.2":
+"@testing-library/user-event@npm:^14.4.0, @testing-library/user-event@npm:^14.4.3, @testing-library/user-event@npm:^14.5.2":
version: 14.5.2
resolution: "@testing-library/user-event@npm:14.5.2"
peerDependencies:
@@ -7698,11 +7845,11 @@ __metadata:
linkType: hard
"@types/mdast@npm:^3.0.0":
- version: 3.0.13
- resolution: "@types/mdast@npm:3.0.13"
+ version: 3.0.15
+ resolution: "@types/mdast@npm:3.0.15"
dependencies:
"@types/unist": "npm:^2"
- checksum: b328d1622075a67db1d8eac78dcbd55aefb4adaf63206b58abfce902c0ce5232a2674bd0bf961696c9a3765d5fcf145378ce03075bd1690a25adc617650f1228
+ checksum: fcbf716c03d1ed5465deca60862e9691414f9c43597c288c7d2aefbe274552e1bbd7aeee91b88a02597e88a28c139c57863d0126fcf8416a95fdc681d054ee3d
languageName: node
linkType: hard
@@ -7785,15 +7932,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:*, @types/node@npm:>= 8, @types/node@npm:>=10.0.0, @types/node@npm:^20.0.0":
- version: 20.11.1
- resolution: "@types/node@npm:20.11.1"
- dependencies:
- undici-types: "npm:~5.26.4"
- checksum: f665cdce28b0b6e57338d1f74e0599ee9b10eac74cff729921c8f473807398e9aba2f8cf74c74a4d3dfbc2d616c73267da7de3003ed3c8152ea366bf4c96a91a
- languageName: node
- linkType: hard
-
"@types/node@npm:^18.0.0":
version: 18.19.3
resolution: "@types/node@npm:18.19.3"
@@ -7947,18 +8085,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/react@npm:*, @types/react@npm:^18.0.37":
- version: 18.2.43
- resolution: "@types/react@npm:18.2.43"
- dependencies:
- "@types/prop-types": "npm:*"
- "@types/scheduler": "npm:*"
- csstype: "npm:^3.0.2"
- checksum: 10477a50fbd3c0cc5b8a2ade679f442717f68fb27c8460b2aa1d3256cd18c48f742bbe5b9ee37a8c4c5f832ffa37b3a23c09fd96dd880a8e3182d8929c05e803
- languageName: node
- linkType: hard
-
-"@types/react@npm:^16.8.0 || ^17.0.0 || ^18.0.0":
+"@types/react@npm:*, @types/react@npm:^16.8.0 || ^17.0.0 || ^18.0.0, @types/react@npm:^18.0.37":
version: 18.2.55
resolution: "@types/react@npm:18.2.55"
dependencies:
@@ -8010,9 +8137,9 @@ __metadata:
linkType: hard
"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0, @types/semver@npm:^7.5.6":
- version: 7.5.6
- resolution: "@types/semver@npm:7.5.6"
- checksum: 196dc32db5f68cbcde2e6a42bb4aa5cbb100fa2b7bd9c8c82faaaf3e03fbe063e205dbb4f03c7cdf53da2edb70a0d34c9f2e601b54281b377eb8dc1743226acd
+ version: 7.5.7
+ resolution: "@types/semver@npm:7.5.7"
+ checksum: fb72d8b86a7779650f14ae89542f1da2ab624adb8188d98754b1d29a2fe3d41f0348bf9435b60ad145df1812fd2a09b3256779aa23b532c199f3dee59619a1eb
languageName: node
linkType: hard
@@ -8260,14 +8387,14 @@ __metadata:
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^6.18.1":
- version: 6.18.1
- resolution: "@typescript-eslint/eslint-plugin@npm:6.18.1"
+ version: 6.21.0
+ resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.5.1"
- "@typescript-eslint/scope-manager": "npm:6.18.1"
- "@typescript-eslint/type-utils": "npm:6.18.1"
- "@typescript-eslint/utils": "npm:6.18.1"
- "@typescript-eslint/visitor-keys": "npm:6.18.1"
+ "@typescript-eslint/scope-manager": "npm:6.21.0"
+ "@typescript-eslint/type-utils": "npm:6.21.0"
+ "@typescript-eslint/utils": "npm:6.21.0"
+ "@typescript-eslint/visitor-keys": "npm:6.21.0"
debug: "npm:^4.3.4"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.2.4"
@@ -8280,7 +8407,7 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: fbcfae9b92f35ce10212f44f43f93c43f6eb3e28a571da7ed0d424396916aaf080f16ce91a5bffb9e1b42ca2d6003a3e2ad65131b4ef72ed2f94a4bedb35a735
+ checksum: f911a79ee64d642f814a3b6cdb0d324b5f45d9ef955c5033e78903f626b7239b4aa773e464a38c3e667519066169d983538f2bf8e5d00228af587c9d438fb344
languageName: node
linkType: hard
@@ -8333,12 +8460,22 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/type-utils@npm:6.18.1":
- version: 6.18.1
- resolution: "@typescript-eslint/type-utils@npm:6.18.1"
+"@typescript-eslint/scope-manager@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/scope-manager@npm:6.21.0"
dependencies:
- "@typescript-eslint/typescript-estree": "npm:6.18.1"
- "@typescript-eslint/utils": "npm:6.18.1"
+ "@typescript-eslint/types": "npm:6.21.0"
+ "@typescript-eslint/visitor-keys": "npm:6.21.0"
+ checksum: eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526
+ languageName: node
+ linkType: hard
+
+"@typescript-eslint/type-utils@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/type-utils@npm:6.21.0"
+ dependencies:
+ "@typescript-eslint/typescript-estree": "npm:6.21.0"
+ "@typescript-eslint/utils": "npm:6.21.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.0.1"
peerDependencies:
@@ -8346,7 +8483,7 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 5198752a51649afd960205708c4d765e0170a46a1eb96c97e706890fecb2642933a6377337cf3632f9737915da0201607872a46c9c551d1accf9176b0e025023
+ checksum: 7409c97d1c4a4386b488962739c4f1b5b04dc60cf51f8cd88e6b12541f84d84c6b8b67e491a147a2c95f9ec486539bf4519fb9d418411aef6537b9c156468117
languageName: node
linkType: hard
@@ -8364,6 +8501,13 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/types@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/types@npm:6.21.0"
+ checksum: 020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/typescript-estree@npm:5.62.0":
version: 5.62.0
resolution: "@typescript-eslint/typescript-estree@npm:5.62.0"
@@ -8401,6 +8545,25 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/typescript-estree@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/typescript-estree@npm:6.21.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:6.21.0"
+ "@typescript-eslint/visitor-keys": "npm:6.21.0"
+ debug: "npm:^4.3.4"
+ globby: "npm:^11.1.0"
+ is-glob: "npm:^4.0.3"
+ minimatch: "npm:9.0.3"
+ semver: "npm:^7.5.4"
+ ts-api-utils: "npm:^1.0.1"
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/utils@npm:5.62.0, @typescript-eslint/utils@npm:^5.62.0":
version: 5.62.0
resolution: "@typescript-eslint/utils@npm:5.62.0"
@@ -8419,20 +8582,20 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/utils@npm:6.18.1":
- version: 6.18.1
- resolution: "@typescript-eslint/utils@npm:6.18.1"
+"@typescript-eslint/utils@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/utils@npm:6.21.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0"
"@types/json-schema": "npm:^7.0.12"
"@types/semver": "npm:^7.5.0"
- "@typescript-eslint/scope-manager": "npm:6.18.1"
- "@typescript-eslint/types": "npm:6.18.1"
- "@typescript-eslint/typescript-estree": "npm:6.18.1"
+ "@typescript-eslint/scope-manager": "npm:6.21.0"
+ "@typescript-eslint/types": "npm:6.21.0"
+ "@typescript-eslint/typescript-estree": "npm:6.21.0"
semver: "npm:^7.5.4"
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
- checksum: b9dcb2fa7cc8c46254c22fee190032320a5dd8ce282fb01e99cb35da6c00e33b157f4285b062d841942e9aad1d7ce1a16aaa46dd05ca7d81de706aedbbfff396
+ checksum: ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d
languageName: node
linkType: hard
@@ -8456,6 +8619,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/visitor-keys@npm:6.21.0":
+ version: 6.21.0
+ resolution: "@typescript-eslint/visitor-keys@npm:6.21.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:6.21.0"
+ eslint-visitor-keys: "npm:^3.4.1"
+ checksum: 7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf
+ languageName: node
+ linkType: hard
+
"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0":
version: 1.2.0
resolution: "@ungap/structured-clone@npm:1.2.0"
@@ -8595,7 +8768,7 @@ __metadata:
languageName: node
linkType: hard
-"@vitest/spy@npm:1.1.3, @vitest/spy@npm:^1.1.3":
+"@vitest/spy@npm:1.1.3":
version: 1.1.3
resolution: "@vitest/spy@npm:1.1.3"
dependencies:
@@ -8604,7 +8777,7 @@ __metadata:
languageName: node
linkType: hard
-"@vitest/spy@npm:1.2.2":
+"@vitest/spy@npm:1.2.2, @vitest/spy@npm:^1.1.3":
version: 1.2.2
resolution: "@vitest/spy@npm:1.2.2"
dependencies:
@@ -9335,14 +9508,7 @@ __metadata:
languageName: node
linkType: hard
-"acorn-walk@npm:^8.1.1":
- version: 8.3.1
- resolution: "acorn-walk@npm:8.3.1"
- checksum: a23d2f7c6b6cad617f4c77f14dfeb062a239208d61753e9ba808d916c550add92b39535467d2e6028280761ac4f5a904cc9df21530b84d3f834e3edef74ddde5
- languageName: node
- linkType: hard
-
-"acorn-walk@npm:^8.3.2":
+"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.3.2":
version: 8.3.2
resolution: "acorn-walk@npm:8.3.2"
checksum: 7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52
@@ -9670,6 +9836,15 @@ __metadata:
languageName: node
linkType: hard
+"aria-hidden@npm:^1.1.1":
+ version: 1.2.3
+ resolution: "aria-hidden@npm:1.2.3"
+ dependencies:
+ tslib: "npm:^2.0.0"
+ checksum: 46b07b7273167ad3fc2625f1ecbb43f8e6f73115c66785cbb5dcf1e2508133a43b6419d610c39676ceaeb563239efbd8974d5c0187695db8b3e8c3e11f549c2d
+ languageName: node
+ linkType: hard
+
"aria-query@npm:5.1.3":
version: 5.1.3
resolution: "aria-query@npm:5.1.3"
@@ -9719,13 +9894,13 @@ __metadata:
languageName: node
linkType: hard
-"array-buffer-byte-length@npm:^1.0.0":
- version: 1.0.0
- resolution: "array-buffer-byte-length@npm:1.0.0"
+"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "array-buffer-byte-length@npm:1.0.1"
dependencies:
- call-bind: "npm:^1.0.2"
- is-array-buffer: "npm:^3.0.1"
- checksum: 12f84f6418b57a954caa41654e5e63e019142a4bbb2c6829ba86d1ba65d31ccfaf1461d1743556fd32b091fac34ff44d9dfbdb001402361c45c373b2c86f5c20
+ call-bind: "npm:^1.0.5"
+ is-array-buffer: "npm:^3.0.4"
+ checksum: f5cdf54527cd18a3d2852ddf73df79efec03829e7373a8322ef5df2b4ef546fb365c19c71d6b42d641cb6bfe0f1a2f19bc0ece5b533295f86d7c3d522f228917
languageName: node
linkType: hard
@@ -9793,16 +9968,29 @@ __metadata:
languageName: node
linkType: hard
-"array.prototype.findlastindex@npm:^1.2.3":
- version: 1.2.3
- resolution: "array.prototype.findlastindex@npm:1.2.3"
+"array.prototype.filter@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "array.prototype.filter@npm:1.0.3"
dependencies:
call-bind: "npm:^1.0.2"
define-properties: "npm:^1.2.0"
es-abstract: "npm:^1.22.1"
- es-shim-unscopables: "npm:^1.0.0"
- get-intrinsic: "npm:^1.2.1"
- checksum: 2c5c4d3f07512d6729f728f6260a314c00f2eb0a243123092661fa1bc65dce90234c3b483b5f978396eccef6f69c50f0bea248448aaf9cdfcd1cedad6217acbb
+ es-array-method-boxes-properly: "npm:^1.0.0"
+ is-string: "npm:^1.0.7"
+ checksum: 8b70b5f866df5d90fa27aa5bfa30f5fefc44cbea94b0513699d761713658077c2a24cbf06aac5179eabddb6c93adc467af4c288b7a839c5bc5a769ee5a2d48ad
+ languageName: node
+ linkType: hard
+
+"array.prototype.findlastindex@npm:^1.2.3":
+ version: 1.2.4
+ resolution: "array.prototype.findlastindex@npm:1.2.4"
+ dependencies:
+ call-bind: "npm:^1.0.5"
+ define-properties: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.3"
+ es-errors: "npm:^1.3.0"
+ es-shim-unscopables: "npm:^1.0.2"
+ checksum: b23ae35cf7621c82c20981ee110626090734a264798e781b052e534e3d61d576f03d125d92cf2e3672062bb5cc5907e02e69f2d80196a55f3cdb0197b4aa8c64
languageName: node
linkType: hard
@@ -9831,30 +10019,31 @@ __metadata:
linkType: hard
"array.prototype.tosorted@npm:^1.1.1":
- version: 1.1.2
- resolution: "array.prototype.tosorted@npm:1.1.2"
+ version: 1.1.3
+ resolution: "array.prototype.tosorted@npm:1.1.3"
dependencies:
- call-bind: "npm:^1.0.2"
- define-properties: "npm:^1.2.0"
- es-abstract: "npm:^1.22.1"
- es-shim-unscopables: "npm:^1.0.0"
- get-intrinsic: "npm:^1.2.1"
- checksum: aa222a0f78e9cdb4ea4d788a11f0acc2b17c2226f0912917e1c89e0f0c4dcdd14414ac88afffbd03025f33501f2649907cfb80664e48aa2af3430c1fb1b0b416
+ call-bind: "npm:^1.0.5"
+ define-properties: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.3"
+ es-errors: "npm:^1.1.0"
+ es-shim-unscopables: "npm:^1.0.2"
+ checksum: a27e1ca51168ecacf6042901f5ef021e43c8fa04b6c6b6f2a30bac3645cd2b519cecbe0bc45db1b85b843f64dc3207f0268f700b4b9fbdec076d12d432cf0865
languageName: node
linkType: hard
-"arraybuffer.prototype.slice@npm:^1.0.2":
- version: 1.0.2
- resolution: "arraybuffer.prototype.slice@npm:1.0.2"
+"arraybuffer.prototype.slice@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "arraybuffer.prototype.slice@npm:1.0.3"
dependencies:
- array-buffer-byte-length: "npm:^1.0.0"
- call-bind: "npm:^1.0.2"
- define-properties: "npm:^1.2.0"
- es-abstract: "npm:^1.22.1"
- get-intrinsic: "npm:^1.2.1"
- is-array-buffer: "npm:^3.0.2"
+ array-buffer-byte-length: "npm:^1.0.1"
+ call-bind: "npm:^1.0.5"
+ define-properties: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.3"
+ es-errors: "npm:^1.2.1"
+ get-intrinsic: "npm:^1.2.3"
+ is-array-buffer: "npm:^3.0.4"
is-shared-array-buffer: "npm:^1.0.2"
- checksum: 96b6e40e439678ffb7fa266398510074d33c3980fbb475490b69980cca60adec3b0777047ef377068a29862157f83edef42efc64ce48ce38977d04d68de5b7fb
+ checksum: d32754045bcb2294ade881d45140a5e52bda2321b9e98fa514797b7f0d252c4c5ab0d1edb34112652c62fa6a9398def568da63a4d7544672229afea283358c36
languageName: node
linkType: hard
@@ -10059,10 +10248,12 @@ __metadata:
languageName: node
linkType: hard
-"available-typed-arrays@npm:^1.0.5":
- version: 1.0.5
- resolution: "available-typed-arrays@npm:1.0.5"
- checksum: c4df567ca72d2754a6cbad20088f5f98b1065b3360178169fa9b44ea101af62c0f423fc3854fa820fd6895b6b9171b8386e71558203103ff8fc2ad503fdcc660
+"available-typed-arrays@npm:^1.0.6, available-typed-arrays@npm:^1.0.7":
+ version: 1.0.7
+ resolution: "available-typed-arrays@npm:1.0.7"
+ dependencies:
+ possible-typed-array-names: "npm:^1.0.0"
+ checksum: d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2
languageName: node
linkType: hard
@@ -11212,14 +11403,16 @@ __metadata:
languageName: node
linkType: hard
-"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5":
- version: 1.0.5
- resolution: "call-bind@npm:1.0.5"
+"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7":
+ version: 1.0.7
+ resolution: "call-bind@npm:1.0.7"
dependencies:
+ es-define-property: "npm:^1.0.0"
+ es-errors: "npm:^1.3.0"
function-bind: "npm:^1.1.2"
- get-intrinsic: "npm:^1.2.1"
- set-function-length: "npm:^1.1.1"
- checksum: a6172c168fd6dacf744fcde745099218056bd755c50415b592655dcd6562157ed29f130f56c3f6db2250f67e4bd62e5c218cdc56d7bfd76e0bda50770fce2d10
+ get-intrinsic: "npm:^1.2.4"
+ set-function-length: "npm:^1.2.1"
+ checksum: a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d
languageName: node
linkType: hard
@@ -11265,14 +11458,7 @@ __metadata:
languageName: node
linkType: hard
-"caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001565":
- version: 1.0.30001570
- resolution: "caniuse-lite@npm:1.0.30001570"
- checksum: e47230d2016edea56e002fa462a5289f697b48dcfbf703fb01aecc6c98ad4ecaf945ab23c253cb7af056c2d05f266e4e4cbebf45132100e2c9367439cb95b95b
- languageName: node
- linkType: hard
-
-"caniuse-lite@npm:^1.0.30001579":
+"caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001565, caniuse-lite@npm:^1.0.30001579":
version: 1.0.30001581
resolution: "caniuse-lite@npm:1.0.30001581"
checksum: 34b048156514eab5932212807428905cbecdef918f7c3d2153d5e8b6885d929e5c0b649f9e135cb1e03e413fbad8e00d1f24ed04cbcca52adc660ef98cad9032
@@ -12605,6 +12791,13 @@ __metadata:
languageName: node
linkType: hard
+"debounce@npm:^1.2.1":
+ version: 1.2.1
+ resolution: "debounce@npm:1.2.1"
+ checksum: 6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500
+ languageName: node
+ linkType: hard
+
"debug@npm:2.6.9, debug@npm:^2.1.3, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.8":
version: 2.6.9
resolution: "debug@npm:2.6.9"
@@ -12693,6 +12886,13 @@ __metadata:
languageName: node
linkType: hard
+"deep-diff@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "deep-diff@npm:1.0.2"
+ checksum: cc3e315ba95963eba4bbb79ed88d0a37d80ba19bd3b0039b79d2ad0e19e48b0e15c692b49bcd617bbe0dcc7358d40464c993889313dd8bf806bb25978b12375d
+ languageName: node
+ linkType: hard
+
"deep-eql@npm:^4.1.3":
version: 4.1.3
resolution: "deep-eql@npm:4.1.3"
@@ -12812,14 +13012,14 @@ __metadata:
languageName: node
linkType: hard
-"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1":
- version: 1.1.1
- resolution: "define-data-property@npm:1.1.1"
+"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.2, define-data-property@npm:^1.1.4":
+ version: 1.1.4
+ resolution: "define-data-property@npm:1.1.4"
dependencies:
- get-intrinsic: "npm:^1.2.1"
+ es-define-property: "npm:^1.0.0"
+ es-errors: "npm:^1.3.0"
gopd: "npm:^1.0.1"
- has-property-descriptors: "npm:^1.0.0"
- checksum: 77ef6e0bceb515e05b5913ab635a84d537cee84f8a7c37c77fdcb31fc5b80f6dbe81b33375e4b67d96aa04e6a0d8d4ea099e431d83f089af8d93adfb584bcb94
+ checksum: dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37
languageName: node
linkType: hard
@@ -12986,6 +13186,13 @@ __metadata:
languageName: node
linkType: hard
+"detect-node-es@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "detect-node-es@npm:1.1.0"
+ checksum: e562f00de23f10c27d7119e1af0e7388407eb4b06596a25f6d79a360094a109ff285de317f02b090faae093d314cf6e73ac3214f8a5bb3a0def5bece94557fbe
+ languageName: node
+ linkType: hard
+
"detect-node@npm:^2.0.4":
version: 2.1.0
resolution: "detect-node@npm:2.1.0"
@@ -13048,9 +13255,9 @@ __metadata:
linkType: hard
"diff@npm:^5.0.0":
- version: 5.1.0
- resolution: "diff@npm:5.1.0"
- checksum: 77a0d9beb9ed54796154ac2511872288432124ac90a1cabb1878783c9b4d81f1847f3b746a0630b1e836181461d2c76e1e6b95559bef86ed16294d114862e364
+ version: 5.2.0
+ resolution: "diff@npm:5.2.0"
+ checksum: aed0941f206fe261ecb258dc8d0ceea8abbde3ace5827518ff8d302f0fc9cc81ce116c4d8f379151171336caf0516b79e01abdc1ed1201b6440d895a66689eb4
languageName: node
linkType: hard
@@ -13799,50 +14006,75 @@ __metadata:
languageName: node
linkType: hard
-"es-abstract@npm:^1.22.1":
- version: 1.22.2
- resolution: "es-abstract@npm:1.22.2"
+"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.22.4":
+ version: 1.22.4
+ resolution: "es-abstract@npm:1.22.4"
dependencies:
- array-buffer-byte-length: "npm:^1.0.0"
- arraybuffer.prototype.slice: "npm:^1.0.2"
- available-typed-arrays: "npm:^1.0.5"
- call-bind: "npm:^1.0.2"
- es-set-tostringtag: "npm:^2.0.1"
+ array-buffer-byte-length: "npm:^1.0.1"
+ arraybuffer.prototype.slice: "npm:^1.0.3"
+ available-typed-arrays: "npm:^1.0.6"
+ call-bind: "npm:^1.0.7"
+ es-define-property: "npm:^1.0.0"
+ es-errors: "npm:^1.3.0"
+ es-set-tostringtag: "npm:^2.0.2"
es-to-primitive: "npm:^1.2.1"
function.prototype.name: "npm:^1.1.6"
- get-intrinsic: "npm:^1.2.1"
- get-symbol-description: "npm:^1.0.0"
+ get-intrinsic: "npm:^1.2.4"
+ get-symbol-description: "npm:^1.0.2"
globalthis: "npm:^1.0.3"
gopd: "npm:^1.0.1"
- has: "npm:^1.0.3"
- has-property-descriptors: "npm:^1.0.0"
+ has-property-descriptors: "npm:^1.0.2"
has-proto: "npm:^1.0.1"
has-symbols: "npm:^1.0.3"
- internal-slot: "npm:^1.0.5"
- is-array-buffer: "npm:^3.0.2"
+ hasown: "npm:^2.0.1"
+ internal-slot: "npm:^1.0.7"
+ is-array-buffer: "npm:^3.0.4"
is-callable: "npm:^1.2.7"
is-negative-zero: "npm:^2.0.2"
is-regex: "npm:^1.1.4"
is-shared-array-buffer: "npm:^1.0.2"
is-string: "npm:^1.0.7"
- is-typed-array: "npm:^1.1.12"
+ is-typed-array: "npm:^1.1.13"
is-weakref: "npm:^1.0.2"
- object-inspect: "npm:^1.12.3"
+ object-inspect: "npm:^1.13.1"
object-keys: "npm:^1.1.1"
- object.assign: "npm:^4.1.4"
- regexp.prototype.flags: "npm:^1.5.1"
- safe-array-concat: "npm:^1.0.1"
- safe-regex-test: "npm:^1.0.0"
+ object.assign: "npm:^4.1.5"
+ regexp.prototype.flags: "npm:^1.5.2"
+ safe-array-concat: "npm:^1.1.0"
+ safe-regex-test: "npm:^1.0.3"
string.prototype.trim: "npm:^1.2.8"
string.prototype.trimend: "npm:^1.0.7"
string.prototype.trimstart: "npm:^1.0.7"
- typed-array-buffer: "npm:^1.0.0"
+ typed-array-buffer: "npm:^1.0.1"
typed-array-byte-length: "npm:^1.0.0"
typed-array-byte-offset: "npm:^1.0.0"
typed-array-length: "npm:^1.0.4"
unbox-primitive: "npm:^1.0.2"
- which-typed-array: "npm:^1.1.11"
- checksum: a491c640a01b7c18f3cc626a3d08b5c67f8d3dac70ff8b4268cda6fa0ebed80bb028ff3ee731137512e054d39e98d02575144da904fe28045019fc59e503f1f8
+ which-typed-array: "npm:^1.1.14"
+ checksum: dc332c3a010c5e7b77b7ea8a4532ac455fa02e7bcabf996a47447165bafa72d0d99967407d0cf5dbbb5fbbf87f53cd8b706608ec70953523b8cd2b831b9a9d64
+ languageName: node
+ linkType: hard
+
+"es-array-method-boxes-properly@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "es-array-method-boxes-properly@npm:1.0.0"
+ checksum: 4b7617d3fbd460d6f051f684ceca6cf7e88e6724671d9480388d3ecdd72119ddaa46ca31f2c69c5426a82e4b3091c1e81867c71dcdc453565cd90005ff2c382d
+ languageName: node
+ linkType: hard
+
+"es-define-property@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "es-define-property@npm:1.0.0"
+ dependencies:
+ get-intrinsic: "npm:^1.2.4"
+ checksum: 6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4
+ languageName: node
+ linkType: hard
+
+"es-errors@npm:^1.0.0, es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0":
+ version: 1.3.0
+ resolution: "es-errors@npm:1.3.0"
+ checksum: 0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85
languageName: node
linkType: hard
@@ -13864,24 +14096,25 @@ __metadata:
linkType: hard
"es-iterator-helpers@npm:^1.0.12, es-iterator-helpers@npm:^1.0.15":
- version: 1.0.15
- resolution: "es-iterator-helpers@npm:1.0.15"
+ version: 1.0.17
+ resolution: "es-iterator-helpers@npm:1.0.17"
dependencies:
asynciterator.prototype: "npm:^1.0.0"
- call-bind: "npm:^1.0.2"
+ call-bind: "npm:^1.0.7"
define-properties: "npm:^1.2.1"
- es-abstract: "npm:^1.22.1"
- es-set-tostringtag: "npm:^2.0.1"
- function-bind: "npm:^1.1.1"
- get-intrinsic: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.4"
+ es-errors: "npm:^1.3.0"
+ es-set-tostringtag: "npm:^2.0.2"
+ function-bind: "npm:^1.1.2"
+ get-intrinsic: "npm:^1.2.4"
globalthis: "npm:^1.0.3"
- has-property-descriptors: "npm:^1.0.0"
+ has-property-descriptors: "npm:^1.0.2"
has-proto: "npm:^1.0.1"
has-symbols: "npm:^1.0.3"
- internal-slot: "npm:^1.0.5"
+ internal-slot: "npm:^1.0.7"
iterator.prototype: "npm:^1.1.2"
- safe-array-concat: "npm:^1.0.1"
- checksum: b4c83f94bfe624260d5238092de3173989f76f1416b1d02c388aea3b2024174e5f5f0e864057311ac99790b57e836ca3545b6e77256b26066dac944519f5e6d6
+ safe-array-concat: "npm:^1.1.0"
+ checksum: d0f281257e7165f068fd4fc3beb63d07ae4f18fbef02a2bbe4a39272b764164c1ce3311ae7c5429ac30003aef290fcdf569050e4a9ba3560e044440f68e9a47c
languageName: node
linkType: hard
@@ -13899,23 +14132,23 @@ __metadata:
languageName: node
linkType: hard
-"es-set-tostringtag@npm:^2.0.1":
- version: 2.0.1
- resolution: "es-set-tostringtag@npm:2.0.1"
+"es-set-tostringtag@npm:^2.0.2":
+ version: 2.0.3
+ resolution: "es-set-tostringtag@npm:2.0.3"
dependencies:
- get-intrinsic: "npm:^1.1.3"
- has: "npm:^1.0.3"
- has-tostringtag: "npm:^1.0.0"
- checksum: 9af096365e3861bb29755cc5f76f15f66a7eab0e83befca396129090c1d9737e54090278b8e5357e97b5f0a5b0459fca07c40c6740884c2659cbf90ef8e508cc
+ get-intrinsic: "npm:^1.2.4"
+ has-tostringtag: "npm:^1.0.2"
+ hasown: "npm:^2.0.1"
+ checksum: f22aff1585eb33569c326323f0b0d175844a1f11618b86e193b386f8be0ea9474cfbe46df39c45d959f7aa8f6c06985dc51dd6bce5401645ec5a74c4ceaa836a
languageName: node
linkType: hard
-"es-shim-unscopables@npm:^1.0.0":
- version: 1.0.0
- resolution: "es-shim-unscopables@npm:1.0.0"
+"es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "es-shim-unscopables@npm:1.0.2"
dependencies:
- has: "npm:^1.0.3"
- checksum: d54a66239fbd19535b3e50333913260394f14d2d7adb136a95396a13ca584bab400cf9cb2ffd9232f3fe2f0362540bd3a708240c493e46e13fe0b90cfcfedc3d
+ hasown: "npm:^2.0.0"
+ checksum: f495af7b4b7601a4c0cfb893581c352636e5c08654d129590386a33a0432cf13a7bdc7b6493801cadd990d838e2839b9013d1de3b880440cb537825e834fe783
languageName: node
linkType: hard
@@ -15598,6 +15831,27 @@ __metadata:
languageName: node
linkType: hard
+"framer-motion@npm:^11.0.3":
+ version: 11.0.6
+ resolution: "framer-motion@npm:11.0.6"
+ dependencies:
+ "@emotion/is-prop-valid": "npm:^0.8.2"
+ tslib: "npm:^2.4.0"
+ peerDependencies:
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ dependenciesMeta:
+ "@emotion/is-prop-valid":
+ optional: true
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ checksum: ba987848120847157eb2d8c7669808aa04081b753002c7d57b2adfca92da5de50470117a73e377476b3eec42d13b4c91456f649b97b7900dcc72cb980262af48
+ languageName: node
+ linkType: hard
+
"fresh@npm:0.5.2, fresh@npm:^0.5.2":
version: 0.5.2
resolution: "fresh@npm:0.5.2"
@@ -15817,7 +16071,7 @@ __metadata:
languageName: node
linkType: hard
-"function-bind@npm:^1.1.1, function-bind@npm:^1.1.2":
+"function-bind@npm:^1.1.2":
version: 1.1.2
resolution: "function-bind@npm:1.1.2"
checksum: d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5
@@ -15950,15 +16204,23 @@ __metadata:
languageName: node
linkType: hard
-"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1":
- version: 1.2.2
- resolution: "get-intrinsic@npm:1.2.2"
+"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4":
+ version: 1.2.4
+ resolution: "get-intrinsic@npm:1.2.4"
dependencies:
+ es-errors: "npm:^1.3.0"
function-bind: "npm:^1.1.2"
has-proto: "npm:^1.0.1"
has-symbols: "npm:^1.0.3"
hasown: "npm:^2.0.0"
- checksum: 4e7fb8adc6172bae7c4fe579569b4d5238b3667c07931cd46b4eee74bbe6ff6b91329bec311a638d8e60f5b51f44fe5445693c6be89ae88d4b5c49f7ff12db0b
+ checksum: 0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7
+ languageName: node
+ linkType: hard
+
+"get-nonce@npm:^1.0.0":
+ version: 1.0.1
+ resolution: "get-nonce@npm:1.0.1"
+ checksum: 2d7df55279060bf0568549e1ffc9b84bc32a32b7541675ca092dce56317cdd1a59a98dcc4072c9f6a980779440139a3221d7486f52c488e69dc0fd27b1efb162
languageName: node
linkType: hard
@@ -16015,13 +16277,14 @@ __metadata:
languageName: node
linkType: hard
-"get-symbol-description@npm:^1.0.0":
- version: 1.0.0
- resolution: "get-symbol-description@npm:1.0.0"
+"get-symbol-description@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "get-symbol-description@npm:1.0.2"
dependencies:
- call-bind: "npm:^1.0.2"
- get-intrinsic: "npm:^1.1.1"
- checksum: 23bc3b44c221cdf7669a88230c62f4b9e30393b61eb21ba4400cb3e346801bd8f95fe4330ee78dbae37aecd874646d53e3e76a17a654d0c84c77f6690526d6bb
+ call-bind: "npm:^1.0.5"
+ es-errors: "npm:^1.3.0"
+ get-intrinsic: "npm:^1.2.4"
+ checksum: 867be6d63f5e0eb026cb3b0ef695ec9ecf9310febb041072d2e142f260bd91ced9eeb426b3af98791d1064e324e653424afa6fd1af17dee373bea48ae03162bc
languageName: node
linkType: hard
@@ -16462,19 +16725,19 @@ __metadata:
languageName: node
linkType: hard
-"has-property-descriptors@npm:^1.0.0":
- version: 1.0.0
- resolution: "has-property-descriptors@npm:1.0.0"
+"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1, has-property-descriptors@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "has-property-descriptors@npm:1.0.2"
dependencies:
- get-intrinsic: "npm:^1.1.1"
- checksum: d4ca882b6960d6257bd28baa3ddfa21f068d260411004a093b30ca357c740e11e985771c85216a6d1eef4161e862657f48c4758ec8ab515223b3895200ad164b
+ es-define-property: "npm:^1.0.0"
+ checksum: 253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236
languageName: node
linkType: hard
-"has-proto@npm:^1.0.1":
- version: 1.0.1
- resolution: "has-proto@npm:1.0.1"
- checksum: c8a8fe411f810b23a564bd5546a8f3f0fff6f1b692740eb7a2fdc9df716ef870040806891e2f23ff4653f1083e3895bf12088703dd1a0eac3d9202d3a4768cd0
+"has-proto@npm:^1.0.1, has-proto@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "has-proto@npm:1.0.3"
+ checksum: 35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205
languageName: node
linkType: hard
@@ -16485,12 +16748,12 @@ __metadata:
languageName: node
linkType: hard
-"has-tostringtag@npm:^1.0.0":
- version: 1.0.0
- resolution: "has-tostringtag@npm:1.0.0"
+"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1, has-tostringtag@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "has-tostringtag@npm:1.0.2"
dependencies:
- has-symbols: "npm:^1.0.2"
- checksum: 1cdba76b7d13f65198a92b8ca1560ba40edfa09e85d182bf436d928f3588a9ebd260451d569f0ed1b849c4bf54f49c862aa0d0a77f9552b1855bb6deb526c011
+ has-symbols: "npm:^1.0.3"
+ checksum: a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c
languageName: node
linkType: hard
@@ -16540,13 +16803,6 @@ __metadata:
languageName: node
linkType: hard
-"has@npm:^1.0.3":
- version: 1.0.4
- resolution: "has@npm:1.0.4"
- checksum: 82c1220573dc1f0a014a5d6189ae52a1f820f99dfdc00323c3a725b5002dcb7f04e44f460fea7af068474b2dd7c88cbe1846925c84017be9e31e1708936d305b
- languageName: node
- linkType: hard
-
"hash-base@npm:^3.0.0":
version: 3.1.0
resolution: "hash-base@npm:3.1.0"
@@ -16589,12 +16845,12 @@ __metadata:
languageName: node
linkType: hard
-"hasown@npm:^2.0.0":
- version: 2.0.0
- resolution: "hasown@npm:2.0.0"
+"hasown@npm:^2.0.0, hasown@npm:^2.0.1":
+ version: 2.0.1
+ resolution: "hasown@npm:2.0.1"
dependencies:
function-bind: "npm:^1.1.2"
- checksum: 5d415b114f410661208c95e7ab4879f1cc2765b8daceff4dc8718317d1cb7b9ffa7c5d1eafd9a4389c9aab7445d6ea88e05f3096cb1e529618b55304956b87fc
+ checksum: 9e27e70e8e4204f4124c8f99950d1ba2b1f5174864fd39ff26da190f9ea6488c1b3927dcc64981c26d1f637a971783c9489d62c829d393ea509e6f1ba20370bb
languageName: node
linkType: hard
@@ -17246,9 +17502,9 @@ __metadata:
linkType: hard
"ignore@npm:^5.0.0, ignore@npm:^5.0.4, ignore@npm:^5.0.5, ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4":
- version: 5.2.4
- resolution: "ignore@npm:5.2.4"
- checksum: 7c7cd90edd9fea6e037f9b9da4b01bf0a86b198ce78345f9bbd983929d68ff14830be31111edc5d70c264921f4962404d75b7262b4d9cc3bc12381eccbd03096
+ version: 5.3.1
+ resolution: "ignore@npm:5.3.1"
+ checksum: 703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd
languageName: node
linkType: hard
@@ -17413,14 +17669,14 @@ __metadata:
languageName: node
linkType: hard
-"internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5":
- version: 1.0.5
- resolution: "internal-slot@npm:1.0.5"
+"internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5, internal-slot@npm:^1.0.7":
+ version: 1.0.7
+ resolution: "internal-slot@npm:1.0.7"
dependencies:
- get-intrinsic: "npm:^1.2.0"
- has: "npm:^1.0.3"
+ es-errors: "npm:^1.3.0"
+ hasown: "npm:^2.0.0"
side-channel: "npm:^1.0.4"
- checksum: 66d8a66b4b5310c042e8ad00ce895dc55cb25165a3a7da0d7862ca18d69d3b1ba86511b4bf3baf4273d744d3f6e9154574af45189ef11135a444945309e39e4a
+ checksum: f8b294a4e6ea3855fc59551bbf35f2b832cf01fd5e6e2a97f5c201a071cc09b49048f856e484b67a6c721da5e55736c5b6ddafaf19e2dbeb4a3ff1821680de6c
languageName: node
linkType: hard
@@ -17433,14 +17689,7 @@ __metadata:
languageName: node
linkType: hard
-"ip@npm:^2.0.0":
- version: 2.0.0
- resolution: "ip@npm:2.0.0"
- checksum: 8d186cc5585f57372847ae29b6eba258c68862055e18a75cc4933327232cb5c107f89800ce29715d542eef2c254fbb68b382e780a7414f9ee7caf60b7a473958
- languageName: node
- linkType: hard
-
-"ip@npm:^2.0.1":
+"ip@npm:^2.0.0, ip@npm:^2.0.1":
version: 2.0.1
resolution: "ip@npm:2.0.1"
checksum: cab8eb3e88d0abe23e4724829621ec4c4c5cb41a7f936a2e626c947128c1be16ed543448d42af7cca95379f9892bfcacc1ccd8d09bc7e8bea0e86d492ce33616
@@ -17530,14 +17779,13 @@ __metadata:
languageName: node
linkType: hard
-"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2":
- version: 3.0.2
- resolution: "is-array-buffer@npm:3.0.2"
+"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4":
+ version: 3.0.4
+ resolution: "is-array-buffer@npm:3.0.4"
dependencies:
call-bind: "npm:^1.0.2"
- get-intrinsic: "npm:^1.2.0"
- is-typed-array: "npm:^1.1.10"
- checksum: 40ed13a5f5746ac3ae2f2e463687d9b5a3f5fd0086f970fb4898f0253c2a5ec2e3caea2d664dd8f54761b1c1948609702416921a22faebe160c7640a9217c80e
+ get-intrinsic: "npm:^1.2.1"
+ checksum: 42a49d006cc6130bc5424eae113e948c146f31f9d24460fc0958f855d9d810e6fd2e4519bf19aab75179af9c298ea6092459d8cafdec523cd19e529b26eab860
languageName: node
linkType: hard
@@ -17613,7 +17861,7 @@ __metadata:
languageName: node
linkType: hard
-"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0":
+"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.8.1":
version: 2.13.1
resolution: "is-core-module@npm:2.13.1"
dependencies:
@@ -17824,6 +18072,20 @@ __metadata:
languageName: node
linkType: hard
+"is-lite@npm:^0.8.2":
+ version: 0.8.2
+ resolution: "is-lite@npm:0.8.2"
+ checksum: ed4b99d47ff12d0bf9730994cec8bcdee20bf46d68810f55e9e041797c76da664ab6d48a9ed2e7fb3aa52843a48ac4a083561a2235311a046551542504d995fd
+ languageName: node
+ linkType: hard
+
+"is-lite@npm:^1.2.0, is-lite@npm:^1.2.1":
+ version: 1.2.1
+ resolution: "is-lite@npm:1.2.1"
+ checksum: 53acb0f6329f0aba96fef4d883d42f3d9aabf2c42baba45821407d14b1782b9c6bea42c3eef72b37788be1acc95d6cf2df8a6b8424cb9f6eb12c08d5a7d81288
+ languageName: node
+ linkType: hard
+
"is-map@npm:^2.0.1, is-map@npm:^2.0.2":
version: 2.0.2
resolution: "is-map@npm:2.0.2"
@@ -17842,9 +18104,9 @@ __metadata:
linkType: hard
"is-negative-zero@npm:^2.0.2":
- version: 2.0.2
- resolution: "is-negative-zero@npm:2.0.2"
- checksum: eda024c158f70f2017f3415e471b818d314da5ef5be68f801b16314d4a4b6304a74cbed778acf9e2f955bb9c1c5f2935c1be0c7c99e1ad12286f45366217b6a3
+ version: 2.0.3
+ resolution: "is-negative-zero@npm:2.0.3"
+ checksum: bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e
languageName: node
linkType: hard
@@ -18021,12 +18283,12 @@ __metadata:
languageName: node
linkType: hard
-"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9":
- version: 1.1.12
- resolution: "is-typed-array@npm:1.1.12"
+"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.3":
+ version: 1.1.13
+ resolution: "is-typed-array@npm:1.1.13"
dependencies:
- which-typed-array: "npm:^1.1.11"
- checksum: 9863e9cc7223c6fc1c462a2c3898a7beff6b41b1ee0fabb03b7d278ae7de670b5bcbc8627db56bb66ed60902fa37d53fe5cce0fd2f7d73ac64fe5da6f409b6ae
+ which-typed-array: "npm:^1.1.14"
+ checksum: fa5cb97d4a80e52c2cc8ed3778e39f175a1a2ae4ddf3adae3187d69586a1fd57cfa0b095db31f66aa90331e9e3da79184cea9c6abdcd1abc722dc3c3edd51cca
languageName: node
linkType: hard
@@ -18735,13 +18997,20 @@ __metadata:
languageName: node
linkType: hard
-"jsonc-parser@npm:3.2.0, jsonc-parser@npm:^3.0.0, jsonc-parser@npm:^3.2.0":
+"jsonc-parser@npm:3.2.0":
version: 3.2.0
resolution: "jsonc-parser@npm:3.2.0"
checksum: 5a12d4d04dad381852476872a29dcee03a57439574e4181d91dca71904fcdcc5e8e4706c0a68a2c61ad9810e1e1c5806b5100d52d3e727b78f5cdc595401045b
languageName: node
linkType: hard
+"jsonc-parser@npm:^3.0.0, jsonc-parser@npm:^3.2.0":
+ version: 3.2.1
+ resolution: "jsonc-parser@npm:3.2.1"
+ checksum: ada66dec143d7f9cb0e2d0d29c69e9ce40d20f3a4cb96b0c6efb745025ac7f9ba647d7ac0990d0adfc37a2d2ae084a12009a9c833dbdbeadf648879a99b9df89
+ languageName: node
+ linkType: hard
+
"jsonexport@npm:^3.0.1":
version: 3.2.0
resolution: "jsonexport@npm:3.2.0"
@@ -19194,12 +19463,12 @@ __metadata:
linkType: hard
"load-plugin@npm:^6.0.0":
- version: 6.0.1
- resolution: "load-plugin@npm:6.0.1"
+ version: 6.0.2
+ resolution: "load-plugin@npm:6.0.2"
dependencies:
"@npmcli/config": "npm:^8.0.0"
import-meta-resolve: "npm:^4.0.0"
- checksum: ae405fad041da559797ac479087ed41196619dbe695ab1e0816f12159658ee2c9f7d6faccfe3701f13223a0d85ea0ef79c67879cde86c0873f2650c7fed7d93a
+ checksum: 01d95e2928f48c09432e52d6c71aa3f278bcb5dd1c313c018c7955f4747755499678fe9e1adee0c3bd951440fa681aa1709ed193fa6a6314ca4a2bafcb4b7259
languageName: node
linkType: hard
@@ -22096,7 +22365,7 @@ __metadata:
languageName: node
linkType: hard
-"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0":
+"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0":
version: 1.13.1
resolution: "object-inspect@npm:1.13.1"
checksum: fad603f408e345c82e946abdf4bfd774260a5ed3e5997a0b057c44153ac32c7271ff19e3a5ae39c858da683ba045ccac2f65245c12763ce4e8594f818f4a648d
@@ -22129,7 +22398,7 @@ __metadata:
languageName: node
linkType: hard
-"object.assign@npm:^4.1.2, object.assign@npm:^4.1.4":
+"object.assign@npm:^4.1.2, object.assign@npm:^4.1.4, object.assign@npm:^4.1.5":
version: 4.1.5
resolution: "object.assign@npm:4.1.5"
dependencies:
@@ -22164,14 +22433,15 @@ __metadata:
linkType: hard
"object.groupby@npm:^1.0.1":
- version: 1.0.1
- resolution: "object.groupby@npm:1.0.1"
+ version: 1.0.2
+ resolution: "object.groupby@npm:1.0.2"
dependencies:
- call-bind: "npm:^1.0.2"
- define-properties: "npm:^1.2.0"
- es-abstract: "npm:^1.22.1"
- get-intrinsic: "npm:^1.2.1"
- checksum: 61e41fbf08cc04ed860363db9629eedeaa590fce243c0960e948fd7b11f78a9d4350065c339936d118a2dd8775d7259e26207340cc8ce688bec66cb615fec6fe
+ array.prototype.filter: "npm:^1.0.3"
+ call-bind: "npm:^1.0.5"
+ define-properties: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.3"
+ es-errors: "npm:^1.0.0"
+ checksum: b6266b1cfec7eb784b8bbe0bca5dc4b371cf9dd3e601b0897d72fa97a5934273d8fb05b3fc5222204104dbec32b50e25ba27e05ad681f71fb739cc1c7e9b81b1
languageName: node
linkType: hard
@@ -23193,7 +23463,7 @@ __metadata:
languageName: node
linkType: hard
-"popper.js@npm:^1.14.4":
+"popper.js@npm:^1.14.4, popper.js@npm:^1.16.0":
version: 1.16.1
resolution: "popper.js@npm:1.16.1"
checksum: 1c1a826f757edb5b8c2049dfd7a9febf6ae1e9d0e51342fc715b49a0c1020fced250d26484619883651e097c5764bbcacd2f31496e3646027f079dd83e072681
@@ -23228,6 +23498,13 @@ __metadata:
languageName: node
linkType: hard
+"possible-typed-array-names@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "possible-typed-array-names@npm:1.0.0"
+ checksum: d9aa22d31f4f7680e20269db76791b41c3a32c01a373e25f8a4813b4d45f7456bfc2b6d68f752dc4aab0e0bb0721cb3d76fb678c9101cb7a16316664bc2c73fd
+ languageName: node
+ linkType: hard
+
"postcss-loader@npm:7.3.3":
version: 7.3.3
resolution: "postcss-loader@npm:7.3.3"
@@ -24072,6 +24349,17 @@ __metadata:
languageName: node
linkType: hard
+"react-confetti@npm:^6.1.0":
+ version: 6.1.0
+ resolution: "react-confetti@npm:6.1.0"
+ dependencies:
+ tween-functions: "npm:^1.2.0"
+ peerDependencies:
+ react: ^16.3.0 || ^17.0.1 || ^18.0.0
+ checksum: 5b4eb23eef564695f6db1d25b294ed31d5fa21ff4092c6a38e641f85cd10e3e0b50014366e3ac0f7cf772e73faaecd14614e5b11a5531336fa769dda8068ab59
+ languageName: node
+ linkType: hard
+
"react-docgen-typescript@npm:^2.2.2":
version: 2.2.2
resolution: "react-docgen-typescript@npm:2.2.2"
@@ -24172,6 +24460,22 @@ __metadata:
languageName: node
linkType: hard
+"react-floater@npm:^0.7.9":
+ version: 0.7.9
+ resolution: "react-floater@npm:0.7.9"
+ dependencies:
+ deepmerge: "npm:^4.3.1"
+ is-lite: "npm:^0.8.2"
+ popper.js: "npm:^1.16.0"
+ prop-types: "npm:^15.8.1"
+ tree-changes: "npm:^0.9.1"
+ peerDependencies:
+ react: 15 - 18
+ react-dom: 15 - 18
+ checksum: 3fa58e968a405fb95a004897ed19c87b4991e52ebd45d837b8577eb9175ddc1dfab223bb653a0fa73ffc22fc2b2a30f2d07e924f41f41a040299793e4d2decd4
+ languageName: node
+ linkType: hard
+
"react-github-button@npm:^0.1.11":
version: 0.1.11
resolution: "react-github-button@npm:0.1.11"
@@ -24197,6 +24501,16 @@ __metadata:
languageName: node
linkType: hard
+"react-innertext@npm:^1.1.5":
+ version: 1.1.5
+ resolution: "react-innertext@npm:1.1.5"
+ peerDependencies:
+ "@types/react": ">=0.0.0 <=99"
+ react: ">=0.0.0 <=99"
+ checksum: 45335918ac83334133a9fa0df4ce109e0d4a45d54b783a8eaec88811b36dd5c3bbb5d9a6af2da1eed518a6f325a6ffae4be7bd30878596e0ec1760b231a8e0ee
+ languageName: node
+ linkType: hard
+
"react-inspector@npm:^6.0.0":
version: 6.0.2
resolution: "react-inspector@npm:6.0.2"
@@ -24234,6 +24548,29 @@ __metadata:
languageName: node
linkType: hard
+"react-joyride@npm:^2.7.2":
+ version: 2.7.3
+ resolution: "react-joyride@npm:2.7.3"
+ dependencies:
+ "@gilbarbara/deep-equal": "npm:^0.3.1"
+ "@gilbarbara/helpers": "npm:^0.9.2"
+ deep-diff: "npm:^1.0.2"
+ deepmerge: "npm:^4.3.1"
+ is-lite: "npm:^1.2.1"
+ react-floater: "npm:^0.7.9"
+ react-innertext: "npm:^1.1.5"
+ react-is: "npm:^16.13.1"
+ scroll: "npm:^3.0.1"
+ scrollparent: "npm:^2.1.0"
+ tree-changes: "npm:^0.11.2"
+ type-fest: "npm:^4.10.2"
+ peerDependencies:
+ react: 15 - 18
+ react-dom: 15 - 18
+ checksum: cd68fde2ba82b771179220f5594bb145ef25e29e6a07cfd182d21cbfaf87c59a374601cf9c96f3990e992ed2e81d47ecbae37a9a5d28f387dda83da723e94332
+ languageName: node
+ linkType: hard
+
"react-lifecycles-compat@npm:^3.0.0":
version: 3.0.4
resolution: "react-lifecycles-compat@npm:3.0.4"
@@ -24328,6 +24665,41 @@ __metadata:
languageName: node
linkType: hard
+"react-remove-scroll-bar@npm:^2.3.3":
+ version: 2.3.5
+ resolution: "react-remove-scroll-bar@npm:2.3.5"
+ dependencies:
+ react-style-singleton: "npm:^2.2.1"
+ tslib: "npm:^2.0.0"
+ peerDependencies:
+ "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 21b2b02818b04f2c755c5062c90385420adb244107ac90ec87d43cd338760d3cc1cae6eeb59ab198bbc9e388e1a5909551e0b8a708b0d87ce221cf50951bb1fc
+ languageName: node
+ linkType: hard
+
+"react-remove-scroll@npm:2.5.5":
+ version: 2.5.5
+ resolution: "react-remove-scroll@npm:2.5.5"
+ dependencies:
+ react-remove-scroll-bar: "npm:^2.3.3"
+ react-style-singleton: "npm:^2.2.1"
+ tslib: "npm:^2.1.0"
+ use-callback-ref: "npm:^1.3.0"
+ use-sidecar: "npm:^1.1.2"
+ peerDependencies:
+ "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 4952657e6a7b9d661d4ad4dfcef81b9c7fa493e35164abff99c35c0b27b3d172ef7ad70c09416dc44dd14ff2e6b38a5ec7da27e27e90a15cbad36b8fd2fd8054
+ languageName: node
+ linkType: hard
+
"react-resize-detector@npm:^7.1.2":
version: 7.1.2
resolution: "react-resize-detector@npm:7.1.2"
@@ -24364,6 +24736,23 @@ __metadata:
languageName: node
linkType: hard
+"react-style-singleton@npm:^2.2.1":
+ version: 2.2.1
+ resolution: "react-style-singleton@npm:2.2.1"
+ dependencies:
+ get-nonce: "npm:^1.0.0"
+ invariant: "npm:^2.2.4"
+ tslib: "npm:^2.0.0"
+ peerDependencies:
+ "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 6d66f3bdb65e1ec79089f80314da97c9a005087a04ee034255a5de129a4c0d9fd0bf99fa7bf642781ac2dc745ca687aae3de082bd8afdd0d117bc953241e15ad
+ languageName: node
+ linkType: hard
+
"react-syntax-highlighter@npm:^15.4.5, react-syntax-highlighter@npm:^15.5.0":
version: 15.5.0
resolution: "react-syntax-highlighter@npm:15.5.0"
@@ -24407,6 +24796,18 @@ __metadata:
languageName: node
linkType: hard
+"react-use-measure@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "react-use-measure@npm:2.1.1"
+ dependencies:
+ debounce: "npm:^1.2.1"
+ peerDependencies:
+ react: ">=16.13"
+ react-dom: ">=16.13"
+ checksum: 77b035189dbd613f50014ae56cbfc1363a4eba5104f68f3bc09cbdd20719ae7fb42884e53328175c30b238215c5b8064c60098d70b3fa9b8d902db6ffb07c6a3
+ languageName: node
+ linkType: hard
+
"react@npm:^18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
@@ -24604,16 +25005,17 @@ __metadata:
linkType: hard
"reflect.getprototypeof@npm:^1.0.4":
- version: 1.0.4
- resolution: "reflect.getprototypeof@npm:1.0.4"
+ version: 1.0.5
+ resolution: "reflect.getprototypeof@npm:1.0.5"
dependencies:
- call-bind: "npm:^1.0.2"
- define-properties: "npm:^1.2.0"
- es-abstract: "npm:^1.22.1"
- get-intrinsic: "npm:^1.2.1"
+ call-bind: "npm:^1.0.5"
+ define-properties: "npm:^1.2.1"
+ es-abstract: "npm:^1.22.3"
+ es-errors: "npm:^1.0.0"
+ get-intrinsic: "npm:^1.2.3"
globalthis: "npm:^1.0.3"
which-builtin-type: "npm:^1.1.3"
- checksum: 02104cdd22658b637efe6b1df73658edab539268347327c8250a72d0cb273dcdf280c284e2d94155d22601d022d16be1a816a8616d679e447cbcbde9860d15cb
+ checksum: 68f2a21494a9f4f5acc19bda5213236aa7fc02f9953ce2b18670c63b9ca3dec294dcabbb9d394d98cd2fc0de46b7cd6354614a60a33cabdbb5de9a6f7115f9a6
languageName: node
linkType: hard
@@ -24684,14 +25086,15 @@ __metadata:
languageName: node
linkType: hard
-"regexp.prototype.flags@npm:^1.2.0, regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.1":
- version: 1.5.1
- resolution: "regexp.prototype.flags@npm:1.5.1"
+"regexp.prototype.flags@npm:^1.2.0, regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.2":
+ version: 1.5.2
+ resolution: "regexp.prototype.flags@npm:1.5.2"
dependencies:
- call-bind: "npm:^1.0.2"
- define-properties: "npm:^1.2.0"
- set-function-name: "npm:^2.0.0"
- checksum: 1de7d214c0a726c7c874a7023e47b0e27b9f7fdb64175bfe1861189de1704aaeca05c3d26c35aa375432289b99946f3cf86651a92a8f7601b90d8c226a23bcd8
+ call-bind: "npm:^1.0.6"
+ define-properties: "npm:^1.2.1"
+ es-errors: "npm:^1.3.0"
+ set-function-name: "npm:^2.0.1"
+ checksum: 0f3fc4f580d9c349f8b560b012725eb9c002f36daa0041b3fbf6f4238cb05932191a4d7d5db3b5e2caa336d5150ad0402ed2be81f711f9308fe7e1a9bf9bd552
languageName: node
linkType: hard
@@ -25268,15 +25671,15 @@ __metadata:
linkType: hard
"resolve@npm:^2.0.0-next.4":
- version: 2.0.0-next.4
- resolution: "resolve@npm:2.0.0-next.4"
+ version: 2.0.0-next.5
+ resolution: "resolve@npm:2.0.0-next.5"
dependencies:
- is-core-module: "npm:^2.9.0"
+ is-core-module: "npm:^2.13.0"
path-parse: "npm:^1.0.7"
supports-preserve-symlinks-flag: "npm:^1.0.0"
bin:
resolve: bin/resolve
- checksum: 1de92669e7c46cfe125294c66d5405e13288bb87b97e9bdab71693ceebbcc0255c789bde30e2834265257d330d8ff57414d7d88e3097d8f69951f3ce978bf045
+ checksum: a6c33555e3482ea2ec4c6e3d3bf0d78128abf69dca99ae468e64f1e30acaa318fd267fb66c8836b04d558d3e2d6ed875fe388067e7d8e0de647d3c21af21c43a
languageName: node
linkType: hard
@@ -25294,15 +25697,15 @@ __metadata:
linkType: hard
"resolve@patch:resolve@npm%3A^2.0.0-next.4#optional!builtin":
- version: 2.0.0-next.4
- resolution: "resolve@patch:resolve@npm%3A2.0.0-next.4#optional!builtin::version=2.0.0-next.4&hash=c3c19d"
+ version: 2.0.0-next.5
+ resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d"
dependencies:
- is-core-module: "npm:^2.9.0"
+ is-core-module: "npm:^2.13.0"
path-parse: "npm:^1.0.7"
supports-preserve-symlinks-flag: "npm:^1.0.0"
bin:
resolve: bin/resolve
- checksum: ed2bb51d616b9cd30fe85cf49f7a2240094d9fa01a221d361918462be81f683d1855b7f192391d2ab5325245b42464ca59690db5bd5dad0a326fc0de5974dd10
+ checksum: 78ad6edb8309a2bfb720c2c1898f7907a37f858866ce11a5974643af1203a6a6e05b2fa9c53d8064a673a447b83d42569260c306d43628bff5bb101969708355
languageName: node
linkType: hard
@@ -25578,15 +25981,15 @@ __metadata:
languageName: node
linkType: hard
-"safe-array-concat@npm:^1.0.1":
- version: 1.0.1
- resolution: "safe-array-concat@npm:1.0.1"
+"safe-array-concat@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "safe-array-concat@npm:1.1.0"
dependencies:
- call-bind: "npm:^1.0.2"
- get-intrinsic: "npm:^1.2.1"
+ call-bind: "npm:^1.0.5"
+ get-intrinsic: "npm:^1.2.2"
has-symbols: "npm:^1.0.3"
isarray: "npm:^2.0.5"
- checksum: 4b15ce5fce5ce4d7e744a63592cded88d2f27806ed229eadb2e42629cbcd40e770f7478608e75f455e7fe341acd8c0a01bdcd7146b10645ea7411c5e3c1d1dd8
+ checksum: 833d3d950fc7507a60075f9bfaf41ec6dac7c50c7a9d62b1e6b071ecc162185881f92e594ff95c1a18301c881352dd6fd236d56999d5819559db7b92da9c28af
languageName: node
linkType: hard
@@ -25611,14 +26014,14 @@ __metadata:
languageName: node
linkType: hard
-"safe-regex-test@npm:^1.0.0":
- version: 1.0.0
- resolution: "safe-regex-test@npm:1.0.0"
+"safe-regex-test@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "safe-regex-test@npm:1.0.3"
dependencies:
- call-bind: "npm:^1.0.2"
- get-intrinsic: "npm:^1.1.3"
+ call-bind: "npm:^1.0.6"
+ es-errors: "npm:^1.3.0"
is-regex: "npm:^1.1.4"
- checksum: 14a81a7e683f97b2d6e9c8be61fddcf8ed7a02f4e64a825515f96bb1738eb007145359313741d2704d28b55b703a0f6300c749dde7c1dbc13952a2b85048ede2
+ checksum: 900bf7c98dc58f08d8523b7012b468e4eb757afa624f198902c0643d7008ba777b0bdc35810ba0b758671ce887617295fb742b3f3968991b178ceca54cb07603
languageName: node
linkType: hard
@@ -25770,6 +26173,20 @@ __metadata:
languageName: node
linkType: hard
+"scroll@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "scroll@npm:3.0.1"
+ checksum: 5b0c98089be0edb43444c7604a4866a0a4c457ac6c3c559b23b9c19e48bb9f977a5876bfeba8e567740b70c05dab75a01cef1a935faf47fdb6a505b538b413ed
+ languageName: node
+ linkType: hard
+
+"scrollparent@npm:^2.1.0":
+ version: 2.1.0
+ resolution: "scrollparent@npm:2.1.0"
+ checksum: b377ec5a461f6ed8021f0c3c6b0c76317341c33838f047e91387cdfe0ba4c28aa2a3855240458f9bcf1e18e6c8718540075eb1215fc094496300114f295b2e6b
+ languageName: node
+ linkType: hard
+
"secure-compare@npm:3.0.1":
version: 3.0.1
resolution: "secure-compare@npm:3.0.1"
@@ -25951,26 +26368,29 @@ __metadata:
languageName: node
linkType: hard
-"set-function-length@npm:^1.1.1":
- version: 1.1.1
- resolution: "set-function-length@npm:1.1.1"
+"set-function-length@npm:^1.2.1":
+ version: 1.2.1
+ resolution: "set-function-length@npm:1.2.1"
dependencies:
- define-data-property: "npm:^1.1.1"
- get-intrinsic: "npm:^1.2.1"
+ define-data-property: "npm:^1.1.2"
+ es-errors: "npm:^1.3.0"
+ function-bind: "npm:^1.1.2"
+ get-intrinsic: "npm:^1.2.3"
gopd: "npm:^1.0.1"
- has-property-descriptors: "npm:^1.0.0"
- checksum: a29e255c116c29e3323b851c4f46c58c91be9bb8b065f191e2ea1807cb2c839df56e3175732a498e0c6d54626ba6b6fef896bf699feb7ab70c42dc47eb247c95
+ has-property-descriptors: "npm:^1.0.1"
+ checksum: 1927e296599f2c04d210c1911f1600430a5e49e04a6d8bb03dca5487b95a574da9968813a2ced9a774bd3e188d4a6208352c8f64b8d4674cdb021dca21e190ca
languageName: node
linkType: hard
"set-function-name@npm:^2.0.0, set-function-name@npm:^2.0.1":
- version: 2.0.1
- resolution: "set-function-name@npm:2.0.1"
+ version: 2.0.2
+ resolution: "set-function-name@npm:2.0.2"
dependencies:
- define-data-property: "npm:^1.0.1"
+ define-data-property: "npm:^1.1.4"
+ es-errors: "npm:^1.3.0"
functions-have-names: "npm:^1.2.3"
- has-property-descriptors: "npm:^1.0.0"
- checksum: 6be7d3e15be47f4db8a5a563a35c60b5e7c4af91cc900e8972ffad33d3aaa227900faa55f60121cdb04b85866a734bb7fe4cd91f654c632861cc86121a48312a
+ has-property-descriptors: "npm:^1.0.2"
+ checksum: fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316
languageName: node
linkType: hard
@@ -27779,6 +28199,26 @@ __metadata:
languageName: node
linkType: hard
+"tree-changes@npm:^0.11.2":
+ version: 0.11.2
+ resolution: "tree-changes@npm:0.11.2"
+ dependencies:
+ "@gilbarbara/deep-equal": "npm:^0.3.1"
+ is-lite: "npm:^1.2.0"
+ checksum: bcdcf9b5503c32020abdcaa74dbd6bf83d7c7e991e15f3178138261f40da0f4b373556d3cd2a6cbbecc8bcdcd773d4bab050526e8a730102c81b40f68fa37e97
+ languageName: node
+ linkType: hard
+
+"tree-changes@npm:^0.9.1":
+ version: 0.9.3
+ resolution: "tree-changes@npm:0.9.3"
+ dependencies:
+ "@gilbarbara/deep-equal": "npm:^0.1.1"
+ is-lite: "npm:^0.8.2"
+ checksum: e7a38ed3c22ed1195a78e800a8f08a01d44cd8f4abac421b2be6ae34f991dfbd60ce4235be4e002882f3debef95b8e0f7e027b3b758325e9b4bda905c34d3d8f
+ languageName: node
+ linkType: hard
+
"tree-kill@npm:1.2.2, tree-kill@npm:^1.2.2":
version: 1.2.2
resolution: "tree-kill@npm:1.2.2"
@@ -28002,6 +28442,13 @@ __metadata:
languageName: node
linkType: hard
+"tween-functions@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "tween-functions@npm:1.2.0"
+ checksum: 7e59295b8b0ee4132ed2fe335f56a9db5c87056dad6b6fd3011be72239fd20398003ddb4403bc98ad9f5c94468890830f64016edbbde35581faf95b32cda8305
+ languageName: node
+ linkType: hard
+
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"
@@ -28035,50 +28482,55 @@ __metadata:
languageName: node
linkType: hard
-"typed-array-buffer@npm:^1.0.0":
- version: 1.0.0
- resolution: "typed-array-buffer@npm:1.0.0"
+"typed-array-buffer@npm:^1.0.1":
+ version: 1.0.2
+ resolution: "typed-array-buffer@npm:1.0.2"
dependencies:
- call-bind: "npm:^1.0.2"
- get-intrinsic: "npm:^1.2.1"
- is-typed-array: "npm:^1.1.10"
- checksum: ebad66cdf00c96b1395dffc7873169cf09801fca5954507a484f41f253feb1388d815db297b0b3bb8ce7421eac6f7ff45e2ec68450a3d68408aa4ae02fcf3a6c
+ call-bind: "npm:^1.0.7"
+ es-errors: "npm:^1.3.0"
+ is-typed-array: "npm:^1.1.13"
+ checksum: 9e043eb38e1b4df4ddf9dde1aa64919ae8bb909571c1cc4490ba777d55d23a0c74c7d73afcdd29ec98616d91bb3ae0f705fad4421ea147e1daf9528200b562da
languageName: node
linkType: hard
"typed-array-byte-length@npm:^1.0.0":
- version: 1.0.0
- resolution: "typed-array-byte-length@npm:1.0.0"
+ version: 1.0.1
+ resolution: "typed-array-byte-length@npm:1.0.1"
dependencies:
- call-bind: "npm:^1.0.2"
+ call-bind: "npm:^1.0.7"
for-each: "npm:^0.3.3"
- has-proto: "npm:^1.0.1"
- is-typed-array: "npm:^1.1.10"
- checksum: 6696435d53ce0e704ff6760c57ccc35138aec5f87859e03eb2a3246336d546feae367952dbc918116f3f0dffbe669734e3cbd8960283c2fa79aac925db50d888
+ gopd: "npm:^1.0.1"
+ has-proto: "npm:^1.0.3"
+ is-typed-array: "npm:^1.1.13"
+ checksum: fcebeffb2436c9f355e91bd19e2368273b88c11d1acc0948a2a306792f1ab672bce4cfe524ab9f51a0505c9d7cd1c98eff4235c4f6bfef6a198f6cfc4ff3d4f3
languageName: node
linkType: hard
"typed-array-byte-offset@npm:^1.0.0":
- version: 1.0.0
- resolution: "typed-array-byte-offset@npm:1.0.0"
+ version: 1.0.2
+ resolution: "typed-array-byte-offset@npm:1.0.2"
dependencies:
- available-typed-arrays: "npm:^1.0.5"
- call-bind: "npm:^1.0.2"
+ available-typed-arrays: "npm:^1.0.7"
+ call-bind: "npm:^1.0.7"
for-each: "npm:^0.3.3"
- has-proto: "npm:^1.0.1"
- is-typed-array: "npm:^1.1.10"
- checksum: 4036ce007ae9752931bed3dd61e0d6de2a3e5f6a5a85a05f3adb35388d2c0728f9b1a1e638d75579f168e49c289bfb5417f00e96d4ab081f38b647fc854ff7a5
+ gopd: "npm:^1.0.1"
+ has-proto: "npm:^1.0.3"
+ is-typed-array: "npm:^1.1.13"
+ checksum: d2628bc739732072e39269389a758025f75339de2ed40c4f91357023c5512d237f255b633e3106c461ced41907c1bf9a533c7e8578066b0163690ca8bc61b22f
languageName: node
linkType: hard
"typed-array-length@npm:^1.0.4":
- version: 1.0.4
- resolution: "typed-array-length@npm:1.0.4"
+ version: 1.0.5
+ resolution: "typed-array-length@npm:1.0.5"
dependencies:
- call-bind: "npm:^1.0.2"
+ call-bind: "npm:^1.0.7"
for-each: "npm:^0.3.3"
- is-typed-array: "npm:^1.1.9"
- checksum: c5163c0103d07fefc8a2ad0fc151f9ca9a1f6422098c00f695d55f9896e4d63614cd62cf8d8a031c6cee5f418e8980a533796597174da4edff075b3d275a7e23
+ gopd: "npm:^1.0.1"
+ has-proto: "npm:^1.0.3"
+ is-typed-array: "npm:^1.1.13"
+ possible-typed-array-names: "npm:^1.0.0"
+ checksum: 5cc0f79196e70a92f8f40846cfa62b3de6be51e83f73655e137116cf65e3c29a288502b18cc8faf33c943c2470a4569009e1d6da338441649a2db2f135761ad5
languageName: node
linkType: hard
@@ -28717,6 +29169,21 @@ __metadata:
languageName: node
linkType: hard
+"use-callback-ref@npm:^1.3.0":
+ version: 1.3.1
+ resolution: "use-callback-ref@npm:1.3.1"
+ dependencies:
+ tslib: "npm:^2.0.0"
+ peerDependencies:
+ "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 6666cd62e13053d03e453b5199037cb8f6475a8f55afd664ff488bd8f2ee2ede4da3b220dd7e60f5ecd4926133364fbf4b1aed463eeb8203e7c5be3b1533b59b
+ languageName: node
+ linkType: hard
+
"use-composed-ref@npm:^1.3.0":
version: 1.3.0
resolution: "use-composed-ref@npm:1.3.0"
@@ -28764,6 +29231,22 @@ __metadata:
languageName: node
linkType: hard
+"use-sidecar@npm:^1.1.2":
+ version: 1.1.2
+ resolution: "use-sidecar@npm:1.1.2"
+ dependencies:
+ detect-node-es: "npm:^1.1.0"
+ tslib: "npm:^2.0.0"
+ peerDependencies:
+ "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 89f0018fd9aee1fc17c85ac18c4bf8944d460d453d0d0e04ddbc8eaddf3fa591e9c74a1f8a438a1bff368a7a2417fab380bdb3df899d2194c4375b0982736de0
+ languageName: node
+ linkType: hard
+
"use@npm:^3.1.0":
version: 3.1.1
resolution: "use@npm:3.1.1"
@@ -29263,9 +29746,9 @@ __metadata:
linkType: hard
"vscode-uri@npm:^3.0.3":
- version: 3.0.7
- resolution: "vscode-uri@npm:3.0.7"
- checksum: 67bc15bc9c9bd2d70dae8b27f2a3164281c6ee8f6484e6c5945a44d89871da93d52f2ba339ebc12ab0c10991d4576171b5d85e49a542454329c16faf977e4c59
+ version: 3.0.8
+ resolution: "vscode-uri@npm:3.0.8"
+ checksum: f7f217f526bf109589969fe6e66b71e70b937de1385a1d7bb577ca3ee7c5e820d3856a86e9ff2fa9b7a0bc56a3dd8c3a9a557d3fedd7df414bc618d5e6b567f9
languageName: node
linkType: hard
@@ -29872,16 +30355,16 @@ __metadata:
languageName: node
linkType: hard
-"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9":
- version: 1.1.11
- resolution: "which-typed-array@npm:1.1.11"
+"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9":
+ version: 1.1.14
+ resolution: "which-typed-array@npm:1.1.14"
dependencies:
- available-typed-arrays: "npm:^1.0.5"
- call-bind: "npm:^1.0.2"
+ available-typed-arrays: "npm:^1.0.6"
+ call-bind: "npm:^1.0.5"
for-each: "npm:^0.3.3"
gopd: "npm:^1.0.1"
- has-tostringtag: "npm:^1.0.0"
- checksum: 2cf4ce417beb50ae0ec3b1b479ea6d72d3e71986462ebd77344ca6398f77c7c59804eebe88f4126ce79f85edbcaa6c7783f54b0a5bf34f785eab7cbb35c30499
+ has-tostringtag: "npm:^1.0.1"
+ checksum: 0960f1e77807058819451b98c51d4cd72031593e8de990b24bd3fc22e176f5eee22921d68d852297c786aec117689f0423ed20aa4fde7ce2704d680677891f56
languageName: node
linkType: hard
diff --git a/docs/addons/addon-migration-guide.md b/docs/addons/addon-migration-guide.md
index 269c615fe42d..2ece7ad87d87 100644
--- a/docs/addons/addon-migration-guide.md
+++ b/docs/addons/addon-migration-guide.md
@@ -2,7 +2,7 @@
title: Addon migration guide for Storybook 8.0
---
-We deeply appreciate the dedication and effort addon creators put into keeping the Storybook ecosystem vibrant and up-to-date. As Storybook evolves to version 8.0, bringing new features and improvements, this guide is here to assist you in migrating your addons from 7.x to 8.0. If you need to migrate your addon from an earlier version of Storybook, please first refer to the [Addon migration guide for Storybook 7.0](https://storybook.js.org/docs/7.6/addons/addon-migration-guide).
+We sincerely appreciate the dedication and effort addon creators put into keeping the Storybook ecosystem vibrant and up-to-date. As Storybook evolves to version 8.0, bringing new features and improvements, this guide is here to assist you in migrating your addons from 7.x to 8.0. If you need to migrate your addon from an earlier version of Storybook, please first refer to the [Addon migration guide for Storybook 7.0](https://storybook.js.org/docs/7.6/addons/addon-migration-guide).
@@ -14,7 +14,7 @@ As we gather feedback from the community, we’ll update this page. We also have
Begin by updating your Storybook dependencies. Use the `next` tag for pre-release versions, `latest` for the most recent stable release, or specify the version directly.
-```json
+```jsonc
{
"dependencies": {
"@storybook/client-logger": "next" // or "latest", or "^8.0.0"
@@ -24,7 +24,7 @@ Begin by updating your Storybook dependencies. Use the `next` tag for pre-releas
## Key changes for addons
-Here are the essential changes in version 8.0 that impact addon development. Please check the [full migration note](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) for an exhaustive list of changes in 8.0.
+Here are the essential changes in version 8.0 that impact addon development. Please check the [complete migration note](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) for an exhaustive list of changes in 8.0.
### Node.js 16 support dropped
@@ -32,26 +32,26 @@ Please upgrade your addon to Node.js 18, as support for Node.js 16 has ended.
### React 18 for manager UI
-UI injected into panels, tools, etc. by addons is now rendered with React 18. Also note that the `key` prop is no longer passed to the render function.
+UI-based addons (e.g., [panels](../addons/addon-types.md#panels), [toolbars](../addons/addon-types.md#toolbars), [tabs](../addons/addon-types.md#tabs)) rely on React 18 to render their elements in the Storybook UI. Also, note that the `key` prop is no longer passed to the render function.
### React peer dependency is no longer required
-To remove your addon's peer dependency on React, and reduce its install size, do the following:
+To remove your addon's peer dependency on React and reduce its install size, do the following:
-1. Move `react`, `react-dom` and the globalized Storybook packages from `peerDependencies` to `devDependencies`
-2. Add the list of globalized packages to the `externals` property in the `tsup` configuration, to ensure they are not part of the bundle.
+1. Move `react`, `react-dom`, and the globalized Storybook packages from `peerDependencies` to `devDependencies`
+2. Add the list of globalized packages to the `externals` property in the `tsup` configuration to ensure they are not part of the bundle.
-For an example, see [the updates we've made to the addon-kit](https://github.com/storybookjs/addon-kit/pull/60/files#diff-8fed899bdbc24789a7bb4973574e624ed6207c6ce572338bc3c3e117672b2a20). These changes are optional but recommended.
+For an example, see [the updates we've made to the Addon Kit](https://github.com/storybookjs/addon-kit/compare/79282986..971e1bb). These changes are optional but recommended.
-
+
-This assumes your addon is using tsup for bundling. If your addon was built with an older version of addon-kit that uses Babel for bundling, you have to first switch to tsup. For guidance, explore these [older changes implemented in the addon-kit](https://github.com/storybookjs/addon-kit/pull/45/files).
+This assumes your addon uses [tsup](https://tsup.egoist.dev/) for bundling. If your addon was built with an older version of the Addon Kit that uses Babel for bundling, you must first switch to tsup. For guidance, explore these older changes implemented in the Addon Kit [repository](https://github.com/storybookjs/addon-kit/pull/45/files).
### @storybook/components deprecations
-`Icons` component from `@storybook/components` is now deprecated in favor of [`@storybook/icons`](https://github.com/storybookjs/icons). Additionally, various `Button` component props are also deprecated, with alternatives provided.
+The `Icons` component from `@storybook/components` is now deprecated in favor of [`@storybook/icons`](https://github.com/storybookjs/icons). Additionally, various `Button` component props are also deprecated, with alternatives provided.
### Storybook layout state API changes
@@ -59,19 +59,27 @@ If you're customizing the Storybook UI configuration with `addons.setConfig({...
### Removal of deprecated features
-Deprecated packages and APIs from 7.0 are now removed in 8.0.Consult the [full migration notes](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecations-which-are-now-removed) for details. Most notably for addons, the removal of the `@storybook/addons` now necessitates a switch to `@storybook/preview-api` and `@storybook/manager-api`.
+Deprecated packages and APIs from 7.0 are now removed in 8.0. Consult the [full migration notes](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecations-which-are-now-removed) for details. Most notably, for addons, the removal of the `@storybook/addons` package requires you to switch to `@storybook/preview-api` and `@storybook/manager-api` for the same functionality.
-### Babel-loader removed from webpack
+### Babel-loader removed from Webpack
Storybook 8 [removes babel-loader from the webpack5 builder](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#removed-babelcore-and-babel-loader-from-storybookbuilder-webpack5). If your addon's preset overrides the `babel()` method, it will break if your users are using SWC to compile their files (which is the new default for Webpack 5-based Storybook projects).
-To solve for both Babel and SWC, the most robust approach is to create an [unplugin](https://github.com/unjs/unplugin) that will work with both Webpack and Vite builders. That will give you full control to run Babel (or whatever you want) on stories and components as they are loaded.
+To ensure your addon supports both Babel and SWC, you can build a custom bundling plugin using [Unplugin](https://unplugin.vercel.app/) that will work with both Webpack and Vite builders, giving you complete control to run Babel (or whatever you want) on stories and components as they are loaded.
-As a workaround, update your documentation to tell users to opt-in to Babel support. This should fix your addon in their project, at the cost of performance:
+As a workaround, update your documentation to tell users to opt-in to Babel support. This should fix your addon in their project at the cost of performance:
-```sh
-npx storybook@latest add @storybook/addon-webpack5-compiler-babel
-```
+
+
+
+
+
## Migration Example
@@ -83,13 +91,13 @@ The Addon Kit [repository](https://github.com/storybookjs/addon-kit) has already
-If you migrate your addon to support ESM, you can safely remove any `manager.js`, `preview.js`, and `preset.js` files from the addon's root directory. For a complete overview of the changes applied to the Addon Kit to fully support Storybook 8.0, see the following [diff view](https://github.com/storybookjs/addon-kit/compare/79282986..cf0875f).
+For a complete overview of the changes applied to the Addon Kit to fully support Storybook 8.0, see the following [diff view](https://github.com/storybookjs/addon-kit/compare/79282986..cf0875f).
## Releasing
-Release a new major version of your addon for Storybook 8.0. We recommend you to continue supporting 7.x with minor or patch versions. We also recommend releasing your own addon using the `next` tag to test it out in projects.
+To support Storybook 8.0, we encourage you to release a new major version of your addon and continue supporting 7.x with minor or patch versions. For experimental features or testing, opt for the `next` tag. This will allow you to test your addon in projects and gather feedback before releasing a stable version.
## Support
diff --git a/docs/api/cli-options.md b/docs/api/cli-options.md
index ad8292dfad31..3aa9f0a3f0a1 100644
--- a/docs/api/cli-options.md
+++ b/docs/api/cli-options.md
@@ -46,7 +46,7 @@ Options include:
| `--no-open` | Do not open Storybook automatically in the browser `storybook dev --no-open` |
| `--quiet` | Suppress verbose build output `storybook dev --quiet` |
| `--debug-webpack` | Display final webpack configurations for debugging purposes `storybook dev --debug-webpack` |
-| `--webpack-stats-json` | Write Webpack Stats JSON to disk `storybook dev --webpack-stats-json /tmp/webpack-stats` |
+| `--stats-json` | Write stats JSON to disk `storybook dev ---stats-json /tmp/stats` NOTE: only works for webpack. |
| `--docs` | Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#preview-storybooks-documentation) `storybook dev --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md) `storybook dev --disable-telemetry` |
@@ -75,7 +75,7 @@ Options include:
| `--loglevel [level]` | Controls level of logging during build. Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent` `storybook build --loglevel warn` |
| `--quiet` | Suppress verbose build output `storybook build --quiet` |
| `--debug-webpack` | Display final webpack configurations for debugging purposes `storybook build --debug-webpack` |
-| `--webpack-stats-json` | Write Webpack Stats JSON to disk `storybook build --webpack-stats-json /my-storybook/webpack-stats` |
+| `---stats-json` | Write stats JSON to disk `storybook build -stats-json /my-storybook/-stats` |
| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation) `storybook build --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md). `storybook build --disable-telemetry` |
| `--test` | Optimize Storybook's production build for performance and tests by removing unnecessary features with the `test` option. Learn more [here](../api/main-config-build.md). `storybook build --test` |
diff --git a/docs/api/doc-block-useof.md b/docs/api/doc-block-useof.md
index 4499478aef5e..613ee99016cb 100644
--- a/docs/api/doc-block-useof.md
+++ b/docs/api/doc-block-useof.md
@@ -56,18 +56,20 @@ import * as ButtonStories from './Button.stories';
## useOf
-## Signature
+### Type
+
```ts
-useOf = (
+(
moduleExportOrType: ModuleExport | 'story' | 'meta' | 'component',
validTypes?: Array<'story' | 'meta' | 'component'>
-): EnhancedResolvedModuleExportType
+) => EnhancedResolvedModuleExportType
```
+
-## Parameters
+### Parameters
-### `moduleExportOrType`
+#### `moduleExportOrType`
(**Required**)
@@ -81,29 +83,29 @@ When the custom block is in an [attached doc](./doc-block-meta.md#attached-vs-un
- `useOf('meta')` returns the annotated meta in attached mode; error in unattached mode
- `useOf('component')` returns the annotated component specified in the meta in attached mode; error in unattached mode
-### `validTypes`
+#### `validTypes`
Type: `Array<'story' | 'meta' | 'component'>`
Optionally specify an array of valid types that your block accepts. Passing anything other than the valid type(s) will result in an error. For example, the [`Canvas`](./doc-block-canvas.md) block uses `useOf(of, ['story'])`, which ensures it only accepts a reference to a story, not a meta or component.
-## Return
+### Return
The return value depends on the matched type:
-### `EnhancedResolvedModuleExportType['type'] === 'story'`
+#### `EnhancedResolvedModuleExportType['type'] === 'story'`
Type: `{ type: 'story', story: PreparedStory }`
For stories, annotated stories are returned as is. They are prepared, meaning that they are already merged with project and meta annotations.
-### `EnhancedResolvedModuleExportType['type'] === 'meta'`
+#### `EnhancedResolvedModuleExportType['type'] === 'meta'`
Type: `{ type: 'meta', csfFile: CSFFile, preparedMeta: PreparedMeta }`
For meta, the parsed CSF file is returned, along with prepared annotated meta. That is, project annotations merged with meta annotations, but no story annotations.
-### `EnhancedResolvedModuleExportType['type'] === 'component'`
+#### `EnhancedResolvedModuleExportType['type'] === 'component'`
Type: `{ type: 'component', component: Component, projectAnnotations: NormalizedProjectAnnotations }`
diff --git a/docs/migration-guide/from-older-version.md b/docs/migration-guide/from-older-version.md
new file mode 100644
index 000000000000..b3740aa2f854
--- /dev/null
+++ b/docs/migration-guide/from-older-version.md
@@ -0,0 +1,115 @@
+---
+title: 'Migration guide from Storybook 6.x to 8.0'
+---
+
+Storybook 8 focuses on performance and stability.
+
+- 💨 [2-4x faster test builds](/blog/optimize-storybook-7-6/#2-4x-faster-builds-with-thetest-flag), [25-50% faster React docgen](/blog/optimize-storybook-7-6/#22x-faster-react-docgen), and [SWC support for Webpack projects](/blog/optimize-storybook-7-6/#using-webpack-enable-swc)
+- ✨ Improved framework support: you no longer need to install React as a peer dependency when using a non-React renderer
+- 🌐 [Support for React Server Components (RSC)](/blog/storybook-react-server-components/): our experimental solution renders async RSC in the browser and mocks Node code
+- ➕ Much, much more
+
+This guide is meant to help you **upgrade from Storybook 6.x to 8.0** successfully!
+
+## Major breaking changes
+
+The rest of this guide will help you upgrade successfully, either automatically or manually. But first, we’ve accumulated a lot of breaking changes in both [Storybook 7](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#70-breaking-changes) and [Storybook 8](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800). Here are the most impactful changes you should know about before you go further:
+
+- [`framework` field is now mandatory](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-framework-api)
+- [Start and build CLI binaries removed](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#start-storybook--build-storybook-binaries-removed)
+- [`storiesOf` API has been removed](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#removal-of-storiesof-api)
+- [`*.stories.mdx` format has been removed](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-storiesmdx-csf-in-mdx-format-and-mdx1-support)
+- [Implicit actions (from `argTypesRegex`) can no longer be used during rendering (e.g. in a play function)](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function)
+- [`react-docgen` (instead of `react-docgen-typescript`) is the default for component analysis](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-docgen-component-analysis-by-default)
+- [Storyshots has been removed](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storyshots-has-been-removed)
+- [Addons API introduced in Storybook 7 is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#new-addons-api)
+- Ecosystem updates
+ - [Webpack4 support discontinued](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#webpack4-support-discontinued)
+ - [IE11 support discontinued](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#modern-browser-support)
+ - [Node 18+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-nodejs-16)
+ - [Next.js 13.5+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#require-nextjs-135-and-up)
+ - [Vue 3+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#require-vue-3-and-up)
+ - [Angular 15+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#require-angular-15-and-up)
+ - [Svelte 4+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#require-svelte-4-and-up)
+ - [Yarn 1 is no longer supported](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-yarn-1)
+
+If any of these changes apply to your project, please read through the linked migration notes before continuing.
+
+If any of these new requirements or changes are blockers for your project, we recommend looking at the [requirements to migrate to Storybook 7](../../release-7-6/docs/migration-guide#major-breaking-changes).
+
+You may wish to read the full migration notes for [Storybook 6 to 7](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-65x-to-700) and [Storybook 7 to 8](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) before migrating. Or you can follow the instructions below and we’ll try to take care of everything for you!
+
+## Automatic upgrade
+
+To upgrade your Storybook:
+
+
+
+
+
+
+
+This will:
+
+1. Determine that none of the [breaking changes](#major-breaking-changes) apply to your project
+ - If they do, you will receive instructions on how to resolve them before continuing
+2. Upgrade your Storybook dependencies to the latest version
+3. Run a collection of _automigrations_, which will:
+ - Check for common upgrade tasks
+ - Explain the necessary changes with links to more information
+ - Ask for approval, then perform the task on your behalf
+
+### Common upgrade issues
+
+While we'll do our best to upgrade your project automatically, there are two issues worth mentioning that you might encounter during the upgrade process:
+
+#### `storyStoreV7:false` and `storiesOf`
+
+If you have `storyStoreV7: false` in your `.storybook/main.js`, you will need to remove it before you're able to upgrade to Storybook 8.
+
+If you are using the `storiesOf` API (which requires `storyStoreV7: false` in Storybook 7), you will need to either [migrate your stories to CSF](../../release-7-6/docs/migration-guide.md#storiesof-to-csf) or use the [new indexer API to continue creating stories dynamically](../../release-7-6/docs/migration-guide.md#storiesof-to-dynamically-created-stories).
+
+#### MDX 1 to MDX 3
+
+Storybook 8 uses MDX 3. If you're coming from MDX 1 (used by Storybook 6), there were significant breaking changes in MDX 2. Please reference our [guidance on upgrading successfully](../../release-7-6/docs/migration-guide.md#upgrade-mdx1-to-mdx2).
+
+## Troubleshooting
+
+The automatic upgrade should get your Storybook into a working state. If you encounter an error running Storybook after upgrading, here’s what to do:
+
+1. Try running the [`doctor` command](../api/cli-options.md#doctor) to check for common issues (such as duplicate dependencies, incompatible addons, or mismatched versions) and see suggestions for fixing them.
+2. If you’re running `storybook` with the `dev` command, try using the `build` command instead. Sometimes `build` errors are more legible than `dev` errors!
+3. Check [the full migration notes](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800), which contains an exhaustive list of noteworthy changes in Storybook 8. Many of these are already handled by automigrations when you upgrade, but not all are. It’s also possible that you’re experiencing a corner case that we’re not aware of.
+4. Search [Storybook issues on GitHub](https://github.com/storybookjs/storybook/issues). If you’re seeing a problem, there’s a good chance other people are too. If so, upvote the issue, try out any workarounds described in the comments, and comment back if you have useful info to contribute.
+5. If there’s no existing issue, you can [file one](https://github.com/storybookjs/storybook/issues/new/choose), ideally with a reproduction attached. We’ll be on top of Storybook 8 issues as we’re stabilizing the release.
+
+If you prefer to debug yourself, here are a few useful things you can do to help narrow down the problem:
+
+1. Try removing all addons that are not in the `@storybook` npm namespace (make sure you don't remove the `storybook` package). Community addons that work well with 7.x might not yet be compatible with 8.0, and this is the fastest way to isolate that possibility. If you find an addon that needs to be upgraded to work with Storybook 8, please post an issue on the addon’s repository, or better yet, a pull request to upgrade it!
+2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
+
+## Optional migrations
+
+In addition to the automigrations and manual migrations above, there are also optional migrations that you should consider. These are features that we’ve deprecated in Storybook 7 and 8 (but remain backwards compatible), or best practices that should help you be more productive in the future.
+
+### CSF 2 to CSF 3
+
+There are [many good reasons](/blog/storybook-csf3-is-here/) to convert your stories from CSF 2 to CSF 3. We provide a codemod which, in most cases, should automatically make the code changes for you (make sure to update the glob to fit your files):
+
+
+
+
+
+
diff --git a/docs/migration-guide.md b/docs/migration-guide/index.md
similarity index 73%
rename from docs/migration-guide.md
rename to docs/migration-guide/index.md
index 1fa7d7c5ad4c..70510a941d86 100644
--- a/docs/migration-guide.md
+++ b/docs/migration-guide/index.md
@@ -11,28 +11,13 @@ Storybook 8 focuses on performance and stability.
This guide is meant to help you **upgrade from Storybook 7.x to 8.0** successfully!
-
-ℹ️ Migrating from Storybook 6.x?
+
-We recommend first upgrading to Storybook 7, then upgrading to Storybook 8.
+**Migrating from Storybook 6.x?**
-To upgrade your project to Storybook 7, run:
+We have a dedicated [migration guide for Storybook 6 to 8](./from-older-version.md).
-
-
-
-
-
-
-Then reference the [Storybook 7 migration guide](../../release-7-6/docs/migration-guide) to ensure you address any relevant breaking changes or manual migrations.
-
-
+
## Major breaking changes
@@ -52,7 +37,9 @@ The rest of this guide will help you upgrade successfully, either automatically
- [Svelte 4+ is now required](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#require-svelte-4-and-up)
- [Yarn 1 is no longer supported](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropping-support-for-yarn-1)
-If any of these apply to your project, please read through the the linked migration notes before continuing. If any of these new requirements or changes do not fit your project, you should probably stick with Storybook 7.x.
+If any of these changes apply to your project, please read through the linked migration notes before continuing.
+
+If any of these new requirements or changes are blockers for your project, we recommend to continue using Storybook 7.x.
You may wish to read the [full migration notes](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) before migrating. Or you can follow the instructions below and we’ll try to take care of everything for you!
@@ -74,12 +61,26 @@ To upgrade your Storybook:
This will:
-1. Upgrade your Storybook dependencies to the latest version
-2. Run a collection of _automigrations_, which will:
+1. Determine that none of the [breaking changes](#major-breaking-changes) apply to your project
+ - If they do, you will receive instructions on how to resolve them before continuing
+2. Upgrade your Storybook dependencies to the latest version
+3. Run a collection of _automigrations_, which will:
- Check for common upgrade tasks
- Explain the necessary changes with links to more information
- Ask for approval, then perform the task on your behalf
+### Common upgrade issues
+
+While we'll do our best to upgrade your project automatically, there is one issue worth mentioning that you might encounter during the upgrade process:
+
+#### `storyStoreV7:false` and `storiesOf`
+
+If you have `storyStoreV7: false` in your `.storybook/main.js`, you will need to remove it before you're able to upgrade to Storybook 8.
+
+If you are using the `storiesOf` API (which requires `storyStoreV7: false` in Storybook 7), you will need to either [migrate your stories to CSF](../../release-7-6/docs/migration-guide.md#storiesof-to-csf) or use the [new indexer API to continue creating stories dynamically](../../release-7-6/docs/migration-guide.md#storiesof-to-dynamically-created-stories).
+
+## New projects
+
To add Storybook to a project that isn’t currently using Storybook:
@@ -103,30 +104,6 @@ This will:
In addition to the automated upgrades above, there are manual migrations that might be required to get Storybook 8 working in your project. We’ve tried to minimize this list to make it easier to upgrade. These include:
-### `storiesOf` to CSF
-
-Storybook architecture is focused on performance and now needs code that is statically analyzable. For that reason, it does not work with `storiesOf`. We provide a codemod which, in most cases, should automatically make the code changes for you (make sure to update the glob to fit your files):
-
-
-
-
-
-
-
-This will transform your stories into [CSF 1](/blog/component-story-format/) stories, which are story functions that don't accept [args](./writing-stories/args.md). These are fully supported in Storybook 8, and will continue to be for the foreseeable future.
-
-However, we recommend [writing all **new** stories in CSF 3](./writing-stories/index.md), which are story objects that are easier to write, reuse, and maintain.
-
-### `storiesOf` to dynamically created stories
-
-If you are using `storiesOf` for its ability to dynamically create stories, you can build your own "storiesOf" implementation by leveraging the new [(experimental) indexer API](/docs/api/main-config-indexers). A proof of concept of such an implementation can be seen in [this StackBlitz demo](https://stackblitz.com/edit/github-h2rgfk?file=README.md). See the demo's README.md for a deeper explanation of the implementation.
-
### `*.stories.mdx` to MDX+CSF
Storybook now requires that MDX pages reference stories written in CSF, rather than the previous `.stories.mdx` hybrid approach. You can automatically convert your files using the following codemod (make sure to update the glob to fit your files):
@@ -145,13 +122,17 @@ Storybook now requires that MDX pages reference stories written in CSF, rather t
You’ll also need to update your stories glob in `.storybook/main.js` to include the newly created .mdx and .stories.js files if it doesn’t already.
+#### Known limitations
+
+- The codemod does not remove the extracted stories from the `.stories.mdx` files. You will need to do this manually.
+
**Note:** this migration supports the Storybook 6 ["CSF stories with MDX docs"](https://github.com/storybookjs/storybook/blob/6e19f0fe426d58f0f7981a42c3d0b0384fab49b1/code/addons/docs/docs/recipes.md#csf-stories-with-mdx-docs) recipe.
## Troubleshooting
The automatic upgrade should get your Storybook into a working state. If you encounter an error running Storybook after upgrading, here’s what to do:
-1. Try running the [`doctor` command](./api/cli-options.md#doctor) to check for common issues (such as duplicate dependencies, incompatible addons, or mismatched versions) and see suggestions for fixing them.
+1. Try running the [`doctor` command](../api/cli-options.md#doctor) to check for common issues (such as duplicate dependencies, incompatible addons, or mismatched versions) and see suggestions for fixing them.
2. If you’re running `storybook` with the `dev` command, try using the `build` command instead. Sometimes `build` errors are more legible than `dev` errors!
3. Check [the full migration notes](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800), which contains an exhaustive list of noteworthy changes in Storybook 8. Many of these are already handled by automigrations when you upgrade, but not all are. It’s also possible that you’re experiencing a corner case that we’re not aware of.
4. Search [Storybook issues on GitHub](https://github.com/storybookjs/storybook/issues). If you’re seeing a problem, there’s a good chance other people are too. If so, upvote the issue, try out any workarounds described in the comments, and comment back if you have useful info to contribute.
@@ -159,14 +140,12 @@ The automatic upgrade should get your Storybook into a working state. If you enc
If you prefer to debug yourself, here are a few useful things you can do to help narrow down the problem:
-1. Try removing all addons that are not in the `@storybook` npm namespace (make sure you don't remove the `storybook` package). Community addons that work well with 7.x might not yet be compatible with 8.0, and this is the fastest way to isolate that possibility. If you find an addon that needs to be upgraded to work with Storybook 8, please post an issue on the addon’s repository, or better yet, a PR to upgrade it!
-2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or PR to the Storybook repo and we’ll do our best to take care of it quickly.
+1. Try removing all addons that are not in the `@storybook` npm namespace (make sure you don't remove the `storybook` package). Community addons that work well with 7.x might not yet be compatible with 8.0, and this is the fastest way to isolate that possibility. If you find an addon that needs to be upgraded to work with Storybook 8, please post an issue on the addon’s repository, or better yet, a pull request to upgrade it!
+2. Another debugging technique is to bisect to older prerelease versions of Storybook to figure out which release broke your Storybook. For example, assuming that the current prerelease of Storybook is `8.0.0-beta.56`, you could set the version to `8.0.0-alpha.0` in your `package.json` and reinstall to verify that it still works (`alpha.0` should be nearly identical to `7.6.x`). If it works, you could then try `8.0.0-beta.0`, then `8.0.0-beta.28` and so forth. Once you’ve isolated the bad release, read through its [CHANGELOG](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) entry and perhaps there’s a change that jumps out as the culprit. If you find the problem, please submit an issue or pull request to the Storybook monorepo and we’ll do our best to take care of it quickly.
## Optional migrations
-In addition to the automigrations and manual migrations above, there are also optional migrations that you should consider. These are things that we’ve deprecated in Storybook 8 (but remain backwards compatible), or best practices that should help you be more productive in the future.
-
-These include:
+In addition to the automigrations and manual migrations above, there are also optional migrations that you should consider. These are features that we’ve deprecated in Storybook 8 (but remain backwards compatible), or best practices that should help you be more productive in the future.
### CSF 2 to CSF 3
diff --git a/docs/snippets/common/storybook-addon-panel-example.js.mdx b/docs/snippets/common/storybook-addon-panel-example.js.mdx
index d326d027390b..30070e4543a4 100644
--- a/docs/snippets/common/storybook-addon-panel-example.js.mdx
+++ b/docs/snippets/common/storybook-addon-panel-example.js.mdx
@@ -12,8 +12,8 @@ addons.register('my/panel', () => {
title: 'Example Storybook panel',
//👇 Sets the type of UI element in Storybook
type: types.PANEL,
- render: ({ active, key }) => (
-
+ render: ({ active }) => (
+
I'm a panel addon in Storybook
),
diff --git a/docs/toc.js b/docs/toc.js
index 601adf1c9218..b7ee152fb8c6 100644
--- a/docs/toc.js
+++ b/docs/toc.js
@@ -753,9 +753,16 @@ module.exports = {
type: 'link',
},
{
- title: 'Migrate to 8.0',
pathSegment: 'migration-guide',
- type: 'link',
+ title: 'Migrate to 8.0',
+ type: 'heading',
+ children: [
+ {
+ title: 'Migrate from 6.x to 8.0',
+ pathSegment: 'from-older-version',
+ type: 'link',
+ },
+ ],
},
],
};
diff --git a/docs/versions/next.json b/docs/versions/next.json
index 588adaa3115b..661a04661201 100644
--- a/docs/versions/next.json
+++ b/docs/versions/next.json
@@ -1 +1 @@
-{"version":"8.0.0-beta.5","info":{"plain":"- Addon-controls: Dont show \\\"setup controls\\\" if control is disabled or a function - [#26120](https://github.com/storybookjs/storybook/pull/26120), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Addon-interactions: Only mutate arg keys when writable - [#26118](https://github.com/storybookjs/storybook/pull/26118), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- CLI: Fix logic to add `^` packages in upgrade - [#26049](https://github.com/storybookjs/storybook/pull/26049), thanks [@ndelangen](https://github.com/ndelangen)!\n- Core: Fix addon bundling script - [#26145](https://github.com/storybookjs/storybook/pull/26145), thanks [@ndelangen](https://github.com/ndelangen)!\n- Vue3: Fix SourceDecorator Exception - [#25773](https://github.com/storybookjs/storybook/pull/25773), thanks [@chakAs3](https://github.com/chakAs3)!\n- Vue: Replace vue-docgen-api with Volar vue-component-meta - [#22285](https://github.com/storybookjs/storybook/pull/22285), thanks [@chakAs3](https://github.com/chakAs3)!"}}
+{"version":"8.0.0-beta.6","info":{"plain":"- Addon-onboarding: Move onboarding to monorepo - [#26176](https://github.com/storybookjs/storybook/pull/26176), thanks [@ndelangen](https://github.com/ndelangen)!\n- Angular: Fix Storybook startup after init - [#26213](https://github.com/storybookjs/storybook/pull/26213), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Angular: Use dedicated tsconfig for compodocs - [#26214](https://github.com/storybookjs/storybook/pull/26214), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Automigration: Fix overflow bug by using a better link - [#26203](https://github.com/storybookjs/storybook/pull/26203), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Automigration: Run the `mdx-to-csf` codemod during automigration - [#26201](https://github.com/storybookjs/storybook/pull/26201), thanks [@ndelangen](https://github.com/ndelangen)!\n- Automigrations: Create addon-postcss automigration - [#26228](https://github.com/storybookjs/storybook/pull/26228), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Automigrations: Enhance experience for upgrades from Storybook 6 to 8 - [#26067](https://github.com/storybookjs/storybook/pull/26067), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Automigrations: General fixes for automigrations, blockers and codemods - [#26210](https://github.com/storybookjs/storybook/pull/26210), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Blocks: Fix `Stories` block rendering duplicate stories when globals are changed - [#26110](https://github.com/storybookjs/storybook/pull/26110), thanks [@JReinhold](https://github.com/JReinhold)!\n- Builder Vite: Pass on errors about not having a real stats object in smoke tests - [#26195](https://github.com/storybookjs/storybook/pull/26195), thanks [@tmeasday](https://github.com/tmeasday)!\n- CLI: Rename `--webpack-stats-json` to `--stats-json` as it works in Vite - [#26128](https://github.com/storybookjs/storybook/pull/26128), thanks [@tmeasday](https://github.com/tmeasday)!\n- Codemods: Enhance mdx-to-csf codemod - [#26164](https://github.com/storybookjs/storybook/pull/26164), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!\n- Core: Export preview symbols for embedding - [#26224](https://github.com/storybookjs/storybook/pull/26224), thanks [@tmeasday](https://github.com/tmeasday)!\n- Docs: Don't show how to setup controls for empty argTypes in doc pages - [#26200](https://github.com/storybookjs/storybook/pull/26200), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- Docs: Fix function prop rendering as `noRefCheck` in storybook docs - [#26104](https://github.com/storybookjs/storybook/pull/26104), thanks [@thisisanto](https://github.com/thisisanto)!\n- Doctor: Add dynamic check for incompatible Storybook packages - [#26219](https://github.com/storybookjs/storybook/pull/26219), thanks [@yannbf](https://github.com/yannbf)!\n- Maintenance: Remove deprecation of `manager-api`'s `types` export - [#26202](https://github.com/storybookjs/storybook/pull/26202), thanks [@ndelangen](https://github.com/ndelangen)!\n- Revert \\\"Revert: \\\"Angular: Reduce the warnings from `ts-loader` via stricter list of `includes`\\\"\\\" - [#26226](https://github.com/storybookjs/storybook/pull/26226), thanks [@ndelangen](https://github.com/ndelangen)!\n- Revert: \\\"Angular: Reduce the warnings from `ts-loader` via stricter list of `includes`\\\" - [#26208](https://github.com/storybookjs/storybook/pull/26208), thanks [@kasperpeulen](https://github.com/kasperpeulen)!\n- UI: Improve sidebar performance when switching stories - [#26184](https://github.com/storybookjs/storybook/pull/26184), thanks [@literalpie](https://github.com/literalpie)!\n- UI: Update theme switcher to use toggle button for two themes - [#25682](https://github.com/storybookjs/storybook/pull/25682), thanks [@ivoilic](https://github.com/ivoilic)!\n- Vue: Improve types for array, union and intersection when using vue-docgen-api - [#26177](https://github.com/storybookjs/storybook/pull/26177), thanks [@larsrickert](https://github.com/larsrickert)!"}}
diff --git a/docs/writing-tests/visual-testing.md b/docs/writing-tests/visual-testing.md
index 5c37a8d3614f..1ade3cdbcf34 100644
--- a/docs/writing-tests/visual-testing.md
+++ b/docs/writing-tests/visual-testing.md
@@ -50,17 +50,17 @@ Select a project from your project list to finish setup. If you're setting up th
### Configure
-The addon includes configuration options covering most use cases by default. You can also fine-tune the addon configuration to match your project's requirements via the [`./chromatic.config.json`](https://www.chromatic.com/docs/cli#configuration) file. Below are the available options and examples of how to use them.
+The addon includes configuration options covering most use cases by default. You can also fine-tune the addon configuration to match your project's requirements via the [`./chromatic.config.json`](https://www.chromatic.com/docs/visual-tests-addon/#configure) file. Below are the available options and examples of how to use them.
-| Option | Description |
-| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `projectId` | Automatically configured. Sets the value for the project identifier `options: { projectId: 'Project:64cbcde96f99841e8b007d75' }` |
-| `buildScriptName` | Optional. Defines the custom Storybook build script `options: { buildScriptName: 'deploy-storybook' }` |
-| `debug` | Optional. Output verbose debugging information to the console. `options: { debug: true }` |
-| `zip` | Optional. Recommended for large projects. Configures the addon to deploy your Storybook to Chromatic as a zip file. `options: { zip: true }` |
+| Option | Description |
+| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| `projectId` | Automatically configured. Sets the value for the project identifier `"projectId": "Project:64cbcde96f99841e8b007d75"` |
+| `buildScriptName` | Optional. Defines the custom Storybook build script `"buildScriptName": "deploy-storybook"` |
+| `debug` | Optional. Output verbose debugging information to the console. `"debug": true` |
+| `zip` | Optional. Recommended for large projects. Configures the addon to deploy your Storybook to Chromatic as a zip file. `"zip": true` |
```jsonc
-// .storybook/chromatic.config.json
+// ./chromatic.config.json
{
"buildScriptName": "deploy-storybook",
"debug": true,
diff --git a/scripts/package.json b/scripts/package.json
index 01818aa20d40..52d2013ec288 100644
--- a/scripts/package.json
+++ b/scripts/package.json
@@ -176,7 +176,7 @@
"ts-dedent": "^2.0.0",
"tsup": "^6.7.0",
"type-fest": "~2.19",
- "typescript": "~5.2.2",
+ "typescript": "^5.3.2",
"util": "^0.12.4",
"uuid": "^9.0.0",
"vitest": "^1.2.2",
diff --git a/scripts/prepare/addon-bundle.ts b/scripts/prepare/addon-bundle.ts
index 752eb45b138c..719522c74902 100755
--- a/scripts/prepare/addon-bundle.ts
+++ b/scripts/prepare/addon-bundle.ts
@@ -85,11 +85,13 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
],
format: ['esm'],
esbuildOptions: (options) => {
- /* eslint-disable no-param-reassign */
options.conditions = ['module'];
options.platform = 'browser';
+ options.loader = {
+ ...options.loader,
+ '.png': 'dataurl',
+ };
Object.assign(options, getESBuildOptions(optimized));
- /* eslint-enable no-param-reassign */
},
};
@@ -124,10 +126,8 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
platform: 'neutral',
external: [...commonExternals, ...globalManagerPackages, ...globalPreviewPackages],
esbuildOptions: (options) => {
- /* eslint-disable no-param-reassign */
options.platform = 'neutral';
Object.assign(options, getESBuildOptions(optimized));
- /* eslint-enable no-param-reassign */
},
})
);
@@ -173,10 +173,8 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => {
platform: 'node',
external: commonExternals,
esbuildOptions: (c) => {
- /* eslint-disable no-param-reassign */
c.platform = 'node';
Object.assign(c, getESBuildOptions(optimized));
- /* eslint-enable no-param-reassign */
},
})
);
diff --git a/scripts/verdaccio.yaml b/scripts/verdaccio.yaml
index fd577fb25147..6ec5ccaf2539 100644
--- a/scripts/verdaccio.yaml
+++ b/scripts/verdaccio.yaml
@@ -83,10 +83,6 @@ packages:
access: $all
publish: $all
proxy: npmjs
- '@storybook/addon-onboarding':
- access: $all
- publish: $all
- proxy: npmjs
'@storybook/expect':
access: $all
publish: $all
diff --git a/scripts/yarn.lock b/scripts/yarn.lock
index 180d2bf8f179..fd8ab47be372 100644
--- a/scripts/yarn.lock
+++ b/scripts/yarn.lock
@@ -361,16 +361,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.4, @babel/parser@npm:^7.7.0":
- version: 7.23.5
- resolution: "@babel/parser@npm:7.23.5"
- bin:
- parser: ./bin/babel-parser.js
- checksum: 3356aa90d7bafb4e2c7310e7c2c3d443c4be4db74913f088d3d577a1eb914ea4188e05fd50a47ce907a27b755c4400c4e3cbeee73dbeb37761f6ca85954f5a20
- languageName: node
- linkType: hard
-
-"@babel/parser@npm:^7.23.6":
+"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.4, @babel/parser@npm:^7.23.6, @babel/parser@npm:^7.7.0":
version: 7.23.9
resolution: "@babel/parser@npm:7.23.9"
bin:
@@ -1481,18 +1472,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.4.4, @babel/types@npm:^7.7.0, @babel/types@npm:^7.8.3":
- version: 7.23.5
- resolution: "@babel/types@npm:7.23.5"
- dependencies:
- "@babel/helper-string-parser": "npm:^7.23.4"
- "@babel/helper-validator-identifier": "npm:^7.22.20"
- to-fast-properties: "npm:^2.0.0"
- checksum: 7dd5e2f59828ed046ad0b06b039df2524a8b728d204affb4fc08da2502b9dd3140b1356b5166515d229dc811539a8b70dcd4bc507e06d62a89f4091a38d0b0fb
- languageName: node
- linkType: hard
-
-"@babel/types@npm:^7.23.6":
+"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.4, @babel/types@npm:^7.23.6, @babel/types@npm:^7.4.4, @babel/types@npm:^7.7.0, @babel/types@npm:^7.8.3":
version: 7.23.9
resolution: "@babel/types@npm:7.23.9"
dependencies:
@@ -2837,7 +2817,7 @@ __metadata:
ts-loader: "npm:^9.4.2"
tsup: "npm:^6.7.0"
type-fest: "npm:~2.19"
- typescript: "npm:~5.2.2"
+ typescript: "npm:^5.3.2"
util: "npm:^0.12.4"
uuid: "npm:^9.0.0"
verdaccio: "npm:^5.19.1"
@@ -3051,14 +3031,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/estree@npm:*":
- version: 1.0.4
- resolution: "@types/estree@npm:1.0.4"
- checksum: de2abd990fb9b36583ab25d6a5898938eac076cf3e47f11ffc8cf9e3fdca1245807e0f166b6bf0924c7dab0676cc314ca8f749679ee5ea8a45771466ded25dd1
- languageName: node
- linkType: hard
-
-"@types/estree@npm:^1.0.0":
+"@types/estree@npm:*, @types/estree@npm:^1.0.0":
version: 1.0.5
resolution: "@types/estree@npm:1.0.5"
checksum: b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d
@@ -14989,23 +14962,23 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:~5.2.2":
- version: 5.2.2
- resolution: "typescript@npm:5.2.2"
+"typescript@npm:^5.3.2":
+ version: 5.3.3
+ resolution: "typescript@npm:5.3.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: 91ae3e6193d0ddb8656d4c418a033f0f75dec5e077ebbc2bd6d76439b93f35683936ee1bdc0e9cf94ec76863aa49f27159b5788219b50e1cd0cd6d110aa34b07
+ checksum: e33cef99d82573624fc0f854a2980322714986bc35b9cb4d1ce736ed182aeab78e2cb32b385efa493b2a976ef52c53e20d6c6918312353a91850e2b76f1ea44f
languageName: node
linkType: hard
-"typescript@patch:typescript@npm%3A~5.2.2#optional!builtin":
- version: 5.2.2
- resolution: "typescript@patch:typescript@npm%3A5.2.2#optional!builtin::version=5.2.2&hash=f3b441"
+"typescript@patch:typescript@npm%3A^5.3.2#optional!builtin":
+ version: 5.3.3
+ resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: 062c1cee1990e6b9419ce8a55162b8dc917eb87f807e4de0327dbc1c2fa4e5f61bc0dd4e034d38ff541d1ed0479b53bcee8e4de3a4075c51a1724eb6216cb6f5
+ checksum: 1d0a5f4ce496c42caa9a30e659c467c5686eae15d54b027ee7866744952547f1be1262f2d40de911618c242b510029d51d43ff605dba8fb740ec85ca2d3f9500
languageName: node
linkType: hard