Merge branch 'next' into docs_vue_2_removal

This commit is contained in:
jonniebigodes 2024-01-22 14:39:48 +00:00 committed by GitHub
commit f3c7f3d538
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1064 changed files with 12941 additions and 16712 deletions

View File

@ -4,8 +4,8 @@ parameters:
workflow:
description: Which workflow to run
type: enum
enum: ['normal', 'merged', 'daily', 'skipped', 'docs']
default: 'skipped'
enum: ["normal", "merged", "daily", "skipped", "docs"]
default: "skipped"
executors:
sb_node_16_classic:
@ -13,8 +13,8 @@ executors:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'medium+', 'large', 'xlarge']
default: 'small'
enum: ["small", "medium", "medium+", "large", "xlarge"]
default: "small"
working_directory: /tmp/storybook
docker:
- image: cimg/node:18.18.0
@ -26,8 +26,8 @@ executors:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'medium+', 'large', 'xlarge']
default: 'small'
enum: ["small", "medium", "medium+", "large", "xlarge"]
default: "small"
working_directory: /tmp/storybook
docker:
- image: cimg/node:18.18.0-browsers
@ -39,8 +39,8 @@ executors:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'medium+', 'large', 'xlarge']
default: 'small'
enum: ["small", "medium", "medium+", "large", "xlarge"]
default: "small"
working_directory: /tmp/storybook
docker:
- image: mcr.microsoft.com/playwright:v1.36.0-focal
@ -56,7 +56,7 @@ orbs:
commands:
cancel-workflow-on-failure:
description: 'Cancels the entire workflow in case the previous step has failed'
description: "Cancels the entire workflow in case the previous step has failed"
steps:
- run:
name: Cancel current workflow
@ -66,13 +66,13 @@ commands:
echo "To execute all checks locally, please run yarn ci-tests"
curl -X POST --header "Content-Type: application/json" "https://circleci.com/api/v2/workflow/${CIRCLE_WORKFLOW_ID}/cancel?circle-token=${WORKFLOW_CANCELER}"
report-workflow-on-failure:
description: 'Reports failures to discord'
description: "Reports failures to discord"
parameters:
template:
description: |
Which template to report in discord. Applicable for parallel sandbox jobs
type: string
default: 'none'
default: "none"
steps:
- run:
when: on_fail
@ -88,7 +88,7 @@ jobs:
name: sb_node_16_classic
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- restore_cache:
name: Restore Yarn cache
keys:
@ -114,7 +114,7 @@ jobs:
name: sb_node_16_classic
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- restore_cache:
name: Restore Yarn cache
keys:
@ -157,7 +157,7 @@ jobs:
name: sb_node_16_classic
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -173,7 +173,7 @@ jobs:
name: sb_node_16_classic
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -187,7 +187,7 @@ jobs:
executor: sb_node_16_browsers
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -215,7 +215,7 @@ jobs:
name: sb_node_16_browsers
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -237,7 +237,7 @@ jobs:
name: sb_node_16_browsers
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- codecov/upload
@ -273,7 +273,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -297,7 +297,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -317,7 +317,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -356,7 +356,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -376,7 +376,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -415,7 +415,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -438,7 +438,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -461,7 +461,7 @@ jobs:
parallelism: << parameters.parallelism >>
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- run:
@ -483,7 +483,7 @@ jobs:
type: string
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
clone_options: "--depth 1 --verbose"
- attach_workspace:
at: .
- when:
@ -706,22 +706,22 @@ workflows:
requires:
- build
- create-sandboxes:
parallelism: 32
parallelism: 34
requires:
- build
# - smoke-test-sandboxes: # disabled for now
# requires:
# - create-sandboxes
- build-sandboxes:
parallelism: 32
parallelism: 34
requires:
- create-sandboxes
- chromatic-sandboxes:
parallelism: 29
parallelism: 31
requires:
- build-sandboxes
- e2e-production:
parallelism: 27
parallelism: 29
requires:
- build-sandboxes
- e2e-dev:
@ -729,7 +729,7 @@ workflows:
requires:
- create-sandboxes
- test-runner-production:
parallelism: 27
parallelism: 29
requires:
- build-sandboxes
@ -744,14 +744,14 @@ workflows:
# - "yarn1"
# - "yarn2"
# - "pnpm"
template:
template:
- "react-vite-ts"
- "nextjs-ts"
- "vue-vite-ts"
# --smoke-test is not supported for the angular builder right now
# - "angular-cli"
- "lit-vite-ts"
# TODO: reenable once we find out the source of flakyness
# - test-runner-dev:
# parallelism: 4

4
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,4 @@
34e364a0ca1d93555d36a7367d78e8e229493de8
c0896915fb7fb9a8dd416b9aebca17abd909d1c1
a41c227037e7e7249b8b376f838f4f8bcc3e3e59
13c46e6c0b7f3dd8cf4ba42d1cfd6714f4777d54

View File

@ -1,11 +1,11 @@
name: Publish canary release of PR
run-name: 'Canary release: PR #${{ inputs.pr }}, triggered by ${{ github.triggering_actor }}'
run-name: "Canary release: PR #${{ inputs.pr }}, triggered by ${{ github.triggering_actor }}"
on:
workflow_dispatch:
inputs:
pr:
description: 'Pull request number to create a canary release for'
description: "Pull request number to create a canary release for"
required: true
type: number
@ -58,7 +58,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Cache dependencies
uses: actions/cache@v3
with:
@ -91,10 +91,10 @@ jobs:
with:
githubToken: ${{ secrets.GH_TOKEN }}
prNumber: ${{ inputs.pr }}
find: 'CANARY_RELEASE_SECTION'
find: "CANARY_RELEASE_SECTION"
isHtmlCommentTag: true
replace: |
This pull request has been released as version [`${{ steps.version.outputs.next-version }}`](https://npmjs.com/package/@storybook/cli/v/${{ steps.version.outputs.next-version }}). Install it by pinning all your Storybook dependencies to that version.
This pull request has been released as version `${{ steps.version.outputs.next-version }}`. Try it out in a new sandbox by running `npx storybook@${{ steps.version.outputs.next-version }} sandbox` or in an existing project with `npx storybook@${{ steps.version.outputs.next-version }} upgrade`.
<details>
<summary>More information</summary>

View File

@ -43,7 +43,7 @@ jobs:
run: yarn local-registry --open &
working-directory: ./code
- name: Wait for registry
run: yarn wait-on http://localhost:6001
run: yarn wait-on tcp:127.0.0.1:6001
working-directory: ./code
- name: Generate
run: yarn generate-sandboxes --local-registry

View File

@ -43,7 +43,7 @@ jobs:
run: yarn local-registry --open &
working-directory: ./code
- name: Wait for registry
run: yarn wait-on http://localhost:6001
run: yarn wait-on tcp:127.0.0.1:6001
working-directory: ./code
- name: Generate
run: yarn generate-sandboxes --local-registry --debug

893
.yarn/releases/yarn-4.0.0.cjs generated vendored

File diff suppressed because one or more lines are too long

893
.yarn/releases/yarn-4.0.2.cjs generated vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -8,4 +8,4 @@ nodeLinker: node-modules
npmPublishAccess: public
yarnPath: .yarn/releases/yarn-4.0.0.cjs
yarnPath: .yarn/releases/yarn-4.0.2.cjs

View File

@ -1,3 +1,19 @@
## 7.6.10
- CLI: Fix existing version detection in `upgrade` - [#25642](https://github.com/storybookjs/storybook/pull/25642), thanks [@JReinhold](https://github.com/JReinhold)!
- React: Fix acorn ecma version warning - [#25634](https://github.com/storybookjs/storybook/pull/25634), thanks [@dannyhw](https://github.com/dannyhw)!
## 7.6.9
- ConfigFile: Fix export specifiers - [#25590](https://github.com/storybookjs/storybook/pull/25590), thanks [@shilman](https://github.com/shilman)!
- Webpack5: Make export-order-loader compatible with both esm and cjs - [#25540](https://github.com/storybookjs/storybook/pull/25540), thanks [@mlazari](https://github.com/mlazari)!
- CLI: Support version specifiers in `init`, `upgrade` and `sandbox` - [#25526](https://github.com/storybookjs/storybook/pull/25526), thanks [@ndelangen](https://github.com/ndelangen), [@jreinhold](https://github.com/jreinhold)!
## 7.6.8
- Addon-actions: Fix module resolution for react-native - [#25296](https://github.com/storybookjs/storybook/pull/25296), thanks [@dannyhw](https://github.com/dannyhw)!
- Storysource: Fix import error - [#25391](https://github.com/storybookjs/storybook/pull/25391), thanks [@unional](https://github.com/unional)!
## 7.6.7
- Core: Skip no-framework error when ignorePreview=true - [#25286](https://github.com/storybookjs/storybook/pull/25286), thanks [@ndelangen](https://github.com/ndelangen)!

View File

@ -1,3 +1,104 @@
## 8.0.0-alpha.13
- Next.js: Fix SWC mode activation - [#25670](https://github.com/storybookjs/storybook/pull/25670), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
## 8.0.0-alpha.12
- Blocks: Fix Controls block not having controls - [#25663](https://github.com/storybookjs/storybook/pull/25663), thanks [@JReinhold](https://github.com/JReinhold)!
- Blocks: Support `subcomponents` in `ArgTypes` and `Controls`, remove `ArgsTable` block - [#25614](https://github.com/storybookjs/storybook/pull/25614), thanks [@JReinhold](https://github.com/JReinhold)!
- CLI: Fix existing version detection in `upgrade` - [#25642](https://github.com/storybookjs/storybook/pull/25642), thanks [@JReinhold](https://github.com/JReinhold)!
- Core: Add preset with experimental server API - [#25664](https://github.com/storybookjs/storybook/pull/25664), thanks [@shilman](https://github.com/shilman)!
- MDX: Replace remark by rehype plugins - [#25615](https://github.com/storybookjs/storybook/pull/25615), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- React: Fix acorn ecma version warning - [#25634](https://github.com/storybookjs/storybook/pull/25634), thanks [@dannyhw](https://github.com/dannyhw)!
- Shortcuts: Require modifier key to trigger shortcuts (`F`,`A`,`D`,`S`,`T`,`/`) - [#25625](https://github.com/storybookjs/storybook/pull/25625), thanks [@cdedreuille](https://github.com/cdedreuille)!
- Theming: Fix export of module augmentation - [#25643](https://github.com/storybookjs/storybook/pull/25643), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- UI: Add links to documentation and videos in UI - [#25565](https://github.com/storybookjs/storybook/pull/25565), thanks [@Integrayshaun](https://github.com/Integrayshaun)!
- Webpack: Use `node:assert` used in `export-order-loader` - [#25622](https://github.com/storybookjs/storybook/pull/25622), thanks [@JReinhold](https://github.com/JReinhold)!
## 8.0.0-alpha.11
- Angular: Remove cached NgModules and introduce a global queue during bootstrapping - [#24982](https://github.com/storybookjs/storybook/pull/24982), thanks [@Marklb](https://github.com/Marklb)!
- CLI: Fix sandbox command versioning - [#25600](https://github.com/storybookjs/storybook/pull/25600), thanks [@ndelangen](https://github.com/ndelangen)!
- CLI: Support upgrading to canary versions - [#25596](https://github.com/storybookjs/storybook/pull/25596), thanks [@JReinhold](https://github.com/JReinhold)!
- ConfigFile: Fix export specifiers - [#25590](https://github.com/storybookjs/storybook/pull/25590), thanks [@shilman](https://github.com/shilman)!
- Interaction: Replace @storybook/jest by @storybook/test - [#25584](https://github.com/storybookjs/storybook/pull/25584), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Next.js: Pass jsConfig to SWC Loader and load config with defaults - [#25203](https://github.com/storybookjs/storybook/pull/25203), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Parameters: Remove passArgsFirst flag - [#25585](https://github.com/storybookjs/storybook/pull/25585), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Preset: Remove deprecated config preset - [#25607](https://github.com/storybookjs/storybook/pull/25607), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- React: Refactor RSC out of Next - [#25591](https://github.com/storybookjs/storybook/pull/25591), thanks [@shilman](https://github.com/shilman)!
- Sandboxes: Update wait-on command to use TCP instead of HTTP - [#25541](https://github.com/storybookjs/storybook/pull/25541), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Telejson: Update stringify options in codebase - [#25564](https://github.com/storybookjs/storybook/pull/25564), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- UI: Fix menu icon on the sidebar - [#25587](https://github.com/storybookjs/storybook/pull/25587), thanks [@cdedreuille](https://github.com/cdedreuille)!
- Webpack5: Make export-order-loader compatible with both esm and cjs - [#25540](https://github.com/storybookjs/storybook/pull/25540), thanks [@mlazari](https://github.com/mlazari)!
## 8.0.0-alpha.10
- API: Remove deprecations from manager and preview api - [#25536](https://github.com/storybookjs/storybook/pull/25536), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Addon Controls: Remove unused hideNoControlsWarning type - [#25417](https://github.com/storybookjs/storybook/pull/25417), thanks [@yannbf](https://github.com/yannbf)!
- Addon Remark-GFM: Upgrade remark-gfm - [#25301](https://github.com/storybookjs/storybook/pull/25301), thanks [@yannbf](https://github.com/yannbf)!
- Addon-actions: Fix module resolution for react-native - [#25296](https://github.com/storybookjs/storybook/pull/25296), thanks [@dannyhw](https://github.com/dannyhw)!
- Angular: Remove deprecated Story type - [#25558](https://github.com/storybookjs/storybook/pull/25558), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- CLI: Add addon `remove` command - [#25538](https://github.com/storybookjs/storybook/pull/25538), thanks [@shilman](https://github.com/shilman)!
- CLI: Check optionalDependencies for storybook versions - [#25406](https://github.com/storybookjs/storybook/pull/25406), thanks [@reyronald](https://github.com/reyronald)!
- CLI: Sandbox script should use current version to init - [#25560](https://github.com/storybookjs/storybook/pull/25560), thanks [@ndelangen](https://github.com/ndelangen)!
- CLI: Versioned installation of monorepo packages - [#25517](https://github.com/storybookjs/storybook/pull/25517), thanks [@ndelangen](https://github.com/ndelangen)!
- CLI: Versioned upgrade of monorepo packages - [#25553](https://github.com/storybookjs/storybook/pull/25553), thanks [@JReinhold](https://github.com/JReinhold)!
- Core: Prevent stories lookup in node_modules - [#25214](https://github.com/storybookjs/storybook/pull/25214), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Core: Refactor preview and deprecate story store - [#24926](https://github.com/storybookjs/storybook/pull/24926), thanks [@tmeasday](https://github.com/tmeasday)!
- Doc blocks: Remove deprecated props from Primary block - [#25461](https://github.com/storybookjs/storybook/pull/25461), thanks [@yannbf](https://github.com/yannbf)!
- Doc blocks: Remove deprecated props from Source block - [#25459](https://github.com/storybookjs/storybook/pull/25459), thanks [@yannbf](https://github.com/yannbf)!
- Doc blocks: Remove deprecated props from Story block - [#25460](https://github.com/storybookjs/storybook/pull/25460), thanks [@yannbf](https://github.com/yannbf)!
- Maintenance: Pin TS to >= 4.2 as typefest 2 requires it - [#25548](https://github.com/storybookjs/storybook/pull/25548), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- Maintenance: Upgrade to prettier 3 - [#25524](https://github.com/storybookjs/storybook/pull/25524), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- Remove deprecated properties from manager-api - [#25578](https://github.com/storybookjs/storybook/pull/25578), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Test: Fix user event being inlined by tsup by using an interface - [#25547](https://github.com/storybookjs/storybook/pull/25547), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- Test: Upgrade test package to vitest 1.1.3 - [#25576](https://github.com/storybookjs/storybook/pull/25576), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- UI: Add configurable tags-based exclusion from sidebar/autodocs - [#25328](https://github.com/storybookjs/storybook/pull/25328), thanks [@shilman](https://github.com/shilman)!
- Webpack: Remove deprecated standalone webpackConfig option - [#25481](https://github.com/storybookjs/storybook/pull/25481), thanks [@yannbf](https://github.com/yannbf)!
## 8.0.0-alpha.9
- AutoTitle: Fix case-insensitive trailing duplicate - [#25452](https://github.com/storybookjs/storybook/pull/25452), thanks [@ksugawara61](https://github.com/ksugawara61)!
- CLI: Fix using wrong package managers in existing projects - [#25474](https://github.com/storybookjs/storybook/pull/25474), thanks [@JReinhold](https://github.com/JReinhold)!
- CLI: Never prompt for ESLint plugin - [#25289](https://github.com/storybookjs/storybook/pull/25289), thanks [@shilman](https://github.com/shilman)!
- CSF-tools: Allow type checking in storySort - [#25265](https://github.com/storybookjs/storybook/pull/25265), thanks [@honzahruby](https://github.com/honzahruby)!
- Core: Remove `storyStoreV7` feature flag - [#24658](https://github.com/storybookjs/storybook/pull/24658), thanks [@ndelangen](https://github.com/ndelangen)!
- Core: Remove deprecated createChannel APIs - [#25487](https://github.com/storybookjs/storybook/pull/25487), thanks [@yannbf](https://github.com/yannbf)!
- Node.js: Update version requirement to >= 18.0.0 - [#25516](https://github.com/storybookjs/storybook/pull/25516), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
- Storysource: Fix import error - [#25391](https://github.com/storybookjs/storybook/pull/25391), thanks [@unional](https://github.com/unional)!
- UI: Fix sidebar top and bottom addon slots - [#25426](https://github.com/storybookjs/storybook/pull/25426), thanks [@ndelangen](https://github.com/ndelangen)!
- Webpack5: Remove babel and SWC compiler from builder - [#25379](https://github.com/storybookjs/storybook/pull/25379), thanks [@valentinpalkovic](https://github.com/valentinpalkovic)!
## 8.0.0-alpha.8
- Addon Links: Remove LinkTo from direct import - [#25418](https://github.com/storybookjs/storybook/pull/25418), thanks [@yannbf](https://github.com/yannbf)!
- Addon docs: Remove deprecated parameters - [#25469](https://github.com/storybookjs/storybook/pull/25469), thanks [@yannbf](https://github.com/yannbf)!
- Builder Vite: Remove StorybookViteConfig type in favor of StorybookConfig - [#25441](https://github.com/storybookjs/storybook/pull/25441), thanks [@yannbf](https://github.com/yannbf)!
- Core: Error on explicit actions while rendering or playing - [#25238](https://github.com/storybookjs/storybook/pull/25238), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- Core: Remove collapseAll and expandAll methods - [#25486](https://github.com/storybookjs/storybook/pull/25486), thanks [@yannbf](https://github.com/yannbf)!
- Core: Remove storyIndexers in favor of experimental_indexers - [#25468](https://github.com/storybookjs/storybook/pull/25468), thanks [@yannbf](https://github.com/yannbf)!
- Core: Remove unused staticDir type - [#25415](https://github.com/storybookjs/storybook/pull/25415), thanks [@yannbf](https://github.com/yannbf)!
- Doc blocks: Remove deprecated props from Description block - [#25457](https://github.com/storybookjs/storybook/pull/25457), thanks [@yannbf](https://github.com/yannbf)!
- Manager API: Remove deprecated navigateToSettingsPage method - [#25467](https://github.com/storybookjs/storybook/pull/25467), thanks [@yannbf](https://github.com/yannbf)!
- React: Remove deprecated setGlobalConfig portable stories api - [#25442](https://github.com/storybookjs/storybook/pull/25442), thanks [@yannbf](https://github.com/yannbf)!
- TypeScript: Remove deprecated addons module types - [#25485](https://github.com/storybookjs/storybook/pull/25485), thanks [@yannbf](https://github.com/yannbf)!
- Types: Remove DecoratorFn, Story, ComponentStory, ComponentStoryObj, ComponentStoryFn and ComponentMeta types - [#25477](https://github.com/storybookjs/storybook/pull/25477), thanks [@yannbf](https://github.com/yannbf)!
- Types: Remove Framework in favor of Renderer types - [#25476](https://github.com/storybookjs/storybook/pull/25476), thanks [@yannbf](https://github.com/yannbf)!
- UI: Remove deprecated WithTooltip props - [#25440](https://github.com/storybookjs/storybook/pull/25440), thanks [@yannbf](https://github.com/yannbf)!
## 8.0.0-alpha.7
- Addon-Docs: Upgrade to MDX3 - [#25303](https://github.com/storybookjs/storybook/pull/25303), thanks [@yannbf](https://github.com/yannbf)!
- CLI: Add Storyshots migration notice - [#25327](https://github.com/storybookjs/storybook/pull/25327), thanks [@yannbf](https://github.com/yannbf)!
- CLI: Fix regex used in upgrade command - [#25284](https://github.com/storybookjs/storybook/pull/25284), thanks [@yannbf](https://github.com/yannbf)!
- CLI: Remove --use-npm flag - [#25414](https://github.com/storybookjs/storybook/pull/25414), thanks [@yannbf](https://github.com/yannbf)!
- Core: Remove unused warnOnLegacyHierarchySeparator type - [#25416](https://github.com/storybookjs/storybook/pull/25416), thanks [@yannbf](https://github.com/yannbf)!
- Core: Remove vite plugins and drop Vite 3 support - [#25427](https://github.com/storybookjs/storybook/pull/25427), thanks [@kasperpeulen](https://github.com/kasperpeulen)!
- Maintenance: Add comment to deprecation notice in Button component - [#25411](https://github.com/storybookjs/storybook/pull/25411), thanks [@yannbf](https://github.com/yannbf)!
- UI: Fix about page layout - [#25396](https://github.com/storybookjs/storybook/pull/25396), thanks [@cdedreuille](https://github.com/cdedreuille)!
- Viewport: Store viewport, rotation in globals - [#25423](https://github.com/storybookjs/storybook/pull/25423), thanks [@shilman](https://github.com/shilman)!
- Vite: Fix Vite 5 CJS warnings - [#25005](https://github.com/storybookjs/storybook/pull/25005), thanks [@JReinhold](https://github.com/JReinhold)!
## 8.0.0-alpha.6
- NextJS: Autoconfigure public directory for new projects - [#25279](https://github.com/storybookjs/storybook/pull/25279), thanks [@shilman](https://github.com/shilman)!

View File

@ -25,6 +25,7 @@ If you run `yarn start` and encounter the following error, try rerunning `yarn s
```sh
> NX ENOENT: no such file or directory, open 'storybook/code/node_modules/nx/package.json'
```
If you are a Storybook contributor and still experience issues, it is recommended that you verify your local Storybook instance for any unintentional local changes. To do this, you can use the following command:
```sh
@ -37,11 +38,11 @@ By executing this command, you will be able to see which untracked or ignored fi
If you have forked the repository, you should [disable Github Actions for your repo](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository) as many of them (e.g. pushing to sandbox) will fail without proper authorization. In your Github repo, go to Settings > Actions > General > set the Actions Permissions to **Disable actions**.
# Running against different sandbox templates
## Running against different sandbox templates
You can also pick a specific template to use as your sandbox by running `yarn task`, which will prompt you to make further choices about which template you want and which task you want to run.
# Making code changes
## Making code changes
If you want to make code changes to Storybook packages while running a sandbox, you'll need to do the following:
@ -56,10 +57,10 @@ yarn build --watch react core-server api addon-docs
3. If you are running the sandbox in "unlinked" mode you'll need to re-run the sandbox from the `publish` step to see the changes:
```
```sh
yarn task --task dev --template <your template> --start-from=publish
```
# Contributing to Storybook
## Contributing to Storybook
For further advice on how to contribute, please refer to our [NEW contributing guide on the Storybook website](https://storybook.js.org/docs/contribute).

File diff suppressed because it is too large Load Diff

View File

@ -134,7 +134,7 @@ For additional help, share your issue in [the repo's GitHub Discussions](https:/
See [Addon / Framework Support Table](https://storybook.js.org/docs/react/api/frameworks-feature-support)
### Deprecated Addons
### Deprecated/Removed Addons
| Addons | |
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
@ -145,12 +145,14 @@ See [Addon / Framework Support Table](https://storybook.js.org/docs/react/api/fr
| [options](https://www.npmjs.com/package/@storybook/addon-options) | Customize the Storybook UI in code |
| [storyshots](https://github.com/storybookjs/storybook/tree/main/code/addons/storyshots-core) | Snapshot testing for components in Storybook |
To continue improving your experience, we have to eventually deprecate certain addons in favor of new and better tools.
To continue improving your experience, we have to eventually deprecate or remove certain addons in favor of new and better tools.
If you're using info/notes, we highly recommend you migrate to [docs](code/addons/docs/) instead, and [here is a guide](code/addons/docs/docs/recipes.md#migrating-from-notesinfo-addons) to help you.
If you're using contexts, we highly recommend you migrate to [toolbars](https://github.com/storybookjs/storybook/tree/next/code/addons/toolbars) and [here is a guide](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-addon-contexts) to help you.
If you're using addon-storyshots, we highly recommend you migrate to the Storybook [test-runner](https://github.com/storybookjs/test-runner) and [here is a guide](https://storybook.js.org/docs/writing-tests/storyshots-migration-guide) to help you.
## Badges & Presentation materials
We have a badge! Link it to your live Storybook example.

View File

@ -8,8 +8,4 @@ svelte-check@3.4.6 (bug: 3.5.x): Type issues
## code/ui/components/package.json
overlayscrollbars@2.2.1 (bug: 2.3.x): The Scrollbar doesn't disappear anymore by default. It might has something to do with the `scrollbars.autoHideSuspend` option, which was introduced in 2.3.0. https://github.com/KingSora/OverlayScrollbars/blob/master/packages/overlayscrollbars/CHANGELOG.md#230
## code/package.json
@babel/core@^7.23.2: Make sure we use the latest version of @babel/traverse, which is a dependency of @babel/core, since it contains a fix for a vulnerability: https://security.snyk.io/vuln/SNYK-JS-BABELTRAVERSE-5962462
overlayscrollbars@2.2.1 (bug: 2.3.x): The Scrollbar doesn't disappear anymore by default. It might has something to do with the `scrollbars.autoHideSuspend` option, which was introduced in 2.3.0. https://github.com/KingSora/OverlayScrollbars/blob/master/packages/overlayscrollbars/CHANGELOG.md#230

View File

@ -17,4 +17,4 @@ ember-output
!.eslintrc.js
!.eslintrc-markdown.js
!.storybook
lib/core-common/templates/base-preview-head.html

View File

@ -23,11 +23,6 @@ module.exports = {
},
plugins: ['local-rules'],
rules: {
// remove as shared eslint has jest rules removed
'jest/no-standalone-expect': 'off',
'jest/no-done-callback': 'off',
'jest/no-deprecated-functions': 'off',
'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
'eslint-comments/no-unused-disable': 'error',
'react-hooks/rules-of-hooks': 'off',
@ -39,6 +34,19 @@ module.exports = {
allowIndexSignaturePropertyAccess: true,
},
],
'@typescript-eslint/no-restricted-imports': [
'error',
{
paths: [
{
name: 'vite',
message: 'Please dynamically import from vite instead, to force the use of ESM',
allowTypeImports: true,
},
],
},
],
'@typescript-eslint/default-param-last': 'off',
},
overrides: [
{

View File

@ -1,8 +1,8 @@
diff --git a/dist/index.js b/dist/index.js
index 5a61947ad50426d27390b4e82533179323ad3ba1..32bfc45909b645cb31cec2e204c8baa23f21fdd2 100644
index 974d6b26f626024fc9904908100c9ecaa54f43e1..5be2d35267e7f0525c6588758dbebe72599f88a9 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -6,23 +6,29 @@ import { processError } from '@vitest/utils/error';
@@ -6,31 +6,37 @@ import { processError } from '@vitest/utils/error';
import { util } from 'chai';
const MATCHERS_OBJECT = Symbol.for("matchers-object");
@ -11,15 +11,19 @@ index 5a61947ad50426d27390b4e82533179323ad3ba1..32bfc45909b645cb31cec2e204c8baa2
+// Otherwise, vitest will override global jest matchers, and crash.
+const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object-storybook");
const GLOBAL_EXPECT = Symbol.for("expect-global");
const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object");
if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
const globalState = /* @__PURE__ */ new WeakMap();
- const matchers = /* @__PURE__ */ Object.create(null);
const assymetricMatchers = /* @__PURE__ */ Object.create(null);
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
get: () => globalState
});
+ Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
+ get: () => assymetricMatchers
+ });
+}
+
+if (!Object.prototype.hasOwnProperty.call(globalThis, JEST_MATCHERS_OBJECT)) {
+ const matchers = /* @__PURE__ */ Object.create(null);
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
@ -30,8 +34,15 @@ index 5a61947ad50426d27390b4e82533179323ad3ba1..32bfc45909b645cb31cec2e204c8baa2
matchers
})
});
- Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
- get: () => assymetricMatchers
- });
}
+
function getState(expect) {
return globalThis[MATCHERS_OBJECT].get(expect);
}
+
function setState(state, expect) {
const map = globalThis[MATCHERS_OBJECT];
const current = map.get(expect) || {};

View File

@ -2,6 +2,8 @@ compressionLevel: 0
enableGlobalCache: true
installStatePath: ../.yarn/code-install-state.gz
logFilters:
- code: YN0005
level: discard
@ -23,7 +25,6 @@ plugins:
unsafeHttpWhitelist:
- localhost
yarnPath: ../.yarn/releases/yarn-4.0.0.cjs
installStatePath: '../.yarn/code-install-state.gz'
yarnPath: ../.yarn/releases/yarn-4.0.2.cjs
# Sometimes you get a "The remote archive doesn't match the expected checksum" error, uncommenting this line will fix it
# checksumBehavior: 'update'

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-a11y",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Test component compliance with web accessibility standards",
"keywords": [
"a11y",
@ -60,7 +60,7 @@
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/theming": "workspace:*",

View File

@ -3,7 +3,7 @@ import { addons } from '@storybook/preview-api';
import { EVENTS } from './constants';
import type { A11yParameters } from './params';
const { document, window: globalWindow } = global;
const { document } = global;
const channel = addons.getChannel();
// Holds axe core running state
@ -11,22 +11,21 @@ let active = false;
// Holds latest story we requested a run
let activeStoryId: string | undefined;
const defaultParameters = { config: {}, options: {} };
/**
* Handle A11yContext events.
* Because the event are sent without manual check, we split calls
*/
const handleRequest = async (storyId: string) => {
const { manual } = await getParams(storyId);
if (!manual) {
await run(storyId);
const handleRequest = async (storyId: string, input: A11yParameters = defaultParameters) => {
if (!input?.manual) {
await run(storyId, input);
}
};
const run = async (storyId: string) => {
const run = async (storyId: string, input: A11yParameters = defaultParameters) => {
activeStoryId = storyId;
try {
const input = await getParams(storyId);
if (!active) {
active = true;
channel.emit(EVENTS.RUNNING);
@ -69,17 +68,5 @@ const run = async (storyId: string) => {
}
};
/** Returns story parameters or default ones. */
const getParams = async (storyId: string): Promise<A11yParameters> => {
const { parameters } =
(await globalWindow.__STORYBOOK_STORY_STORE__.loadStory({ storyId })) || {};
return (
parameters.a11y || {
config: {},
options: {},
}
);
};
channel.on(EVENTS.REQUEST, handleRequest);
channel.on(EVENTS.MANUAL, run);

View File

@ -6,7 +6,12 @@ import { ActionBar, ScrollArea } from '@storybook/components';
import { SyncIcon, CheckIcon } from '@storybook/icons';
import type { AxeResults } from 'axe-core';
import { useChannel, useParameter, useStorybookState } from '@storybook/manager-api';
import {
useChannel,
useParameter,
useStorybookApi,
useStorybookState,
} from '@storybook/manager-api';
import { Report } from './Report';
@ -59,6 +64,7 @@ export const A11YPanel: React.FC = () => {
const [error, setError] = React.useState<unknown>(undefined);
const { setResults, results } = useA11yContext();
const { storyId } = useStorybookState();
const api = useStorybookApi();
React.useEffect(() => {
setStatus(manual ? 'manual' : 'initial');
@ -92,7 +98,7 @@ export const A11YPanel: React.FC = () => {
const handleManual = useCallback(() => {
setStatus('running');
emit(EVENTS.MANUAL, storyId);
emit(EVENTS.MANUAL, storyId, api.getParameters(storyId, 'a11y'));
}, [storyId]);
const manualActionItems = useMemo(

View File

@ -57,6 +57,7 @@ describe('A11YPanel', () => {
});
const getCurrentStoryData = vi.fn();
const getParameters = vi.fn();
beforeEach(() => {
mockedApi.useChannel.mockReset();
mockedApi.useStorybookApi.mockReset();
@ -65,7 +66,8 @@ describe('A11YPanel', () => {
mockedApi.useAddonState.mockImplementation((_, defaultState) => React.useState(defaultState));
mockedApi.useChannel.mockReturnValue(vi.fn());
getCurrentStoryData.mockReset().mockReturnValue({ id: storyId, type: 'story' });
mockedApi.useStorybookApi.mockReturnValue({ getCurrentStoryData } as any);
getParameters.mockReturnValue({});
mockedApi.useStorybookApi.mockReturnValue({ getCurrentStoryData, getParameters } as any);
});
it('should render children', () => {
@ -94,7 +96,7 @@ describe('A11YPanel', () => {
mockedApi.useChannel.mockReturnValue(emit);
const { rerender } = render(<A11yContextProvider active={false} />);
rerender(<A11yContextProvider active />);
expect(emit).toHaveBeenLastCalledWith(EVENTS.REQUEST, storyId);
expect(emit).toHaveBeenLastCalledWith(EVENTS.REQUEST, storyId, {});
});
it('should emit highlight with no values when inactive', () => {

View File

@ -70,7 +70,7 @@ export const A11yContextProvider: React.FC<React.PropsWithChildren<A11yContextPr
);
}, []);
const handleRun = (renderedStoryId: string) => {
emit(EVENTS.REQUEST, renderedStoryId);
emit(EVENTS.REQUEST, renderedStoryId, api.getParameters(renderedStoryId, 'a11y'));
};
const handleClearHighlights = React.useCallback(() => setHighlighted([]), []);
const handleSetTab = React.useCallback((index: number) => {

View File

@ -63,7 +63,6 @@ interface ElementsProps {
export const Elements: FC<ElementsProps> = ({ elements, type }) => (
<ol>
{elements.map((element, index) => (
// eslint-disable-next-line react/no-array-index-key
<Element element={element} key={index} type={type} />
))}
</ol>

View File

@ -27,12 +27,11 @@ function areAllRequiredElementsHighlighted(
highlighted.includes(item.target[0] as any)
).length;
// eslint-disable-next-line no-nested-ternary
return highlightedCount === 0
? CheckBoxStates.UNCHECKED
: highlightedCount === elementsToHighlight.length
? CheckBoxStates.CHECKED
: CheckBoxStates.INDETERMINATE;
? CheckBoxStates.CHECKED
: CheckBoxStates.INDETERMINATE;
}
const HighlightToggle: React.FC<ToggleProps> = ({ toggleId, elementsToHighlight = [] }) => {

View File

@ -91,7 +91,6 @@ export const Rules: FC<RulesProps> = ({ rules }) => {
return (
<List>
{rules.map((rule, index) => (
// eslint-disable-next-line react/no-array-index-key
<Rule rule={rule} key={index} />
))}
</List>

View File

@ -121,7 +121,6 @@ export const Tabs: React.FC<TabsProps> = ({ tabs }) => {
<TabsWrapper>
{tabs.map((tab, index) => (
<Item
/* eslint-disable-next-line react/no-array-index-key */
key={index}
data-index={index}
active={activeTab === index}

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-actions",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Get UI feedback when an action is performed on an interactive element",
"keywords": [
"storybook",
@ -40,6 +40,7 @@
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"react-native": "dist/index.mjs",
"types": "dist/index.d.ts",
"typesVersions": {
"*": {

View File

@ -65,10 +65,8 @@ export default class ActionLogger extends Component<ActionLoggerProps, ActionLog
const actions = [...prevState.actions];
const previous = actions.length && actions[0];
if (previous && safeDeepEqual(previous.data, action.data)) {
// eslint-disable-next-line no-plusplus
previous.count++;
} else {
// eslint-disable-next-line no-param-reassign
action.count = 1;
actions.unshift(action);
}

View File

@ -71,7 +71,6 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti
if (storyRenderer) {
const deprecated = !window?.FEATURES?.disallowImplicitActionsInRenderV8;
const error = new ImplicitActionsDuringRendering({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
phase: storyRenderer.phase!,
name,
deprecated,

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-backgrounds",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Switch backgrounds to view components in different settings",
"keywords": [
"addon",
@ -53,7 +53,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"memoizerific": "^1.11.3",
"ts-dedent": "^2.0.0"
},

View File

@ -37,40 +37,24 @@ const createBackgroundSelectorItem = memoize(1000)(
})
);
const getDisplayedItems = memoize(10)(
(
backgrounds: Background[],
selectedBackgroundColor: string | null,
change: (arg: { selected: string; name: string }) => void
) => {
const backgroundSelectorItems = backgrounds.map(({ name, value }) =>
createBackgroundSelectorItem(
null,
name,
value,
true,
change,
value === selectedBackgroundColor
)
);
const getDisplayedItems = memoize(10)((
backgrounds: Background[],
selectedBackgroundColor: string | null,
change: (arg: { selected: string; name: string }) => void
) => {
const backgroundSelectorItems = backgrounds.map(({ name, value }) =>
createBackgroundSelectorItem(null, name, value, true, change, value === selectedBackgroundColor)
);
if (selectedBackgroundColor !== 'transparent') {
return [
createBackgroundSelectorItem(
'reset',
'Clear background',
'transparent',
null,
change,
false
),
...backgroundSelectorItems,
];
}
return backgroundSelectorItems;
if (selectedBackgroundColor !== 'transparent') {
return [
createBackgroundSelectorItem('reset', 'Clear background', 'transparent', null, change, false),
...backgroundSelectorItems,
];
}
);
return backgroundSelectorItems;
});
const DEFAULT_BACKGROUNDS_CONFIG: BackgroundsParameter = {
default: null,

View File

@ -176,4 +176,4 @@ Like [story parameters](https://storybook.js.org/docs/react/writing-stories/para
### How do controls work with MDX?
When importing stories from your CSF file into MDX, controls will work the same way. See [the documentation](https://storybook.js.org/docs/writing-docs/mdx#basic-example) for examples.
When importing stories from your CSF file into MDX, controls will work the same way. See [the documentation](https://storybook.js.org/docs/writing-docs/mdx#basic-example) for examples.

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-controls",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Interact with component inputs dynamically in the Storybook UI",
"keywords": [
"addon",

View File

@ -16,9 +16,6 @@ interface ControlsParameters {
sort?: SortType;
expanded?: boolean;
presetColors?: PresetColor[];
/** @deprecated No longer used, will be removed in Storybook 8.0 */
hideNoControlsWarning?: boolean;
}
export const ControlsPanel: FC = () => {

View File

@ -110,20 +110,6 @@ export default {
};
```
If using in conjunction with the [storyshots add-on](https://github.com/storybookjs/storybook/blob/next/code/addons/storyshots-core/README.md), you will need to
configure Jest to transform MDX stories into something Storyshots can understand:
Add the following to your Jest configuration:
```json
{
"transform": {
"^.+\\.[tj]sx?$": "babel-jest",
"^.+\\.mdx$": "@storybook/addon-docs/jest-transform-mdx"
}
}
```
### Be sure to check framework specific installation needs
- [React](https://github.com/storybookjs/storybook/tree/next/code/addons/docs/react) (covered here)
@ -143,7 +129,6 @@ export default {
{
name: '@storybook/addon-docs',
options: {
jsxOptions: {},
csfPluginOptions: null,
mdxPluginOptions: {},
},
@ -152,8 +137,6 @@ export default {
};
```
`jsxOptions` are options that will be passed to `@babel/preset-react` for `.md` and `.mdx` files.
`csfPluginOptions` is an object for configuring `@storybook/csf-plugin`. When set to `null` it tells docs not to run the `csf-plugin` at all, which can be used as an optimization, or if you're already using `csf-plugin` in your `main.js`.
> With the release of version 7.0, it is no longer possible to import `.md` files directly into Storybook using the `transcludeMarkdown` [option](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#importing-plain-markdown-files-with-transcludemarkdown-has-changed). Instead, we recommend using the [`Markdown`](https://storybook.js.org/docs/react/api/doc-block-markdown) Doc Block for importing Markdown files into your Storybook documentation.

View File

@ -154,7 +154,7 @@ addParameters({
});
```
With that function, anyone using the docs addon for `@storybook/vue` can make their stories render inline, either globally with the `inlineStories` docs parameter, or on a per-story-basis using the `inline` prop on the `<Story>` doc block. If you come up with an elegant and flexible implementation for the `prepareForInline` function for your own framework, let us know! We'd love to make it the default configuration, to make inline stories more accessible for a larger variety of frameworks!
With that function, anyone using the docs addon for `@storybook/vue` can make their stories render inline, either globally with the `docs.story.inline` parameter, or on a per-story-basis using the `inline` prop on the `<Story>` doc block. If you come up with an elegant and flexible implementation for the `prepareForInline` function for your own framework, let us know! We'd love to make it the default configuration, to make inline stories more accessible for a larger variety of frameworks!
## Show/Hide code

View File

@ -12,7 +12,6 @@ Storybook Docs automatically generates props tables for components in supported
- [Controls](#controls)
- [Customization](#customization)
- [Customizing ArgTypes](#customizing-argtypes)
- [Custom ArgTypes in MDX](#custom-argtypes-in-mdx)
- [Reporting a bug](#reporting-a-bug)
- [Known limitations](#known-limitations)
- [More resources](#more-resources)
@ -60,7 +59,7 @@ Starting in SB 6.0, the `ArgsTable` block has built-in `Controls` (formerly know
<br/>
These controls are implemented appear automatically in the props table when your story accepts [Storybook Args](https://storybook.js.org/docs/react/api/csf#args-story-inputs) as its input. This is done slightly differently depending on whether you're using `DocsPage` or `MDX`.
These controls are implemented to appear automatically in the props table when your story accepts [Storybook Args](https://storybook.js.org/docs/react/api/csf#args-story-inputs) as its input. This is done slightly differently depending on whether you're using `DocsPage` or `MDX`.
**DocsPage.** In [DocsPage](./docspage.md), simply write your story to consume args and the auto-generated props table will display controls in the right-most column:
@ -202,32 +201,6 @@ Here are the possible customizations for the rest of the prop table:
| `table.defaultValue.detail` | A longer version of the default value (if it's a complex value) |
| `control` | See [`addon-controls` README](https://storybook.js.org/docs/react/essentials/controls#configuration) |
### Custom ArgTypes in MDX
To do the equivalent of the above customization [in MDX](./mdx.md), use the following.
Overriding at the component level:
```jsx
<Meta
title="MyComponent"
component={MyComponent}
argTypes={{
label: { name: 'label' /* ... */ },
}}
/>
```
And at the story level:
```jsx
<Story name="some story" argTypes={{
label: { name: 'label', ... }
}}>
{/* story contents */}
</Story>
```
## Reporting a bug
Extracting component properties from source is a tricky problem with thousands of corner cases. We've designed this package and its tests to accurately isolate problems, since the cause could either be in this package or (likely) one of the packages it depends on.

View File

@ -1,22 +0,0 @@
const path = require('path');
const { ScriptTransformer } = require('@jest/transform');
const { dedent } = require('ts-dedent');
const { compile } = require('@storybook/mdx2-csf');
module.exports = {
async processAsync(src, filename, config, { instrument }) {
const code = await compile(src, { skipCsf: false });
const result = dedent`
/* @jsx mdx */
import React from 'react'
import { mdx } from '@mdx-js/react'
${code}
`;
const extension = path.extname(filename);
const jsFileName = `${filename.slice(0, -extension.length)}.js`;
return new ScriptTransformer(config).transformSource(jsFileName, result, instrument);
},
};

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-docs",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Document component usage and properties in Markdown",
"keywords": [
"addon",
@ -65,13 +65,13 @@
"require": "./dist/shims/mdx-react-shim.js",
"import": "./dist/shims/mdx-react-shim.mjs"
},
"./mdx-loader": "./dist/mdx-loader.js",
"./svelte/HOC.svelte": "./svelte/HOC.svelte",
"./ember": "./ember/index.js",
"./ember/index.js": "./ember/index.js",
"./angular": "./angular/index.js",
"./angular/index.js": "./angular/index.js",
"./web-components/index.js": "./web-components/index.js",
"./jest-transform-mdx": "./jest-transform-mdx.js",
"./package.json": "./package.json"
},
"main": "dist/index.js",
@ -98,15 +98,14 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@jest/transform": "^29.3.1",
"@mdx-js/react": "^2.1.5",
"@babel/core": "^7.12.3",
"@mdx-js/react": "^3.0.0",
"@storybook/blocks": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/components": "workspace:*",
"@storybook/csf-plugin": "workspace:*",
"@storybook/csf-tools": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/mdx2-csf": "^1.0.0",
"@storybook/node-logger": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/react-dom-shim": "workspace:*",
@ -115,12 +114,14 @@
"fs-extra": "^11.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"remark-external-links": "^8.0.0",
"remark-slug": "^6.0.0",
"rehype-external-links": "^3.0.0",
"rehype-slug": "^6.0.0",
"ts-dedent": "^2.0.0"
},
"devDependencies": {
"@mdx-js/mdx": "^3.0.0",
"@rollup/pluginutils": "^5.0.2",
"@storybook/test": "workspace:*",
"typescript": "^5.3.2",
"vite": "^4.0.4"
},
@ -133,10 +134,8 @@
"./src/preset.ts",
"./src/preview.ts",
"./src/blocks.ts",
"./src/shims/mdx-react-shim.ts"
],
"externals": [
"@storybook/mdx1-csf"
"./src/shims/mdx-react-shim.ts",
"./src/mdx-loader.ts"
]
},
"gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17",

View File

@ -1,2 +1 @@
// eslint-disable-next-line import/export
export * from './dist/preview';

View File

@ -0,0 +1,669 @@
import { expect, describe, it } from 'vitest';
import { dedent } from 'ts-dedent';
import { compileSync, compile } from './index';
expect.addSnapshotSerializer({
serialize: (val: any) => (typeof val === 'string' ? val : val.toString()),
test: (_val) => true,
});
// Remove unnecessary noise from snapshots
const clean = (code: string) => {
const mdxContentRegex =
/export default function MDXContent\([^)]*\) \{(?:[^{}]*|{(?:[^{}]*|{[^{}]*})*})*\}/gs;
const mdxMissingReferenceRegex =
/function _missingMdxReference\([^)]*\) \{(?:[^{}]*|{(?:[^{}]*|{[^{}]*})*})*\}/gs;
return code.replace(mdxMissingReferenceRegex, '').replace(mdxContentRegex, '');
};
describe('mdx3', () => {
it('supports AdjacentBlockJSX', () => {
const input = dedent`
<style>{\`
h1 {
color: blue;
}
\`}</style>
`;
expect(clean(compileSync(input))).toMatchInlineSnapshot(`
import {jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
function _createMdxContent(props) {
return _jsx("style", {
children: \`
h1 {
color: blue;
}
\`
});
}
`);
});
it('supports Await in MDX', () => {
const input = dedent`
{await Promise.resolve('Hello World')}
`;
expect(clean(compileSync(input))).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
async function _createMdxContent(props) {
return _jsx(_Fragment, {
children: await Promise.resolve('Hello World')
});
}
`);
});
it('supports ES2024', () => {
const input = dedent`
export const obj = {
nested: {
property: 'Hello world!'
}
};
export const value = obj?.nested?.property ?? 'Default Value';
Value: {value}
`;
expect(clean(compileSync(input))).toMatchInlineSnapshot(`
import {jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
export const obj = {
nested: {
property: 'Hello world!'
}
};
export const value = obj?.nested?.property ?? 'Default Value';
function _createMdxContent(props) {
const _components = {
p: "p",
..._provideComponents(),
...props.components
};
return _jsxs(_components.p, {
children: ["Value: ", value]
});
}
`);
});
});
describe('mdx2', () => {
it('works', () => {
const input = dedent`
# hello
<Meta title="foobar" />
world {2 + 1}
`;
expect(clean(compileSync(input))).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
..._provideComponents(),
...props.components
}, {Meta} = _components;
if (!Meta) _missingMdxReference("Meta", true);
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "hello"
}), "\\n", _jsx(Meta, {
title: "foobar"
}), "\\n", _jsxs(_components.p, {
children: ["world ", 2 + 1]
})]
});
}
`);
});
it('standalone jsx expressions', () => {
expect(
clean(
compileSync(dedent`
# Standalone JSX expressions
{3 + 3}
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
function _createMdxContent(props) {
const _components = {
h1: "h1",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "Standalone JSX expressions"
}), "\\n", 3 + 3]
});
}
`);
});
});
describe('full snapshots', () => {
it('compileSync', () => {
const input = dedent`
# hello
<Meta title="foobar" />
world {2 + 1}
`;
expect(compileSync(input)).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
..._provideComponents(),
...props.components
}, {Meta} = _components;
if (!Meta) _missingMdxReference("Meta", true);
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "hello"
}), "\\n", _jsx(Meta, {
title: "foobar"
}), "\\n", _jsxs(_components.p, {
children: ["world ", 2 + 1]
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = {
..._provideComponents(),
...props.components
};
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
function _missingMdxReference(id, component) {
throw new Error("Expected " + (component ? "component" : "object") + " \`" + id + "\` to be defined: you likely forgot to import, pass, or provide it.");
}
`);
});
it('compile', async () => {
const input = dedent`
# hello
<Meta title="foobar" />
world {2 + 1}
`;
expect(await compile(input)).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
..._provideComponents(),
...props.components
}, {Meta} = _components;
if (!Meta) _missingMdxReference("Meta", true);
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "hello"
}), "\\n", _jsx(Meta, {
title: "foobar"
}), "\\n", _jsxs(_components.p, {
children: ["world ", 2 + 1]
})]
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = {
..._provideComponents(),
...props.components
};
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
function _missingMdxReference(id, component) {
throw new Error("Expected " + (component ? "component" : "object") + " \`" + id + "\` to be defined: you likely forgot to import, pass, or provide it.");
}
`);
});
it('sync & async should match', async () => {
const input = dedent`
# hello
<Meta title="foobar" />
world {2 + 1}
`;
const ou1 = compileSync(input);
const ou2 = await compile(input);
expect(ou1).toEqual(ou2);
});
it('canvas without story children', () => {
const input = dedent`
import { Canvas } from '@storybook/addon-docs';
<Canvas>
<h2>Some here</h2>
</Canvas>
`;
expect(compileSync(input)).toMatchInlineSnapshot(`
import {jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Canvas} from '@storybook/addon-docs';
function _createMdxContent(props) {
return _jsx(Canvas, {
children: _jsx("h2", {
children: "Some here"
})
});
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = {
..._provideComponents(),
...props.components
};
return MDXLayout ? _jsx(MDXLayout, {
...props,
children: _jsx(_createMdxContent, {
...props
})
}) : _createMdxContent(props);
}
`);
});
});
describe('docs-mdx-compiler-plugin', () => {
it('csf-imports.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Story, Meta, Canvas } from '@storybook/addon-docs';
import { Welcome, Button } from '@storybook/angular/demo';
import * as MyStories from './My.stories';
import { Other } from './Other.stories';
<Meta title="MDX/CSF imports" />
# Stories from CSF imports
<Story of={MyStories.Basic} />
<Canvas>
<Story of={Other} />
</Canvas>
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Story, Meta, Canvas} from '@storybook/addon-docs';
import {Welcome, Button} from '@storybook/angular/demo';
import * as MyStories from './My.stories';
import {Other} from './Other.stories';
function _createMdxContent(props) {
const _components = {
h1: "h1",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(Meta, {
title: "MDX/CSF imports"
}), "\\n", _jsx(_components.h1, {
children: "Stories from CSF imports"
}), "\\n", _jsx(Story, {
of: MyStories.Basic
}), "\\n", _jsx(Canvas, {
children: _jsx(Story, {
of: Other
})
})]
});
}
`);
});
it('docs-only.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Meta } from '@storybook/addon-docs';
<Meta title="docs-only" />
# Documentation only
This is a documentation-only MDX file which cleans a dummy 'docsOnly: true' story.
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Meta} from '@storybook/addon-docs';
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(Meta, {
title: "docs-only"
}), "\\n", _jsx(_components.h1, {
children: "Documentation only"
}), "\\n", _jsx(_components.p, {
children: "This is a documentation-only MDX file which cleans a dummy 'docsOnly: true' story."
})]
});
}
`);
});
it('meta-quotes-in-title.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Meta } from '@storybook/addon-docs';
<Meta title="Addons/Docs/what's in a title?" />
`)
)
).toMatchInlineSnapshot(`
import {jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Meta} from '@storybook/addon-docs';
function _createMdxContent(props) {
return _jsx(Meta, {
title: "Addons/Docs/what's in a title?"
});
}
`);
});
it('non-story-exports.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs';
<Meta title="Button" />
# Story definition
<Story of={Button} />
export const two = 2;
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Button} from '@storybook/react/demo';
import {Story, Meta} from '@storybook/addon-docs';
export const two = 2;
function _createMdxContent(props) {
const _components = {
h1: "h1",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(Meta, {
title: "Button"
}), "\\n", _jsx(_components.h1, {
children: "Story definition"
}), "\\n", _jsx(Story, {
of: Button
})]
});
}
`);
});
it('story-current.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Story } from '@storybook/addon-docs';
# Current story
<Story id="." />
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Story} from '@storybook/addon-docs';
function _createMdxContent(props) {
const _components = {
h1: "h1",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "Current story"
}), "\\n", _jsx(Story, {
id: "."
})]
});
}
`);
});
it('story-references.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Story } from '@storybook/addon-docs';
# Story reference
<Story id="welcome--welcome" />
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Story} from '@storybook/addon-docs';
function _createMdxContent(props) {
const _components = {
h1: "h1",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(_components.h1, {
children: "Story reference"
}), "\\n", _jsx(Story, {
id: "welcome--welcome"
})]
});
}
`);
});
it('title-template-string.mdx', () => {
expect(
clean(
compileSync(
[
"import { Meta, Story } from '@storybook/addon-docs';",
"import { titleFunction } from '../title-generators';",
'',
"<Meta title={`${titleFunction('template')}`} />",
].join('\n')
)
)
).toMatchInlineSnapshot(`
import {jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Meta, Story} from '@storybook/addon-docs';
import {titleFunction} from '../title-generators';
function _createMdxContent(props) {
return _jsx(Meta, {
title: \`\${titleFunction('template')}\`
});
}
`);
});
describe('csf3', () => {
it('auto-title-docs-only.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Meta } from '@storybook/addon-docs';
<Meta />
# Auto-title Docs Only
Some **markdown** here!
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Meta} from '@storybook/addon-docs';
function _createMdxContent(props) {
const _components = {
h1: "h1",
p: "p",
strong: "strong",
..._provideComponents(),
...props.components
};
return _jsxs(_Fragment, {
children: [_jsx(Meta, {}), "\\n", _jsx(_components.h1, {
children: "Auto-title Docs Only"
}), "\\n", _jsxs(_components.p, {
children: ["Some ", _jsx(_components.strong, {
children: "markdown"
}), " here!"]
})]
});
}
`);
});
it('auto-title.mdx', () => {
expect(
clean(
compileSync(dedent`
import { Button } from '@storybook/react/demo';
import { Story, Meta } from '@storybook/addon-docs';
<Meta component={Button} />
`)
)
).toMatchInlineSnapshot(`
import {jsx as _jsx} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Button} from '@storybook/react/demo';
import {Story, Meta} from '@storybook/addon-docs';
function _createMdxContent(props) {
return _jsx(Meta, {
component: Button
});
}
`);
});
});
it('style tag', () => {
expect(
clean(
compileSync(dedent`
import { Meta } from '@storybook/addon-docs';
<Meta title="Example/Introduction" />
<style>{\`
.subheading {
--mediumdark: '#999999';
font-weight: 900;
font-size: 13px;
color: #999;
letter-spacing: 6px;
line-height: 24px;
text-transform: uppercase;
margin-bottom: 12px;
margin-top: 40px;
}
.link-list {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
row-gap: 10px;
}
\`}</style>
`)
)
).toMatchInlineSnapshot(`
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
import {Meta} from '@storybook/addon-docs';
function _createMdxContent(props) {
return _jsxs(_Fragment, {
children: [_jsx(Meta, {
title: "Example/Introduction"
}), "\\n", _jsx("style", {
children: \`
.subheading {
--mediumdark: '#999999';
font-weight: 900;
font-size: 13px;
color: #999;
letter-spacing: 6px;
line-height: 24px;
text-transform: uppercase;
margin-bottom: 12px;
margin-top: 40px;
}
.link-list {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
row-gap: 10px;
}
\`
})]
});
}
`);
});
});

View File

@ -0,0 +1,29 @@
import { compile as mdxCompile, compileSync as mdxCompileSync } from '@mdx-js/mdx';
import type { CompileOptions, MdxCompileOptions } from './types';
export type { CompileOptions, MdxCompileOptions };
export const compile = async (input: string, { mdxCompileOptions = {} }: CompileOptions = {}) => {
const options = getCompilerOptions(mdxCompileOptions);
const mdxResult = await mdxCompile(input, options);
return mdxResult.toString();
};
export const compileSync = (input: string, { mdxCompileOptions = {} }: CompileOptions = {}) => {
const options = getCompilerOptions(mdxCompileOptions);
const mdxResult = mdxCompileSync(input, options);
return mdxResult.toString();
};
function getCompilerOptions(mdxCompileOptions: MdxCompileOptions): MdxCompileOptions {
return {
providerImportSource: '@mdx-js/react',
rehypePlugins: [],
...mdxCompileOptions,
};
}

View File

@ -0,0 +1,7 @@
import type { compile as mdxCompile } from '@mdx-js/mdx';
export type MdxCompileOptions = Parameters<typeof mdxCompile>[1];
export interface CompileOptions {
mdxCompileOptions?: MdxCompileOptions;
}

View File

@ -0,0 +1,42 @@
import type { transformAsync } from '@babel/core';
import type { compile as mdxCompile } from '@mdx-js/mdx';
import { compile } from './compiler';
export type MdxCompileOptions = Parameters<typeof mdxCompile>[1];
export type BabelOptions = Parameters<typeof transformAsync>[1];
export interface CompileOptions {
mdxCompileOptions?: MdxCompileOptions;
}
const DEFAULT_RENDERER = `
import React from 'react';
`; // Adjust this import based on your actual webpack version and typings
// Kind of like a mock so we don't have to install Webpack just for the types
type LoaderOptions = {
filepath?: string;
[key: string]: any;
} & any;
interface LoaderContext {
async: () => (err: Error | null, result?: string) => void;
getOptions: () => LoaderOptions;
resourcePath: string;
}
async function loader(this: LoaderContext, content: string): Promise<void> {
const callback = this.async();
const options = { ...this.getOptions(), filepath: this.resourcePath };
try {
const result = await compile(content, options);
const code = `${DEFAULT_RENDERER}\n${result}`;
return callback(null, code);
} catch (err: any) {
console.error('Error loading:', this.resourcePath);
return callback(err);
}
}
export default loader;

View File

@ -1,16 +1,15 @@
import type { Options } from '@storybook/types';
import type { Plugin } from 'vite';
import remarkSlug from 'remark-slug';
import remarkExternalLinks from 'remark-external-links';
import rehypeSlug from 'rehype-slug';
import rehypeExternalLinks from 'rehype-external-links';
import { createFilter } from '@rollup/pluginutils';
import { dirname, join } from 'path';
const isStorybookMdx = (id: string) => id.endsWith('.stories.mdx') || id.endsWith('.story.mdx');
import type { CompileOptions } from '../compiler';
import { compile } from '../compiler';
/**
* Storybook uses two different loaders when dealing with MDX:
* Storybook uses a single loader when dealing with MDX:
*
* - *stories.mdx and *story.mdx are compiled with the CSF compiler
* - *.mdx are compiled with the MDX compiler directly
*
* @see https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/recipes.md#csf-stories-with-arbitrary-mdx
@ -18,8 +17,9 @@ const isStorybookMdx = (id: string) => id.endsWith('.stories.mdx') || id.endsWit
export async function mdxPlugin(options: Options): Promise<Plugin> {
const include = /\.mdx$/;
const filter = createFilter(include);
const { features, presets } = options;
const { mdxPluginOptions, jsxOptions } = await presets.apply<Record<string, any>>('options', {});
const { presets } = options;
const presetOptions = await presets.apply<Record<string, any>>('options', {});
const mdxPluginOptions = presetOptions?.mdxPluginOptions as CompileOptions;
return {
name: 'storybook:mdx-plugin',
@ -27,11 +27,7 @@ export async function mdxPlugin(options: Options): Promise<Plugin> {
async transform(src, id) {
if (!filter(id)) return undefined;
const { compile } = features?.legacyMdx1
? await import('@storybook/mdx1-csf')
: await import('@storybook/mdx2-csf');
const mdxLoaderOptions = await options.presets.apply('mdxLoaderOptions', {
const mdxLoaderOptions: CompileOptions = await presets.apply('mdxLoaderOptions', {
...mdxPluginOptions,
mdxCompileOptions: {
providerImportSource: join(
@ -39,23 +35,20 @@ export async function mdxPlugin(options: Options): Promise<Plugin> {
'/dist/shims/mdx-react-shim'
),
...mdxPluginOptions?.mdxCompileOptions,
remarkPlugins: [remarkSlug, remarkExternalLinks].concat(
mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? []
),
rehypePlugins: [
...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []),
rehypeSlug,
rehypeExternalLinks,
],
},
jsxOptions,
});
const code = String(
await compile(src, {
skipCsf: !isStorybookMdx(id),
...mdxLoaderOptions,
})
);
const code = String(await compile(src, mdxLoaderOptions));
return {
code,
map: null, // TODO: update mdx2-csf to return the map
// TODO: support source maps
map: null,
};
},
};

View File

@ -1 +0,0 @@
declare module '@storybook/mdx1-csf';

View File

@ -1,15 +1,11 @@
import fs from 'fs-extra';
import { dirname, join } from 'path';
import remarkSlug from 'remark-slug';
import remarkExternalLinks from 'remark-external-links';
import { dedent } from 'ts-dedent';
import rehypeSlug from 'rehype-slug';
import rehypeExternalLinks from 'rehype-external-links';
import type { DocsOptions, Indexer, Options, PresetProperty } from '@storybook/types';
import type { DocsOptions, Options, PresetProperty } from '@storybook/types';
import type { CsfPluginOptions } from '@storybook/csf-plugin';
import type { JSXOptions, CompileOptions } from '@storybook/mdx2-csf';
import { global } from '@storybook/global';
import { loadCsf } from '@storybook/csf-tools';
import { logger } from '@storybook/node-logger';
import type { CompileOptions } from './compiler';
/**
* Get the resolvedReact preset, which points either to
@ -27,27 +23,14 @@ const getResolvedReact = async (options: Options) => {
// addon-docs, causing addon-docs's dependencies not to be hoisted.
// This might also affect regular users who have a similar setup.
// Explicitly alias @mdx-js/react to avoid this issue.
mdx: resolvedReact.mdx ?? dirname(require.resolve('@mdx-js/react/package.json')),
mdx: resolvedReact.mdx ?? dirname(require.resolve('@mdx-js/react')),
};
};
async function webpack(
webpackConfig: any = {},
options: Options & {
/**
* @deprecated
* Use `jsxOptions` to customize options used by @babel/preset-react
*/
configureJsx: boolean;
/**
* @deprecated
* Use `jsxOptions` to customize options used by @babel/preset-react
*/
mdxBabelOptions?: any;
/** @deprecated */
sourceLoaderOptions: any;
csfPluginOptions: CsfPluginOptions | null;
jsxOptions?: JSXOptions;
mdxPluginOptions?: CompileOptions;
} /* & Parameters<
typeof createCompiler
@ -55,19 +38,9 @@ async function webpack(
) {
const { module = {} } = webpackConfig;
// it will reuse babel options that are already in use in storybook
// also, these babel options are chained with other presets.
const {
csfPluginOptions = {},
jsxOptions = {},
sourceLoaderOptions = null,
configureJsx,
mdxBabelOptions,
mdxPluginOptions = {},
} = options;
const { csfPluginOptions = {}, mdxPluginOptions = {} } = options;
const mdxLoaderOptions: CompileOptions = await options.presets.apply('mdxLoaderOptions', {
skipCsf: true,
...mdxPluginOptions,
mdxCompileOptions: {
providerImportSource: join(
@ -75,39 +48,15 @@ async function webpack(
'/dist/shims/mdx-react-shim'
),
...mdxPluginOptions.mdxCompileOptions,
remarkPlugins: [remarkSlug, remarkExternalLinks].concat(
mdxPluginOptions?.mdxCompileOptions?.remarkPlugins ?? []
),
rehypePlugins: [
...(mdxPluginOptions?.mdxCompileOptions?.rehypePlugins ?? []),
rehypeSlug,
rehypeExternalLinks,
],
},
jsxOptions,
});
if (sourceLoaderOptions) {
throw new Error(dedent`
Addon-docs no longer uses source-loader in 7.0.
To update your configuration, please see migration instructions here:
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropped-source-loader--storiesof-static-snippets
`);
}
if (mdxBabelOptions || configureJsx) {
throw new Error(dedent`
Addon-docs no longer uses configureJsx or mdxBabelOptions in 7.0.
To update your configuration, please see migration instructions here:
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#dropped-addon-docs-manual-babel-configuration
`);
}
const mdxVersion = global.FEATURES?.legacyMdx1 ? 'MDX1' : 'MDX2';
logger.info(`Addon-docs: using ${mdxVersion}`);
const mdxLoader = global.FEATURES?.legacyMdx1
? require.resolve('@storybook/mdx1-csf/loader')
: require.resolve('@storybook/mdx2-csf/loader');
logger.info(`Addon-docs: using MDX3`);
// Use the resolvedReact preset to alias react and react-dom to either the users version or the version shipped with addon-docs
const { react, reactDom, mdx } = await getResolvedReact(options);
@ -155,24 +104,12 @@ async function webpack(
...module,
rules: [
...(module.rules || []),
{
test: /(stories|story)\.mdx$/,
use: [
{
loader: mdxLoader,
options: {
...mdxLoaderOptions,
skipCsf: false,
},
},
],
},
{
test: /\.mdx$/,
exclude: /(stories|story)\.mdx$/,
use: [
{
loader: mdxLoader,
loader: require.resolve('./mdx-loader'),
options: mdxLoaderOptions,
},
],
@ -184,36 +121,6 @@ async function webpack(
return result;
}
export const createStoriesMdxIndexer = (legacyMdx1?: boolean): Indexer => ({
test: /(stories|story)\.mdx$/,
createIndex: async (fileName, opts) => {
let code = (await fs.readFile(fileName, 'utf-8')).toString();
const { compile } = legacyMdx1
? await import('@storybook/mdx1-csf')
: await import('@storybook/mdx2-csf');
code = await compile(code, {});
const csf = loadCsf(code, { ...opts, fileName }).parse();
const { indexInputs, stories } = csf;
return indexInputs.map((input, index) => {
const docsOnly = stories[index].parameters?.docsOnly;
const tags = input.tags ? input.tags : [];
if (docsOnly) {
tags.push('stories-mdx-docsOnly');
}
// the mdx-csf compiler automatically adds the 'stories-mdx' tag to meta, here' we're just making sure it is always there
if (!tags.includes('stories-mdx')) {
tags.push('stories-mdx');
}
return { ...input, tags };
});
},
});
const indexers: PresetProperty<'experimental_indexers'> = (existingIndexers) =>
[createStoriesMdxIndexer(global.FEATURES?.legacyMdx1)].concat(existingIndexers || []);
const docs = (docsOptions: DocsOptions) => {
return {
...docsOptions,
@ -259,7 +166,6 @@ export const viteFinal = async (config: any, options: Options) => {
* something down the dependency chain is using typescript namespaces, which are not supported by rollup-plugin-dts
*/
const webpackX = webpack as any;
const indexersX = indexers as any;
const docsX = docs as any;
/**
@ -272,7 +178,7 @@ const docsX = docs as any;
export const resolvedReact = async (existing: any) => ({
react: existing?.react ?? dirname(require.resolve('react/package.json')),
reactDom: existing?.reactDom ?? dirname(require.resolve('react-dom/package.json')),
mdx: existing?.mdx ?? dirname(require.resolve('@mdx-js/react/package.json')),
mdx: existing?.mdx ?? dirname(require.resolve('@mdx-js/react')),
});
const optimizeViteDeps = [
@ -283,4 +189,4 @@ const optimizeViteDeps = [
'markdown-to-jsx',
];
export { webpackX as webpack, indexersX as experimental_indexers, docsX as docs, optimizeViteDeps };
export { webpackX as webpack, docsX as docs, optimizeViteDeps };

View File

@ -1,8 +1,30 @@
import type { PreparedStory } from '@storybook/types';
import { global } from '@storybook/global';
const excludeTags = Object.entries(global.TAGS_OPTIONS ?? {}).reduce(
(acc, entry) => {
const [tag, option] = entry;
if ((option as any).excludeFromDocsStories) {
acc[tag] = true;
}
return acc;
},
{} as Record<string, boolean>
);
export const parameters: any = {
docs: {
renderer: async () => {
const { DocsRenderer } = (await import('./DocsRenderer')) as any;
return new DocsRenderer();
},
stories: {
filter: (story: PreparedStory) => {
const tags = story.tags || [];
return (
tags.filter((tag) => excludeTags[tag]).length === 0 && !story.parameters.docs?.disable
);
},
},
},
};

View File

@ -1,9 +1,6 @@
declare module '@egoist/vue-to-react';
declare module 'remark-slug';
declare module 'remark-external-links';
declare module 'acorn-jsx';
declare module 'vue/dist/vue';
declare module '@storybook/mdx1-csf';
declare module 'sveltedoc-parser' {
export function parse(options: any): Promise<any>;
@ -12,3 +9,5 @@ declare module 'sveltedoc-parser' {
declare var FEATURES: import('@storybook/types').StorybookConfigRaw['features'];
declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined;
declare var TAGS_OPTIONS: import('@storybook/types').TagsOptions;

View File

@ -1,5 +1,5 @@
import { global as globalThis } from '@storybook/global';
import { expect } from '@storybook/jest';
import { expect } from '@storybook/test';
import { within } from '@storybook/testing-library';
export default {

View File

@ -1,9 +1,10 @@
import { global as globalThis } from '@storybook/global';
import { fn } from '@storybook/test';
export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
args: { label: 'Click Me!' },
args: { label: 'Click Me!', onClick: fn() },
parameters: { chromatic: { disable: true } },
};

View File

@ -2,7 +2,6 @@ import { global as globalThis } from '@storybook/global';
export default {
component: globalThis.Components.Button,
// FIXME: remove array subcomponents in 7.0?
subcomponents: {
Pre: globalThis.Components.Pre,
},

View File

@ -1,22 +0,0 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { global as globalThis } from '@storybook/global';
<Meta component={globalThis.Components.Button} />
# MDX Stories
This file demonstrates defining stories inside MDX.
<Canvas>
<Story name="Primary" args={{ label: 'Primary' }} />
</Canvas>
<ArgsTable story="^" />
<Canvas>
<Story name="Secondary" args={{ label: 'Secondary' }} />
</Canvas>
<Canvas>
<div>Non-story content</div>
</Canvas>

View File

@ -1,19 +0,0 @@
// NOTE: commented out default since these stories are re-exported
// in the primary file './csf-docs-with-mdx-docs.stories.mdx'
//
// export default {
// title: 'Addons/Docs/csf-with-mdx-docs',
// component: Button,
// };
export const Primary = {
args: { label: 'Primary' },
};
export const Secondary = {
args: { label: 'Secondary' },
};
export const ImplicitName = {
args: { label: 'Implicit Name' },
};

View File

@ -1,29 +0,0 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { global as globalThis } from '@storybook/global';
import * as Csf from './csf-in-mdx.non-stories.js';
<Meta component={globalThis.Components.Button} />
# Legacy CSF in MDX Stories
This file demonstrates legacy reuse of CSF stories inside MDX. This mechanism has been
overhauled and improved in 7.0, and the legacy mode is now deprecated and will be
removed in 8.0.
<Canvas>
<Story story={Csf.Primary} />
</Canvas>
<ArgsTable story="^" />
<Canvas>
<Story story={Csf.Secondary} />
</Canvas>
## Duplicate stories
Reference story by ID to show it multiple times in a page.
<Canvas>
<Story id="addons-docs-stories-mdx-csf-in-mdx--primary" />
</Canvas>

View File

@ -1,18 +0,0 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { global as globalThis } from '@storybook/global';
<Meta component={globalThis.Components.Button} />
# MDX Stories
This file demonstrates rendering iframe stories in MDX.
<Canvas>
<Story name="Primary" args={{ label: 'Primary' }} inline={false} />
</Canvas>
<ArgsTable story="^" />
<Canvas>
<Story name="Secondary" args={{ label: 'Secondary' }} inline={false} />
</Canvas>

View File

@ -1,16 +0,0 @@
import { Meta, Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { global as globalThis } from '@storybook/global';
<Meta component={globalThis.Components.Button} play={() => console.log('component play')} />
# MDX Play function Stories
This file demonstrates defining stories inside MDX.
<Canvas>
<Story name="Component Play" args={{ label: 'Component Play' }} />
</Canvas>
<Canvas>
<Story name="Story Play" args={{ label: 'Story Play' }} play={() => console.log('story play')} />
</Canvas>

View File

@ -1,5 +0,0 @@
import { Meta } from '@storybook/addon-docs';
<Meta title="stories-mdx/unattached" />
# Unattached `stories.mdx` file

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-essentials",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Curated addons to bring out the best of Storybook",
"keywords": [
"addon",
@ -43,6 +43,7 @@
"./outline/manager": "./dist/outline/manager.js",
"./toolbars/manager": "./dist/toolbars/manager.js",
"./viewport/manager": "./dist/viewport/manager.js",
"./viewport/preview": "./dist/viewport/preview.js",
"./package.json": "./package.json"
},
"main": "dist/index.js",
@ -102,7 +103,8 @@
"./src/docs/preview.ts",
"./src/highlight/preview.ts",
"./src/measure/preview.ts",
"./src/outline/preview.ts"
"./src/outline/preview.ts",
"./src/viewport/preview.ts"
]
},
"gitHead": "e6a7fd8a655c69780bc20b9749c2699e44beae17"

View File

@ -1,10 +1,8 @@
import { dirname, join } from 'path';
// eslint-disable-next-line import/export
export * from '@storybook/addon-docs/dist/preset';
export const mdxLoaderOptions = async (config: any) => {
// eslint-disable-next-line no-param-reassign
config.mdxCompileOptions.providerImportSource = join(
dirname(require.resolve('@storybook/addon-docs/package.json')),
'/dist/shims/mdx-react-shim'

View File

@ -1,2 +1 @@
// eslint-disable-next-line import/export
export * from '@storybook/addon-docs/dist/preview';

View File

@ -0,0 +1,2 @@
// @ts-expect-error (no types needed for this)
export * from '@storybook/addon-viewport/preview';

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-mdx-gfm",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "GitHub Flavored Markdown in Storybook",
"keywords": [
"addon",
@ -45,7 +45,7 @@
},
"dependencies": {
"@storybook/node-logger": "workspace:*",
"remark-gfm": "^3.0.1",
"remark-gfm": "^4.0.0",
"ts-dedent": "^2.0.0"
},
"devDependencies": {

View File

@ -1,4 +1,3 @@
/* eslint-disable no-param-reassign */
import { dedent } from 'ts-dedent';
import { deprecate } from '@storybook/node-logger';
@ -11,7 +10,7 @@ export const mdxLoaderOptions = async (config: any) => {
};
deprecate(dedent`
The "@storybook/addon-mdx-gfm" addon is meant as a migration assistant for Storybook 7.0; and will likely be removed in a future version.
The "@storybook/addon-mdx-gfm" addon is meant as a migration assistant for Storybook 8.0; and will likely be removed in a future version.
It's recommended you read this document:
https://storybook.js.org/docs/react/writing-docs/mdx#lack-of-github-flavored-markdown-gfm

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-highlight",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Highlight DOM nodes within your stories",
"keywords": [
"storybook-addons",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-interactions",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Automate, test and debug user interactions",
"keywords": [
"storybook-addons",
@ -49,7 +49,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"@storybook/types": "workspace:*",
"jest-mock": "^27.0.6",
"polished": "^4.2.2",
@ -62,7 +62,6 @@
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/instrumenter": "workspace:*",
"@storybook/jest": "next",
"@storybook/manager-api": "workspace:*",
"@storybook/preview-api": "workspace:*",
"@storybook/testing-library": "next",

View File

@ -4,10 +4,10 @@ import React, { Fragment, memo, useEffect, useMemo, useRef, useState } from 'rea
import { useAddonState, useChannel, useParameter } from '@storybook/manager-api';
import {
FORCE_REMOUNT,
IGNORED_EXCEPTION,
STORY_RENDER_PHASE_CHANGED,
STORY_THREW_EXCEPTION,
PLAY_FUNCTION_THREW_EXCEPTION,
UNHANDLED_ERRORS_WHILE_PLAYING,
} from '@storybook/core-events';
import { EVENTS, type Call, CallStates, type LogItem } from '@storybook/instrumenter';
@ -91,6 +91,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
hasException: false,
caughtException: undefined,
interactionsCount: 0,
unhandledErrors: undefined,
});
// local state
@ -104,6 +105,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
interactions = [],
isPlaying = false,
caughtException = undefined,
unhandledErrors = undefined,
} = addonState;
// Log and calls are tracked in a ref so we don't needlessly rerender.
@ -157,6 +159,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
hasException: false,
caughtException: undefined,
interactionsCount: 0,
unhandledErrors: undefined,
});
return;
}
@ -180,11 +183,10 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
set((s) => ({ ...s, isErrored: true }));
},
[PLAY_FUNCTION_THREW_EXCEPTION]: (e) => {
if (e?.message !== IGNORED_EXCEPTION.message) {
set((s) => ({ ...s, caughtException: e }));
} else {
set((s) => ({ ...s, caughtException: undefined }));
}
set((s) => ({ ...s, caughtException: e }));
},
[UNHANDLED_ERRORS_WHILE_PLAYING]: (e) => {
set((s) => ({ ...s, unhandledErrors: e }));
},
},
[collapsed]
@ -224,7 +226,10 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
const [fileName] = storyFilePath.toString().split('/').slice(-1);
const scrollToTarget = () => scrollTarget?.scrollIntoView({ behavior: 'smooth', block: 'end' });
const hasException = !!caughtException || interactions.some((v) => v.status === CallStates.ERROR);
const hasException =
!!caughtException ||
!!unhandledErrors ||
interactions.some((v) => v.status === CallStates.ERROR);
if (isErrored) {
return <Fragment key="interactions" />;
@ -240,6 +245,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
fileName={fileName}
hasException={hasException}
caughtException={caughtException}
unhandledErrors={unhandledErrors}
isPlaying={isPlaying}
pausedAt={pausedAt}
endRef={endRef}

View File

@ -0,0 +1,99 @@
import React, { useEffect, useState } from 'react';
import { Link } from '@storybook/components';
import { DocumentIcon, VideoIcon } from '@storybook/icons';
import { Consumer, useStorybookApi } from '@storybook/manager-api';
import { styled } from '@storybook/theming';
import { DOCUMENTATION_LINK, TUTORIAL_VIDEO_LINK } from '../constants';
const Wrapper = styled.div(({ theme }) => ({
height: '100%',
display: 'flex',
padding: 0,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
gap: 15,
background: theme.background.content,
}));
const Content = styled.div({
display: 'flex',
flexDirection: 'column',
gap: 4,
maxWidth: 415,
});
const Title = styled.div(({ theme }) => ({
fontWeight: theme.typography.weight.bold,
fontSize: theme.typography.size.s2 - 1,
textAlign: 'center',
color: theme.textColor,
}));
const Description = styled.div(({ theme }) => ({
fontWeight: theme.typography.weight.regular,
fontSize: theme.typography.size.s2 - 1,
textAlign: 'center',
color: theme.textMutedColor,
}));
const Links = styled.div(({ theme }) => ({
display: 'flex',
fontSize: theme.typography.size.s2 - 1,
gap: 25,
}));
const Divider = styled.div(({ theme }) => ({
width: 1,
height: 16,
backgroundColor: theme.appBorderColor,
}));
export const Empty = () => {
const [isLoading, setIsLoading] = useState(true);
const api = useStorybookApi();
const docsUrl = api.getDocsUrl({
subpath: DOCUMENTATION_LINK,
versioned: true,
renderer: true,
});
// We are adding a small delay to avoid flickering when the story is loading.
// It takes a bit of time for the controls to appear, so we don't want
// to show the empty state for a split second.
useEffect(() => {
const load = setTimeout(() => {
setIsLoading(false);
}, 100);
return () => clearTimeout(load);
}, []);
if (isLoading) return null;
return (
<Wrapper>
<Content>
<Title>Interaction testing</Title>
<Description>
Interaction tests allow you to verify the functional aspects of UIs. Write a play function
for your story and you&apos;ll see it run here.
</Description>
</Content>
<Links>
<Link href={TUTORIAL_VIDEO_LINK} target="_blank" withArrow>
<VideoIcon /> Watch 8m video
</Link>
<Divider />
<Consumer>
{({ state }) => (
<Link href={docsUrl} target="_blank" withArrow>
<DocumentIcon /> Read docs
</Link>
)}
</Consumer>
</Links>
</Wrapper>
);
};

View File

@ -1,5 +1,5 @@
import type { ComponentStoryObj, ComponentMeta } from '@storybook/react';
import { expect } from '@storybook/jest';
import type { StoryObj, Meta } from '@storybook/react';
import { expect } from '@storybook/test';
import { CallStates } from '@storybook/instrumenter';
import { userEvent, within } from '@storybook/testing-library';
import { getCalls } from '../mocks';
@ -7,7 +7,7 @@ import { getCalls } from '../mocks';
import { Interaction } from './Interaction';
import SubnavStories from './Subnav.stories';
type Story = ComponentStoryObj<typeof Interaction>;
type Story = StoryObj<typeof Interaction>;
export default {
title: 'Addons/Interactions/Interaction',
@ -17,7 +17,7 @@ export default {
controls: SubnavStories.args.controls,
controlStates: SubnavStories.args.controlStates,
},
} as ComponentMeta<typeof Interaction>;
} as Meta<typeof Interaction>;
export const Active: Story = {
args: {

View File

@ -10,6 +10,7 @@ import { MethodCall } from './MethodCall';
import { StatusIcon } from './StatusIcon';
import type { Controls } from './InteractionsPanel';
import { isJestError } from '../utils';
const MethodCallWrapper = styled.div(() => ({
fontFamily: typography.fonts.mono,
@ -112,8 +113,8 @@ const RowMessage = styled('div')(({ theme }) => ({
},
}));
const Exception = ({ exception }: { exception: Call['exception'] }) => {
if (exception.message.startsWith('expect(')) {
export const Exception = ({ exception }: { exception: Call['exception'] }) => {
if (isJestError(exception)) {
return <MatcherResult {...exception} />;
}
const paragraphs = exception.message.split('\n\n');
@ -121,7 +122,6 @@ const Exception = ({ exception }: { exception: Call['exception'] }) => {
return (
<RowMessage>
<pre>{paragraphs[0]}</pre>
{exception.showDiff && exception.diff ? (
<>
<br />

View File

@ -3,7 +3,7 @@ import type { StoryObj, Meta } from '@storybook/react';
import { CallStates } from '@storybook/instrumenter';
import { styled } from '@storybook/theming';
import { userEvent, within, waitFor } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { expect } from '@storybook/test';
import isChromatic from 'chromatic/isChromatic';
import { getCalls, getInteractions } from '../mocks';

View File

@ -1,5 +1,4 @@
import * as React from 'react';
import { Link, Placeholder } from '@storybook/components';
import { type Call, CallStates, type ControlStates } from '@storybook/instrumenter';
import { styled } from '@storybook/theming';
import { transparentize } from 'polished';
@ -7,6 +6,8 @@ import { transparentize } from 'polished';
import { Subnav } from './Subnav';
import { Interaction } from './Interaction';
import { isTestAssertionError } from '../utils';
import { Empty } from './EmptyState';
export interface Controls {
start: (args: any) => void;
@ -30,6 +31,7 @@ interface InteractionsPanelProps {
fileName?: string;
hasException?: boolean;
caughtException?: Error;
unhandledErrors?: SerializedError[];
isPlaying?: boolean;
pausedAt?: Call['id'];
calls: Map<string, any>;
@ -37,16 +39,15 @@ interface InteractionsPanelProps {
onScrollToEnd?: () => void;
}
const Container = styled.div<{ withException: boolean }>(({ theme, withException }) => ({
minHeight: '100%',
const Container = styled.div(({ theme }) => ({
height: '100%',
background: theme.background.content,
...(withException && {
backgroundColor:
theme.base === 'dark' ? transparentize(0.93, theme.color.negative) : theme.background.warning,
}),
}));
const CaughtException = styled.div(({ theme }) => ({
borderBottom: `1px solid ${theme.appBorderColor}`,
backgroundColor:
theme.base === 'dark' ? transparentize(0.93, theme.color.negative) : theme.background.warning,
padding: 15,
fontSize: theme.typography.size.s2 - 1,
lineHeight: '19px',
@ -69,9 +70,13 @@ const CaughtExceptionDescription = styled.p({
margin: 0,
padding: '0 0 20px',
});
const CaughtExceptionStack = styled.pre(({ theme }) => ({
margin: 0,
padding: 0,
'&:not(:last-child)': {
paddingBottom: 16,
},
fontSize: theme.typography.size.s1 - 1,
}));
@ -84,19 +89,19 @@ export const InteractionsPanel: React.FC<InteractionsPanelProps> = React.memo(
fileName,
hasException,
caughtException,
unhandledErrors,
isPlaying,
pausedAt,
onScrollToEnd,
endRef,
}) {
return (
<Container withException={!!caughtException}>
<Container>
{(interactions.length > 0 || hasException) && (
<Subnav
controls={controls}
controlStates={controlStates}
status={
// eslint-disable-next-line no-nested-ternary
isPlaying ? CallStates.ACTIVE : hasException ? CallStates.ERROR : CallStates.DONE
}
storyFileName={fileName}
@ -119,34 +124,47 @@ export const InteractionsPanel: React.FC<InteractionsPanelProps> = React.memo(
/>
))}
</div>
{caughtException && !caughtException.message?.startsWith('ignoredException') && (
{caughtException && !isTestAssertionError(caughtException) && (
<CaughtException>
<CaughtExceptionTitle>
Caught exception in <CaughtExceptionCode>play</CaughtExceptionCode> function
</CaughtExceptionTitle>
<CaughtExceptionDescription>
This story threw an error after it finished rendering which means your interactions
couldn&apos; t be run.Go to this story&apos; s play function in {fileName} to fix.
</CaughtExceptionDescription>
<CaughtExceptionStack data-chromatic="ignore">
{caughtException.stack || `${caughtException.name}: ${caughtException.message}`}
{printSerializedError(caughtException)}
</CaughtExceptionStack>
</CaughtException>
)}
<div ref={endRef} />
{!isPlaying && !caughtException && interactions.length === 0 && (
<Placeholder>
No interactions found
<Link
href="https://storybook.js.org/docs/react/writing-stories/play-function"
target="_blank"
withArrow
>
Learn how to add interactions to your story
</Link>
</Placeholder>
{unhandledErrors && (
<CaughtException>
<CaughtExceptionTitle>Unhandled Errors</CaughtExceptionTitle>
<CaughtExceptionDescription>
Found {unhandledErrors.length} unhandled error{unhandledErrors.length > 1 ? 's' : ''}{' '}
while running the play function. This might cause false positive assertions. Resolve
unhandled errors or ignore unhandled errors with setting the
<CaughtExceptionCode>test.dangerouslyIgnoreUnhandledErrors</CaughtExceptionCode>{' '}
parameter to <CaughtExceptionCode>true</CaughtExceptionCode>.
</CaughtExceptionDescription>
{unhandledErrors.map((error, i) => (
<CaughtExceptionStack key={i} data-chromatic="ignore">
{printSerializedError(error)}
</CaughtExceptionStack>
))}
</CaughtException>
)}
<div ref={endRef} />
{!isPlaying && !caughtException && interactions.length === 0 && <Empty />}
</Container>
);
}
);
interface SerializedError {
name: string;
stack?: string;
message: string;
}
function printSerializedError(error: SerializedError) {
return error.stack || `${error.name}: ${error.message}`;
}

View File

@ -1,4 +1,3 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { styled, typography } from '@storybook/theming';
import { Node } from './MethodCall';

View File

@ -34,7 +34,7 @@ export const Args = () => (
<Node value="Hello world" />
<Node value="https://github.com/storybookjs/storybook/blob/next/README.md" />
<Node value="012345678901234567890123456789012345678901234567890123456789" />
{/* eslint-disable-next-line react/jsx-boolean-value */}
{}
<Node value={true} />
<Node value={false} />
<Node value={12345} />

View File

@ -1,4 +1,3 @@
/* eslint-disable react/no-array-index-key */
import { ObjectInspector } from '@devtools-ds/object-inspector';
import type { Call, CallRef, ElementRef } from '@storybook/instrumenter';
import { useTheme } from '@storybook/theming';

View File

@ -1,2 +1,5 @@
export const ADDON_ID = 'storybook/interactions';
export const PANEL_ID = `${ADDON_ID}/panel`;
export const TUTORIAL_VIDEO_LINK = 'https://youtu.be/Waht9qq7AoA';
export const DOCUMENTATION_LINK = 'writing-tests/interaction-testing';

View File

@ -1,61 +1,22 @@
/* eslint-disable no-param-reassign,no-underscore-dangle */
/// <reference types="node" />
import { addons } from '@storybook/preview-api';
import { global } from '@storybook/global';
import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events';
/* eslint-disable no-underscore-dangle */
import type {
Renderer,
ArgsEnhancer,
Args,
LoaderFunction,
PlayFunction,
PlayFunctionContext,
StepLabel,
Args,
} from '@storybook/types';
import { instrument } from '@storybook/instrumenter';
import { ModuleMocker } from 'jest-mock';
const JestMock = new ModuleMocker(global);
const fn = JestMock.fn.bind(JestMock);
export const { step: runStep } = instrument(
{
step: (label: StepLabel, play: PlayFunction, context: PlayFunctionContext<any>) =>
play(context),
},
{ intercept: true }
);
// Aliasing `fn` to `action` here, so we get a more descriptive label in the UI.
const { action } = instrument({ action: fn }, { retain: true });
const channel = addons.getChannel();
const spies: any[] = [];
channel.on(FORCE_REMOUNT, () => spies.forEach((mock) => mock?.mockClear?.()));
channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => {
if (newPhase === 'loading') spies.forEach((mock) => mock?.mockClear?.());
});
const addSpies = (id: string, val: any, key?: string): any => {
try {
if (Object.prototype.toString.call(val) === '[object Object]') {
// We have to mutate the original object for this to survive HMR.
// eslint-disable-next-line no-restricted-syntax
for (const [k, v] of Object.entries(val)) val[k] = addSpies(id, v, k);
return val;
}
if (Array.isArray(val)) {
return val.map((item, index) => addSpies(id, item, `${key}[${index}]`));
}
if (typeof val === 'function' && val.isAction && !val._isMockFunction) {
Object.defineProperty(val, 'name', { value: key, writable: false });
Object.defineProperty(val, '__storyId__', { value: id, writable: false });
const spy = action(val);
spies.push(spy);
return spy;
}
} catch (e) {
// ignore
}
return val;
};
const addActionsFromArgTypes: ArgsEnhancer<Renderer> = ({ id, initialArgs }) =>
addSpies(id, initialArgs);
const instrumentSpies: ArgsEnhancer = ({ initialArgs }) => {
const instrumentSpies: LoaderFunction = ({ initialArgs }) => {
const argTypesWithAction = Object.entries(initialArgs).filter(
([, value]) =>
typeof value === 'function' &&
@ -68,20 +29,13 @@ const instrumentSpies: ArgsEnhancer = ({ initialArgs }) => {
const instrumented = instrument({ [key]: () => value }, { retain: true })[key];
acc[key] = instrumented();
// this enhancer is being called multiple times
value._instrumented = true;
return acc;
}, {} as Args);
};
export const argsEnhancers = [addActionsFromArgTypes, instrumentSpies];
export const { step: runStep } = instrument(
{
step: (label: StepLabel, play: PlayFunction, context: PlayFunctionContext<any>) =>
play(context),
},
{ intercept: true }
);
export const argsEnhancers = [instrumentSpies];
export const parameters = {
throwPlayFunctionExceptions: false,

View File

@ -0,0 +1,23 @@
export function isTestAssertionError(error: unknown) {
return isChaiError(error) || isJestError(error);
}
export function isChaiError(error: unknown) {
return (
error &&
typeof error === 'object' &&
'name' in error &&
typeof error.name === 'string' &&
error.name === 'AssertionError'
);
}
export function isJestError(error: unknown) {
return (
error &&
typeof error === 'object' &&
'message' in error &&
typeof error.message === 'string' &&
error.message.startsWith('expect(')
);
}

View File

@ -1,17 +1,18 @@
import { global as globalThis } from '@storybook/global';
import {
within,
waitFor,
expect,
fn,
fireEvent,
userEvent,
waitFor,
waitForElementToBeRemoved,
} from '@storybook/testing-library';
import { expect } from '@storybook/jest';
within,
} from '@storybook/test';
export default {
component: globalThis.Components.Form,
argTypes: {
onSuccess: { type: 'function' },
args: {
onSuccess: fn(),
},
};
@ -101,15 +102,14 @@ export const UserEventSetup = {
const { args, canvasElement, step } = context;
const user = userEvent.setup();
const canvas = within(canvasElement);
await step('Select, type and paste on input using user-event v14 setup', async () => {
const input = await canvas.getByRole('textbox');
await step('Select and type on input using user-event v14 setup', async () => {
const input = canvas.getByRole('textbox');
await user.click(input);
await user.type(input, 'Pasting: ');
await user.paste('foobar');
await user.type(input, 'Typing ...');
});
await step('Tab and press enter on submit button', async () => {
await user.pointer([
{ keys: '[TouchA>]', target: await canvas.getByRole('textbox') },
{ keys: '[TouchA>]', target: canvas.getByRole('textbox') },
{ keys: '[/TouchA]' },
]);
await user.tab();

View File

@ -0,0 +1,23 @@
import { global as globalThis } from '@storybook/global';
import { userEvent, within } from '@storybook/test';
export default {
component: globalThis.Components.Button,
args: {
label: 'Button',
},
argTypes: {
onClick: { type: 'function' },
},
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
},
};
export const Default = {
play: async (context) => {
const { args, canvasElement } = context;
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button'));
},
};

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",

View File

@ -79,7 +79,6 @@ export function Result(props: ResultProps) {
{isOpen ? (
<Fragment>
{failureMessages.map((msg: string, i: number) => (
// eslint-disable-next-line react/no-array-index-key
<Message msg={msg} key={i} />
))}
</Fragment>

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-links",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Link stories together to build demos and prototypes with your UI components",
"keywords": [
"addon",

View File

@ -1,20 +1 @@
import { dedent } from 'ts-dedent';
let hasWarned = false;
/**
* @deprecated please import this specific function from @storybook/addon-links/react
*/
export function LinkTo(): null {
if (!hasWarned) {
// eslint-disable-next-line no-console
console.error(dedent`
LinkTo has moved to addon-links/react:
import LinkTo from '@storybook/addon-links/react';
`);
hasWarned = true;
}
return null;
}
export { linkTo, hrefTo, withLinks, navigate } from './utils';

View File

@ -17,10 +17,6 @@ vi.mock('@storybook/global', () => ({
search: 'search',
},
},
window: global,
__STORYBOOK_STORY_STORE__: {
fromId: vi.fn(() => ({})),
},
},
}));

View File

@ -23,7 +23,6 @@ function parseQuery(queryString: string) {
.split('&')
.filter(Boolean);
// eslint-disable-next-line no-plusplus
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split('=');
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-measure",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Inspect layouts by visualizing the box model",
"keywords": [
"storybook-addons",
@ -65,7 +65,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"tiny-invariant": "^1.3.1"
},
"devDependencies": {

View File

@ -1,4 +1,3 @@
/* eslint-disable no-param-reassign */
import { global } from '@storybook/global';
import invariant from 'tiny-invariant';

View File

@ -1,5 +1,3 @@
/* eslint-disable operator-assignment */
/* eslint-disable no-param-reassign */
type LabelType = 'margin' | 'padding' | 'border' | 'content';
type LabelPosition = 'top' | 'right' | 'bottom' | 'left' | 'center';
type Direction = 'top' | 'right' | 'bottom' | 'left';

View File

@ -1,4 +1,3 @@
/* eslint-disable operator-assignment */
/**
* Based on https://gist.github.com/awestbro/e668c12662ad354f02a413205b65fce7
*/

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-outline",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "Outline all elements with CSS to help with layout placement and alignment",
"keywords": [
"storybook-addons",
@ -55,7 +55,7 @@
},
"dependencies": {
"@storybook/global": "^5.0.0",
"@storybook/icons": "^1.2.1",
"@storybook/icons": "^1.2.3",
"ts-dedent": "^2.0.0"
},
"devDependencies": {

View File

@ -20,8 +20,8 @@ export const OutlineSelector = memo(function OutlineSelector() {
useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Outline [O]',
defaultShortcut: ['O'],
label: 'Toggle Outline',
defaultShortcut: ['alt', 'O'],
actionName: 'outline',
showInMenu: false,
action: toggleOutline,

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storysource",
"version": "8.0.0-alpha.6",
"version": "8.0.0-alpha.13",
"description": "View a storys source code to see how it works and paste into your app",
"keywords": [
"addon",

View File

@ -1 +1 @@
import './dist/preset';
require('./dist/preset');

View File

@ -4,7 +4,7 @@
<!-- **NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import Bootstrap"](#🥾-import-bootstrap). -->
To get started, **install the package** as a dev dependency
To get started, **install the package** as a dev dependency.
yarn:
@ -29,14 +29,11 @@ pnpm add -D @storybook/addon-themes
Now, **include the addon** in your `.storybook/main.js` file.
```diff
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
"@storybook/addon-essentials",
+ "@storybook/addon-themes"
'@storybook/addon-essentials',
+ '@storybook/addon-themes',
],
};
```
@ -46,10 +43,10 @@ module.exports = {
To give your stories access to Bootstrap's styles and JavaScript, import them into your `.storybook/preview.js` file.
```diff
import { Preview } from "@storybook/your-renderer";
import { Preview } from '@storybook/your-renderer';
+import "bootstrap/dist/css/bootstrap.min.css";
+import "bootstrap/dist/js/bootstrap.bundle";
+import 'bootstrap/dist/css/bootstrap.min.css';
+import 'bootstrap/dist/js/bootstrap.bundle';
const preview: Preview = {
parameters: { /* ... */ },
@ -65,23 +62,23 @@ Bootstrap now supports light and dark color modes out of the box as well as the
To enable switching between these modes in a click for your stories, use our `withThemeByDataAttribute` decorator by adding the following code to your `.storybook/preview.js` file.
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeByDataAttribute } from "@storybook/addon-themes";
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeByDataAttribute } from '@storybook/addon-themes';
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.bundle";
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle';
const preview: Preview = {
parameters: { /* ... */ },
+ decorators: [
+ withThemeByDataAttribute<Renderer>({
+ themes: {
+ light: "light",
+ dark: "dark",
+ light: 'light',
+ dark: 'dark',
+ },
+ defaultTheme: "light",
+ attributeName: "data-bs-theme",
+ defaultTheme: 'light',
+ attributeName: 'data-bs-theme',
+ }),
+ ]
};

View File

@ -4,7 +4,7 @@
<!-- **NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Provide your themes"](#🎨-provide-your-themes). -->
To get started, **install the package** as a dev dependency
To get started, **install the package** as a dev dependency.
yarn:
@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes
## 🧩 Register Addon
Now, **include the addon** in your `.storybook/main.js` file
Now, **include the addon** in your `.storybook/main.js` file.
```diff
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
"@storybook/addon-essentials",
+ "@storybook/addon-themes"
'@storybook/addon-essentials',
+ '@storybook/addon-themes',
],
};
```
@ -45,14 +42,14 @@ module.exports = {
Finally, provide your theme(s) and global styles component to your stories with our `withThemeFromJSXProvider` decorator.
Make the following changes to your `.storybook/preview.js`
Make the following changes to your `.storybook/preview.js`:
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeFromJSXProvider } from "@storybook/addon-themes";
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeFromJSXProvider } from '@storybook/addon-themes';
+import { ThemeProvider } from '@emotion/react';
+import { GlobalStyles, lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs
+import { GlobalStyles, lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs
const preview: Preview = {
@ -63,7 +60,7 @@ const preview: Preview = {
+ light: lightTheme,
+ dark: darkTheme,
+ },
+ defaultTheme: "light",
+ defaultTheme: 'light',
+ Provider: ThemeProvider,
+ GlobalStyles: GlobalStyles,
+ }),

View File

@ -4,7 +4,7 @@
<!-- **NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import fonts"](#🔤-import-fonts). -->
To get started, **install the package** as a dev dependency
To get started, **install the package** as a dev dependency.
yarn:
@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes
## 🧩 Register Addon
Now, **include the addon** in your `.storybook/main.js` file
Now, **include the addon** in your `.storybook/main.js` file.
```diff
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
"@storybook/addon-essentials",
+ "@storybook/addon-themes",
'@storybook/addon-essentials',
+ '@storybook/addon-themes',
],
};
```
@ -48,14 +45,14 @@ module.exports = {
These can be imported into your `.storybook/preview.js` file.
```diff
import { Preview } from "@storybook/your-renderer";
import { Preview } from '@storybook/your-renderer';
+// Load Material UI fonts
+import "@fontsource/roboto/300.css";
+import "@fontsource/roboto/400.css";
+import "@fontsource/roboto/500.css";
+import "@fontsource/roboto/700.css";
+import "@fontsource/material-icons";
+import '@fontsource/roboto/300.css';
+import '@fontsource/roboto/400.css';
+import '@fontsource/roboto/500.css';
+import '@fontsource/roboto/700.css';
+import '@fontsource/material-icons';
const preview: Preview = {
parameters: { /* ... */ },
@ -68,21 +65,21 @@ export default preview;
While Material UI comes with a default theme that works out of the box. You can create your own theme(s) and provide them to your stories with our `withThemeFromJSXProvider` decorator.
Make the following changes to your `.storybook/preview.js`
Make the following changes to your `.storybook/preview.js`:
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeFromJSXProvider } from "@storybook/addon-themes";
+import { CssBaseline, ThemeProvider } from "@mui/material";
+import { lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeFromJSXProvider } from '@storybook/addon-themes';
+import { CssBaseline, ThemeProvider } from '@mui/material';
+import { lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs
// Load Roboto fonts
import "@fontsource/roboto/300.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
import "@fontsource/material-icons";
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import '@fontsource/material-icons';
const preview: Preview = {
parameters: { /* ... */ },
@ -92,7 +89,7 @@ const preview: Preview = {
+ light: lightTheme,
+ dark: darkTheme,
+ },
+ defaultTheme: "light",
+ defaultTheme: 'light',
+ Provider: ThemeProvider,
+ GlobalStyles: CssBaseline,
+ }),

View File

@ -4,7 +4,7 @@
<!-- **NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Provide your themes"](#🎨-provide-your-themes). -->
To get started, **install the package** as a dev dependency
To get started, **install the package** as a dev dependency.
yarn:
@ -26,17 +26,14 @@ pnpm add -D @storybook/addon-themes
## 🧩 Register Addon
Now, **include the addon** in your `.storybook/main.js` file
Now, **include the addon** in your `.storybook/main.js` file.
```diff
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
"@storybook/addon-essentials",
+ "@storybook/addon-themes"
'@storybook/addon-essentials',
+ '@storybook/addon-themes',
],
};
```
@ -45,14 +42,14 @@ module.exports = {
Finally, provide your theme(s) and global styles component to your stories with our `withThemeFromJSXProvider` decorator.
Make the following changes to your `.storybook/preview.js`
Make the following changes to your `.storybook/preview.js`:
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeFromJSXProvider } from "@storybook/addon-themes";
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeFromJSXProvider } from '@storybook/addon-themes';
+import { ThemeProvider } from 'styled-components';
+import { GlobalStyles, lightTheme, darkTheme } from "../src/themes"; // import your custom theme configs
+import { GlobalStyles, lightTheme, darkTheme } from '../src/themes'; // Import your custom theme configs
const preview: Preview = {
parameters: { /* ... */ },
@ -62,7 +59,7 @@ const preview: Preview = {
+ light: lightTheme,
+ dark: darkTheme,
+ },
+ defaultTheme: "light",
+ defaultTheme: 'light',
+ Provider: ThemeProvider,
+ GlobalStyles: GlobalStyles,
+ }),

View File

@ -4,7 +4,7 @@
<!-- **NOTE:** As of Storybook 7.2, `@storybook/addon-themes` ships in `@storybook/addon-essentials`. If you're using Storybook >= 7.2, skip to ["Import your css"](#🥾-import-your-css). -->
To get started, **install the package** as a dev dependency
To get started, **install the package** as a dev dependency.
yarn:
@ -29,14 +29,11 @@ pnpm add -D @storybook/addon-themes
Now, **include the addon** in your `.storybook/main.js` file.
```diff
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
"@storybook/addon-essentials",
+ "@storybook/addon-themes"
'@storybook/addon-essentials',
+ '@storybook/addon-themes',
],
};
```
@ -46,9 +43,9 @@ module.exports = {
To give your stories access to Tailwind styles, import them into your `.storybook/preview.js` file.
```diff
import { Preview } from "@storybook/your-renderer";
import { Preview } from '@storybook/your-renderer';
+import "../src/index.css";
+import '../src/index.css';
const preview: Preview = {
parameters: { /* ... */ },
@ -64,11 +61,11 @@ Tailwind supports light and dark color modes out of the box. These modes can be
To enable switching between these modes in a click for your stories, use our `withThemeByClassName` decorator by adding the following code to your `.storybook/preview.js` file.
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeByClassName } from "@storybook/addon-themes";
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeByClassName } from '@storybook/addon-themes';
import "../src/index.css";
import '../src/index.css';
const preview: Preview = {
@ -76,10 +73,10 @@ const preview: Preview = {
+ decorators: [
+ withThemeByClassName<Renderer>({
+ themes: {
+ light: "",
+ dark: "dark",
+ light: '',
+ dark: 'dark',
+ },
+ defaultTheme: "light",
+ defaultTheme: 'light',
+ }),
+ ]
};
@ -92,11 +89,11 @@ export default preview;
If you've configured Tailwind to toggle themes with a data attribute, use our `withThemeByDataAttribute` decorator by adding the following code to your `.storybook/preview.js` file.
```diff
-import { Preview } from "@storybook/your-renderer";
+import { Preview, Renderer } from "@storybook/your-renderer";
+import { withThemeByDataAttribute } from "@storybook/addon-themes";
-import { Preview } from '@storybook/your-renderer';
+import { Preview, Renderer } from '@storybook/your-renderer';
+import { withThemeByDataAttribute } from '@storybook/addon-themes';
import "../src/index.css";
import '../src/index.css';
const preview: Preview = {
@ -104,11 +101,11 @@ const preview: Preview = {
+ decorators: [
+ withThemeByDataAttribute<Renderer>({
+ themes: {
+ light: "light",
+ dark: "dark",
+ light: 'light',
+ dark: 'dark',
+ },
+ defaultTheme: "light",
+ attributeName: "data-theme",
+ defaultTheme: 'light',
+ attributeName: 'data-theme',
+ }),
+ ]
};

Some files were not shown because too many files have changed in this diff Show More