Merge branch 'next' into pr/11936

This commit is contained in:
Michael Shilman 2021-07-19 21:00:29 +08:00
commit 618fc1c34b
3162 changed files with 181409 additions and 80805 deletions

View File

@ -12,13 +12,30 @@ const withTests = {
],
};
// type BabelMode = 'cjs' | 'esm' | 'modern';
const modules = process.env.BABEL_MODE === 'cjs' ? 'auto' : false;
// FIXME: optional chaining introduced in chrome 80, not supported by wepback4
// https://github.com/webpack/webpack/issues/10227#issuecomment-642734920
const targets = process.env.BABEL_MODE === 'modern' ? { chrome: '79' } : 'defaults';
module.exports = {
ignore: [
'./lib/codemod/src/transforms/__testfixtures__',
'./lib/postinstall/src/__testfixtures__',
],
presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
[
'@babel/preset-env',
{
shippedProposals: true,
useBuiltIns: 'usage',
corejs: '3',
targets,
modules,
},
],
'@babel/preset-typescript',
'@babel/preset-react',
'@babel/preset-flow',
@ -52,7 +69,16 @@ module.exports = {
{
test: './lib',
presets: [
['@babel/preset-env', { shippedProposals: true, useBuiltIns: 'usage', corejs: '3' }],
[
'@babel/preset-env',
{
shippedProposals: true,
useBuiltIns: 'usage',
corejs: '3',
modules,
targets,
},
],
'@babel/preset-react',
],
plugins: [
@ -71,6 +97,11 @@ module.exports = {
{
test: [
'./lib/node-logger',
'./lib/core',
'./lib/core-common',
'./lib/core-server',
'./lib/builder-webpack4',
'./lib/builder-webpack5',
'./lib/codemod',
'./addons/storyshots',
'**/src/server/**',
@ -83,8 +114,9 @@ module.exports = {
shippedProposals: true,
useBuiltIns: 'usage',
targets: {
node: '8.11',
node: '10',
},
modules,
corejs: '3',
},
],
@ -104,5 +136,22 @@ module.exports = {
test: withTests,
},
},
{
test: ['**/virtualModuleEntry.template.js'],
presets: [
[
'@babel/preset-env',
{
shippedProposals: true,
useBuiltIns: 'usage',
targets: {
node: '10',
},
corejs: '3',
modules: false,
},
],
],
},
],
};

View File

@ -1,16 +0,0 @@
component_depth: 2
languages:
- javascript
- name: javascript
production:
exclude:
- .*\.test\.js
- .*\/__test__\/.*\.js
- .*\/__mock__\/.*\.js
- .*\.stories\.js
test:
include:
- .*\.test\.js
- .*\/__test__\/.*\.js
- .*\.storyshot

View File

@ -1,62 +1,116 @@
version: 2.1
aliases:
- &defaults
executors:
sb_node_12_classic:
parameters:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'large', 'xlarge']
default: 'medium'
working_directory: /tmp/storybook
docker:
- image: circleci/node:10-browsers
- image: circleci/node:12
environment:
NODE_OPTIONS: --max_old_space_size=3076
resource_class: <<parameters.class>>
sb_node_12_browsers:
parameters:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'large', 'xlarge']
default: 'medium'
working_directory: /tmp/storybook
docker:
- image: circleci/node:12-browsers
environment:
NODE_OPTIONS: --max_old_space_size=3076
resource_class: <<parameters.class>>
sb_cypress_6_node_12:
parameters:
class:
description: The Resource class
type: enum
enum: ['small', 'medium', 'large', 'xlarge']
default: 'medium'
working_directory: /tmp/storybook
docker:
# ⚠️ The Cypress docker image is based on Node.js one so be careful when updating it because it can also
# cause an upgrade of the Node.
- image: cypress/included:6.8.0
environment:
NODE_OPTIONS: --max_old_space_size=3076
resource_class: <<parameters.class>>
orbs:
git-shallow-clone: guitarrapc/git-shallow-clone@2.0.3
commands:
ensure-pr-is-labeled-with:
description: 'A command looking for the labels set on the PR associated to this workflow and checking it contains the label given as parameter'
parameters:
label:
type: string
steps:
- run:
name: Check if PR is labeled with "<< parameters.label >>"
command: |
apt-get -y install jq
PR_NUMBER=$(echo "$CIRCLE_PULL_REQUEST" | sed "s/.*\/pull\///")
echo "PR_NUMBER: $PR_NUMBER"
API_GITHUB="https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
PR_REQUEST_URL="$API_GITHUB/pulls/$PR_NUMBER"
PR_RESPONSE=$(curl -H "Authorization: token $GITHUB_TOKEN_STORYBOOK_BOT_READ_REPO" "$PR_REQUEST_URL")
if [ $(echo $PR_RESPONSE | jq '.labels | map(select(.name == "<< parameters.label >>")) | length') -ge 1 ] ||
( [ $(echo $PR_RESPONSE | jq '.labels | length') -ge 1 ] && [ "<< parameters.label >>" == "*" ])
then
echo "🚀 The PR is labelled with '<< parameters.label >>', job will continue!"
else
echo "🏁 The PR isn't labelled with '<< parameters.label >>' so this job will end at the current step."
circleci-agent step halt
fi
jobs:
install:
<<: *defaults
build:
executor:
class: xlarge
name: sb_node_12_classic
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- restore_cache:
name: Restore core dependencies cache
name: Restore Yarn cache
keys:
- core-dependencies-v5-{{ checksum "yarn.lock" }}
- core-dependencies-v5-
- build-yarn-2-cache-v1--{{ checksum "yarn.lock" }}
- run:
name: Install dependencies
command: yarn install
- run:
name: Check that yarn.lock is not corrupted
command: yarn repo-dirty-check
- save_cache:
name: Cache core dependencies
key: core-dependencies-v5-{{ checksum "yarn.lock" }}
paths:
- node_modules
- persist_to_workspace:
root: .
paths:
- node_modules
- examples
- addons
- dev-kits
- app
- lib
build:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: .
command: yarn install --immutable
- run:
name: Bootstrap
command: yarn bootstrap --core
- save_cache:
name: Save Yarn cache
key: build-yarn-2-cache-v1--{{ checksum "yarn.lock" }}
paths:
- ~/.yarn/berry/cache
- persist_to_workspace:
root: .
paths:
- examples
- node_modules
- addons
- dev-kits
- app
- lib
chromatic:
<<: *defaults
parallelism: 11
executor: sb_node_12_browsers
parallelism: 4
steps:
# Keep using default checkout because Chromatic needs some git history to work properly
- checkout
- attach_workspace:
at: .
@ -65,9 +119,12 @@ jobs:
command: |
yarn run-chromatics
packtracker:
<<: *defaults
executor:
class: medium
name: sb_node_12_browsers
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -76,10 +133,13 @@ jobs:
cd examples/official-storybook
yarn packtracker
examples:
<<: *defaults
parallelism: 11
executor:
class: medium
name: sb_node_12_browsers
parallelism: 4
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -91,9 +151,12 @@ jobs:
paths:
- built-storybooks
publish:
<<: *defaults
executor:
class: medium
name: sb_node_12_classic
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -103,15 +166,24 @@ jobs:
root: .
paths:
- .verdaccio-cache
examples-v2:
docker:
- image: cypress/included:4.7.0
environment:
TERM: xterm
working_directory: /tmp/storybook
parallelism: 10
e2e-tests-extended:
executor:
class: medium
name: sb_cypress_6_node_12
parallelism: 4
steps:
- checkout
- when:
condition:
and:
- not:
equal: [main, << pipeline.git.branch >>]
- not:
equal: [next, << pipeline.git.branch >>]
steps:
- ensure-pr-is-labeled-with:
label: 'run e2e extended test suite'
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -119,58 +191,95 @@ jobs:
command: yarn local-registry --port 6000 --open
background: true
- run:
name: wait for registry
name: Wait for registry
command: yarn wait-on http://localhost:6000
- run:
name: set registry
command: yarn config set registry http://localhost:6000/
- run:
name: test local registry
command: yarn info @storybook/core
- run:
name: run e2e tests
command: yarn test:e2e-framework
name: Run E2E tests
command: yarn test:e2e-framework --clean --all --skip angular11 --skip angular --skip vue3 --skip web_components_typescript --skip cra
no_output_timeout: 5m
- store_artifacts:
path: /tmp/storybook/cypress
path: /tmp/cypress-record
destination: cypress
examples-v2-yarn-2:
docker:
- image: cypress/included:4.7.0
environment:
TERM: xterm
working_directory: /tmp/storybook
# parallelism: 10
e2e-tests-core:
executor:
class: medium
name: sb_cypress_6_node_12
parallelism: 2
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
name: running local registry
name: Running local registry
command: yarn local-registry --port 6000 --open
background: true
- run:
name: wait for registry
name: Wait for registry
command: yarn wait-on http://localhost:6000
- run:
name: set registry
command: yarn config set registry http://localhost:6000/
name: Run E2E tests
# Do not test CRA here because it's done in PnP part
# TODO: Remove `web_components_typescript` as soon as Lit 2 stable is released
command: yarn test:e2e-framework vue3 angular angular11 web_components_typescript web_components_lit2
no_output_timeout: 5m
- store_artifacts:
path: /tmp/cypress-record
destination: cypress
cra-bench:
executor:
class: medium
name: sb_cypress_6_node_12
working_directory: /tmp/storybook
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
name: test local registry
command: yarn info @storybook/core
name: Running local registry
command: yarn local-registry --port 6000 --open
background: true
- run:
name: Wait for registry
command: yarn wait-on http://localhost:6000
- run:
name: Run @storybook/bench on a CRA project
command: |
cd ..
npx create-react-app cra-bench
cd cra-bench
npx @storybook/bench 'npx sb init' --label cra --extra-flags "--modern"
e2e-tests-pnp:
executor:
class: medium
name: sb_cypress_6_node_12
working_directory: /tmp/storybook
steps:
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
name: Running local registry
command: yarn local-registry --port 6000 --open
background: true
- run:
name: Wait for registry
command: yarn wait-on http://localhost:6000
- run:
name: run e2e tests
command: yarn test:e2e-framework --use-yarn-2 cra
command: yarn test:e2e-framework --pnp sfcVue cra
- store_artifacts:
path: /tmp/storybook/cypress
path: /tmp/cypress-record
destination: cypress
e2e:
working_directory: /tmp/storybook
docker:
- image: cypress/included:4.7.0
environment:
TERM: xterm
e2e-tests-examples:
executor:
class: small
name: sb_cypress_6_node_12
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -182,15 +291,21 @@ jobs:
command: yarn await-serve-storybooks
- run:
name: cypress run
command: yarn test:e2e
command: yarn test:e2e-examples
- store_artifacts:
path: /tmp/storybook/cypress
path: /tmp/cypress-record
destination: cypress
smoke-tests:
<<: *defaults
executor:
class: medium
name: sb_node_12_browsers
environment:
# Disable ESLint when running smoke tests to improve perf + As of CRA 4.0.3, CRA kitchen sinks are throwing
# because of some ESLint warnings, related to: https://github.com/facebook/create-react-app/pull/10590
DISABLE_ESLINT_PLUGIN: 'true'
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -223,76 +338,65 @@ jobs:
command: |
cd examples/ember-cli
yarn storybook --smoke-test --quiet
- run:
name: Run marko-cli (smoke test)
command: |
cd examples/marko-cli
yarn storybook --smoke-test --quiet
- run:
name: Run official-storybook (smoke test)
command: |
cd examples/official-storybook
yarn storybook --smoke-test --quiet
- run:
name: Run mithril kitchen-sink (smoke test)
command: |
cd examples/mithril-kitchen-sink
yarn storybook --smoke-test --quiet
- run:
name: Run riot kitchen-sink (smoke test)
command: |
cd examples/riot-kitchen-sink
yarn storybook --smoke-test --quiet
- run:
name: Run preact kitchen-sink (smoke test)
command: |
cd examples/preact-kitchen-sink
yarn storybook --smoke-test --quiet
- run:
name: Run cra reac15 (smoke test)
name: Run cra react15 (smoke test)
command: |
cd examples/cra-react15
yarn storybook --smoke-test --quiet
frontpage:
<<: *defaults
executor: sb_node_12_browsers
steps:
- checkout
- restore_cache:
name: Restore core dependencies cache
keys:
- core-dependencies-v5-{{ checksum "yarn.lock" }}
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- run:
name: Install dependencies
command: yarn bootstrap --install
command: yarn install --immutable
- run:
name: Trigger build
command: ./scripts/build-frontpage.js
lint:
<<: *defaults
executor:
class: small
name: sb_node_12_classic
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
name: Lint
command: yarn lint
test:
<<: *defaults
unit-tests:
executor: sb_node_12_browsers
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
name: Test
command: yarn test --coverage --w2 --core
command: yarn test --coverage --runInBand --ci
- persist_to_workspace:
root: .
paths:
- coverage
coverage:
<<: *defaults
executor:
class: small
name: sb_node_12_browsers
steps:
- checkout
- git-shallow-clone/checkout_advanced:
clone_options: '--depth 1 --verbose'
- attach_workspace:
at: .
- run:
@ -302,17 +406,14 @@ jobs:
workflows:
test:
jobs:
- install
- build:
requires:
- install
- build
- lint:
requires:
- build
- examples:
requires:
- build
- e2e:
- e2e-tests-examples:
requires:
- examples
- smoke-tests:
@ -321,22 +422,28 @@ workflows:
- packtracker:
requires:
- build
- test:
- unit-tests:
requires:
- build
- coverage:
requires:
- test
- unit-tests
- chromatic:
requires:
- examples
- publish:
requires:
- build
- examples-v2:
- e2e-tests-extended:
requires:
- publish
- examples-v2-yarn-2:
- e2e-tests-core:
requires:
- publish
- e2e-tests-pnp:
requires:
- publish
- cra-bench:
requires:
- publish
deploy:

View File

@ -7,7 +7,11 @@ docs/public
storybook-static
built-storybooks
lib/cli/test
lib/manager-webpack4/prebuilt
lib/manager-webpack5/prebuilt
lib/core-server/prebuilt
lib/codemod/src/transforms/__testfixtures__
lib/components/src/controls/react-editable-json-tree
scripts/storage
*.bundle.js
*.js.map
@ -18,7 +22,6 @@ examples/cra-ts-kitchen-sink/*.json
examples/cra-ts-kitchen-sink/public/*
examples/cra-ts-essentials/*.json
examples/cra-ts-essentials/public/*
examples/rax-kitchen-sink/src/document/*
ember-output
.yarn
!.remarkrc.js

View File

@ -1,6 +1,9 @@
module.exports = {
root: true,
extends: ['@storybook/eslint-config-storybook'],
rules: {
'@typescript-eslint/ban-ts-comment': 'warn',
},
overrides: [
{
files: [
@ -10,8 +13,6 @@ module.exports = {
'**/*.test.*',
'**/*.stories.*',
'**/storyshots/**/stories/**',
'docs/src/new-components/lib/StoryLinkWrapper.js',
'docs/src/stories/**',
],
rules: {
'@typescript-eslint/no-empty-function': 'off',
@ -37,7 +38,9 @@ module.exports = {
{
files: ['**/*.tsx', '**/*.ts'],
rules: {
'react/require-default-props': 'off',
'react/prop-types': 'off', // we should use types
'react/forbid-prop-types': 'off', // we should use types
'no-dupe-class-members': 'off', // this is called overloads in typescript
},
},

2
.gitattributes vendored
View File

@ -1 +1 @@
.yarn/releases/yarn-*.js linguist-generated=true
/.yarn/** linguist-generated

47
.github/CODEOWNERS vendored
View File

@ -1,47 +0,0 @@
.circleci/ @ndelangen
.github/ @danielduan
/addons/a11y/ @jbovenschen @codebyalex
/addons/actions/ @rhalff
/addons/backgrounds/ @ndelangen
/addons/events/ @z4o4z @ndelangen
/addons/graphql/ @mnmtanish
/addons/info/ @theinterned @z4o4z @UsulPro @dangreenisrael
/addons/jest/ @renaudtertrais
/addons/knobs/ @alexandrebodin @theinterned @leonrodenburg @alterx
/addons/links/ @ndelangen
/addons/notes/ @alexandrebodin
/addons/options/ @UsulPro
/addons/storyshots/ @igor-dv @thomasbertet
/addons/storysource/ @igor-dv
/addons/viewport/ @saponifi3d
/app/angular/ @alterx @igor-dv
/app/react/ @xavcz @shilman @thomasbertet
/app/vue/ @thomasbertet @kazupon
/app/svelte/ @plumpNation
/docs/ @ndelangen @shilman
/examples/angular-cli/ @igor-dv @alterx
/examples/cra-kitchen-sink/ @ndelangen @UsulPro
/examples/cra-ts-kitchen-sink/ @mucsi96
/examples/official-storybook/ @UsulPro
/examples/vue-kitchen-sink/ @igor-dv @alexandrebodin
/examples/svelte-kitchen-sink/ @plumpNation
/examples-native/crna-kitchen-sink/ @Gongreg
/lib/addons/ @ndelangen @theinterned
/lib/channel-postmessage/ @mnmtanish @ndelangen
/lib/channel-websocket/ @mnmtanish @ndelangen
/lib/channels/ @mnmtanish @ndelangen
/lib/cli/ @ndelangen @shilman @stijnkoopal
/lib/client-logger/ @dangreenisrael
/lib/codemod/ @aaronmcadam @ndelangen
/lib/components/ @ndelangen @tmeasday
/lib/core/ @tmeasday @igor-dv @alterx
/lib/node-logger/ @dangreenisrael
/lib/ui/ @tmeasday @igor-dv @ndelangen
/scripts/ @ndelangen @igor-dv

View File

@ -1,47 +0,0 @@
If you are reporting a bug or requesting support, start here:
### Bug or support request summary
_Please provide issue details here - What did you expect to happen? What happened instead?_
### Steps to reproduce
_Please provide necessary steps for reproduction of this issue. Describe the exact steps a maintainer has to take to make the problem occur. If the problem is non-trivial to reproduce, please link a repository or provide some code snippets._
_(A screencast can be useful for visual bugs, but it is not a substitute for a textual description.)_
### Please specify which version of Storybook and optionally any affected addons that you're running
- @storybook/react x.x.x
- @storybook/addon-something x.x.x
### Affected platforms
- _If UI related, please indicate browser, OS, and version_
- _If dependency related, please include relevant version numbers_
- _If developer tooling related, please include the platform information_
### Screenshots / Screencast / Code Snippets (Optional)
```js
// code here
```
End bug report support request - delete the rest below
If you are creating a issue to track work to be completed, start here:
### Work summary
_Please provide a description of the work to be completed here - Include some context as to why something needs to be done and link any related tickets._
### Where to start
_Please list the file(s) a contributor needs to figure out where to start work and include any docs or tutorials that may be applicable._
### Acceptance criteria
_Please include a checklist of the requirements necessary to close this ticket. The work should be narrowly scoped and limited to a few hours worth by an experienced developer at the most._
### Who to contact
_Add yourself and/or people who are familiar with the code changes and requirements. These people should be able to review the completed code._
End work issue - please tag this issue with the correct status and type labels

View File

@ -1,29 +1,17 @@
---
name: Bug report
about: Create a report to help us improve
name: Bug report 🐞
about: Something is broken and you have a reliable reproduction? Let us know here. For questions, please use "Question" below.
labels: needs triage, bug
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
Please create a reproduction by running `npx sb@next repro` and following the instructions. Read our [documentation](https://storybook.js.org/docs/react/contribute/how-to-reproduce) to learn more about creating reproductions.
Paste your repository and deployed reproduction here. We prioritize issues with reproductions over those without.
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Code snippets**
If applicable, add code samples to help explain your problem.
**System:**
**System**
Please paste the results of `npx sb@next info` here.
**Additional context**

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Documentation 📚
url: https://storybook.js.org/docs/
about: Check out the official docs for answers to common questions
- name: Questions & discussions 🤔
url: https://github.com/storybookjs/storybook/discussions
about: Ask questions, request features & discuss RFCs
- name: Community Discord 💬
url: https://discord.gg/storybook
about: Community discussions, interactive support, contributor help

View File

@ -1,19 +1,19 @@
---
name: Feature request
name: Feature request 💡
about: Suggest an idea for this project
labels: needs triage, feature request
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Is your feature request related to a problem? Please describe**
A clear and concise description of the problem. E.g. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
What would you like to see added to Storybook to solve problem?
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
Any alternative solutions or features you've considered.
**Are you able to assist bring the feature to reality?**
**Are you able to assist to bring the feature to reality?**
no | yes, I can...
**Additional context**

View File

@ -12,7 +12,7 @@ If your answer is yes to any of these, please make sure to include it in your PR
<!--
Everybody: Please submit all PRs to the `next` branch unless they are specific to current release. Storybook maintainers cherry-pick bug and documentation fixes into the `master` branch as part of the release process, so you shouldn't need to worry about this.
Everybody: Please submit all PRs to the `next` branch unless they are specific to the current release. Storybook maintainers cherry-pick bug and documentation fixes into the `master` branch as part of the release process, so you shouldn't need to worry about this.
Maintainers: Please tag your pull request with at least one of the following:
`["cleanup", "BREAKING CHANGE", "feature request", "bug", "documentation", "maintenance", "dependencies", "other"]`

View File

@ -1,24 +0,0 @@
'app: angular': ['kroeder', 'igor-dv', 'MaximSagan']
'app: ember': ['gabrielcsapo']
'app: html': ['Hypnosphi']
'app: marko': ['nm123github']
'app: preact': ['BartWaardenburg']
'app: rax': ['SoloJiang']
'app: svelte': ['rixo', 'plumpNation']
'app: vue': ['backbone87', 'elevatebart', 'pksunkara', 'Aaron-Pool', 'pocka']
'app: web-components': ['daKmoR']
'api: addons': ['ndelangen']
'addon: a11y': ['CodeByAlex', 'Armanio', 'jsomsanith']
'addon: toolbars': ['shilman']
'addon: docs': ['shilman', 'patricklafrance']
'addon: knobs': ['leoyli', 'Armanio']
'addon: storysource': ['igor-dv', 'libetl']
typescript: ['kroeder', 'gaetanmaisse', 'ndelangen', 'emilio-martinez']
theming: ['ndelangen', 'domyen']
cra: ['mrmckeb']
cli: ['Keraito']
dependencies: ['ndelangen']
'babel / webpack': ['ndelangen', 'igor-dv', 'shilman']
'yarn / npm': ['ndelangen', 'tmeasday']
'source-loader': ['libetl']
documentation: ['jonniebigodes']

83
.github/renovate.json5 vendored Normal file
View File

@ -0,0 +1,83 @@
{
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
extends: ['config:base', 'group:allNonMajor'],
prHourlyLimit: 1,
prConcurrentLimit: 1,
// Custom ignore paths to include our `examples/` directory
ignorePaths: [
'**/node_modules/**',
'**/bower_components/**',
'**/vendor/**',
'**/__tests__/**',
'**/test/**',
'**/tests/**',
'**/__fixtures__/**',
],
enabledManagers: ['npm'],
// Ignore release notes for non-breaking changes
fetchReleaseNotes: false,
// Always bump minor/patch to latest
rangeStrategy: 'bump',
major: {
// Replace ranges when there is a major
rangeStrategy: 'replace',
// Get us the release notes for breaking changes
fetchReleaseNotes: true,
},
packageRules: [
// Leave peerDependencies and engines alone
{
depTypeList: ['peerDependencies', 'engines'],
enabled: false,
},
// Handle patch updates under 0.1.0 as potentially breaking
// Workaround for https://github.com/renovatebot/renovate/issues/3588
{
managers: ['npm'],
matchCurrentVersion: '<0.1.0',
rangeStrategy: 'replace',
groupName: 'maybe breaking patch updates',
groupSlug: 'maybe-breaking-patch',
// Get us the release notes for potentially breaking changes
fetchReleaseNotes: true,
},
// Handle minor updates under 1.0.0 as potentially breaking
// Workaround for https://github.com/renovatebot/renovate/issues/3588
{
managers: ['npm'],
matchCurrentVersion: '<1.0.0 >=0.1.0',
minor: {
// FIXME: "rangeStrategy": "replace",
groupName: 'maybe breaking minor updates',
groupSlug: 'maybe-breaking-minor',
// Get us the release notes for potentially breaking changes
fetchReleaseNotes: true,
},
},
// Group Storybook's linter configs together
{
packageNames: ['@storybook/eslint-config-storybook', '@storybook/linter-config'],
groupName: 'storybook linting',
},
// Group Puppeteer packages together
{
packagePatterns: ['^puppeteer', '^@types/puppeteer'],
groupName: 'puppeteer',
},
// Group Acorn packages together
{
packagePatterns: ['^acorn'],
groupName: 'acorn',
},
// Group React packages together
{
packageNames: ['react', '@types/react', 'react-dom', '@types/react-dom'],
groupName: 'react',
},
],
// Simplifies the PR body
prBodyTemplate: '{{{table}}}{{{notes}}}{{{changelogs}}}',
prBodyColumns: ['Package', 'Change'],
// https://docs.renovatebot.com/merge-confidence/#enabling-and-disabling
ignorePresets: ['github>whitesource/merge-confidence:beta'],
}

1
.github/stale.yml vendored
View File

@ -11,7 +11,6 @@ exemptLabels:
- 'needs review'
- 'high priority'
- 'good first issue'
- dependencies:update
# Label to use when marking an issue as stale
staleLabel: inactive

17
.github/workflows/danger-js.yml vendored Normal file
View File

@ -0,0 +1,17 @@
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
name: Danger JS
jobs:
dangerJS:
name: Danger JS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Danger JS
uses: danger/danger-js@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: --dangerfile .ci/danger/dangerfile.ts

View File

@ -1,12 +0,0 @@
on: issues
name: Automention Issues
jobs:
automention:
name: Automention
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Automention
uses: shilman/automention@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,14 +0,0 @@
on: label
name: Dangerfile JS Label
jobs:
dangerJS:
name: Danger JS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Danger JS
uses: danger/danger-js@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: --dangerfile .ci/danger/dangerfile.ts

View File

@ -1,12 +0,0 @@
on: pull_request
name: Automention PRs
jobs:
automention:
name: Automention
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Automention
uses: shilman/automention@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,14 +0,0 @@
on: pull_request
name: Dangerfile JS Pull
jobs:
dangerJS:
name: Danger JS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Danger JS
uses: danger/danger-js@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: --dangerfile .ci/danger/dangerfile.ts

View File

@ -1,60 +0,0 @@
name: CLI tests
on:
push
# push:
# disabled for now:
# https://github.community/t5/GitHub-Actions/Preserve-status-from-previous-action-run/m-p/33821#M1661
# paths:
# - 'lib/**'
# - 'app/**'
jobs:
cli:
name: CLI Fixtures
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: increase filewatcher limit
run: |
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: build-v2-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
build-v2-${{ env.cache-name }}-
build-v2-
- name: install, bootstrap
run: |
yarn bootstrap --core
- name: cli
run: |
yarn test --cli
cli-yarn-2:
name: CLI Fixtures with Yarn 2
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: build-v2-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
build-v2-${{ env.cache-name }}-
build-v2-
- name: install, bootstrap
run: |
yarn bootstrap --core
- name: cli with Yarn 2
run: |
cd lib/cli
yarn test-yarn-2

View File

@ -7,21 +7,15 @@ jobs:
name: Core Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: '10.x'
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v1
with:
path: node_modules
key: build-v2-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
build-v2-${{ env.cache-name }}-
build-v2-
- name: install, bootstrap
run: |
yarn bootstrap --core
- name: test
run: |
yarn test --core
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "12.x"
cache: yarn
- name: install, bootstrap
run: |
yarn install --immutable
yarn bootstrap --core
- name: test
run: |
yarn test --runInBand --ci

View File

@ -0,0 +1,21 @@
name: Trigger CircleCI workflow
on:
pull_request:
types: [labeled]
jobs:
trigger:
if: github.event.label.name == 'run e2e extended test suite' && github.event.pull_request.head.repo.fork == false
name: Run workflow with all e2e tests
runs-on: ubuntu-latest
steps:
- name: Make request to CircleCI
run: >
curl --request POST
--url https://circleci.com/api/v2/project/gh/storybookjs/storybook/pipeline
--header 'Circle-Token: '"$CIRCLE_CI_TOKEN"' '
--header 'content-type: application/json'
--data '{"branch":"${{ github.event.pull_request.head.ref }}"}'
env:
CIRCLE_CI_TOKEN: ${{ secrets.CIRCLE_CI_TOKEN }}

12
.gitignore vendored
View File

@ -5,7 +5,6 @@ node_modules
*.sw*
npm-shrinkwrap.json
dist
ts3.5
.tern-port
*.DS_Store
.cache
@ -32,3 +31,14 @@ cypress/screenshots
examples/ember-cli/ember-output
.verdaccio-cache
tsconfig.tsbuildinfo
lib/manager-webpack4/prebuilt
lib/manager-webpack5/prebuilt
examples/angular-cli/addon-jest.testresults.json
# Yarn stuff
/**/.yarn/*
!/**/.yarn/releases
!/**/.yarn/plugins
!/**/.yarn/sdks
!/**/.yarn/versions
/**/.pnp.*

View File

@ -71,7 +71,6 @@ storybook.js.org
YuzuJS
setImmediate
Malte
Ubl
Katić
Domenic
Kowal

View File

@ -0,0 +1,31 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.VcsTrigger
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'TestWorkflow'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("TestWorkflow")) {
triggers {
val trigger1 = find<VcsTrigger> {
vcs {
quietPeriodMode = VcsTrigger.QuietPeriodMode.USE_DEFAULT
triggerRules = "-:.teamcity/**"
branchFilter = """
+:<default>
+:next
+:main
+:pull/*
""".trimIndent()
}
}
trigger1.apply {
quietPeriodMode = VcsTrigger.QuietPeriodMode.DO_NOT_USE
}
}
}

154
.teamcity/settings.kts vendored
View File

@ -46,8 +46,6 @@ project {
buildType(E2E)
buildType(SmokeTests)
buildType(Frontpage)
buildType(Docs)
buildType(Lint)
buildType(Test)
buildType(Coverage)
@ -59,8 +57,6 @@ project {
RelativeId("E2E"),
RelativeId("SmokeTests"),
RelativeId("Frontpage"),
RelativeId("Docs"),
RelativeId("Lint"),
RelativeId("Test"),
RelativeId("Coverage")
)
@ -129,11 +125,10 @@ object Build : BuildType({
#!/bin/bash
set -e -x
yarn install
yarn repo-dirty-check
yarn install --immutable
yarn bootstrap --core
""".trimIndent()
dockerImage = "node:10"
dockerImage = "node:12"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -177,7 +172,7 @@ object ExamplesTemplate : Template({
scriptContent = """
#!/bin/bash
set -e -x
yarn install
rm -rf built-storybooks
mkdir -p built-storybooks
@ -319,15 +314,15 @@ object E2E : BuildType({
scriptContent = """
#!/bin/bash
set -e -x
yarn install
yarn install --immutable
yarn cypress install
yarn serve-storybooks &
yarn await-serve-storybooks
yarn cypress run --reporter teamcity || :
yarn ts-node --transpile-only cypress/report-teamcity-metadata.ts || :
""".trimIndent()
dockerImage = "cypress/base:10.18.1"
dockerImage = "cypress/base:12.19.0"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -361,51 +356,48 @@ object SmokeTests : BuildType({
}
}
params {
// Disable ESLint when running smoke tests to improve perf and as of CRA 4.0.3, CRA kitchen sinks are throwing
// because of some ESLint warnings, related to: https://github.com/facebook/create-react-app/pull/10590
param("env.DISABLE_ESLINT_PLUGIN", "true")
}
steps {
script {
scriptContent = """
#!/bin/bash
set -e -x
yarn install
yarn install --immutable
cd examples/cra-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../cra-ts-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../vue-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../svelte-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../angular-cli
yarn storybook --smoke-test --quiet
cd ../ember-cli
yarn storybook --smoke-test --quiet
cd ../marko-cli
yarn storybook --smoke-test --quiet
cd ../official-storybook
yarn storybook --smoke-test --quiet
cd ../mithril-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../riot-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../preact-kitchen-sink
yarn storybook --smoke-test --quiet
cd ../cra-react15
yarn storybook --smoke-test --quiet
""".trimIndent()
dockerImage = "node:10"
dockerImage = "node:12"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -420,11 +412,12 @@ object Frontpage : BuildType({
scriptContent = """
#!/bin/bash
set -e -x
yarn install --immutable
yarn bootstrap --install
node ./scripts/build-frontpage.js
""".trimIndent()
dockerImage = "node:10"
dockerImage = "node:12"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -433,88 +426,7 @@ object Frontpage : BuildType({
vcs {
quietPeriodMode = VcsTrigger.QuietPeriodMode.USE_DEFAULT
triggerRules = "-:.teamcity/**"
branchFilter = "+:master"
}
}
})
object Docs : BuildType({
name = "Docs"
type = Type.DEPLOYMENT
steps {
script {
workingDir = "docs"
scriptContent = """
#!/bin/bash
set -e -x
yarn install
yarn build
""".trimIndent()
dockerImage = "node:10"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
triggers {
vcs {
quietPeriodMode = VcsTrigger.QuietPeriodMode.USE_DEFAULT
triggerRules = "-:.teamcity/**"
branchFilter = """
+:<default>
+:next
+:master
+:pull/*
""".trimIndent()
}
}
})
object Lint : BuildType({
name = "Lint"
dependencies {
dependency(Build) {
snapshot {
onDependencyFailure = FailureAction.CANCEL
}
artifacts {
artifactRules = "dist.tar.gz!** => ."
}
}
}
steps {
script {
scriptContent = """
#!/bin/bash
set -e -x
yarn install
# TODO remove after merging
mkdir temp-eslint-teamcity
cd temp-eslint-teamcity
yarn init -y
yarn add -D eslint-teamcity
cd ..
yarn lint:js --format ./temp-eslint-teamcity/node_modules/eslint-teamcity/index.js .
yarn lint:md .
""".trimIndent()
dockerImage = "node:10"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
failureConditions {
failOnMetricChange {
metric = BuildFailureOnMetric.MetricType.INSPECTION_ERROR_COUNT
threshold = 0
units = BuildFailureOnMetric.MetricUnit.DEFAULT_UNIT
comparison = BuildFailureOnMetric.MetricComparison.MORE
compareTo = value()
branchFilter = "+:main"
}
}
})
@ -545,12 +457,13 @@ object Test : BuildType({
mkdir temp-jest-teamcity
cd temp-jest-teamcity
yarn init -y
touch yarn.lock
yarn add -D jest-teamcity
cd ..
yarn jest --coverage -w 2 --reporters=${'$'}PWD/temp-jest-teamcity/node_modules/jest-teamcity
""".trimIndent()
dockerImage = "node:10"
dockerImage = "node:12"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -581,7 +494,7 @@ object Coverage : BuildType({
yarn install
yarn coverage
""".trimIndent()
dockerImage = "node:10"
dockerImage = "node:12"
dockerImagePlatform = ScriptBuildStep.ImagePlatform.Linux
}
}
@ -595,7 +508,6 @@ object TestWorkflow : BuildType({
dependencies {
snapshot(E2E) {}
snapshot(SmokeTests) {}
snapshot(Lint) {}
snapshot(Coverage) {}
}
@ -606,7 +518,7 @@ object TestWorkflow : BuildType({
branchFilter = """
+:<default>
+:next
+:master
+:main
+:pull/*
""".trimIndent()
}

View File

@ -1,14 +0,0 @@
language: node_js
node_js:
- "10"
# install:
# - yarn install
# - yarn bootstrap --core
script:
jobs:
include:
- script: echo "placeholder task"
name: "Placeholder task"

8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs generated vendored Normal file

File diff suppressed because one or more lines are too long

622
.yarn/releases/yarn-sources.cjs generated vendored Executable file

File diff suppressed because one or more lines are too long

16
.yarnrc.yml Normal file
View File

@ -0,0 +1,16 @@
compressionLevel: 0
enableGlobalCache: true
nodeLinker: node-modules
npmRegistryServer: "https://registry.yarnpkg.com"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
unsafeHttpWhitelist:
- localhost
yarnPath: .yarn/releases/yarn-sources.cjs

13180
CHANGELOG.md

File diff suppressed because it is too large Load Diff

10235
CHANGELOG.v1-5.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,437 +1,5 @@
# Contributing to Storybook
<h1>Contributing to Storybook</h1>
Thanks for your interest in improving Storybook! We are a community-driven project and welcome contributions of all kinds: from discussion to documentation to bugfixes to feature improvements.
Please refer to our [NEW contributing guide on the Storybook website](https://storybook.js.org/docs/react/contribute/how-to-contribute).
Please review this document to help to streamline the process and save everyone's precious time.
This repo uses yarn workspaces, so you should install `yarn` as the package manager. See [installation guide](https://yarnpkg.com/en/docs/install).
## Issues
No software is bug-free. So, if you got an issue, follow these steps:
- Search the [issue list](https://github.com/storybookjs/storybook/issues?utf8=%E2%9C%93&q=) for current and old issues.
- If you find an existing issue, please UPVOTE the issue by adding a "thumbs-up reaction". We use this to help prioritize issues!
- If none of that is helping, create an issue with the following information:
- Clear title (shorter is better).
- Describe the issue in clear language.
- Share error logs, screenshots and etc.
- To speed up the issue fixing process, send us a sample repo with the issue you faced:
### Testing against `master`
To test your project against the current latest version of storybook, you can clone the repository and link it with `yarn`. Try following these steps:
#### 1. Download the latest version of this project, and build it:
```sh
git clone https://github.com/storybookjs/storybook.git
cd storybook
yarn bootstrap
```
> NOTE: on windows you may need to run `yarn` before `yarn bootstrap`!
The bootstrap command might ask which sections of the codebase you want to bootstrap. Unless you're going to work with ReactNative or the Documentation, you can keep the default.
You can also pick directly from CLI:
```sh
yarn bootstrap --core
```
#### 2a. Run unit tests
You can use one of the example projects in `examples/` to develop on.
This command will list all the suites and options for running tests.
```sh
yarn test
```
The options for running tests can be selected from the cli or be passed to `yarn test` with specific parameters. Available modes include `--watch`, `--coverage`, and `--runInBand`, which will respectively run tests in watch mode, output code coverage, and run selected test suites serially in the current process.
You can use the `--update` flag to update snapshots or screenshots as needed.
You can also pick suites from CLI. Suites available are listed below.
##### Core & Examples Tests
`yarn test --core`
This option executes tests from `<rootdir>/app/react`, `<rootdir>/app/vue`, and `<rootdir>/lib`.
Before the tests are run, the project must be bootstrapped with core. You can accomplish this with `yarn bootstrap --core`
##### CRA-kitchen-sink - Image snapshots using Storyshots
`yarn test --image`
This option executes tests from `<rootdir>/examples/official-storybook`
In order for the image snapshots to be correctly generated, you must have a static build of the storybook up-to-date :
```sh
cd examples/official-storybook
yarn build-storybook
cd ../..
yarn test --image
```
Puppeteer is used to launch and grab screenshots of example pages, while jest is used to assert matching images. (just like integration tests)
#### 2b. Run e2e tests for CLI
If you made any changes to the `lib/cli` package, the easiest way to verify that it doesn't break anything is to run e2e tests:
```sh
yarn test --cli
```
This will run a bash script located at `lib/cli/test/run_tests.sh`. It will copy the contents of `fixtures` into a temporary `run` directory, run `getstorybook` in each of the subdirectories, and check that storybook starts successfully using `yarn storybook --smoke-test`.
After that, the `run` directory content will be compared with `snapshots`. You can update the snapshots by passing an `--update` flag:
```sh
yarn test --cli --update
```
In that case, please check the git diff before committing to make sure it only contains the intended changes.
#### 2c. Run Linter
We use eslint as a linter for all code (including typescript code).
All you have to run is:
```sh
yarn lint
```
It can be immensely helpful to get feedback in your editor, if you're using VsCode, you should install the `eslint` plugin and configure it with these settings:
```plaintext
"eslint.autoFixOnSave": true,
"eslint.packageManager": "yarn",
"eslint.options": {
"cache": true,
"cacheLocation": ".cache/eslint",
"extensions": [".js", ".jsx", ".mjs", ".json", ".ts", ".tsx"]
},
"eslint.validate": [
"javascript",
"javascriptreact",
{"language": "typescript", "autoFix": true },
{"language": "typescriptreact", "autoFix": true }
],
"eslint.alwaysShowStatus": true
```
This should enable auto-fix for all source files, and give linting warnings and errors within your editor.
### 2d. Run Cypress tests
First make sure the repo is bootstrapped.
Then run `yarn build-storybooks`, this creates a static website from all examples.
Then run `yarn serve-storybooks`, this will run the static site on the port cypress expects.
Then run `yarn add cypress -W --optional`. When this has completed cypress should be installed on your system. If it is already on your system, this step can be skipped.
Then run `yarn cypress open` if you want to see the tests run in the UI, or `yarn cypress run` to run the tests headless.
### Reproductions
#### In the monorepo
The best way to help figure out an issue you are having is to produce a minimal reproduction against the `master` branch.
A good way to do that is using the example `cra-kitchen-sink` app embedded in this repository:
```sh
# Download and build this repository:
git clone https://github.com/storybookjs/storybook.git
cd storybook
yarn bootstrap --core
# NOTE: on windows you may need to run `yarn` before `yarn bootstrap`!
# make changes to try and reproduce the problem, such as adding components + stories
cd examples/cra-kitchen-sink
yarn storybook
# see if you can see the problem, if so, commit it:
git checkout "branch-describing-issue"
git add -A
git commit -m "reproduction for issue #123"
# fork the storybook repo to your account, then add the resulting remote
git remote add <your-username> https://github.com/<your-username>/storybook.git
git push -u <your-username> master
```
If you follow that process, you can then link to the GitHub repository in the issue. See <https://github.com/storybookjs/storybook/issues/708#issuecomment-290589886> for an example.
**NOTE**: If your issue involves a webpack config, create-react-app will prevent you from modifying the _app's_ webpack config, however, you can still modify storybook's to mirror your app's version of the storybook. Alternatively, use `yarn eject` in the CRA app to get a modifiable webpack config.
#### Outside the monorepo
Sometimes your storybook is deeply ingrained in your own setup and it's hard to create a minimal viable reproduction somewhere else.
Inside the storybook repo we have a script that allows you to test the packages inside this repo in your own separate project.
You can use `npm link` on all packages, but npm linking is cumbersome and has subtle differences from what happens in a registry-based installation.
So the way our script works is that it:
- sets up a npm registry running on your own local machine
- changes your default registry to this local one
- builds all packages in the storybook repo
- publishes all packages as latest
Our script leaves the local registry running, for **as long as you keep it running** you can install storybook packages from this local registry.
- Navigate to your own project and then change `package.json` so the storybook packages match the version of the one you just published.
- Then you can install using `yarn` or `npm`
- Start using your storybook as normally.
If you've made a change to storybook's codebase and would want this change to be reflected in your app:
- Ensure the storybook packages are transpiled, by either having run `yarn dev` or `yarn bootstrap --core`.
- Go to the terminal where the local regitry is running and press `<Enter>`. This will kick off a new publish.
- Run the install procedure again in your local repo, (you may need to clean out node_modules first).
- Restart your storybook.
### Updating Tests
Before any contributions are submitted in a PR, make sure to add or update meaningful tests. A PR that has failing tests will be regarded as a “Work in Progress” and will not be merged until all tests pass.
When creating new unit test files, the tests should adhere to a particular folder structure and naming convention, as defined below.
```sh
# Proper naming convention and structure for js tests files
+-- parentFolder
| +-- [filename].js
| +-- [filename].test.js
```
## Pull Requests (PRs)
We welcome all contributions. There are many ways you can help us. This is few of those ways:
Before you submit a new PR, make sure you run `yarn test`. Do not submit a PR if tests are failing. If you need any help, the best way is to [join the discord server and ask in the maintenance channel](https://discord.gg/sMFvFsG).
### Reviewing PRs
**As a PR submitter**, you should reference the issue if there is one, include a short description of what you contributed and, if it is a code change, instructions for how to manually test out the change. This is informally enforced by our [PR template](https://github.com/storybookjs/storybook/blob/master/.github/PULL_REQUEST_TEMPLATE.md). If your PR is reviewed as only needing trivial changes (e.g. small typos etc), and you have commit access then you can merge the PR after making those changes.
> NOTE: Although the latest stable version of storybook corresponds to the `master` branch, nearly all Storybook development happens in the `next` branch. If you submit a PR, branch off `next` and target your PR to `next`.
**As a PR reviewer**, you should read through the changes and comment on any potential problems. If you see something cool, a kind word never hurts either! Additionally, you should follow the testing instructions and manually test the changes. If the instructions are missing, unclear, or overly complex, feel free to request better instructions from the submitter. Unless the PR is tagged with the `do not merge` label, if you approve the review and there is no other required discussion or changes, you should also go ahead and merge the PR.
## Issue Triage
If you are looking for a way to help the project, triaging issues is a great place to start. Here's how you can help:
### Responding to issues
Issues that are tagged `question / support` or `needs reproduction` are great places to help. If you can answer a question, it will help the asker as well as anyone who has a similar question. Also in the future if anyone has that same question they can easily find it by searching. If an issue needs reproduction, you may be able to guide the reporter toward one, or even reproduce it yourself using [this technique](https://github.com/storybookjs/storybook/blob/master/CONTRIBUTING.md#reproductions).
### Triaging issues
Once you've helped out on a few issues, if you'd like triage access you can help label issues and respond to reporters.
We use the following label scheme to categorize issues:
- **type** - `bug`, `feature`, `question / support`, `discussion`, `dependencies`, `maintenance`.
- **area** - `addon: x`, `addons-api`, `stories-api`, `ui`, etc.
- **status** - `needs reproduction`, `needs PR`, `in progress`, etc.
All issues should have a `type` label. `bug`/`feature`/`question`/`discussion` are self-explanatory. `dependencies` is for keeping package dependencies up to date. `maintenance` is a catch-all for any kind of cleanup or refactoring.
They should also have one or more `area`/`status` labels. We use these labels to filter issues down so we can see all of the issues for a particular area, and keep the total number of open issues under control.
For example, here is the list of [open, untyped issues](https://github.com/storybookjs/storybook/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20-label%3A%22bug%22%20-label%3A%22discussion%22%20-label%3A%22feature%22%20-label%3A%22maintenance%22%20-label%3A%22question%20%2F%20support%22%20-label%3A%22documentation%22%20-label%3A%22greenkeeper%22), or here is a list of [bugs that have not been modified since 2017-04-01](https://github.com/storybookjs/storybook/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3A%22bug%22%20updated%3A%3C%3D2017-04-01%20). For more info see [searching issues](https://help.github.com/articles/searching-issues/) in the Github docs.
If an issue is a `bug`, and it doesn't have a clear reproduction that you have personally confirmed, label it `needs reproduction` and ask the author to try and create a reproduction, or have a go yourself.
### Closing issues
- Duplicate issues should be closed with a link to the original.
- Unreproducible issues should be closed if it's not possible to reproduce them (if the reporter drops offline,
it is reasonable to wait 2 weeks before closing).
- `bug`s should be labelled `merged` when merged, and be closed when the issue is fixed and released.
- `feature`s, `maintenance`s, `greenkeeper`s should be labelled `merged` when merged,
and closed when released or if the feature is deemed not appropriate.
- `question / support`s should be closed when the question has been answered.
If the questioner drops offline, a reasonable period to wait is two weeks.
- `discussion`s should be closed at a maintainer's discretion.
## Development Guide
### Prerequisites
Please have the **_latest_** stable versions of the following on your machine
- node
- yarn
### Initial Setup
If you run into trouble here, make sure your node, npm, and **_yarn_** are on the latest versions (yarn at least v1.3.2).
1. `cd ~` (optional)
2. `git clone https://github.com/storybookjs/storybook.git` _bonus_: use your own fork for this step
3. `cd storybook`
4. `yarn bootstrap --core`
5. `yarn test --core`
6. `yarn dev` _You must have this running for your changes to show up_
> NOTE: on windows you may need to run `yarn` before `yarn bootstrap` (between steps 3 and 4).
#### Bootstrapping everything
_This method is slow_
1. `yarn bootstrap --all`
2. Take a break 🍵
3. `yarn test` (to verify everything worked)
#### Building specific packages
If you're working on one or a few packages, for every change that you make, you have to rebuild those packages. To make the process easier, there is a CLI command for that:
- Run `yarn build` to bring you a list of packages to select from. There will be also an option to run in watch mode.
- Run `yarn build <package-name>` to build that package specifically. \
For the package name, use its short version. Example: for `@storybook/addon-docs`, run `yarn build addon-docs`.
- Run `yarn build --all` to build everything.
- Add `--watch` to run automatically in watch more if you are either building a selection of packages by name or building all.
Example: `yarn build core addon-docs --watch` or `yarn build --all --watch`.
### Working with the kitchen sink apps
Within the `examples` folder of the Storybook repo, you will find kitchen sink examples of storybook implementations for the various platforms that storybook supports.
Not only do these show many of the options and add-ons available, they are also automatically linked to all the development packages. We highly encourage you to use these to develop/test contributions on.
#### React and Vue
1. `cd examples/official-storybook`
2. `yarn storybook`
3. Verify that your local version works
### Working with your own app
#### Linking Storybook
Storybook is broken up into sub-projects that you can install as you need them. For this example, we will be working with `@storybook/react`.
**Note:** You need to `yarn link` from inside the subproject you are working on **_NOT_** the storybook root directory
1. `cd app/react`
2. `yarn link`
#### Connecting Your App To Storybook
**_Note:_** If you aren't seeing addons after linking storybook, you probably have a versioning issue which can be fixed by linking each addon you want to use.
This applies for the kitchen sink apps as well as your own projects.
_Make sure `yarn dev` is running_
##### 1. Setup storybook in your project
First we are going to install storybook, then we are going to link `@storybook/react` into our project. This will replace `node_modules/@storybook/react` with a symlink to our local version of storybook.
1. `getstorybook`
2. `yarn storybook`
3. Verify that your local version works
##### 2. Link
**_Note_**: This process is the same for `@storybook/vue`, `@storybook/addon-foo`, etc
1. Go to your storybook _root_ directory
2. `yarn dev`
3. Wait until the output stops (changes you make will be transpiled into dist and logged here)
4. Go to your storybook-sandbox-app directory
5. `yarn link @storybook/react`
6. `yarn storybook`
#### Verify your local version is working
You should now have a working storybook dev environment up and running.
Save and go to `http://localhost:9011` (or wherever storybook is running)
If you don't see the changes rerun `yarn storybook` again in your sandbox app
## Release Guide
This section is for Storybook maintainers who will be creating releases. It assumes:
- yarn >= 1.3.2
- you've yarn linked `pr-log` from <https://github.com/storybookjs/pr-log/pull/2>
The current manual release sequence is as follows:
- Generate a changelog and verify the release by hand
- Push the changelog to master or the release branch
- Clean, build and publish the release
- Cut and paste the changelog to the github release page, and mark it as a (pre-) release
**NOTE:** The very first time you publish a scoped package (`@storybook/x`) you need to make sure that it's package.json contains the following
```js
"publishConfig": {
"access": "public"
}
```
This sequence applies to both releases and pre-releases, but differs slightly between the two.
**NOTE: This is a work in progress. Don't try this unless you know what you're doing. We hope to automate this in CI, so this process is designed with that in mind.**
#### Prerelease:
```sh
# make sure you current with origin/next.
git checkout next
git status
# generate changelog and edit as appropriate
# generates a Next section
yarn changelog:next x.y.z-alpha.a
# Edit the changelog/PRs as needed, then commit
git commit -m "x.y.z-alpha.a changelog"
# clean build
yarn bootstrap --reset --core
# publish and tag the release
yarn run publish:next
# update the release page
open https://github.com/storybookjs/storybook/releases
```
#### Full release:
```sh
# make sure you current with origin/master.
git checkout master
git status
# generate changelog and edit as appropriate
# generates a vNext section
yarn changelog x.y.z
# Edit the changelog/PRs as needed, then commit
git commit -m "x.y.z changelog"
# clean build
yarn bootstrap --reset --core
# publish and tag the release
yarn run publish:latest
# update the release page
open https://github.com/storybookjs/storybook/releases
```
During the transition, we're also preserving our [OLD, out of date contributing guide](./CONTRIBUTING.old.md) in parallel.

450
CONTRIBUTING.old.md Normal file
View File

@ -0,0 +1,450 @@
<h1>Contributing to Storybook</h1>
- [Issues](#issues)
- [Testing against `main`](#testing-against-main)
- [1. Download the latest version of this project, and build it:](#1-download-the-latest-version-of-this-project-and-build-it)
- [2a. Run unit tests](#2a-run-unit-tests)
- [Core & Examples Tests](#core--examples-tests)
- [2b. Run Linter](#2b-run-linter)
- [2c. Run Cypress tests](#2c-run-cypress-tests)
- [Reproductions](#reproductions)
- [In the monorepo](#in-the-monorepo)
- [Outside the monorepo](#outside-the-monorepo)
- [Updating Tests](#updating-tests)
- [Pull Requests (PRs)](#pull-requests-prs)
- [Reviewing PRs](#reviewing-prs)
- [Issue Triage](#issue-triage)
- [Responding to issues](#responding-to-issues)
- [Triaging issues](#triaging-issues)
- [Closing issues](#closing-issues)
- [Development Guide](#development-guide)
- [Prerequisites](#prerequisites)
- [Initial Setup](#initial-setup)
- [Bootstrapping everything](#bootstrapping-everything)
- [Building specific packages](#building-specific-packages)
- [Working with the kitchen sink apps](#working-with-the-kitchen-sink-apps)
- [React and Vue](#react-and-vue)
- [Working with your own app](#working-with-your-own-app)
- [Linking Storybook](#linking-storybook)
- [Connecting Your App To Storybook](#connecting-your-app-to-storybook)
- [1. Setup storybook in your project](#1-setup-storybook-in-your-project)
- [2. Link](#2-link)
- [Verify your local version is working](#verify-your-local-version-is-working)
- [Documentation](#documentation)
- [Release Guide](#release-guide)
- [Prerelease:](#prerelease)
- [Full release:](#full-release)
Thanks for your interest in improving Storybook! We are a community-driven project and welcome contributions of all kinds: from discussion to documentation to bugfixes to feature improvements.
Please review this document to help to streamline the process and save everyone's precious time.
This repo uses yarn workspaces, so you should install `yarn` as the package manager. See [installation guide](https://yarnpkg.com/en/docs/install).
## Issues
No software is bug-free. So, if you got an issue, follow these steps:
- Search the [issue list](https://github.com/storybookjs/storybook/issues) for current and old issues.
- If you find an existing issue, please UPVOTE the issue by adding a "thumbs-up reaction". We use this to help prioritize issues!
- If none of that is helping, create an issue with the following information:
- Clear title (shorter is better).
- Describe the issue in clear language.
- Share error logs, screenshots and etc.
- To speed up the issue fixing process, send us a sample repo with the issue you faced:
### Testing against `main`
To test your project against the current latest version of storybook, you can clone the repository and link it with `yarn`. Try following these steps:
#### 1. Download the latest version of this project, and build it:
```sh
git clone https://github.com/storybookjs/storybook.git
cd storybook
yarn bootstrap
```
> **_Note:_** On Windows, you may need to run `yarn` before `yarn bootstrap`!
The bootstrap command might ask which sections of the codebase you want to bootstrap. Unless you're doing something special you can keep the default.
You can also pick directly from CLI:
```sh
yarn bootstrap --core
```
#### 2a. Run unit tests
You can use one of the example projects in `examples/` to develop on.
This command will list all the suites and options for running tests.
```sh
yarn test
```
The options for running tests can be selected from the cli or be passed to `yarn test` with specific parameters. Available modes include `--watch`, `--coverage`, and `--runInBand`, which will respectively run tests in watch mode, output code coverage, and run selected test suites serially in the current process.
You can use the `--update` flag (or `jest -u`) to update snapshots or screenshots as needed.
> **_Note:_** On Windows, remember to make sure git config `core.autocrlf` is set to false, in order to not override EOL in snapshots ( `git config --global core.autocrlf false` to set it globally). It is also recommended to run tests from WSL2 to avoid errors with unix-style paths.
You can also pick suites from CLI. Suites available are listed below.
##### Core & Examples Tests
`yarn test`
This option executes tests from `<rootdir>/app/react`, `<rootdir>/app/vue`, and `<rootdir>/lib`.
Before the tests are run, the project must be bootstrapped with core. You can accomplish this with `yarn bootstrap --core`
#### 2b. Run Linter
We use eslint as a linter for all code (including typescript code).
All you have to run is:
```sh
yarn lint
```
It can be immensely helpful to get feedback in your editor, if you're using VsCode, you should install the `eslint` plugin and configure it with these settings:
```json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.packageManager": "yarn",
"eslint.options": {
"cache": true,
"cacheLocation": ".cache/eslint",
"extensions": [".js", ".jsx", ".json", ".html", ".ts", ".tsx", ".mjs"]
},
"eslint.alwaysShowStatus": true
}
```
This should enable auto-fix for all source files, and give linting warnings and errors within your editor.
### 2c. Run Cypress tests
First make sure the repo is bootstrapped.
Then run `yarn build-storybooks`, this creates a static website from all examples.
Then run `yarn serve-storybooks`, this will run the static site on the port cypress expects.
Then run `yarn add cypress -W --optional`. When this has completed cypress should be installed on your system. If it is already on your system, this step can be skipped.
Then run `yarn cypress open` if you want to see the tests run in the UI, or `yarn cypress run` to run the tests headless.
### Reproductions
#### In the monorepo
The best way to help figure out an issue you are having is to produce a minimal reproduction against the `main` branch.
A good way to do that is using the example `cra-kitchen-sink` app embedded in this repository:
```sh
# Download and build this repository:
git clone https://github.com/storybookjs/storybook.git
cd storybook
yarn
yarn bootstrap --core
# make changes to try and reproduce the problem, such as adding components + stories
cd examples/cra-kitchen-sink
yarn storybook
# see if you can see the problem, if so, commit it:
git checkout "branch-describing-issue"
git add -A
git commit -m "reproduction for issue #123"
# fork the storybook repo to your account, then add the resulting remote
git remote add <your-username> https://github.com/<your-username>/storybook.git
git push -u <your-username> next
```
If you follow that process, you can then link to the GitHub repository in the issue. See <https://github.com/storybookjs/storybook/issues/708#issuecomment-290589886> for an example.
**_Note:_** If your issue involves a webpack config, create-react-app will prevent you from modifying the _app's_ webpack config, however, you can still modify storybook's to mirror your app's version of the storybook. Alternatively, use `yarn eject` in the CRA app to get a modifiable webpack config.
#### Outside the monorepo
Sometimes your storybook is deeply ingrained in your own setup and it's hard to create a minimal viable reproduction somewhere else.
Inside the storybook repo we have a script that allows you to test the packages inside this repo in your own separate project.
You can use `npm link` on all packages, but npm linking is cumbersome and has subtle differences from what happens in a registry-based installation.
So the way our script works is that it:
- sets up a npm registry running on your own local machine
- changes your default registry to this local one
- builds all packages in the storybook repo
- publishes all packages as latest
Our script leaves the local registry running, for **as long as you keep it running** you can install storybook packages from this local registry.
- Navigate to your own project and then change `package.json` so the storybook packages match the version of the one you just published.
- Then you can install using `yarn` or `npm`
- Start using your storybook as normally.
If you've made a change to storybook's codebase and would want this change to be reflected in your app:
- Ensure the storybook packages are transpiled, by either having run `yarn dev` or `yarn bootstrap --core`.
- Go to the terminal where the local registry is running and press `<Enter>`. This will kick off a new publish.
- Run the install procedure again in your local repo, (you may need to clean out node_modules first).
- Restart your storybook.
### Updating Tests
Before any contributions are submitted in a PR, make sure to add or update meaningful tests. A PR that has failing tests will be regarded as a “Work in Progress” and will not be merged until all tests pass.
When creating new unit test files, the tests should adhere to a particular folder structure and naming convention, as defined below.
```sh
# Proper naming convention and structure for js tests files
+-- parentFolder
| +-- [filename].js
| +-- [filename].test.js
```
## Pull Requests (PRs)
We welcome all contributions. There are many ways you can help us. This is few of those ways:
Before you submit a new PR, make sure you run `yarn test`. Do not submit a PR if tests are failing. If you need any help, the best way is to [join the discord server and ask in the maintenance channel](https://discord.gg/storybook).
### Reviewing PRs
**As a PR submitter**, you should reference the issue if there is one, include a short description of what you contributed and, if it is a code change, instructions for how to manually test out the change. This is informally enforced by our [PR template](https://github.com/storybookjs/storybook/blob/main/.github/PULL_REQUEST_TEMPLATE.md). If your PR is reviewed as only needing trivial changes (e.g. small typos etc), and you have commit access then you can merge the PR after making those changes.
> **_Note:_** Although the latest stable version of storybook corresponds to the `main` branch, nearly all Storybook development happens in the `next` branch. If you submit a PR, branch off `next` and target your PR to `next`.
**As a PR reviewer**, you should read through the changes and comment on any potential problems. If you see something cool, a kind word never hurts either! Additionally, you should follow the testing instructions and manually test the changes. If the instructions are missing, unclear, or overly complex, feel free to request better instructions from the submitter. Unless the PR is tagged with the `do not merge` label, if you approve the review and there is no other required discussion or changes, you should also go ahead and merge the PR.
## Issue Triage
If you are looking for a way to help the project, triaging issues is a great place to start. Here's how you can help:
### Responding to issues
Issues that are tagged `question / support` or `needs reproduction` are great places to help. If you can answer a question, it will help the asker as well as anyone who has a similar question. Also in the future if anyone has that same question they can easily find it by searching. If an issue needs reproduction, you may be able to guide the reporter toward one, or even reproduce it yourself using [this technique](https://github.com/storybookjs/storybook/blob/main/CONTRIBUTING.md#reproductions).
### Triaging issues
Once you've helped out on a few issues, if you'd like triage access you can help label issues and respond to reporters.
We use the following label scheme to categorize issues:
- **type** - `bug`, `feature`, `question / support`, `discussion`, `dependencies`, `maintenance`.
- **area** - `addon: x`, `addons-api`, `stories-api`, `ui`, etc.
- **status** - `needs reproduction`, `needs PR`, `in progress`, etc.
All issues should have a `type` label. `bug`/`feature`/`question`/`discussion` are self-explanatory. `dependencies` is for keeping package dependencies up to date. `maintenance` is a catch-all for any kind of cleanup or refactoring.
They should also have one or more `area`/`status` labels. We use these labels to filter issues down so we can see all of the issues for a particular area, and keep the total number of open issues under control.
For example, here is the list of [open, untyped issues](https://github.com/storybookjs/storybook/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20-label%3A%22bug%22%20-label%3A%22discussion%22%20-label%3A%22feature%22%20-label%3A%22maintenance%22%20-label%3A%22question%20%2F%20support%22%20-label%3A%22documentation%22%20-label%3A%22greenkeeper%22), or here is a list of [bugs that have not been modified since 2017-04-01](https://github.com/storybookjs/storybook/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3A%22bug%22%20updated%3A%3C%3D2017-04-01%20). For more info see [searching issues](https://help.github.com/articles/searching-issues/) in the GitHub docs.
If an issue is a `bug`, and it doesn't have a clear reproduction that you have personally confirmed, label it `needs reproduction` and ask the author to try and create a reproduction, or have a go yourself.
### Closing issues
- Duplicate issues should be closed with a link to the original.
- Unreproducible issues should be closed if it's not possible to reproduce them (if the reporter drops offline,
it is reasonable to wait 2 weeks before closing).
- `bug`s should be labelled `merged` when merged, and be closed when the issue is fixed and released.
- `feature`s, `maintenance`s, `greenkeeper`s should be labelled `merged` when merged,
and closed when released or if the feature is deemed not appropriate.
- `question / support`s should be closed when the question has been answered.
If the questioner drops offline, a reasonable period to wait is two weeks.
- `discussion`s should be closed at a maintainer's discretion.
## Development Guide
### Prerequisites
Please have the **_latest_** stable versions of the following on your machine
- node
- yarn
### Initial Setup
If you run into trouble here, make sure your node, npm, and **_yarn_** are on the latest versions (yarn at least v1.3.2).
1. `cd ~` (optional)
2. `git clone https://github.com/storybookjs/storybook.git` _bonus_: use your own fork for this step
3. `cd storybook`
4. `yarn bootstrap --core`
> **_Note:_** On Windows, you may need to run `yarn` before `yarn bootstrap` (between steps 3 and 4).
This builds the entire project statically, but when you're updating Storybook code it's nice to see those changes show up in the example apps under `examples`. There are two ways to do this:
1. `yarn dev`
2. OR `yarn build <package1> <package2> --watch`
The former watches ALL packages, which is extremely slow. The latter only watches a fixed list of packages, e.g. `yarn build add-docs components --watch` to build `@storybook/addon-docs` and `@storybook/components`. This is much more practical on slower machines or if you know ahead of time the packages you'll be updating.
#### Bootstrapping everything
_This method is slow_
1. `yarn bootstrap --all`
2. Take a break 🍵
3. `yarn test` (to verify everything worked)
#### Building specific packages
If you're working on one or several packages, for every change that you make, you have to rebuild those packages. To make the process easier, there is a CLI command for that:
- Run `yarn build` to bring you a list of packages to select from. There will be also an option to run in watch mode.
- Run `yarn build <package-name>` to build that package specifically. \
For the package name, use its short version. Example: for `@storybook/addon-docs`, run `yarn build addon-docs`.
- Run `yarn build --all` to build everything.
- Add `--watch` to run automatically in watch mode if you are either building a selection of packages by name or building all.
Example: `yarn build core addon-docs --watch` or `yarn build --all --watch`.
### Working with the kitchen sink apps
Within the `examples` folder of the Storybook repo, you will find kitchen sink examples of storybook implementations for the various platforms that storybook supports.
Not only do these show many of the options and add-ons available, they are also automatically linked to all the development packages. We highly encourage you to use these to develop/test contributions on.
#### React and Vue
1. `cd examples/official-storybook`
2. `yarn storybook`
3. Verify that your local version works
### Working with your own app
#### Linking Storybook
Storybook is broken up into sub-projects that you can install as you need them. For this example, we will be working with `@storybook/react`.
**_Note:_** You need to `yarn link` from inside the subproject you are working on **_NOT_** the storybook root directory.
1. `cd app/react`
2. `yarn link`
#### Connecting Your App To Storybook
**_Note:_** If you aren't seeing addons after linking storybook, you probably have a versioning issue which can be fixed by linking each addon you want to use.
This applies for the kitchen sink apps as well as your own projects.
_Make sure `yarn dev` is running_
##### 1. Setup storybook in your project
First we are going to install storybook, then we are going to link `@storybook/react` into our project. This will replace `node_modules/@storybook/react` with a symlink to our local version of storybook.
1. `getstorybook`
2. `yarn storybook`
3. Verify that your local version works
##### 2. Link
**_Note:_** This process is the same for `@storybook/vue`, `@storybook/addon-foo`, etc
1. Go to your storybook _root_ directory
2. `yarn dev`
3. Wait until the output stops (changes you make will be transpiled into dist and logged here)
4. Go to your storybook-sandbox-app directory
5. `yarn link @storybook/react`
6. `yarn storybook`
#### Verify your local version is working
You should now have a working storybook dev environment up and running.
Save and go to `http://localhost:9011` (or wherever storybook is running).
If you don't see the changes rerun `yarn storybook` again in your sandbox app.
### Documentation
The documentation for Storybook is served by the [frontpage](https://github.com/storybookjs/frontpage), but the docs files are in this repository.
To see changes in a development version of the docs, use the "linking" method documented [here](https://github.com/storybookjs/frontpage#docs-content).
## Release Guide
This section is for Storybook maintainers who will be creating releases. It assumes:
- yarn >= 1.3.2
- you've yarn linked `pr-log` from <https://github.com/storybookjs/pr-log/pull/2>
The current manual release sequence is as follows:
- Generate a changelog and verify the release by hand
- Push the changelog to main or the release branch
- Clean, build and publish the release
- Cut and paste the changelog to the [GitHub release page](https://github.com/storybookjs/storybook/releases), and mark it as a (pre-) release
**_Note:_** The very first time you publish a scoped package (`@storybook/x`) you need to make sure that its package.json contains the following
```js
"publishConfig": {
"access": "public"
}
```
This sequence applies to both releases and pre-releases, but differs slightly between the two.
**_Note:_ This is a work in progress. Don't try this unless you know what you're doing. We hope to automate this in CI, so this process is designed with that in mind.**
#### Prerelease:
```sh
# make sure you current with origin/next.
git checkout next
git status
# generate changelog and edit as appropriate
# generates a Next section
yarn changelog:next x.y.z-alpha.a
# Edit the changelog/PRs as needed, then commit
git commit -m "x.y.z-alpha.a changelog"
# clean build
yarn bootstrap --reset --core
# publish and tag the release
yarn run publish:next
# update the release page
open https://github.com/storybookjs/storybook/releases
```
#### Full release:
```sh
# make sure you current with origin/main.
git checkout main
git status
# generate changelog and edit as appropriate
# generates a vNext section
yarn changelog x.y.z
# Edit the changelog/PRs as needed, then commit
git commit -m "x.y.z changelog"
# clean build
yarn bootstrap --reset --core
# publish and tag the release
yarn run publish:latest
# update the release page
open https://github.com/storybookjs/storybook/releases
```

View File

@ -1,54 +1,77 @@
This document will document some of the processes that members of the documentation team should adhere to.
This document outlines some of the processes that the maintainers should adhere to.
# PR Process
1. Triage with the correct [label](#labels)
2. If there is a change related to it ensure it has been published and tested before closing
2. If there is a change related to it, ensure it has been published and tested before closing
# Labels
| label name | purpose |
|:--------------:|:------------|
| accessibility | |
| addon:(name) | |
| app:(name) | |
| api:(name) | |
| cleanup | Minor cleanup style change that won't show up in release changelog |
| bug | |
| cli | |
| good first review | |
| compatibility with other tools | |
| patch | Bugfix & documentation PR that need to be picked to release branch |
| picked | Patch PRs cherry-picked to master |
| compatibility with other tools | |
| components | |
| core | |
| decorators | |
| dependencies:update | |
| dependencies | |
| discussion | |
| do not merge | |
| documentation | |
| feature request | |
| good first issue | |
| has workaround | |
| help wanted | |
| high priority | |
| in progress | |
| inactive | |
| maintenance | |
| merged | |
| needs example | |
| needs more info | |
| needs rebase | |
| needs reproduction | |
| needs review | |
| performance issue | |
| presets | |
| question / support | |
| ready | |
| security | |
| todo | |
| typescript | |
| ui | |
| won't fix | |
| label name | purpose |
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| accessibility | Issue, bug, or pull request related to accessibility |
| addon:(name) | Issue, bug, or pull request related to Storybook addons (e.g., [Controls](/docs/essentials/controls.md)) |
| app:(name) | Issue, bug, or pull request related to Storybook's supported frameworks (e.g., React) |
| api:(name) | Issue, bug, or pull request related to Storybook's API (e.g.,[makeDecorator](/docs/addons/addons-api.md#makeDecorator-API)) |
| args | Issue, bug, or pull request related to Storybook's [args](/docs/writing-stories/args.md) |
| babel/webpack | Issue, bug, or pull request related to Storybook's build system (e.g., Webpack or Babel), for Webpack 5 issues see below |
| block:(name) | Issue or bug within a certain surface are of Storybook (e.g., [argsTable](/docs/writing-docs/doc-blocks.md#argstable)) |
| BREAKING CHANGE | Issue or pull request that introduces a breaking change within Storybook's ecosystem. |
| BREAKING PRERELASE | Breaking, but only for prerelease users (not relative to the stable release) |
| build-storybook | Issue, bug, or pull request related to Storybook's production build |
| cleanup | Minor cleanup style change that won't show up in release changelog |
| bug | A bug within Storybook |
| cli | Issue, bug, or pull request that affects the Storybook's CLI |
| compatibility with other tools | Issue, bug, or pull request between Storybook and other tools (e.g., [Nuxt](https://nuxtjs.org/)) |
| components | Issue, bug, or pull request related to Storybook's internal components |
| composition | Issue, bug, or pull request related to Storybook [Composition](/docs/workflows/storybook-composition.md) |
| configuration | Issue, bug, or pull request related to Storybook [configuration](/docs/configure/overview.md) |
| core | Issue, bug, or pull request related to Storybook's Core |
| cra | Issue, bug, or pull request that affects Storybook's compatibility with Create React APP ([CRA](https://create-react-app.dev/docs/getting-started/))|
| CSF | Issue, bug, or pull request related to Storybook's [Component Story Format (CSF)](/docs/api/csf.md) |
| decorators | Issue, bug, or pull related to Storybook's [Decorators](/docs/writing-stories/decorators.md) |
| dependencies | Issue, bug, or pull request that related to upstream dependencies |
| discussion | Issue currently being discussed between the maintainers and community |
| do not merge | Pull request that will introduce regressions and will not be merged |
| documentation | Issue, bug, or pull request that affects Storybook's documentation |
| duplicate | Question or issue already asked in the repo's issues |
| feature request | Request for a new feature to be included in Storybook |
| flow | Issue, bug, or pull request related to Storybook and Flow |
| Funded on Issuehunt | Storybook issue funded on [IssueHunt](https://issuehunt.io/) |
| gatsby | Issue, bug, or pull request that affects Storybook and [Gatsby](https://www.gatsbyjs.com/) |
| good first issue | Low impact Storybook issues that help new members get involved and start contributing |
| has workaround | Issue or bug that has an alternative way to be solved with Storybook |
| help wanted | Issue, or bug that requires additional help from the community |
| ie11 | Issue, bug, or pull request related to Storybook and IE11 |
| in progress | Issue or pull request that is currently being reviewed or worked on with the author |
| inactive | Issue, or pull request that has gone stale and no active development has been done |
| maintenance | Issue, or pull request related to Storybook's internal maintenance |
| mdx | Issue, bug, or pull request related to MDX and Storybook |
| medium | Issue or pull request that involves a significant amount of work within Storybook |
| monorepos | Issue, bug, or pull request related to Storybook and monorepos (e.g., [lerna](https://lerna.js.org/) ) |
| mui | Issue, bug, or pull request that affects Storybook and [Material-UI](https://material-ui.com/) |
| multiframework | Issue, bug, or pull request that affects multiple supported frameworks (e.g., React, Vue) |
| needs more info | Issue, or bug that requires additional context from the author |
| needs reproduction | Issue, or bug that requires a reproduction to be looked at |
| needs triage | Issue, bug, or pull request that requires further investigation from the maintainers |
| nextjs | Issue, bug, or pull request related to Storybook's integration with [Next.js](https://nextjs.org/) |
| nx | Issue, bug, or pull request related to Storybook's integration with [NX](https://nx.dev/) |
| other | Storybook's miscellaneous issue or pull request |
| P(n) | Bug or issue priority. Ranges from `0` (most urgent) to `N` (least urgent) |
| patch | Bug fix and documentation pull request that will be picked to the main branch |
| performance issue | Issue, bug or pull request that affects Storybook's performance |
| picked | Patch PRs cherry-picked to the main branch |
| presets | Issue, bug, or pull requests that affect Storybook's presets |
| question / support | General question about Storybook |
| run e2e extended test suite | Pull request that affects Storybook's testing suite |
| search | Issue, bug or pull request related to Storybook's search functionality |
| security | Issue, bug, or pull request that addresses security with Storybook |
| small | Issue or pull request that requires a small amount of work to be done |
| source-loader | Issue, bug, or pull request related to code display within Storybook's stories |
| theming | Issue, bug, or pull request related to Storybook customization (e.g., [theming](/docs/configure/theming.md)) |
| todo | Issue or pull request currently being worked on |
| typescript | Issue, bug, or pull request related to TypeScript |
| ui | Issue, bug, or pull request related to Storybook's UI |
| webpack5 | Issue, bug, or pull request related to Webpack 5 |
| won't fix | Issue or pull request that won't be addressed by the maintainers (e.g., introduces a regression) |
| yarn/npm | Issue or pull request related to node package managers |

View File

@ -1,5 +1,38 @@
<h1>Migration</h1>
- [From version 6.2.x to 6.3.0](#from-version-62x-to-630)
- [Webpack 5 manager build](#webpack-5-manager-build)
- [Angular 12 upgrade](#angular-12-upgrade)
- [Lit support](#lit-support)
- [No longer inferring default values of args](#no-longer-inferring-default-values-of-args)
- [6.3 deprecations](#63-deprecations)
- [Deprecated addon-knobs](#deprecated-addon-knobs)
- [Deprecated scoped blocks imports](#deprecated-scoped-blocks-imports)
- [Deprecated layout URL params](#deprecated-layout-url-params)
- [From version 6.1.x to 6.2.0](#from-version-61x-to-620)
- [MDX pattern tweaked](#mdx-pattern-tweaked)
- [6.2 Angular overhaul](#62-angular-overhaul)
- [New Angular storyshots format](#new-angular-storyshots-format)
- [Deprecated Angular story component](#deprecated-angular-story-component)
- [New Angular renderer](#new-angular-renderer)
- [Components without selectors](#components-without-selectors)
- [Packages now available as ESModules](#packages-now-available-as-esmodules)
- [6.2 Deprecations](#62-deprecations)
- [Deprecated implicit PostCSS loader](#deprecated-implicit-postcss-loader)
- [Deprecated default PostCSS plugins](#deprecated-default-postcss-plugins)
- [Deprecated showRoots config option](#deprecated-showroots-config-option)
- [Deprecated control.options](#deprecated-controloptions)
- [Deprecated storybook components html entry point](#deprecated-storybook-components-html-entry-point)
- [From version 6.0.x to 6.1.0](#from-version-60x-to-610)
- [Addon-backgrounds preset](#addon-backgrounds-preset)
- [Single story hoisting](#single-story-hoisting)
- [React peer dependencies](#react-peer-dependencies)
- [6.1 deprecations](#61-deprecations)
- [Deprecated DLL flags](#deprecated-dll-flags)
- [Deprecated storyFn](#deprecated-storyfn)
- [Deprecated onBeforeRender](#deprecated-onbeforerender)
- [Deprecated grid parameter](#deprecated-grid-parameter)
- [Deprecated package-composition disabled parameter](#deprecated-package-composition-disabled-parameter)
- [From version 5.3.x to 6.0.x](#from-version-53x-to-60x)
- [Hoisted CSF annotations](#hoisted-csf-annotations)
- [Zero config typescript](#zero-config-typescript)
@ -14,6 +47,7 @@
- [DocsPage slots removed](#docspage-slots-removed)
- [React prop tables with Typescript](#react-prop-tables-with-typescript)
- [ConfigureJSX true by default in React](#configurejsx-true-by-default-in-react)
- [User babelrc disabled by default in MDX](#user-babelrc-disabled-by-default-in-mdx)
- [Docs description parameter](#docs-description-parameter)
- [6.0 Inline stories](#60-inline-stories)
- [New addon presets](#new-addon-presets)
@ -33,6 +67,7 @@
- [6.0 Addon API changes](#60-addon-api-changes)
- [Consistent local addon paths in main.js](#consistent-local-addon-paths-in-mainjs)
- [Deprecated setAddon](#deprecated-setaddon)
- [Deprecated disabled parameter](#deprecated-disabled-parameter)
- [Actions addon uses parameters](#actions-addon-uses-parameters)
- [Removed action decorator APIs](#removed-action-decorator-apis)
- [Removed withA11y decorator](#removed-witha11y-decorator)
@ -113,8 +148,8 @@
- [Addon story parameters](#addon-story-parameters)
- [From version 3.3.x to 3.4.x](#from-version-33x-to-34x)
- [From version 3.2.x to 3.3.x](#from-version-32x-to-33x)
- [`babel-core` is now a peer dependency (#2494)](#babel-core-is-now-a-peer-dependency-2494)
- [Base webpack config now contains vital plugins (#1775)](#base-webpack-config-now-contains-vital-plugins-1775)
- [`babel-core` is now a peer dependency #2494](#babel-core-is-now-a-peer-dependency-2494)
- [Base webpack config now contains vital plugins #1775](#base-webpack-config-now-contains-vital-plugins-1775)
- [Refactored Knobs](#refactored-knobs)
- [From version 3.1.x to 3.2.x](#from-version-31x-to-32x)
- [Moved TypeScript addons definitions](#moved-typescript-addons-definitions)
@ -127,6 +162,408 @@
- [Packages renaming](#packages-renaming)
- [Deprecated embedded addons](#deprecated-embedded-addons)
## From version 6.2.x to 6.3.0
### Webpack 5 manager build
Storybook 6.2 introduced **experimental** webpack5 support for building user components. Storybook 6.3 also supports building the manager UI in webpack 5 to avoid strange hoisting issues.
If you're upgrading from 6.2 and already using the experimental webpack5 feature, this might be a breaking change (hence the 'experimental' label) and you should try adding the manager builder:
```shell
yarn add @storybook/manager-webpack5 --dev
# Or
npm install @storybook/manager-webpack5 --save-dev
```
Because Storybook uses `webpack@4` as the default, it's possible for the wrong version of webpack to get hoisted by your package manager. If you receive an error that looks like you might be using the wrong version of webpack, install `webpack@5` explicitly as a dev dependency to force it to be hoisted:
```shell
yarn add webpack@5 --dev
# Or
npm install webpack@5 --save-dev
```
### Angular 12 upgrade
Storybook 6.3 supports Angular 12 out of the box when you install it fresh. However, if you're upgrading your project from a previous version, you'll need to do the following steps to force Storybook to use webpack 5 for building your project:
```shell
yarn add @storybook/builder-webpack5@next @storybook/manager-webpack5@next --dev
# Or
npm install @storybook/builder-webpack5@next @storybook/manager-webpack5@next --save-dev
```
Then edit your `.storybook/main.js` config:
```js
module.exports = {
core: {
builder: 'webpack5',
},
};
```
### Lit support
Storybook 6.3 introduces Lit 2 support in a non-breaking way to ease migration from `lit-html`/`lit-element` to `lit`.
To do so, it relies on helpers added in the latest minor versions of `lit-html`/`lit-element`. So when upgrading to Storybook 6.3, please ensure your project is using `lit-html` 1.4.x or `lit-element` 2.5.x.
According to the package manager you are using, it can be handled automatically when updating Storybook or can require to manually update the versions and regenerate the lockfile.
### No longer inferring default values of args
Previously, unset `args` were set to the `argType.defaultValue` if set or inferred from the component's prop types (etc.). In 6.3 we no longer infer default values and instead set arg values to `undefined` when unset, allowing the framework to supply the default value.
If you were using `argType.defaultValue` to fix issues with the above inference, it should no longer be necessary, you can remove that code.
If you were using `argType.defaultValue` or relying on inference to set a default value for an arg, you should now set a value for the arg at the component level:
```js
export default {
component: MyComponent,
args: {
argName: 'default-value',
},
};
```
To manually configure the value that is shown in the ArgsTable doc block, you can configure the `table.defaultValue` setting:
```js
export default {
component: MyComponent,
argTypes: {
argName: {
table: { defaultValue: { summary: 'SomeType<T>' } },
},
},
};
```
### 6.3 deprecations
#### Deprecated addon-knobs
We are replacing `@storybook/addon-knobs` with `@storybook/addon-controls`.
- [Rationale & discussion](https://github.com/storybookjs/storybook/discussions/15060)
- [Migration notes](https://github.com/storybookjs/storybook/blob/next/addons/controls/README.md#how-do-i-migrate-from-addon-knobs)
#### Deprecated scoped blocks imports
In 6.3, we changed doc block imports from `@storybook/addon-docs/blocks` to `@storybook/addon-docs`. This makes it possible for bundlers to automatically choose the ESM or CJS version of the library depending on the context.
To update your code, you should be able to global replace `@storybook/addon-docs/blocks` with `@storybook/addon-docs`. Example:
```js
// before
import { Meta, Story } from '@storybook/addon-docs/blocks';
// after
import { Meta, Story } from '@storybook/addon-docs';
```
#### Deprecated layout URL params
Several URL params to control the manager layout have been deprecated and will be removed in 7.0:
- `addons=0`: use `panel=false` instead
- `panelRight=1`: use `panel=right` instead
- `stories=0`: use `nav=false` instead
Additionally, support for legacy URLs using `selectedKind` and `selectedStory` will be removed in 7.0. Use `path` instead.
## From version 6.1.x to 6.2.0
### MDX pattern tweaked
In 6.2 files ending in `stories.mdx` or `story.mdx` are now processed with Storybook's MDX compiler. Previously it only applied to files ending in `.stories.mdx` or `.story.mdx`. See more here: [#13996](https://github.com/storybookjs/storybook/pull/13996).
### 6.2 Angular overhaul
#### New Angular storyshots format
We've updated the Angular storyshots format in 6.2, which is technically a breaking change. Apologies to semver purists: if you're using storyshots, you'll need to [update your snapshots](https://jestjs.io/docs/en/snapshot-testing#updating-snapshots).
The new format hides the implementation details of `@storybook/angular` so that we can evolve its renderer without breaking your snapshots in the future.
#### Deprecated Angular story component
Storybook 6.2 for Angular uses `parameters.component` as the preferred way to specify your stories' components. The previous method, in which the component was a return value of the story, has been deprecated.
Consider the existing story from 6.1 or earlier:
```ts
export default { title: 'Button' };
export const Basic = () => ({
component: Button,
props: { label: 'Label' },
});
```
From 6.2 this should be rewritten as:
```ts
export default { title: 'Button', component: Button };
export const Basic = () => ({
props: { label: 'Label' },
});
```
The new convention is consistent with how other frameworks and addons work in Storybook. The old way will be supported until 7.0. For a full discussion see https://github.com/storybookjs/storybook/issues/8673.
#### New Angular renderer
We've rewritten the Angular renderer in Storybook 6.2. It's meant to be entirely backwards compatible, but if you need to use the legacy renderer it's still available via a [parameter](https://storybook.js.org/docs/angular/writing-stories/parameters). To opt out of the new renderer, add the following to `.storybook/preview.ts`:
```ts
export const parameters = {
angularLegacyRendering: true,
};
```
Please also file an issue if you need to opt out. We plan to remove the legacy renderer in 7.0.
#### Components without selectors
When the new Angular renderer is used, all Angular Story components must either have a selector, or be added to the `entryComponents` array of the story's `moduleMetadata`. If the component has any `Input`s or `Output`s to be controlled with `args`, a selector should be added.
### Packages now available as ESModules
Many Storybook packages are now available as ESModules in addition to CommonJS. If your jest tests stop working, this is likely why. One common culprit is doc blocks, which [is fixed in 6.3](#deprecated-scoped-blocks-imports). In 6.2, you can configure jest to transform the packages like so ([more info](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring)):
```json
// In your jest config
transformIgnorePatterns: ['/node_modules/(?!@storybook)']
```
### 6.2 Deprecations
#### Deprecated implicit PostCSS loader
Previously, `@storybook/core` would automatically add the `postcss-loader` to your preview. This caused issues for consumers when PostCSS upgraded to v8 and tools, like Autoprefixer and Tailwind, starting requiring the new version. Implicitly adding `postcss-loader` will be removed in Storybook 7.0.
Instead of continuing to include PostCSS inside the core library, it has been moved to [`@storybook/addon-postcss`](https://github.com/storybookjs/addon-postcss). This addon provides more fine-grained customization and will be upgraded more flexibly to track PostCSS upgrades.
If you require PostCSS support, please install `@storybook/addon-postcss` in your project, add it to your list of addons inside `.storybook/main.js`, and configure a `postcss.config.js` file.
Further information is available at https://github.com/storybookjs/storybook/issues/12668 and https://github.com/storybookjs/storybook/pull/13669.
If you're not using Postcss and you don't want to see the warning, you can disable it by adding the following to your `.storybook/main.js`:
```js
module.exports = {
features: {
postcss: false,
},
};
```
#### Deprecated default PostCSS plugins
When relying on the [implicit PostCSS loader](#deprecated-implicit-postcss-loader), it would also add [autoprefixer v9](https://www.npmjs.com/package/autoprefixer/v/9.8.6) and [postcss-flexbugs-fixes v4](https://www.npmjs.com/package/postcss-flexbugs-fixes/v/4.2.1) plugins to the `postcss-loader` configuration when you didn't have a PostCSS config file (such as `postcss.config.js`) within your project.
They will no longer be applied when switching to `@storybook/addon-postcss` and the implicit PostCSS features will be removed in Storybook 7.0.
If you depend upon these plugins being applied, install them and create a `postcss.config.js` file within your project that contains:
```js
module.exports = {
plugins: [
require('postcss-flexbugs-fixes'),
require('autoprefixer')({
flexbox: 'no-2009',
}),
],
};
```
#### Deprecated showRoots config option
Config options for the sidebar are now under the `sidebar` namespace. The `showRoots` option should be set as follows:
```js
addons.setConfig({
sidebar: {
showRoots: false,
},
// showRoots: false <- this is deprecated
});
```
The top-level `showRoots` option will be removed in Storybook 7.0.
#### Deprecated control.options
Possible `options` for a radio/check/select controls has been moved up to the argType level, and no longer accepts an object. Instead, you should specify `options` as an array. You can use `control.labels` to customize labels. Additionally, you can use a `mapping` to deal with complex values.
```js
argTypes: {
answer:
options: ['yes', 'no'],
mapping: {
yes: <Check />,
no: <Cross />,
},
control: {
type: 'radio',
labels: {
yes: 'да',
no: 'нет',
}
}
}
}
```
Keys in `control.labels` as well as in `mapping` should match the values in `options`. Neither object has to be exhaustive, in case of a missing property, the option value will be used directly.
If you are currently using an object as value for `control.options`, be aware that the key and value are reversed in `control.labels`.
#### Deprecated storybook components html entry point
Storybook HTML components are now exported directly from '@storybook/components' for better ESM and Typescript compatibility. The old entry point will be removed in SB 7.0.
```js
// before
import { components } from '@storybook/components/html';
// after
import { components } from '@storybook/components';
```
## From version 6.0.x to 6.1.0
### Addon-backgrounds preset
In 6.1 we introduced an unintentional breaking change to `addon-backgrounds`.
The addon uses decorators which are set up automatically by a preset. The required preset is ignored if you register the addon in `main.js` with the `/register` entry point. This used to be valid in `v6.0.x` and earlier:
```js
module.exports = {
stories: ['../**/*.stories.js'],
addons: ['@storybook/addon-backgrounds/register'],
};
```
To fix it, just replace `@storybook/addon-backgrounds/register` with `@storybook/addon-backgrounds`:
```js
module.exports = {
stories: ['../**/*.stories.js'],
addons: ['@storybook/addon-backgrounds'],
};
```
### Single story hoisting
Stories which have **no siblings** (i.e. the component has only one story) and which name **exactly matches** the component name will now be hoisted up to replace their parent component in the sidebar. This means you can have a hierarchy like this:
```
DESIGN SYSTEM [root]
- Atoms [group]
- Button [component]
- Button [story]
- Checkbox [component]
- Checkbox [story]
```
This will then be visually presented in the sidebar like this:
```
DESIGN SYSTEM [root]
- Atoms [group]
- Button [story]
- Checkbox [story]
```
See [Naming components and hierarchy](https://storybook.js.org/docs/react/writing-stories/naming-components-and-hierarchy#single-story-hoisting) for details.
### React peer dependencies
Starting in 6.1, `react` and `react-dom` are required peer dependencies of `@storybook/react`, meaning that if your React project does not have dependencies on them, you need to add them as `devDependencies`. If you don't you'll see errors like this:
```
Error: Cannot find module 'react-dom/package.json'
```
They were also peer dependencies in earlier versions, but due to the package structure they would be installed by Storybook if they were not required by the user's project. For more discussion: https://github.com/storybookjs/storybook/issues/13269
### 6.1 deprecations
#### Deprecated DLL flags
Earlier versions of Storybook used Webpack DLLs as a performance crutch. In 6.1, we've removed Storybook's built-in DLLs and have deprecated the command-line parameters `--no-dll` and `--ui-dll`. They will be removed in 7.0.
#### Deprecated storyFn
Each item in the story store contains a field called `storyFn`, which is a fully decorated story that's applied to the denormalized story parameters. Starting in 6.0 we've stopped using this API internally, and have replaced it with a new field called `unboundStoryFn` which, unlike `storyFn`, must passed a story context, typically produced by `applyLoaders`;
Before:
```js
const { storyFn } = store.fromId('some--id');
console.log(storyFn());
```
After:
```js
const { unboundStoryFn, applyLoaders } = store.fromId('some--id');
const context = await applyLoaders();
console.log(unboundStoryFn(context));
```
If you're not using loaders, `storyFn` will work as before. If you are, you'll need to use the new approach.
> NOTE: If you're using `@storybook/addon-docs`, this deprecation warning is triggered by the Docs tab in 6.1. It's safe to ignore and we will be providing a proper fix in a future release. You can track the issue at https://github.com/storybookjs/storybook/issues/13074.
#### Deprecated onBeforeRender
The `@storybook/addon-docs` previously accepted a `jsx` option called `onBeforeRender`, which was unfortunately named as it was called after the render.
We've renamed it `transformSource` and also allowed it to receive the `StoryContext` in case source rendering requires additional information.
#### Deprecated grid parameter
Previously when using `@storybook/addon-backgrounds` if you wanted to customize the grid, you would define a parameter like this:
```js
export const Basic = () => <Button />
Basic.parameters: {
grid: {
cellSize: 10
}
},
```
As grid is not an addon, but rather backgrounds is, the grid configuration was moved to be inside `backgrounds` parameter instead. Also, there are new properties that can be used to further customize the grid. Here's an example with the default values:
```js
export const Basic = () => <Button />
Basic.parameters: {
backgrounds: {
grid: {
disable: false,
cellSize: 20,
opacity: 0.5,
cellAmount: 5,
offsetX: 16, // default is 0 if story has 'fullscreen' layout, 16 if layout is 'padded'
offsetY: 16, // default is 0 if story has 'fullscreen' layout, 16 if layout is 'padded'
}
}
},
```
#### Deprecated package-composition disabled parameter
Like [Deprecated disabled parameter](#deprecated-disabled-parameter). The `disabled` parameter has been deprecated, please use `disable` instead.
For more information, see the [the related documentation](https://storybook.js.org/docs/react/workflows/package-composition#configuring).
## From version 5.3.x to 6.0.x
### Hoisted CSF annotations
@ -157,7 +594,7 @@ Basic.decorators = [ ... ];
2. Similar to React's `displayName`, `propTypes`, `defaultProps` annotations
3. We're introducing a new feature, [Storybook Args](https://docs.google.com/document/d/1Mhp1UFRCKCsN8pjlfPdz8ZdisgjNXeMXpXvGoALjxYM/edit?usp=sharing), where the new syntax will be significantly more ergonomic
To help you upgrade your stories, we've crated a codemod:
To help you upgrade your stories, we've created a codemod:
```
npx @storybook/cli@next migrate csf-hoist-story-annotations --glob="**/*.stories.js"
@ -263,7 +700,7 @@ In SB5.2, we introduced the concept of [DocsPage slots](https://github.com/story
In 5.3, we introduced `docs.x` story parameters like `docs.prepareForInline` which get filled in by frameworks and can also be overwritten by users, which is a more natural/convenient way to make global customizations.
We also introduced introduced [Custom DocsPage](https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/docspage.md#replacing-docspage), which makes it possible to add/remove/update DocBlocks on the page.
We also introduced [Custom DocsPage](https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/docspage.md#replacing-docspage), which makes it possible to add/remove/update DocBlocks on the page.
These mechanisms are superior to slots, so we've removed slots in 6.0. For each slot, we provide a migration path here:
@ -301,6 +738,23 @@ module.exports = {
};
```
#### User babelrc disabled by default in MDX
In SB 6.0, the Storybook Docs no longer applies the user's babelrc by default when processing MDX files. It caused lots of hard-to-diagnose bugs.
To restore the old behavior, or pass any MDX-specific babel options, you can configure `.storybook/main.js`:
```js
module.exports = {
addons: [
{
name: '@storybook/addon-docs',
options: { mdxBabelOptions: { babelrc: true, configFile: true } },
},
],
};
```
#### Docs description parameter
In 6.0, you can customize a component description using the `docs.description.component` parameter, and a story description using `docs.description.story` parameter.
@ -434,7 +888,7 @@ The story store no longer emits `renderCurrentStory`/`RENDER_CURRENT_STORY` to t
We've removed the ability to specify the hierarchy separators (how you control the grouping of story kinds in the sidebar). From Storybook 6.0 we have a single separator `/`, which cannot be configured.
If you are currently using using custom separators, we encourage you to migrate to using `/` as the sole separator. If you are using `|` or `.` as a separator currently, we provide a codemod, [`upgrade-hierarchy-separators`](https://github.com/storybookjs/storybook/blob/next/lib/codemod/README.md#upgrade-hierarchy-separators), that can be used to rename your components. **Note: the codemod will not work for `.mdx` components, you will need to make the changes by hand.**
If you are currently using custom separators, we encourage you to migrate to using `/` as the sole separator. If you are using `|` or `.` as a separator currently, we provide a codemod, [`upgrade-hierarchy-separators`](https://github.com/storybookjs/storybook/blob/next/lib/codemod/README.md#upgrade-hierarchy-separators), that can be used to rename your components. **Note: the codemod will not work for `.mdx` components, you will need to make the changes by hand.**
```
npx sb@next migrate upgrade-hierarchy-separators --glob="*/**/*.stories.@(tsx|jsx|ts|js)"
@ -443,7 +897,7 @@ npx sb@next migrate upgrade-hierarchy-separators --glob="*/**/*.stories.@(tsx|js
We also now default to showing "roots", which are non-expandable groupings in the sidebar for the top-level groups. If you'd like to disable this, set the `showRoots` option in `.storybook/manager.js`:
```js
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
addons.setConfig({
showRoots: false,
@ -581,6 +1035,22 @@ We've deprecated the `setAddon` method of the `storiesOf` API and plan to remove
Since early versions, Storybook shipped with a `setAddon` API, which allows you to extend `storiesOf` with arbitrary code. We've removed this from all core addons long ago and recommend writing stories in [Component Story Format](https://medium.com/storybookjs/component-story-format-66f4c32366df) rather than using the internal Storybook API.
#### Deprecated disabled parameter
Starting in 6.0.17, we've renamed the `disabled` parameter to `disable` to resolve an inconsistency where `disabled` had been used to hide the addon panel, whereas `disable` had been used to disable an addon's execution. Since `disable` was much more widespread in the code, we standardized on that.
So, for example:
```
Story.parameters = { actions: { disabled: true } }
```
Should be rewritten as:
```
Story.parameters = { actions: { disable: true } }
```
#### Actions addon uses parameters
Leveraging the new preset `@storybook/addon-actions` uses parameters to pass action options. If you previously had:
@ -606,7 +1076,7 @@ StoryOne.story = {
#### Removed action decorator APIs
In 6.0 we removed the actions addon decorate API. Actions handles can be configured globaly, for a collection of stories or per story via parameters. The ability to manipulate the data arguments of an event is only relevant in a few frameworks and is not a common enough usecase to be worth the complexity of supporting.
In 6.0 we removed the actions addon decorate API. Actions handles can be configured globally, for a collection of stories or per story via parameters. The ability to manipulate the data arguments of an event is only relevant in a few frameworks and is not a common enough usecase to be worth the complexity of supporting.
#### Removed withA11y decorator
@ -679,17 +1149,17 @@ We've deprecated the following in 6.0: `addon-info`, `addon-notes`, `addon-conte
The info/notes addons have been replaced by [addon-docs](https://github.com/storybookjs/storybook/tree/next/addons/docs). We've documented a migration in the [docs recipes](https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/recipes.md#migrating-from-notesinfo-addons).
Both addons are still widely used, and their source code is still available in the [deprecated-addons repo](https://github.com/storybookjs/deprecated-addons). We're looking for maintainers for both addons. If you're interested, please get in touch on [our Discord](https://discordapp.com/invite/UUt2PJb).
Both addons are still widely used, and their source code is still available in the [deprecated-addons repo](https://github.com/storybookjs/deprecated-addons). We're looking for maintainers for both addons. If you're interested, please get in touch on [our Discord](https://discord.gg/storybook).
#### Deprecated addon-contexts
The contexts addon has been replaced by [addon-toolbars](https://github.com/storybookjs/storybook/blob/next/addons/toolbars), which is simpler, more ergonomic, and compatible with all Storybook frameworks.
The addon's source code is still available in the [deprecated-addons repo](https://github.com/storybookjs/deprecated-addons). If you're interested in maintaining it, please get in touch on [our Discord](https://discordapp.com/invite/UUt2PJb).
The addon's source code is still available in the [deprecated-addons repo](https://github.com/storybookjs/deprecated-addons). If you're interested in maintaining it, please get in touch on [our Discord](https://discord.gg/storybook).
#### Removed addon-centered
In 6.0 we removed the centered addon. Centering is now core feature of storybook, so w no longer need an addon.
In 6.0 we removed the centered addon. Centering is now core feature of storybook, so we no longer need an addon.
Remove the addon-centered decorator and instead add a `layout` parameter:
@ -704,7 +1174,7 @@ Other possible values are: `padded` (default) and `fullscreen`.
#### Deprecated polymer
We've deprecated `@storybook/polymer` and are focusing on `@storybook/web-components`. If you use Polymer and are interested in maintaining it, please get in touch on [our Discord](https://discordapp.com/invite/UUt2PJb).
We've deprecated `@storybook/polymer` and are focusing on `@storybook/web-components`. If you use Polymer and are interested in maintaining it, please get in touch on [our Discord](https://discord.gg/storybook).
#### Deprecated immutable options parameters
@ -714,7 +1184,7 @@ You should use `addon.setConfig` to set them:
```js
// in .storybook/manager.js
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
addons.setConfig({
showRoots: false,
@ -968,7 +1438,7 @@ addParameters({
### Grid toolbar-feature
The grid feature in the toolbar has been relocated to [addon-background](https://github.com/storybookjs/storybook/tree/next/addons/backgrounds), follow the setup intructions on that addon to get the feature again.
The grid feature in the toolbar has been relocated to [addon-background](https://github.com/storybookjs/storybook/tree/next/addons/backgrounds), follow the setup instructions on that addon to get the feature again.
### Docs mode docgen
@ -1125,7 +1595,7 @@ sortedModules.forEach((key) => {
### Webpack config simplification
The API for custom webpack configuration has been simplifed in 5.0, but it's a breaking change. Storybook's "full control mode" for webpack allows you to override the webpack config with a function that returns a configuration object.
The API for custom webpack configuration has been simplified in 5.0, but it's a breaking change. Storybook's "full control mode" for webpack allows you to override the webpack config with a function that returns a configuration object.
In Storybook 5 there is a single signature for full-control mode that takes a parameters object with the fields `config` and `mode`:
@ -1156,7 +1626,7 @@ In 5.0, we now provide recommended defaults:
}
```
This means if you use the characters { `|`, `/`, `.` } in your story kinds it will triggger the story hierarchy to appear. For example `storiesOf('UI|Widgets/Basics/Button')` will create a story root called `UI` containing a `Widgets/Basics` group, containing a `Button` component.
This means if you use the characters { `|`, `/`, `.` } in your story kinds it will trigger the story hierarchy to appear. For example `storiesOf('UI|Widgets/Basics/Button')` will create a story root called `UI` containing a `Widgets/Basics` group, containing a `Button` component.
If you wish to opt-out of this new behavior and restore the flat UI, set them back to `null` in your storybook config, or remove { `|`, `/`, `.` } from your story kinds:
@ -1375,7 +1845,7 @@ import centered from '@storybook/addon-centered/vue';
Storybook's keyboard shortcuts are updated in 5.0, but they are configurable via the menu so if you want to set them back you can:
| Shorctut | Old | New |
| Shortcut | Old | New |
| ---------------------- | ----------- | ----- |
| Toggle sidebar | cmd-shift-X | S |
| Toggle addons panel | cmd-shift-Z | A |
@ -1630,7 +2100,7 @@ There are no expected breaking changes in the 3.4.x release, but 3.4 contains a
It wasn't expected that there would be any breaking changes in this release, but unfortunately it turned out that there are some. We're revisiting our [release strategy](https://github.com/storybookjs/storybook/blob/master/RELEASES.md) to follow semver more strictly.
Also read on if you're using `addon-knobs`: we advise an update to your code for efficiency's sake.
### `babel-core` is now a peer dependency ([#2494](https://github.com/storybookjs/storybook/pull/2494))
### `babel-core` is now a peer dependency #2494
This affects you if you don't use babel in your project. You may need to add `babel-core` as dev dependency:
@ -1640,7 +2110,7 @@ yarn add babel-core --dev
This was done to support different major versions of babel.
### Base webpack config now contains vital plugins ([#1775](https://github.com/storybookjs/storybook/pull/1775))
### Base webpack config now contains vital plugins #1775
This affects you if you use custom webpack config in [Full Control Mode](https://storybook.js.org/docs/react/configure/webpack#full-control-mode) while not preserving the plugins from `storybookBaseConfig`. Before `3.3`, preserving them was a recommendation, but now it [became](https://github.com/storybookjs/storybook/pull/2578) a requirement.

142
README.md
View File

@ -19,17 +19,17 @@
<img src="https://snyk.io/test/github/storybookjs/storybook/badge.svg" alt="Known Vulnerabilities" />
</a>
<a href="https://codecov.io/gh/storybookjs/storybook">
<img src="https://codecov.io/gh/storybookjs/storybook/branch/master/graph/badge.svg" alt="codecov" />
<img src="https://codecov.io/gh/storybookjs/storybook/branch/main/graph/badge.svg" alt="codecov" />
</a>
<a href="https://github.com/storybookjs/storybook/blob/master/LICENSE">
<a href="https://github.com/storybookjs/storybook/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/storybookjs/storybook.svg" alt="License" />
</a>
<br/>
<a href="https://discord.gg/sMFvFsG">
<a href="https://discord.gg/storybook">
<img src="https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat" />
</a>
<a href="https://now-examples-slackin-rrirkqohko.now.sh/">
<img src="https://now-examples-slackin-rrirkqohko.now.sh/badge.svg?logo=slack" alt="Storybook Slack" />
<a href="https://storybook.js.org/community/">
<img src="https://img.shields.io/badge/community-join-4BC424.svg" alt="Storybook Community" />
</a>
<a href="#backers">
<img src="https://opencollective.com/storybook/backers/badge.svg" alt="Backers on Open Collective" />
@ -42,27 +42,19 @@
</a>
</p>
Storybook is a development environment for UI components.
It allows you to browse a component library, view the different states of each component, and interactively develop and test components.
[Storybook](https://storybook.js.org) is a development environment for UI components.
It allows you to browse a component library, view the different states of each component, and interactively develop and test components. Find out more at https://storybook.js.org.
<center>
<img src="https://raw.githubusercontent.com/storybookjs/storybook/master/media/storybook-intro.gif" width="100%" />
<img src="https://raw.githubusercontent.com/storybookjs/storybook/main/media/storybook-intro.gif" width="100%" />
</center>
<p align="center">
View README for:<br/>
<a href="https://github.com/storybookjs/storybook/blob/master/README.md" title="latest"><img alt="latest" src="https://img.shields.io/npm/v/@storybook/core/latest.svg" /></a>
<a href="https://github.com/storybookjs/storybook/blob/main/README.md" title="latest"><img alt="latest" src="https://img.shields.io/npm/v/@storybook/core/latest.svg" /></a>
<a href="https://github.com/storybookjs/storybook/blob/next/README.md" title="next"><img alt="next" src="https://img.shields.io/npm/v/@storybook/core/next.svg" /></a>
</p>
## Intro
Storybook runs outside of your app. This allows you to develop UI components in isolation, which can improve component reuse, testability, and development speed. You can build quickly without having to worry about application-specific dependencies.
Here are some featured examples that you can reference to see how Storybook works: <https://storybook.js.org/docs/examples/>
Storybook comes with a lot of [addons](https://storybook.js.org/docs/react/configure/storybook-addons) for component design, documentation, testing, interactivity, and so on. Storybook's API makes it possible to configure and extend in various ways. It has even been extended to support React Native development for mobile.
## Table of contents
- 🚀 [Getting Started](#getting-started)
@ -80,40 +72,43 @@ Storybook comes with a lot of [addons](https://storybook.js.org/docs/react/confi
## Getting Started
First install storybook:
Visit [Storybook's website](https://storybook.js.org) to learn more about Storybook, and to get started.
```sh
cd my-react-app
npx sb init
```
### Documentation
If you'd rather set up your project manually, take a look at our [Slow Start Guide](https://storybook.js.org/docs/react/configure/overview).
Documentation can be found [Storybook's docs site](https://storybook.js.org/docs).
Once it's installed, you can `npm run storybook` and it will run the development server on your local machine, and give you a URL to browse some sample stories.
### Examples
For full documentation on using Storybook visit: [storybook.js.org](https://storybook.js.org)
Here are some featured examples that you can reference to see how Storybook works: <https://storybook.js.org/docs/react/get-started/examples>
For additional help, join us [in our Discord](https://discord.gg/sMFvFsG) or [Slack (legacy)](https://now-examples-slackin-rrirkqohko.now.sh/)
Storybook comes with a lot of [addons](https://storybook.js.org/docs/react/configure/storybook-addons) for component design, documentation, testing, interactivity, and so on. Storybook's API makes it possible to configure and extend in various ways. It has even been extended to support React Native, Android, iOS, and Flutter development for mobile.
### Community
For additional help, join us in the [Storybook Discord](https://discord.gg/storybook).
## Projects
### Supported Frameworks
| Framework | Demo | |
| -------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [React](app/react) | [v6.0.x](https://storybookjs.netlify.com/official-storybook/?path=/story/*) | [![React](https://img.shields.io/npm/dm/@storybook/react.svg)](app/react) |
| [React Native](app/react-native) | - | [![React Native](https://img.shields.io/npm/dm/@storybook/react-native.svg)](app/react-native) |
| [Vue](app/vue) | [v6.0.x](https://storybookjs.netlify.com/vue-kitchen-sink/) | [![Vue](https://img.shields.io/npm/dm/@storybook/vue.svg)](app/vue) |
| [Angular](app/angular) | [v6.0.x](https://storybookjs.netlify.com/angular-cli/) | [![Angular](https://img.shields.io/npm/dm/@storybook/angular.svg)](app/angular) |
| [Marionette.js](app/marionette) | - | [![Marionette.js](https://img.shields.io/npm/dm/@storybook/marionette.svg)](app/marionette) |
| [Mithril](app/mithril) | [v6.0.x](https://storybookjs.netlify.com/mithril-kitchen-sink/) | [![Mithril](https://img.shields.io/npm/dm/@storybook/mithril.svg)](app/mithril) |
| [Marko](app/marko) | [v6.0.x](https://storybookjs.netlify.com/marko-cli/) | [![Marko](https://img.shields.io/npm/dm/@storybook/marko.svg)](app/marko) |
| [HTML](app/html) | [v6.0.x](https://storybookjs.netlify.com/html-kitchen-sink/) | [![HTML](https://img.shields.io/npm/dm/@storybook/html.svg)](app/html) |
| [Svelte](app/svelte) | [v6.0.x](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/svelte.svg)](app/svelte) |
| [Riot](app/riot) | [v6.0.x](https://storybookjs.netlify.com/riot-kitchen-sink/) | [![Riot](https://img.shields.io/npm/dm/@storybook/riot.svg)](app/riot) |
| [Ember](app/ember) | [v6.0.x](https://storybookjs.netlify.com/ember-cli/) | [![Ember](https://img.shields.io/npm/dm/@storybook/ember.svg)](app/ember) |
| [Preact](app/preact) | [v6.0.x](https://storybookjs.netlify.com/preact-kitchen-sink/) | [![Preact](https://img.shields.io/npm/dm/@storybook/preact.svg)](app/preact) |
| [Rax](app/rax) | [v6.0.x](https://storybookjs.netlify.com/rax-kitchen-sink/) | [![Rax](https://img.shields.io/npm/dm/@storybook/rax.svg)](app/rax) |
| Framework | Demo | |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| [React](app/react) | [v6.3.x](https://storybookjs.netlify.com/official-storybook/?path=/story/*) | [![React](https://img.shields.io/npm/dm/@storybook/react.svg)](app/react) |
| [Vue](app/vue) | [v6.3.x](https://storybookjs.netlify.com/vue-kitchen-sink/) | [![Vue](https://img.shields.io/npm/dm/@storybook/vue.svg)](app/vue) |
| [Angular](app/angular) | [v6.3.x](https://storybookjs.netlify.com/angular-cli/) | [![Angular](https://img.shields.io/npm/dm/@storybook/angular.svg)](app/angular) |
| [Web components](app/web-components) | [v6.3.x](https://storybookjs.netlify.com/web-components-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/web-components.svg)](app/web-components) |
| [React Native](https://github.com/storybookjs/react-native) | - | [![React Native](https://img.shields.io/npm/dm/@storybook/react-native.svg)](app/react-native) |
| [HTML](app/html) | [v6.3.x](https://storybookjs.netlify.com/html-kitchen-sink/) | [![HTML](https://img.shields.io/npm/dm/@storybook/html.svg)](app/html) |
| [Ember](app/ember) | [v6.3.x](https://storybookjs.netlify.com/ember-cli/) | [![Ember](https://img.shields.io/npm/dm/@storybook/ember.svg)](app/ember) |
| [Svelte](app/svelte) | [v6.3.x](https://storybookjs.netlify.com/svelte-kitchen-sink/) | [![Svelte](https://img.shields.io/npm/dm/@storybook/svelte.svg)](app/svelte) |
| [Preact](app/preact) | [v6.3.x](https://storybookjs.netlify.com/preact-kitchen-sink/) | [![Preact](https://img.shields.io/npm/dm/@storybook/preact.svg)](app/preact) |
| [Marionette.js](app/marionette) | - | [![Marionette.js](https://img.shields.io/npm/dm/@storybook/marionette.svg)](app/marionette) |
| [Mithril](app/mithril) | [v6.3.x](https://storybookjs.netlify.com/mithril-kitchen-sink/) | [![Mithril](https://img.shields.io/npm/dm/@storybook/mithril.svg)](app/mithril) |
| [Marko](app/marko) | [v6.3.x](https://storybookjs.netlify.com/marko-cli/) | [![Marko](https://img.shields.io/npm/dm/@storybook/marko.svg)](app/marko) |
| [Riot](app/riot) | [v6.3.x](https://storybookjs.netlify.com/riot-kitchen-sink/) | [![Riot](https://img.shields.io/npm/dm/@storybook/riot.svg)](app/riot) |
| [Rax](app/rax) | [v6.3.x](https://storybookjs.netlify.com/rax-kitchen-sink/) | [![Rax](https://img.shields.io/npm/dm/@storybook/rax.svg)](app/rax) |
| [Android, iOS, Flutter](https://github.com/storybookjs/native) | [v6.3.x](https://storybookjs.github.io/native/@storybook/native-flutter-example/index.html) | [![Rax](https://img.shields.io/npm/dm/@storybook/native.svg)](https://github.com/storybookjs/native) |
### Sub Projects
@ -122,35 +117,35 @@ For additional help, join us [in our Discord](https://discord.gg/sMFvFsG) or [Sl
### Addons
| Addons | |
| ------------------------------------------- | -------------------------------------------------------------------------- |
| [a11y](addons/a11y/) | Test components for user accessibility in Storybook |
| [actions](addons/actions/) | Log actions as users interact with components in the Storybook UI |
| [backgrounds](addons/backgrounds/) | Let users choose backgrounds in the Storybook UI |
| [cssresources](addons/cssresources/) | Dynamically add/remove css resources to the component iframe |
| [design assets](addons/design-assets/) | View images, videos, weblinks alongside your story |
| [docs](addons/docs/) | Add high quality documentation to your components |
| [events](addons/events/) | Interactively fire events to components that respond to EventEmitter |
| [google-analytics](addons/google-analytics) | Reports google analytics on stories |
| [graphql](addons/graphql/) | Query a GraphQL server within Storybook stories |
| [jest](addons/jest/) | View the results of components' unit tests in Storybook |
| [knobs](addons/knobs/) | Interactively edit component prop data in the Storybook UI |
| [links](addons/links/) | Create links between stories |
| [query params](addons/queryparams/) | Mock query params |
| [storyshots](addons/storyshots/) | Snapshot testing for components in Storybook |
| [storysource](addons/storysource/) | View the code of your stories within the Storybook UI |
| [viewport](addons/viewport/) | Change display sizes and layouts for responsive components using Storybook |
| Addons | |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| [a11y](addons/a11y/) | Test components for user accessibility in Storybook |
| [actions](addons/actions/) | Log actions as users interact with components in the Storybook UI |
| [backgrounds](addons/backgrounds/) | Let users choose backgrounds in the Storybook UI |
| [cssresources](https://github.com/storybookjs/addon-cssresources) | Dynamically add/remove css resources to the component iframe |
| [design assets](https://github.com/storybookjs/addon-design-assets) | View images, videos, weblinks alongside your story |
| [docs](addons/docs/) | Add high quality documentation to your components |
| [events](https://github.com/storybookjs/addon-events) | Interactively fire events to components that respond to EventEmitter |
| [google-analytics](https://github.com/storybookjs/addon-google-analytics) | Reports google analytics on stories |
| [graphql](https://github.com/storybookjs/addon-graphql) | Query a GraphQL server within Storybook stories |
| [jest](addons/jest/) | View the results of components' unit tests in Storybook |
| [links](addons/links/) | Create links between stories |
| [query params](https://github.com/storybookjs/addon-queryparams) | Mock query params |
| [storyshots](addons/storyshots/) | Snapshot testing for components in Storybook |
| [storysource](addons/storysource/) | View the code of your stories within the Storybook UI |
| [viewport](addons/viewport/) | Change display sizes and layouts for responsive components using Storybook |
See [Addon / Framework Support Table](ADDONS_SUPPORT.md)
See [Addon / Framework Support Table](https://storybook.js.org/docs/react/api/frameworks-feature-support)
### Deprecated Addons
| Addons | |
| ---------------------------------------------------------------------------------- | -------------------------------------------------------- |
| [info](https://github.com/storybookjs/deprecated-addons/tree/master/addons/info) | Annotate stories with extra component usage information |
| [notes](https://github.com/storybookjs/deprecated-addons/tree/master/addons/notes) | Annotate Storybook stories with notes |
| [contexts](https://github.com/storybookjs/storybook/tree/master/addons/contexts) | Addon for driving your components under dynamic contexts |
| [options](https://github.com/storybookjs/storybook/tree/master/addons/options/) | Customize the Storybook UI in code |
| Addons | |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [info](https://github.com/storybookjs/deprecated-addons/tree/main/addons/info) | Annotate stories with extra component usage information |
| [notes](https://github.com/storybookjs/deprecated-addons/tree/main/addons/notes) | Annotate Storybook stories with notes |
| [contexts](https://storybook.js.org/addons/@storybook/addon-contexts/) | Addon for driving your components under dynamic contexts |
| [options](https://www.npmjs.com/package/@storybook/addon-options) | Customize the Storybook UI in code |
| [knobs](https://github.com/storybookjs/addon-knobs) | Interactively edit component prop data in the Storybook UI |
In order to continue improving your experience, we have to eventually deprecate certain addons in favor of new, better tools.
@ -162,10 +157,10 @@ If you're using contexts, we highly recommend you to migrate to [toolbars](https
We have a badge! Link it to your live Storybook example.
![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@master/badge/badge-storybook.svg)
![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)
```md
[![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@master/badge/badge-storybook.svg)](link to site)
[![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](link to site)
```
If you're looking for material to use in your presentation about storybook, like logo's video material and the colors we use etc, you can find all of that at our [brand repo](https://github.com/storybookjs/brand).
@ -173,9 +168,8 @@ If you're looking for material to use in your presentation about storybook, like
## Community
- Tweeting via [@storybookjs](https://twitter.com/storybookjs)
- Blogging at [Medium](https://medium.com/storybookjs)
- Chatting on [Slack](https://now-examples-slackin-rrirkqohko.now.sh/)
- Discussions on [Discord](https://discord.gg/sMFvFsG)
- Blogging at [storybook.js.org](https://storybook.js.org/blog/) and [Medium](https://medium.com/storybookjs)
- Chatting on [Discord](https://discord.gg/storybook)
- Streaming saved at [Youtube](https://www.youtube.com/channel/UCr7Quur3eIyA_oe8FNYexfg)
## Contributing
@ -184,12 +178,12 @@ We welcome contributions to Storybook!
- 📥 Pull requests and 🌟 Stars are always welcome.
- Read our [contributing guide](CONTRIBUTING.md) to get started,
or find us on [Discord](https://discord.gg/sMFvFsG), we will take the time to guide you
or find us on [Discord](https://discord.gg/storybook), we will take the time to guide you
Looking for a first issue to tackle?
- We tag issues with [![Good First Issue](https://img.shields.io/github/issues/storybookjs/storybook/good%20first%20issue.svg)](https://github.com/storybookjs/storybook/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) when we think they are well suited for people who are new to the codebase or OSS in general.
- [Talk to us](https://discord.gg/sMFvFsG), we'll find something to suits your skills and learning interest.
- [Talk to us](https://discord.gg/storybook), we'll find something to suits your skills and learning interest.
### Development scripts
@ -285,6 +279,6 @@ Support us with a monthly donation and help us continue our activities. \[[Becom
## License
[MIT](https://github.com/storybookjs/storybook/blob/master/LICENSE)
[MIT](https://github.com/storybookjs/storybook/blob/main/LICENSE)
-the end-

View File

@ -2,11 +2,11 @@
A Storybook release process based on [Semver](http://semver.org/). In short:
1. Merge all PRs into `next`, which is the default branch. Cherry-pick bugfix PRs into `master`, which is the stable release.
1. Merge all PRs into `next`, which is the default branch. Cherry-pick bugfix PRs into `main`, which is the stable release.
2. Every week: release patch releases off `master` to the NPM tag `latest`, and alpha releases off `next` to the NPM tag `next` (as e.g. `4.1.0-alpha.0`).
2. Every week: release patch releases off `main` to the NPM tag `latest`, and alpha releases off `next` to the NPM tag `next` (as e.g. `4.1.0-alpha.0`).
3. Every month or two, merge `next` into `master` and release a minor/major version according to `semver`. We set minor release dates as soon as we have feature clarity and manage to those dates:
3. Every month or two, merge `next` into `main` and release a minor/major version according to `semver`. We set minor release dates as soon as we have feature clarity and manage to those dates:
- Date should be a Monday so if we slip we don't get pushed into the weekend
- T-3wk:
- Draft announcement and socialize
@ -48,7 +48,7 @@ releases. We'll try our best to restrict breaking changes to MAJOR releases.
Every bugfix should go out as soon as we've verified the fix, and based on the
current rate of contribution, we should be issuing PATCH releases weekly.
Eventually, we'll automate the process so that a release will go out every time a PR is
merged into `master`, and we've already laid most of the groundwork for this.
merged into `main`, and we've already laid most of the groundwork for this.
## MINOR releases

View File

@ -15,4 +15,4 @@ You can submit a vulnerability in a Storybook package at: https://www.npmjs.com/
You can also reach out to the maintainers directly on Twitter: https://twitter.com/storybookjs
When we fix a security issue, we will post a security advisory on Github/NPM, describe the change in the [release notes](https://github.com/storybookjs/storybook/releases), and also announce notify the community on [our Discord](https://discord.com/invite/UUt2PJb).
When we fix a security issue, we will post a security advisory on Github/NPM, describe the change in the [release notes](https://github.com/storybookjs/storybook/releases), and also announce notify the community on [our Discord](https://discord.gg/storybook).

View File

@ -1,4 +1,4 @@
const fs = jest.genMockFromModule('fs');
const fs = jest.createMockFromModule('fs');
// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the

View File

@ -4,7 +4,7 @@ import { storiesOf, moduleMetadata } from '@storybook/angular';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
template: '<div>{{this.getStoreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
@ -13,7 +13,7 @@ class WithStoreComponent {
this.store = store;
}
getSotreState(): string {
getStoreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}

View File

@ -5,7 +5,7 @@ import React from 'react';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
template: '<div>{{this.getStoreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
@ -14,7 +14,7 @@ class WithStoreComponent {
this.store = store;
}
getSotreState(): string {
getStoreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}

View File

@ -1,8 +1,8 @@
# storybook-addon-a11y
This storybook addon can be helpful to make your UI components more accessible.
This Storybook addon can be helpful to make your UI components more accessible.
[Framework Support](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)
[Framework Support](https://github.com/storybookjs/storybook/blob/main/ADDONS_SUPPORT.md)
![Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/HEAD/addons/a11y/docs/screenshot.png)
@ -14,7 +14,7 @@ First, install the addon.
$ yarn add @storybook/addon-a11y --dev
```
Add this line to your `main.js` file (create this file inside your storybook config directory if needed).
Add this line to your `main.js` file (create this file inside your Storybook config directory if needed).
```js
module.exports = {
@ -29,18 +29,132 @@ export default {
title: 'button',
};
export const accessible = () => <button>Accessible button</button>;
export const Accessible = () => <button>Accessible button</button>;
export const inaccessible = () => (
export const Inaccessible = () => (
<button style={{ backgroundColor: 'red', color: 'darkRed' }}>Inaccessible button</button>
);
```
If you wish to selectively disable `a11y` checks for a subset of stories, you can control this with story parameters:
## Handling failing rules
When Axe reports accessibility violations in stories, there are multiple ways to handle these failures depending on your needs.
### Story-level overrides
At the Story level, override rules using `parameters.a11y.config.rules`.
```js
export const InputWithoutAutofill = () => <input type="text" autofill="nope" />;
InputWithoutAutofill.parameters = {
a11y: {
// Avoid doing this, as it will fully disable all accessibility checks for this story.
disable: true,
// Instead, override rules 👇
// axe-core configurationOptions (https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#parameters-1)
config: {
rules: [
{
// You can exclude some elements from failing a specific rule:
id: 'autocomplete-valid',
selector: '*:not([autocomplete="nope"])',
},
{
// You can also signify that a violation will need to be fixed in the future
// by overriding the result of a rule to return "Needs Review"
// rather than "Violation" if the rule fails:
id: 'landmark-complementary-is-top-level',
reviewOnFail: true,
},
],
},
},
};
```
Alternatively, you can disable specific rules in a Story:
```js
export const Inaccessible = () => (
<button style={{ backgroundColor: 'red', color: 'darkRed' }}>Inaccessible button</button>
);
Inaccessible.parameters = {
a11y: {
config: {
rules: [{ id: 'color-contrast', enabled: false }],
},
},
};
```
Tip: clearly explain in a comment why a rule was overridden, itll help you and your team trace back why certain violations arent being reported or need to be addressed. For example:
```js
MyStory.parameters = {
a11y: {
options: {
rules: [
{
// Allow `autocomplete="nope"` on form elements,
// a workaround to disable autofill in Chrome.
// @link https://bugs.chromium.org/p/chromium/issues/detail?id=468153
id: 'autocomplete-valid',
selector: '*:not([autocomplete="nope"])',
},
{
// @fixme Color contrast of subdued text fails, as raised in issue #123.
id: 'color-contrast',
reviewOnFail: true,
},
],
},
},
};
```
### Global overrides
When you want to ignore an accessibility rule or change its settings across all stories, set `parameters.a11y.config.rules` in your Storybooks `preview.ts` file. This can be particularly useful for ignoring false positives commonly reported by Axe.
```ts
// .storybook/preview.ts
export const parameters = {
a11y: {
config: {
rules: [
{
// This tells Axe to run the 'autocomplete-valid' rule on selectors
// that match '*:not([autocomplete="nope"])' (all elements except [autocomplete="nope"]).
// This is the safest way of ignoring a violation across all stories,
// as Axe will only ignore very specific elements and keep reporting
// violations on other elements of this rule.
id: 'autocomplete-valid',
selector: '*:not([autocomplete="nope"])',
},
{
// To disable a rule across all stories, set `enabled` to `false`.
// Use with caution: all violations of this rule will be ignored!
id: 'autocomplete-valid',
enabled: false,
},
],
},
},
};
```
### Disabling checks
If you wish to entirely disable `a11y` checks for a subset of stories, you can control this with story parameters:
```js
export const MyNonCheckedStory = () => <SomeComponent />;
MyNonCheckedStory.parameters = {
// Avoid doing this, as it fully disables all accessibility checks for this story,
// and consider the techniques described above.
a11y: { disable: true },
};
```
@ -48,7 +162,7 @@ MyNonCheckedStory.parameters = {
## Parameters
For more customizability use parameters to configure [aXe options](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure).
You can override these options [at story level too](https://storybook.js.org/docs/react/configure/features-and-behavior#per-story-options.
You can override these options [at story level too](https://storybook.js.org/docs/react/configure/features-and-behavior#per-story-options).
```js
import React from 'react';

View File

@ -1,74 +1,93 @@
{
"name": "@storybook/addon-a11y",
"version": "6.0.4",
"description": "a11y addon for storybook",
"version": "6.4.0-alpha.18",
"description": "Test component compliance with web accessibility standards",
"keywords": [
"a11y",
"accessibility",
"addon",
"storybook",
"valid",
"verify"
"verify",
"test"
],
"homepage": "https://github.com/storybookjs/storybook#readme",
"homepage": "https://github.com/storybookjs/storybook/tree/main/addons/a11y",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/storybookjs/storybook.git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/a11y"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/ts3.9/index.d.ts",
"typesVersions": {
"<3.8": {
"*": [
"dist/ts3.4/*"
]
}
},
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts",
"ts3.5/**/*"
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.4",
"@storybook/api": "6.0.4",
"@storybook/channels": "6.0.4",
"@storybook/client-api": "6.0.4",
"@storybook/client-logger": "6.0.4",
"@storybook/components": "6.0.4",
"@storybook/core-events": "6.0.4",
"@storybook/theming": "6.0.4",
"axe-core": "^3.5.2",
"core-js": "^3.0.1",
"global": "^4.3.2",
"lodash": "^4.17.15",
"react-sizeme": "^2.5.2",
"regenerator-runtime": "^0.13.3",
"ts-dedent": "^1.1.1",
"@storybook/addons": "6.4.0-alpha.18",
"@storybook/api": "6.4.0-alpha.18",
"@storybook/channels": "6.4.0-alpha.18",
"@storybook/client-api": "6.4.0-alpha.18",
"@storybook/client-logger": "6.4.0-alpha.18",
"@storybook/components": "6.4.0-alpha.18",
"@storybook/core-events": "6.4.0-alpha.18",
"@storybook/theming": "6.4.0-alpha.18",
"axe-core": "^4.2.0",
"core-js": "^3.8.2",
"global": "^4.4.0",
"lodash": "^4.17.20",
"react-sizeme": "^3.0.1",
"regenerator-runtime": "^0.13.7",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"@testing-library/react": "^10.0.4",
"@types/webpack-env": "^1.15.2",
"react": "^16.13.1",
"react-dom": "^16.13.1"
"@testing-library/react": "^11.2.2",
"@types/webpack-env": "^1.16.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "ca50653f3a10879a7e0677d2464677a86678c3fe",
"typesVersions": {
"<=3.5": {
"*": [
"ts3.5/*"
]
}
"gitHead": "285782b4010609c6d6c9f306b8f8a110ae55f035",
"sbmodern": "dist/modern/index.js",
"storybook": {
"displayName": "Accessibility",
"icon": "https://user-images.githubusercontent.com/263385/101991665-47042f80-3c7c-11eb-8f00-64b5a18f498a.png",
"unsupportedFrameworks": [
"react-native"
]
}
}

View File

@ -1 +1,13 @@
module.exports = require('./dist/preset');
function managerEntries(entry = []) {
return [...entry, require.resolve('./dist/esm/register')];
}
function config(entry = []) {
return [
...entry,
require.resolve('./dist/esm/a11yRunner'),
require.resolve('./dist/esm/a11yHighlight'),
];
}
module.exports = { managerEntries, config };

View File

@ -1 +1 @@
require('./dist/register');
require('./dist/esm/register');

View File

@ -1,9 +1,11 @@
import { document } from 'global';
import addons from '@storybook/addons';
import global from 'global';
import { addons } from '@storybook/addons';
import { STORY_CHANGED } from '@storybook/core-events';
import { EVENTS, HIGHLIGHT_STYLE_ID } from './constants';
import { higlightStyle } from './highlight';
import { highlightStyle } from './highlight';
const { document } = global;
if (module && module.hot && module.hot.decline) {
module.hot.decline();
@ -21,13 +23,16 @@ const highlight = (infos: HighlightInfo) => {
const id = HIGHLIGHT_STYLE_ID;
resetHighlight();
// Make sure there are no duplicated selectors
const elements = Array.from(new Set(infos.elements));
const sheet = document.createElement('style');
sheet.setAttribute('id', id);
sheet.innerHTML = infos.elements
sheet.innerHTML = elements
.map(
(target) =>
`${target}{
${higlightStyle(infos.color)}
`${target}{
${highlightStyle(infos.color)}
}`
)
.join(' ');

View File

@ -1,4 +1,4 @@
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
import { EVENTS } from './constants';
jest.mock('@storybook/addons');

View File

@ -1,22 +1,39 @@
import { document, window } from 'global';
import global from 'global';
import axe from 'axe-core';
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
import { EVENTS } from './constants';
import { Setup } from './params';
import { A11yParameters } from './params';
const { document, window: globalWindow } = global;
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}
const channel = addons.getChannel();
// Holds axe core running state
let active = false;
// Holds latest story we requested a run
let activeStoryId: string | undefined;
const getElement = () => {
const storyRoot = document.getElementById('story-root');
return storyRoot ? storyRoot.children : document.getElementById('root');
};
/**
* Handle A11yContext events.
* Because the event are sent without manual check, we split calls
*/
const handleRequest = (storyId: string) => {
const { manual } = getParams(storyId);
if (!manual) {
run(storyId);
}
};
const run = async (storyId: string) => {
activeStoryId = storyId;
try {
const input = getParams(storyId);
@ -24,14 +41,23 @@ const run = async (storyId: string) => {
active = true;
channel.emit(EVENTS.RUNNING);
const { element = getElement(), config, options } = input;
const { element = getElement(), config, options = {} } = input;
axe.reset();
if (config) {
axe.configure(config);
}
const result = await axe.run(element, options);
channel.emit(EVENTS.RESULT, result);
// It's possible that we requested a new run on a different story.
// Unfortunately, axe doesn't support a cancel method to abort current run.
// We check if the story we run against is still the current one,
// if not, trigger a new run using the current story
if (activeStoryId === storyId) {
channel.emit(EVENTS.RESULT, result);
} else {
active = false;
run(activeStoryId);
}
}
} catch (error) {
channel.emit(EVENTS.ERROR, error);
@ -41,8 +67,8 @@ const run = async (storyId: string) => {
};
/** Returns story parameters or default ones. */
const getParams = (storyId: string): Setup => {
const { parameters } = window.__STORYBOOK_STORY_STORE__.fromId(storyId) || {};
const getParams = (storyId: string): A11yParameters => {
const { parameters } = globalWindow.__STORYBOOK_STORY_STORE__.fromId(storyId) || {};
return (
parameters.a11y || {
config: {},
@ -53,5 +79,5 @@ const getParams = (storyId: string): Setup => {
);
};
channel.on(EVENTS.REQUEST, run);
channel.on(EVENTS.REQUEST, handleRequest);
channel.on(EVENTS.MANUAL, run);

View File

@ -1,3 +1,3 @@
import { withA11y } from '../index';
import { withA11y } from '.';
export const decorators = [withA11y];

View File

@ -1,16 +1,16 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { styled } from '@storybook/theming';
import { ActionBar, Icons, ScrollArea } from '@storybook/components';
import { AxeResults } from 'axe-core';
import { useChannel, useParameter, useStorybookState, useAddonState } from '@storybook/api';
import { useChannel, useParameter, useStorybookState } from '@storybook/api';
import { Report } from './Report';
import { Tabs } from './Tabs';
import { useA11yContext } from './A11yContext';
import { EVENTS, ADDON_ID } from '../constants';
import { EVENTS } from '../constants';
import { A11yParameters } from '../params';
export enum RuleType {
@ -51,13 +51,13 @@ const Centered = styled.span<{}>({
type Status = 'initial' | 'manual' | 'running' | 'error' | 'ran' | 'ready';
export const A11YPanel: React.FC = () => {
const [status, setStatus] = useAddonState<Status>(ADDON_ID, 'initial');
const [error, setError] = React.useState<unknown>(undefined);
const { setResults, results } = useA11yContext();
const { storyId } = useStorybookState();
const { manual } = useParameter<Pick<A11yParameters, 'manual'>>('a11y', {
manual: false,
});
const [status, setStatus] = useState<Status>(manual ? 'manual' : 'initial');
const [error, setError] = React.useState<unknown>(undefined);
const { setResults, results } = useA11yContext();
const { storyId } = useStorybookState();
React.useEffect(() => {
setStatus(manual ? 'manual' : 'initial');

View File

@ -64,9 +64,9 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
: prevHighlighted.filter((t) => !target.includes(t))
);
}, []);
const handleRun = React.useCallback(() => {
emit(EVENTS.REQUEST, storyId);
}, [storyId]);
const handleRun = (renderedStoryId: string) => {
emit(EVENTS.REQUEST, renderedStoryId);
};
const handleClearHighlights = React.useCallback(() => setHighlighted([]), []);
const handleSetTab = React.useCallback((index: number) => {
handleClearHighlights();
@ -90,7 +90,7 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
React.useEffect(() => {
if (active) {
handleRun();
handleRun(storyId);
} else {
handleClearHighlights();
}

View File

@ -24,7 +24,10 @@ const HighlightToggleElement = styled.span({
fontWeight: 'normal',
alignSelf: 'center',
paddingRight: 15,
input: { margin: 0 },
input: {
margin: 0,
display: 'block',
},
});
interface ElementProps {

View File

@ -69,7 +69,7 @@ describe('<HighlightToggle />', () => {
${['#root']} | ${['#root']} | ${false}
${['#root']} | ${['#root', '#root1']} | ${true}
`(
'should be triggerd with $expected when highlighted is $highlighted and elementsToHighlight is $elementsToHighlight',
'should be triggered with $expected when highlighted is $highlighted and elementsToHighlight is $elementsToHighlight',
({ highlighted, elementsToHighlight, expected }) => {
const { getByRole } = render(
<A11yContext.Provider

View File

@ -7,7 +7,7 @@ const Wrapper = styled.div({
padding: 12,
marginBottom: 10,
});
const Help = styled.p({
const Description = styled.p({
margin: '0 0 12px',
});
const Link = styled.a({
@ -24,7 +24,7 @@ interface InfoProps {
export const Info: FunctionComponent<InfoProps> = ({ item }) => {
return (
<Wrapper>
<Help>{item.help}</Help>
<Description>{item.description}</Description>
<Link href={item.helpUrl} target="_blank">
More info...
</Link>

View File

@ -33,6 +33,7 @@ const Icon = styled<any, any>(Icons)(({ theme }) => ({
const HeaderBar = styled.div<{}>(({ theme }) => ({
padding: theme.layoutMargin,
paddingLeft: theme.layoutMargin - 3,
lineHeight: '20px',
background: 'none',
color: 'inherit',
textAlign: 'left',
@ -51,7 +52,10 @@ const HighlightToggleElement = styled.span({
float: 'right',
marginRight: 15,
alignSelf: 'center',
input: { margin: 0 },
input: {
margin: 0,
display: 'block',
},
});
interface ItemProps {
@ -78,7 +82,7 @@ export const Item = (props: ItemProps) => {
transform: `rotate(${open ? 0 : -90}deg)`,
}}
/>
{item.description}
{item.help}
</HeaderBar>
<HighlightToggleElement>
<HighlightToggle toggleId={highlightToggleId} elementsToHighlight={item.nodes} />

View File

@ -71,8 +71,8 @@ const Rule: FunctionComponent<RuleProps> = ({ rule }) => {
}
return (
<SizeMe refreshMode="debounce">
{({ size }: { size: { width: number; height: number } }) => (
<Item elementWidth={size.width}>
{({ size }) => (
<Item elementWidth={size.width || 0}>
<StyledBadge status={badgeType}>{formatSeverityText(rule.impact)}</StyledBadge>
<Message>{rule.message}</Message>
</Item>

View File

@ -18,30 +18,29 @@ const Container = styled.div({
const HighlightToggleLabel = styled.label<{}>(({ theme }) => ({
cursor: 'pointer',
userSelect: 'none',
marginBottom: 3,
marginRight: 3,
color: theme.color.dark,
}));
const GlobalToggle = styled.div<{ elementWidth: number }>(({ elementWidth }) => {
const GlobalToggle = styled.div<{ elementWidth: number }>(({ elementWidth, theme }) => {
const maxWidthBeforeBreak = 450;
return {
cursor: 'pointer',
fontSize: '14px',
padding: elementWidth > maxWidthBeforeBreak ? '12px 15px 10px 0' : '12px 0px 3px 12px',
fontSize: 13,
lineHeight: '20px',
padding: elementWidth > maxWidthBeforeBreak ? '10px 15px 10px 0' : '10px 0px 10px 15px',
height: '40px',
border: 'none',
marginTop: elementWidth > maxWidthBeforeBreak ? -40 : 0,
float: elementWidth > maxWidthBeforeBreak ? 'right' : 'left',
display: elementWidth > maxWidthBeforeBreak ? 'flex' : 'block',
display: 'flex',
alignItems: 'center',
width: elementWidth > maxWidthBeforeBreak ? 'auto' : '100%',
borderBottom: elementWidth > maxWidthBeforeBreak ? 'none' : '1px solid rgba(0,0,0,.1)',
borderBottom: elementWidth > maxWidthBeforeBreak ? 'none' : `1px solid ${theme.appBorderColor}`,
input: {
marginLeft: 10,
marginRight: 0,
marginTop: 0,
marginTop: -1,
marginBottom: 0,
},
};
@ -112,7 +111,7 @@ export const Tabs: React.FC<TabsProps> = ({ tabs }) => {
const highlightLabel = `Highlight results`;
return (
<SizeMe refreshMode="debounce">
{({ size }: { size: any }) => (
{({ size }) => (
<Container>
<List>
<TabsWrapper>
@ -130,7 +129,7 @@ export const Tabs: React.FC<TabsProps> = ({ tabs }) => {
</TabsWrapper>
</List>
{tabs[activeTab].items.length > 0 ? (
<GlobalToggle elementWidth={size.width}>
<GlobalToggle elementWidth={size.width || 0}>
<HighlightToggleLabel htmlFor={highlightToggleId}>
{highlightLabel}
</HighlightToggleLabel>

View File

@ -7,15 +7,16 @@ import { Filters } from './ColorFilters';
const iframeId = 'storybook-preview-iframe';
const baseList = [
'protanopia',
'protanomaly',
'deuteranopia',
'blurred vision',
'deuteranomaly',
'tritanopia',
'deuteranopia',
'protanomaly',
'protanopia',
'tritanomaly',
'achromatopsia',
'tritanopia',
'achromatomaly',
'mono',
'achromatopsia',
'grayscale',
] as const;
type Filter = typeof baseList[number] | null;
@ -24,7 +25,10 @@ const getFilter = (filter: Filter) => {
if (!filter) {
return 'none';
}
if (filter === 'mono') {
if (filter === 'blurred vision') {
return 'blur(2px)';
}
if (filter === 'grayscale') {
return 'grayscale(100%)';
}
return `url('#${filter}')`;
@ -87,7 +91,7 @@ const getColorList = (active: Filter, set: (i: Filter) => void): Link[] => [
})),
];
export const ColorBlindness: FunctionComponent = () => {
export const VisionSimulator: FunctionComponent = () => {
const [filter, setFilter] = useState<Filter>(null);
return (
@ -114,8 +118,8 @@ export const ColorBlindness: FunctionComponent = () => {
closeOnClick
onDoubleClick={() => setFilter(null)}
>
<IconButton key="filter" active={!!filter} title="Color Blindness Emulation">
<Icons icon="mirror" />
<IconButton key="filter" active={!!filter} title="Vision simulator">
<Icons icon="accessibility" />
</IconButton>
</WithTooltip>
<Hidden>

View File

@ -1,8 +1,7 @@
export const higlightStyle = (color: string) => `
export const highlightStyle = (color: string) => `
outline: 2px dashed ${color};
outline-offset: 2px;
box-shadow: 0 0 0 6px rgba(255,255,255,0.6);
}
`;
export const highlightObject = (color: string) => ({

View File

@ -1,7 +0,0 @@
export function managerEntries(entry: any[] = []) {
return [...entry, require.resolve('../register')];
}
export function config(entry: any[] = []) {
return [...entry, require.resolve('../a11yRunner'), require.resolve('../a11yHighlight')];
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { addons, types } from '@storybook/addons';
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants';
import { ColorBlindness } from './components/ColorBlindness';
import { VisionSimulator } from './components/VisionSimulator';
import { A11YPanel } from './components/A11YPanel';
import { A11yContextProvider } from './components/A11yContext';
@ -10,7 +10,7 @@ addons.register(ADDON_ID, () => {
title: '',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => <ColorBlindness />,
render: () => <VisionSimulator />,
});
addons.add(PANEL_ID, {

View File

@ -1,2 +1 @@
declare module 'global';
declare module 'react-sizeme';

View File

@ -11,5 +11,12 @@
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["src/__tests__/**/*"]
"exclude": [
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}

View File

@ -8,7 +8,7 @@ This document describes the pre-6.0 usage of the addon, and as such is no longer
Import the `action` function and use it to create actions handlers. When creating action handlers, provide a **name** to make it easier to identify.
> _Note: Make sure NOT to use reserved words as function names. [issues#29](https://github.com/storybookjs/storybook-addon-actions/issues/29#issuecomment-288274794)_
> _Note: Be mindful of the choice of the function's name. Avoid using Javascript reserved words such as **default** or **if**, as they will lead into unexpected errors._
```js
import { action } from '@storybook/addon-actions';

View File

@ -1,11 +1,13 @@
{
"name": "@storybook/addon-actions",
"version": "6.0.4",
"description": "Action Logger addon for storybook",
"version": "6.4.0-alpha.18",
"description": "Get UI feedback when an action is performed on an interactive element",
"keywords": [
"storybook"
"storybook",
"essentials",
"data-state"
],
"homepage": "https://github.com/storybookjs/storybook/tree/master/addons/actions",
"homepage": "https://github.com/storybookjs/storybook/tree/main/addons/actions",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
@ -14,56 +16,75 @@
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/actions"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/ts3.9/index.d.ts",
"typesVersions": {
"<3.8": {
"*": [
"dist/ts3.4/*"
]
}
},
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts",
"ts3.5/**/*"
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.4",
"@storybook/api": "6.0.4",
"@storybook/client-api": "6.0.4",
"@storybook/components": "6.0.4",
"@storybook/core-events": "6.0.4",
"@storybook/theming": "6.0.4",
"core-js": "^3.0.1",
"fast-deep-equal": "^3.1.1",
"global": "^4.3.2",
"lodash": "^4.17.15",
"polished": "^3.4.4",
"@storybook/addons": "6.4.0-alpha.18",
"@storybook/api": "6.4.0-alpha.18",
"@storybook/client-api": "6.4.0-alpha.18",
"@storybook/components": "6.4.0-alpha.18",
"@storybook/core-events": "6.4.0-alpha.18",
"@storybook/theming": "6.4.0-alpha.18",
"core-js": "^3.8.2",
"fast-deep-equal": "^3.1.3",
"global": "^4.4.0",
"lodash": "^4.17.20",
"polished": "^4.0.5",
"prop-types": "^15.7.2",
"react": "^16.8.3",
"react-inspector": "^5.0.1",
"regenerator-runtime": "^0.13.3",
"ts-dedent": "^1.1.1",
"react-inspector": "^5.1.0",
"regenerator-runtime": "^0.13.7",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2",
"uuid": "^8.0.0"
"uuid-browser": "^3.1.0"
},
"devDependencies": {
"@types/lodash": "^4.14.150",
"@types/uuid": "^7.0.3",
"@types/webpack-env": "^1.15.2"
"@types/lodash": "^4.14.167",
"@types/webpack-env": "^1.16.0"
},
"peerDependencies": {
"react-dom": "*"
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "ca50653f3a10879a7e0677d2464677a86678c3fe",
"typesVersions": {
"<=3.5": {
"*": [
"ts3.5/*"
]
}
"gitHead": "285782b4010609c6d6c9f306b8f8a110ae55f035",
"sbmodern": "dist/modern/index.js",
"storybook": {
"displayName": "Actions",
"unsupportedFrameworks": [
"react-native"
],
"icon": "https://user-images.githubusercontent.com/263385/101991666-479cc600-3c7c-11eb-837b-be4e5ffa1bb8.png"
}
}

View File

@ -1 +1,16 @@
module.exports = require('./dist/preset');
function managerEntries(entry, options) {
return [...entry, require.resolve('./dist/esm/register')];
}
function config(entry = [], { addDecorator = true } = {}) {
const actionConfig = [];
if (addDecorator) {
actionConfig.push(require.resolve('./dist/esm/preset/addDecorator'));
}
return [...entry, ...actionConfig, require.resolve('./dist/esm/preset/addArgs')];
}
module.exports = {
managerEntries,
config,
};

View File

@ -1 +1 @@
require('./dist/register');
require('./dist/esm/register');

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import deepEqual from 'fast-deep-equal';
import { API } from '@storybook/api';
import { STORY_RENDERED } from '@storybook/core-events';
import { STORY_CHANGED } from '@storybook/core-events';
import { ActionLogger as ActionLoggerComponent } from '../../components/ActionLogger';
import { EVENT_ID } from '../..';
@ -39,14 +39,14 @@ export default class ActionLogger extends Component<ActionLoggerProps, ActionLog
const { api } = this.props;
api.on(EVENT_ID, this.addAction);
api.on(STORY_RENDERED, this.handleStoryChange);
api.on(STORY_CHANGED, this.handleStoryChange);
}
componentWillUnmount() {
this.mounted = false;
const { api } = this.props;
api.off(STORY_RENDERED, this.handleStoryChange);
api.off(STORY_CHANGED, this.handleStoryChange);
api.off(EVENT_ID, this.addAction);
}

View File

@ -1,76 +0,0 @@
import { StoryContext } from '@storybook/addons';
import { inferActionsFromArgTypesRegex, addActionsFromArgTypes } from './addArgs';
const withDefaultValue = (argTypes) =>
Object.keys(argTypes).filter((key) => !!argTypes[key].defaultValue);
describe('actions parameter enhancers', () => {
describe('actions.argTypesRegex parameter', () => {
const baseParameters = {
argTypes: { onClick: {}, onFocus: {}, somethingElse: {} },
actions: { argTypesRegex: '^on.*' },
};
it('should add actions that match a pattern', () => {
const parameters = baseParameters;
const argTypes = inferActionsFromArgTypesRegex({ parameters } as StoryContext);
expect(withDefaultValue(argTypes)).toEqual(['onClick', 'onFocus']);
});
it('should prioritize pre-existing argTypes unless they are null', () => {
const parameters = {
...baseParameters,
argTypes: {
onClick: { defaultValue: 'pre-existing value' },
onFocus: { defaultValue: null },
},
};
const argTypes = inferActionsFromArgTypesRegex({ parameters } as StoryContext);
expect(withDefaultValue(argTypes)).toEqual(['onClick', 'onFocus']);
expect(argTypes.onClick.defaultValue).toEqual('pre-existing value');
expect(argTypes.onFocus.defaultValue).not.toBeNull();
});
it('should do nothing if actions are disabled', () => {
const parameters = {
...baseParameters,
actions: { ...baseParameters.actions, disable: true },
};
const result = inferActionsFromArgTypesRegex({ parameters } as StoryContext);
expect(result).toEqual(parameters.argTypes);
});
});
describe('argTypes.action parameter', () => {
const baseParameters = {
argTypes: {
onClick: { action: 'clicked!' },
onBlur: { action: 'blurred!' },
},
};
it('should add actions based on action.args', () => {
const parameters = baseParameters;
const argTypes = addActionsFromArgTypes({ parameters } as StoryContext);
expect(withDefaultValue(argTypes)).toEqual(['onClick', 'onBlur']);
});
it('should prioritize pre-existing args', () => {
const parameters = {
...baseParameters,
argTypes: {
onClick: { defaultValue: 'pre-existing value', action: 'onClick' },
onBlur: { action: 'onBlur' },
},
};
const argTypes = addActionsFromArgTypes({ parameters } as StoryContext);
expect(withDefaultValue(argTypes)).toEqual(['onClick', 'onBlur']);
expect(argTypes.onClick.defaultValue).toEqual('pre-existing value');
});
it('should do nothing if actions are disabled', () => {
const parameters = { ...baseParameters, actions: { disable: true } };
const result = addActionsFromArgTypes({ parameters } as StoryContext);
expect(result).toEqual(parameters.argTypes);
});
});
});

View File

@ -1,48 +1,3 @@
import mapValues from 'lodash/mapValues';
import { ArgTypesEnhancer } from '@storybook/client-api';
import { addActionsFromArgTypes, inferActionsFromArgTypesRegex } from './addArgsHelpers';
import { action } from '../index';
// interface ActionsParameter {
// disable?: boolean;
// argTypesRegex?: RegExp;
// }
/**
* Automatically add action args for argTypes whose name
* matches a regex, such as `^on.*` for react-style `onClick` etc.
*/
export const inferActionsFromArgTypesRegex: ArgTypesEnhancer = (context) => {
const { actions, argTypes } = context.parameters;
if (!actions || actions.disable || !actions.argTypesRegex || !argTypes) {
return argTypes;
}
const argTypesRegex = new RegExp(actions.argTypesRegex);
return mapValues(argTypes, (argType, name) => {
if (!argTypesRegex.test(name)) {
return argType;
}
return { ...argType, defaultValue: argType.defaultValue || action(name) };
});
};
/**
* Add action args for list of strings.
*/
export const addActionsFromArgTypes: ArgTypesEnhancer = (context) => {
const { argTypes, actions } = context.parameters;
if (actions?.disable || !argTypes) {
return argTypes;
}
return mapValues(argTypes, (argType, name) => {
if (!argType.action) {
return argType;
}
const message = typeof argType.action === 'string' ? argType.action : name;
return { ...argType, defaultValue: argType.defaultValue || action(message) };
});
};
export const argTypesEnhancers = [addActionsFromArgTypes, inferActionsFromArgTypesRegex];
export const argsEnhancers = [addActionsFromArgTypes, inferActionsFromArgTypesRegex];

View File

@ -0,0 +1,115 @@
import { StoryContext } from '@storybook/addons';
import { inferActionsFromArgTypesRegex, addActionsFromArgTypes } from './addArgsHelpers';
describe('actions parameter enhancers', () => {
describe('actions.argTypesRegex parameter', () => {
const parameters = { actions: { argTypesRegex: '^on.*' } };
const argTypes = { onClick: {}, onFocus: {}, somethingElse: {} };
it('should add actions that match a pattern', () => {
const args = inferActionsFromArgTypesRegex(({
args: {},
argTypes,
parameters,
} as unknown) as StoryContext);
expect(args).toEqual({
onClick: expect.any(Function),
onFocus: expect.any(Function),
});
});
it('should NOT override pre-existing args', () => {
const args = inferActionsFromArgTypesRegex(({
args: { onClick: 'pre-existing value' },
argTypes,
parameters,
} as unknown) as StoryContext);
expect(args).toEqual({ onFocus: expect.any(Function) });
});
it('should NOT override pre-existing args, if null', () => {
const args = inferActionsFromArgTypesRegex(({
args: { onClick: null },
argTypes,
parameters,
} as unknown) as StoryContext);
expect(args).toEqual({ onFocus: expect.any(Function) });
});
it('should override pre-existing args, if undefined', () => {
const args = inferActionsFromArgTypesRegex(({
args: { onClick: undefined },
argTypes,
parameters,
} as unknown) as StoryContext);
expect(args).toEqual({ onClick: expect.any(Function), onFocus: expect.any(Function) });
});
it('should do nothing if actions are disabled', () => {
const args = inferActionsFromArgTypesRegex(({
args: {},
argTypes,
parameters: {
...parameters,
actions: { ...parameters.actions, disable: true },
},
} as unknown) as StoryContext);
expect(args).toEqual({});
});
});
describe('argTypes.action parameter', () => {
const argTypes = {
onClick: { action: 'clicked!' },
onBlur: { action: 'blurred!' },
};
it('should add actions based on action.args', () => {
expect(
addActionsFromArgTypes(({ args: {}, argTypes, parameters: {} } as unknown) as StoryContext)
).toEqual({
onClick: expect.any(Function),
onBlur: expect.any(Function),
});
});
it('should NOT override pre-existing args', () => {
expect(
addActionsFromArgTypes(({
argTypes: { onClick: { action: 'clicked!' } },
args: { onClick: 'pre-existing value' },
parameters: {},
} as unknown) as StoryContext)
).toEqual({});
});
it('should NOT override pre-existing args, if null', () => {
expect(
addActionsFromArgTypes(({
argTypes: { onClick: { action: 'clicked!' } },
args: { onClick: null },
parameters: {},
} as unknown) as StoryContext)
).toEqual({});
});
it('should override pre-existing args, if undefined', () => {
expect(
addActionsFromArgTypes(({
argTypes: { onClick: { action: 'clicked!' } },
args: { onClick: undefined },
parameters: {},
} as unknown) as StoryContext)
).toEqual({ onClick: expect.any(Function) });
});
it('should do nothing if actions are disabled', () => {
expect(
addActionsFromArgTypes(({
args: {},
argTypes,
parameters: { actions: { disable: true } },
} as unknown) as StoryContext)
).toEqual({});
});
});
});

View File

@ -0,0 +1,59 @@
import { Args } from '@storybook/addons';
import { ArgsEnhancer } from '@storybook/client-api';
import { action } from '../index';
// interface ActionsParameter {
// disable?: boolean;
// argTypesRegex?: RegExp;
// }
/**
* Automatically add action args for argTypes whose name
* matches a regex, such as `^on.*` for react-style `onClick` etc.
*/
export const inferActionsFromArgTypesRegex: ArgsEnhancer = (context) => {
const {
args,
argTypes,
parameters: { actions },
} = context;
if (!actions || actions.disable || !actions.argTypesRegex || !argTypes) {
return {};
}
const argTypesRegex = new RegExp(actions.argTypesRegex);
const argTypesMatchingRegex = Object.entries(argTypes).filter(
([name]) => !!argTypesRegex.test(name)
);
return argTypesMatchingRegex.reduce((acc, [name, argType]) => {
if (typeof args[name] === 'undefined') {
acc[name] = action(name);
}
return acc;
}, {} as Args);
};
/**
* Add action args for list of strings.
*/
export const addActionsFromArgTypes: ArgsEnhancer = (context) => {
const {
args,
argTypes,
parameters: { actions },
} = context;
if (actions?.disable || !argTypes) {
return {};
}
const argTypesWithAction = Object.entries(argTypes).filter(([name, argType]) => !!argType.action);
return argTypesWithAction.reduce((acc, [name, argType]) => {
if (typeof args[name] === 'undefined') {
acc[name] = action(typeof argType.action === 'string' ? argType.action : name);
}
return acc;
}, {} as Args);
};

View File

@ -1,15 +0,0 @@
interface ActionsOptions {
addDecorator?: boolean;
}
export function managerEntries(entry: any[] = [], options: any) {
return [...entry, require.resolve('../register')];
}
export function config(entry: any[] = [], { addDecorator = true }: ActionsOptions = {}) {
const actionConfig = [];
if (addDecorator) {
actionConfig.push(require.resolve('./addDecorator'));
}
return [...entry, ...actionConfig, require.resolve('./addArgs')];
}

View File

@ -1,4 +1,4 @@
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
import { action, configureActions } from '../..';
jest.mock('@storybook/addons');
@ -16,7 +16,7 @@ describe('Action', () => {
action('test-action')('one');
expect(getChannelData(channel)[0]).toEqual('one');
expect(getChannelData(channel)).toEqual('one');
});
it('with multiple arguments', () => {
@ -46,7 +46,7 @@ describe('Depth config', () => {
},
});
expect(getChannelData(channel)[0]).toEqual({
expect(getChannelData(channel)).toEqual({
root: {
one: {
two: 'foo',
@ -76,7 +76,7 @@ describe('Depth config', () => {
},
});
expect(getChannelData(channel)[0]).toEqual({
expect(getChannelData(channel)).toEqual({
root: {
one: {
two: {
@ -111,7 +111,7 @@ describe('allowFunction config', () => {
},
});
expect(getChannelData(channel)[0]).toEqual({
expect(getChannelData(channel)).toEqual({
root: {
one: {
a: 1,
@ -140,7 +140,7 @@ describe('allowFunction config', () => {
},
});
expect(getChannelData(channel)[0]).toEqual({
expect(getChannelData(channel)).toEqual({
root: {
one: {
a: 1,

View File

@ -1,4 +1,4 @@
import addons from '@storybook/addons';
import { addons } from '@storybook/addons';
import { actions } from '../..';
jest.mock('@storybook/addons');
@ -20,7 +20,7 @@ describe('Actions', () => {
expect(Object.keys(actionsResult)).toEqual(['test-action']);
actionsResult['test-action']('one');
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: ['one'] });
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: 'one' });
});
it('with multiple arguments', () => {
@ -33,8 +33,8 @@ describe('Actions', () => {
actionsResult['test-action']('one');
actionsResult['test-action2']('two');
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: ['one'] });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: ['two'] });
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: 'one' });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: 'two' });
});
it('with multiple arguments + config', () => {
@ -47,8 +47,8 @@ describe('Actions', () => {
actionsResult['test-action']('one');
actionsResult['test-action2']('two');
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: ['one'] });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: ['two'] });
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: 'one' });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: 'two' });
expect(getChannelOptions(channel, 0).some).toEqual('config');
expect(getChannelOptions(channel, 1).some).toEqual('config');
@ -67,8 +67,8 @@ describe('Actions', () => {
actionsResult['test-action']('one');
actionsResult['test-action2']('two');
expect(getChannelData(channel, 0)).toEqual({ name: 'test action', args: ['one'] });
expect(getChannelData(channel, 1)).toEqual({ name: 'test action two', args: ['two'] });
expect(getChannelData(channel, 0)).toEqual({ name: 'test action', args: 'one' });
expect(getChannelData(channel, 1)).toEqual({ name: 'test action two', args: 'two' });
});
it('with first argument as array of arguments + config', () => {
@ -81,8 +81,8 @@ describe('Actions', () => {
actionsResult['test-action']('one');
actionsResult['test-action2']('two');
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: ['one'] });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: ['two'] });
expect(getChannelData(channel, 0)).toEqual({ name: 'test-action', args: 'one' });
expect(getChannelData(channel, 1)).toEqual({ name: 'test-action2', args: 'two' });
expect(getChannelOptions(channel, 0).some).toEqual('config');
expect(getChannelOptions(channel, 1).some).toEqual('config');

View File

@ -1,4 +1,4 @@
import { v4 as uuidv4 } from 'uuid';
import uuidv4 from 'uuid-browser/v4';
import { addons } from '@storybook/addons';
import { EVENT_ID } from '../constants';
import { ActionDisplay, ActionOptions, HandlerFunction } from '../models';
@ -14,11 +14,12 @@ export function action(name: string, options: ActionOptions = {}): HandlerFuncti
const channel = addons.getChannel();
const id = uuidv4();
const minDepth = 5; // anything less is really just storybook internals
const normalizedArgs = args.length > 1 ? args : args[0];
const actionDisplayToEmit: ActionDisplay = {
id,
count: 0,
data: { name, args },
data: { name, args: normalizedArgs },
options: {
...actionOptions,
depth: minDepth + (actionOptions.depth || 3),

View File

@ -1,5 +1,5 @@
// Based on http://backbonejs.org/docs/backbone.html#section-164
import { document, Element } from 'global';
import global from 'global';
import { useEffect } from '@storybook/client-api';
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';
@ -9,6 +9,8 @@ import { actions } from './actions';
import { PARAM_KEY } from '../constants';
const { document, Element } = global;
const delegateEventSplitter = /^(\S+)\s*(.*)$/;
const isIE = Element != null && !Element.prototype.matches;

View File

@ -1,11 +1,27 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { addons, types } from '@storybook/addons';
import { STORY_CHANGED } from '@storybook/core-events';
import ActionLogger from './containers/ActionLogger';
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants';
import { ADDON_ID, EVENT_ID, PANEL_ID, PARAM_KEY } from './constants';
addons.register(ADDON_ID, (api) => {
addons.addPanel(PANEL_ID, {
title: 'Actions',
title() {
const [actionsCount, setActionsCount] = useState(0);
const onEvent = () => setActionsCount((previous) => previous + 1);
const onChange = () => setActionsCount(0);
useEffect(() => {
api.on(EVENT_ID, onEvent);
api.on(STORY_CHANGED, onChange);
return () => {
api.off(EVENT_ID, onEvent);
api.off(STORY_CHANGED, onChange);
};
});
const suffix = actionsCount === 0 ? '' : ` (${actionsCount})`;
return `Actions${suffix}`;
},
type: types.PANEL,
render: ({ active, key }) => <ActionLogger key={key} api={api} active={active} />,
paramKey: PARAM_KEY,

View File

@ -1,3 +1,4 @@
// TODO: following packages need definition files or a TS migration
declare module 'global';
declare module 'react-inspector';
declare module 'uuid-browser/v4';

View File

@ -5,5 +5,12 @@
"types": ["webpack-env", "jest"]
},
"include": ["src/**/*"],
"exclude": ["src/__tests__/**/*", "src/**/*.test.ts"]
"exclude": [
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}

View File

@ -4,7 +4,7 @@ Storybook Addon Backgrounds can be used to change background colors inside the p
[Framework Support](https://storybook.js.org/docs/react/api/frameworks-feature-support)
![React Storybook Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/master/docs/static/img/addon-backgrounds.gif)
![React Storybook Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/main/docs/static/img/addon-backgrounds.gif)
## Installation

View File

@ -1,14 +1,16 @@
{
"name": "@storybook/addon-backgrounds",
"version": "6.0.4",
"description": "A storybook addon to show different backgrounds for your preview",
"version": "6.4.0-alpha.18",
"description": "Switch backgrounds to view components in different settings",
"keywords": [
"addon",
"background",
"react",
"storybook"
"storybook",
"essentials",
"design"
],
"homepage": "https://github.com/storybookjs/storybook/tree/master/addons/backgrounds",
"homepage": "https://github.com/storybookjs/storybook/tree/main/addons/backgrounds",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
@ -17,47 +19,70 @@
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/backgrounds"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"author": "jbaxleyiii",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/ts3.9/index.d.ts",
"typesVersions": {
"<3.8": {
"*": [
"dist/ts3.4/*"
]
}
},
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts",
"ts3.5/**/*"
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.4",
"@storybook/api": "6.0.4",
"@storybook/client-logger": "6.0.4",
"@storybook/components": "6.0.4",
"@storybook/core-events": "6.0.4",
"@storybook/theming": "6.0.4",
"core-js": "^3.0.1",
"@storybook/addons": "6.4.0-alpha.18",
"@storybook/api": "6.4.0-alpha.18",
"@storybook/client-logger": "6.4.0-alpha.18",
"@storybook/components": "6.4.0-alpha.18",
"@storybook/core-events": "6.4.0-alpha.18",
"@storybook/theming": "6.4.0-alpha.18",
"core-js": "^3.8.2",
"global": "^4.4.0",
"memoizerific": "^1.11.3",
"react": "^16.8.3",
"regenerator-runtime": "^0.13.3"
"regenerator-runtime": "^0.13.7",
"ts-dedent": "^2.0.0",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"@types/webpack-env": "^1.15.2"
"@types/webpack-env": "^1.16.0"
},
"peerDependencies": {
"react-dom": "*"
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
"gitHead": "ca50653f3a10879a7e0677d2464677a86678c3fe",
"typesVersions": {
"<=3.5": {
"*": [
"ts3.5/*"
]
}
"gitHead": "285782b4010609c6d6c9f306b8f8a110ae55f035",
"sbmodern": "dist/modern/index.js",
"storybook": {
"displayName": "Backgrounds",
"icon": "https://user-images.githubusercontent.com/263385/101991667-479cc600-3c7c-11eb-96d3-410e936252e7.png",
"unsupportedFrameworks": [
"react-native"
]
}
}

View File

@ -1 +1,16 @@
module.exports = require('./dist/preset');
function config(entry = []) {
return [
...entry,
require.resolve('./dist/esm/preset/addDecorator'),
require.resolve('./dist/esm/preset/addParameter'),
];
}
function managerEntries(entry = [], options) {
return [...entry, require.resolve('./dist/esm/register')];
}
module.exports = {
managerEntries,
config,
};

View File

@ -1 +1 @@
require('./dist/register');
require('./dist/esm/register');

View File

@ -1,51 +1,14 @@
import React, { Component, Fragment, ReactElement } from 'react';
import React, { FunctionComponent, Fragment, useCallback, useMemo, memo } from 'react';
import memoize from 'memoizerific';
import { Combo, Consumer, API } from '@storybook/api';
import { Global, Theme } from '@storybook/theming';
import { useParameter, useGlobals } from '@storybook/api';
import { logger } from '@storybook/client-logger';
import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
import { PARAM_KEY as BACKGROUNDS_PARAM_KEY, EVENTS } from '../constants';
import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants';
import { ColorIcon } from '../components/ColorIcon';
interface GlobalState {
name: string | undefined;
selected: string | undefined;
}
interface Props {
api: API;
}
interface BackgroundSelectorItem {
id: string;
title: string;
onClick: () => void;
value: string;
right?: ReactElement;
}
interface Background {
name: string;
value: string;
}
interface BackgroundsParameter {
default?: string;
disable?: boolean;
values: Background[];
}
interface BackgroundsConfig {
backgrounds: Background[] | null;
selectedBackground: string | null;
defaultBackgroundName: string | null;
disable: boolean;
}
const iframeId = 'storybook-preview-iframe';
import { BackgroundSelectorItem, Background, BackgroundsParameter, GlobalState } from '../types';
import { getBackgroundColorByName } from '../helpers';
const createBackgroundSelectorItem = memoize(1000)(
(
@ -53,7 +16,8 @@ const createBackgroundSelectorItem = memoize(1000)(
name: string,
value: string,
hasSwatch: boolean,
change: (arg: { selected: string; name: string }) => void
change: (arg: { selected: string; name: string }) => void,
active: boolean
): BackgroundSelectorItem => ({
id: id || name,
title: name,
@ -62,6 +26,7 @@ const createBackgroundSelectorItem = memoize(1000)(
},
value,
right: hasSwatch ? <ColorIcon background={value} /> : undefined,
active,
})
);
@ -72,12 +37,26 @@ const getDisplayedItems = memoize(10)(
change: (arg: { selected: string; name: string }) => void
) => {
const backgroundSelectorItems = backgrounds.map(({ name, value }) =>
createBackgroundSelectorItem(null, name, value, true, change)
createBackgroundSelectorItem(
null,
name,
value,
true,
change,
value === selectedBackgroundColor
)
);
if (selectedBackgroundColor !== 'transparent') {
return [
createBackgroundSelectorItem('reset', 'Clear background', 'transparent', null, change),
createBackgroundSelectorItem(
'reset',
'Clear background',
'transparent',
null,
change,
false
),
...backgroundSelectorItems,
];
}
@ -86,131 +65,78 @@ const getDisplayedItems = memoize(10)(
}
);
const getSelectedBackgroundColor = (
backgrounds: Background[] = [],
currentSelectedValue: string,
defaultName: string
): string => {
if (currentSelectedValue === 'transparent') {
return 'transparent';
}
if (backgrounds.find((background) => background.value === currentSelectedValue)) {
return currentSelectedValue;
}
const defaultBackground = backgrounds.find((background) => background.name === defaultName);
if (defaultBackground) {
return defaultBackground.value;
}
if (defaultName) {
const availableColors = backgrounds.map((background) => background.name).join(', ');
logger.warn(
`Backgrounds Addon: could not find the default color "${defaultName}".
These are the available colors for your story based on your configuration: ${availableColors}`
);
}
return 'transparent';
const DEFAULT_BACKGROUNDS_CONFIG: BackgroundsParameter = {
default: null,
disable: true,
values: [],
};
const getBackgroundsConfig = ({ api, state }: Combo): BackgroundsConfig => {
const backgroundsParameter = api.getCurrentParameter<BackgroundsParameter>(BACKGROUNDS_PARAM_KEY);
const selectedBackgroundValue = state.addons[BACKGROUNDS_PARAM_KEY] || null;
export const BackgroundSelector: FunctionComponent = memo(() => {
const backgroundsConfig = useParameter<BackgroundsParameter>(
BACKGROUNDS_PARAM_KEY,
DEFAULT_BACKGROUNDS_CONFIG
);
if (Array.isArray(backgroundsParameter)) {
const [globals, updateGlobals] = useGlobals();
const globalsBackgroundColor = globals[BACKGROUNDS_PARAM_KEY]?.value;
const selectedBackgroundColor = useMemo(() => {
return getBackgroundColorByName(
globalsBackgroundColor,
backgroundsConfig.values,
backgroundsConfig.default
);
}, [backgroundsConfig, globalsBackgroundColor]);
if (Array.isArray(backgroundsConfig)) {
logger.warn(
'Addon Backgrounds api has changed in Storybook 6.0. Please refer to the migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md'
);
}
const isBackgroundsEmpty = !backgroundsParameter?.values?.length;
if (backgroundsParameter?.disable || isBackgroundsEmpty) {
// other null properties are necessary to keep the same return shape for Consumer memoization
return {
disable: true,
backgrounds: null,
selectedBackground: null,
defaultBackgroundName: null,
};
const onBackgroundChange = useCallback(
(value: string) => {
updateGlobals({ [BACKGROUNDS_PARAM_KEY]: { ...globals[BACKGROUNDS_PARAM_KEY], value } });
},
[backgroundsConfig, globals, updateGlobals]
);
if (backgroundsConfig.disable) {
return null;
}
return {
disable: false,
backgrounds: backgroundsParameter?.values,
selectedBackground: selectedBackgroundValue,
defaultBackgroundName: backgroundsParameter?.default,
};
};
export class BackgroundSelector extends Component<Props> {
change = ({ selected, name }: GlobalState) => {
const { api } = this.props;
if (typeof selected === 'string') {
api.setAddonState<string>(BACKGROUNDS_PARAM_KEY, selected);
}
api.emit(EVENTS.UPDATE, { selected, name });
};
render() {
return (
<Consumer filter={getBackgroundsConfig}>
{({
disable,
backgrounds,
selectedBackground,
defaultBackgroundName,
}: BackgroundsConfig) => {
if (disable) {
return null;
}
const selectedBackgroundColor = getSelectedBackgroundColor(
backgrounds,
selectedBackground,
defaultBackgroundName
);
return (
<Fragment>
<WithTooltip
placement="top"
trigger="click"
closeOnClick
tooltip={({ onHide }) => {
return (
<Fragment>
{selectedBackgroundColor ? (
<Global
styles={(theme: Theme) => ({
[`#${iframeId}`]: {
background:
selectedBackgroundColor === 'transparent'
? theme.background.content
: selectedBackgroundColor,
},
})}
/>
) : null}
<WithTooltip
placement="top"
trigger="click"
closeOnClick
tooltip={({ onHide }) => (
<TooltipLinkList
links={getDisplayedItems(backgrounds, selectedBackgroundColor, (i) => {
this.change(i);
onHide();
})}
/>
)}
>
<IconButton
key="background"
title="Change the background of the preview"
active={selectedBackgroundColor !== 'transparent'}
>
<Icons icon="photo" />
</IconButton>
</WithTooltip>
</Fragment>
<TooltipLinkList
links={getDisplayedItems(
backgroundsConfig.values,
selectedBackgroundColor,
({ selected }: GlobalState) => {
if (selectedBackgroundColor !== selected) {
onBackgroundChange(selected);
}
onHide();
}
)}
/>
);
}}
</Consumer>
);
}
}
>
<IconButton
key="background"
title="Change the background of the preview"
active={selectedBackgroundColor !== 'transparent'}
>
<Icons icon="photo" />
</IconButton>
</WithTooltip>
</Fragment>
);
});

View File

@ -1,51 +1,35 @@
import React, { FunctionComponent, memo } from 'react';
import { useAddonState, useParameter } from '@storybook/api';
import { Global } from '@storybook/theming';
import { useGlobals, useParameter } from '@storybook/api';
import { Icons, IconButton } from '@storybook/components';
import { ADDON_ID, GRID_PARAM_KEY } from '../constants';
export interface BackgroundGridParameters {
cellSize: number;
}
const iframeId = 'storybook-preview-iframe';
import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants';
export const GridSelector: FunctionComponent = memo(() => {
const [state, setState] = useAddonState<boolean>(`${ADDON_ID}/grid`);
const { cellSize } = useParameter<BackgroundGridParameters>(GRID_PARAM_KEY, { cellSize: 20 });
const [globals, updateGlobals] = useGlobals();
const { grid } = useParameter(BACKGROUNDS_PARAM_KEY, {
grid: { disable: false },
});
if (grid?.disable) {
return null;
}
const isActive = globals[BACKGROUNDS_PARAM_KEY]?.grid || false;
return (
<IconButton
key="background"
active={state}
title="Change the background of the preview"
onClick={() => setState(!state)}
active={isActive}
title="Apply a grid to the preview"
onClick={() =>
updateGlobals({
[BACKGROUNDS_PARAM_KEY]: { ...globals[BACKGROUNDS_PARAM_KEY], grid: !isActive },
})
}
>
<Icons icon="grid" />
{state ? (
<Global
styles={{
[`#${iframeId}`]: {
backgroundSize: [
`${cellSize * 5}px ${cellSize * 5}px`,
`${cellSize * 5}px ${cellSize * 5}px`,
`${cellSize}px ${cellSize}px`,
`${cellSize}px ${cellSize}px`,
].join(', '),
backgroundPosition: '-1px -1px, -1px -1px, -1px -1px, -1px -1px',
backgroundBlendMode: 'difference',
backgroundImage: [
'linear-gradient(rgba(130, 130, 130, 0.5) 1px, transparent 1px)',
'linear-gradient(90deg, rgba(130, 130, 130, 0.5) 1px, transparent 1px)',
'linear-gradient(rgba(130, 130, 130, 0.25) 1px, transparent 1px)',
'linear-gradient(90deg, rgba(130, 130, 130, 0.25) 1px, transparent 1px)',
].join(', '),
},
}}
/>
) : null}
</IconButton>
);
});

View File

@ -0,0 +1,2 @@
export * from './withBackground';
export * from './withGrid';

View File

@ -0,0 +1,65 @@
import { StoryFn as StoryFunction, StoryContext, useMemo, useEffect } from '@storybook/addons';
import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants';
import {
clearStyles,
addBackgroundStyle,
getBackgroundColorByName,
isReduceMotionEnabled,
} from '../helpers';
export const withBackground = (StoryFn: StoryFunction, context: StoryContext) => {
const { globals, parameters } = context;
const globalsBackgroundColor = globals[BACKGROUNDS_PARAM_KEY]?.value;
const backgroundsConfig = parameters[BACKGROUNDS_PARAM_KEY];
const selectedBackgroundColor = useMemo(() => {
if (backgroundsConfig.disable) {
return 'transparent';
}
return getBackgroundColorByName(
globalsBackgroundColor,
backgroundsConfig.values,
backgroundsConfig.default
);
}, [backgroundsConfig, globalsBackgroundColor]);
const isActive = useMemo(
() => selectedBackgroundColor && selectedBackgroundColor !== 'transparent',
[selectedBackgroundColor]
);
const selector =
context.viewMode === 'docs' ? `#anchor--${context.id} .docs-story` : '.sb-show-main';
const backgroundStyles = useMemo(() => {
const transitionStyle = 'transition: background-color 0.3s;';
return `
${selector} {
background: ${selectedBackgroundColor} !important;
${isReduceMotionEnabled() ? '' : transitionStyle}
}
`;
}, [selectedBackgroundColor, selector]);
useEffect(() => {
const selectorId =
context.viewMode === 'docs'
? `addon-backgrounds-docs-${context.id}`
: `addon-backgrounds-color`;
if (!isActive) {
clearStyles(selectorId);
return;
}
addBackgroundStyle(
selectorId,
backgroundStyles,
context.viewMode === 'docs' ? context.id : null
);
}, [isActive, backgroundStyles, context]);
return StoryFn();
};

View File

@ -0,0 +1,79 @@
import dedent from 'ts-dedent';
import deprecate from 'util-deprecate';
import { StoryFn as StoryFunction, StoryContext, useMemo, useEffect } from '@storybook/addons';
import { clearStyles, addGridStyle } from '../helpers';
import { PARAM_KEY as BACKGROUNDS_PARAM_KEY } from '../constants';
const deprecatedCellSizeWarning = deprecate(
() => {},
dedent`
Backgrounds Addon: The cell size parameter has been changed.
- parameters.grid.cellSize should now be parameters.backgrounds.grid.cellSize
See https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-grid-parameter
`
);
export const withGrid = (StoryFn: StoryFunction, context: StoryContext) => {
const { globals, parameters } = context;
const gridParameters = parameters[BACKGROUNDS_PARAM_KEY].grid;
const isActive = globals[BACKGROUNDS_PARAM_KEY]?.grid === true && gridParameters.disable !== true;
const { cellAmount, cellSize, opacity } = gridParameters;
const isInDocs = context.viewMode === 'docs';
let gridSize: number;
if (parameters.grid?.cellSize) {
gridSize = parameters.grid.cellSize;
deprecatedCellSizeWarning();
} else {
gridSize = cellSize;
}
const isLayoutPadded = parameters.layout === undefined || parameters.layout === 'padded';
// 16px offset in the grid to account for padded layout
const defaultOffset = isLayoutPadded ? 16 : 0;
const offsetX = gridParameters.offsetX ?? (isInDocs ? 20 : defaultOffset);
const offsetY = gridParameters.offsetY ?? (isInDocs ? 20 : defaultOffset);
const gridStyles = useMemo(() => {
const selector =
context.viewMode === 'docs' ? `#anchor--${context.id} .docs-story` : '.sb-show-main';
const backgroundSize = [
`${gridSize * cellAmount}px ${gridSize * cellAmount}px`,
`${gridSize * cellAmount}px ${gridSize * cellAmount}px`,
`${gridSize}px ${gridSize}px`,
`${gridSize}px ${gridSize}px`,
].join(', ');
return `
${selector} {
background-size: ${backgroundSize} !important;
background-position: ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px, ${offsetX}px ${offsetY}px !important;
background-blend-mode: difference !important;
background-image: linear-gradient(rgba(130, 130, 130, ${opacity}) 1px, transparent 1px),
linear-gradient(90deg, rgba(130, 130, 130, ${opacity}) 1px, transparent 1px),
linear-gradient(rgba(130, 130, 130, ${opacity / 2}) 1px, transparent 1px),
linear-gradient(90deg, rgba(130, 130, 130, ${
opacity / 2
}) 1px, transparent 1px) !important;
}
`;
}, [gridSize]);
useEffect(() => {
const selectorId =
context.viewMode === 'docs'
? `addon-backgrounds-grid-docs-${context.id}`
: `addon-backgrounds-grid`;
if (!isActive) {
clearStyles(selectorId);
return;
}
addGridStyle(selectorId, gridStyles);
}, [isActive, gridStyles, context]);
return StoryFn();
};

View File

@ -0,0 +1,93 @@
import global from 'global';
import dedent from 'ts-dedent';
import { logger } from '@storybook/client-logger';
import { Background } from '../types';
const { document, window } = global;
export const isReduceMotionEnabled = () => {
const prefersReduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
return prefersReduceMotion.matches;
};
export const getBackgroundColorByName = (
currentSelectedValue: string,
backgrounds: Background[] = [],
defaultName: string
): string => {
if (currentSelectedValue === 'transparent') {
return 'transparent';
}
if (backgrounds.find((background) => background.value === currentSelectedValue)) {
return currentSelectedValue;
}
const defaultBackground = backgrounds.find((background) => background.name === defaultName);
if (defaultBackground) {
return defaultBackground.value;
}
if (defaultName) {
const availableColors = backgrounds.map((background) => background.name).join(', ');
logger.warn(
dedent`
Backgrounds Addon: could not find the default color "${defaultName}".
These are the available colors for your story based on your configuration:
${availableColors}.
`
);
}
return 'transparent';
};
export const clearStyles = (selector: string | string[]) => {
const selectors = Array.isArray(selector) ? selector : [selector];
selectors.forEach(clearStyle);
};
const clearStyle = (selector: string) => {
const element = document.getElementById(selector) as HTMLElement;
if (element) {
element.parentElement.removeChild(element);
}
};
export const addGridStyle = (selector: string, css: string) => {
const existingStyle = document.getElementById(selector) as HTMLElement;
if (existingStyle) {
if (existingStyle.innerHTML !== css) {
existingStyle.innerHTML = css;
}
} else {
const style = document.createElement('style') as HTMLElement;
style.setAttribute('id', selector);
style.innerHTML = css;
document.head.appendChild(style);
}
};
export const addBackgroundStyle = (selector: string, css: string, storyId: string) => {
const existingStyle = document.getElementById(selector) as HTMLElement;
if (existingStyle) {
if (existingStyle.innerHTML !== css) {
existingStyle.innerHTML = css;
}
} else {
const style = document.createElement('style') as HTMLElement;
style.setAttribute('id', selector);
style.innerHTML = css;
const gridStyleSelector = `addon-backgrounds-grid${storyId ? `-docs-${storyId}` : ''}`;
// If grids already exist, we want to add the style tag BEFORE it so the background doesn't override grid
const existingGridStyle = document.getElementById(gridStyleSelector) as HTMLElement;
if (existingGridStyle) {
existingGridStyle.parentElement.insertBefore(style, existingGridStyle);
} else {
document.head.appendChild(style);
}
}
};

View File

@ -1,3 +1,6 @@
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}
// make it work with --isolatedModules
export default {};

View File

@ -0,0 +1,3 @@
import { withGrid, withBackground } from '../decorators';
export const decorators = [withGrid, withBackground];

View File

@ -1,5 +1,10 @@
export const parameters = {
backgrounds: {
grid: {
cellSize: 20,
opacity: 0.5,
cellAmount: 5,
},
values: [
{ name: 'light', value: '#F8F8F8' },
{ name: 'dark', value: '#333333' },

View File

@ -1,7 +0,0 @@
export function config(entry: any[] = []) {
return [...entry, require.resolve('./defaultParameters')];
}
export function managerEntries(entry: any[] = [], options: any) {
return [...entry, require.resolve('../register')];
}

View File

@ -5,14 +5,14 @@ import { ADDON_ID } from './constants';
import { BackgroundSelector } from './containers/BackgroundSelector';
import { GridSelector } from './containers/GridSelector';
addons.register(ADDON_ID, (api) => {
addons.register(ADDON_ID, () => {
addons.add(ADDON_ID, {
title: 'Backgrounds',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
render: () => (
<Fragment>
<BackgroundSelector api={api} />
<BackgroundSelector />
<GridSelector />
</Fragment>
),

View File

@ -0,0 +1,33 @@
import { ReactElement } from 'react';
export interface GlobalState {
name: string | undefined;
selected: string | undefined;
}
export interface BackgroundSelectorItem {
id: string;
title: string;
onClick: () => void;
value: string;
active: boolean;
right?: ReactElement;
}
export interface Background {
name: string;
value: string;
}
export interface BackgroundsParameter {
default?: string;
disable?: boolean;
values: Background[];
}
export interface BackgroundsConfig {
backgrounds: Background[] | null;
selectedBackgroundName: string | null;
defaultBackgroundName: string | null;
disable: boolean;
}

View File

@ -8,6 +8,11 @@
"src/**/*"
],
"exclude": [
"src/__tests__/**/*"
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}

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