Merge branch 'master' into files-knob

This commit is contained in:
Norbert de Langen 2018-03-26 19:55:13 +02:00 committed by GitHub
commit ab8a5662dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
683 changed files with 12781 additions and 7273 deletions

View File

@ -1,6 +1,7 @@
{
"presets": ["env", "stage-0", "react"],
"plugins": [
"babel-plugin-macros",
["transform-runtime", {
"polyfill": false,
"regenerator": true

View File

@ -33,8 +33,11 @@ jobs:
key: core-dependencies-{{ checksum "yarn.lock" }}
paths:
- node_modules
- example/cra-kitchen-sink/node_modules
- example/vue-kitchen-sink/node_modules
- examples/angular-cli/node_modules
- examples/cra-kitchen-sink/node_modules
- examples/official-storybook/node_modules
- examples/polymer-cli/node_modules
- examples/vue-kitchen-sink/node_modules
- save_cache:
name: "Cache core dist"
key: core-dist-{{ .Revision }}
@ -42,6 +45,21 @@ jobs:
- addons
- app
- lib
danger:
<<: *defaults
environment:
- TOKEN_HEAD: 49aa9a6549007391dfcef9c76fca32a73560fd8
steps:
- checkout
- restore_cache:
name: "Restore core dependencies cache"
keys:
- core-dependencies-{{ checksum "yarn.lock" }}
- run:
name: "Danger"
command: |
echo $DANGER_GITHUB_API_TOKEN
DANGER_GITHUB_API_TOKEN=${TOKEN_HEAD}3 yarn danger ci
example-kitchen-sinks:
<<: *defaults
steps:
@ -54,18 +72,9 @@ jobs:
name: "Restore core dist cache"
keys:
- core-dist-{{ .Revision }}
- run:
name: "Link packages"
command: |
yarn install
- run:
name: Workaround for https://github.com/GoogleChrome/puppeteer/issues/290
command: sh ./scripts/workaround-puppeteer-issue-290.sh
- run:
name: "Build official-storybook"
command: |
cd examples/official-storybook
yarn build-storybook
- run:
name: "Build react kitchen-sink"
command: |
@ -81,6 +90,39 @@ jobs:
command: |
cd examples/angular-cli
yarn build-storybook
- run:
name: "Build polymer-cli"
command: |
cd examples/polymer-cli
yarn build-storybook
- run:
name: "Build official-storybook"
command: |
cd examples/official-storybook
yarn build-storybook
- run:
name: "Visually test storybook"
command: |
yarn chromatic
- run:
name: "Run image snapshots"
command: yarn test --image
- store_artifacts:
path: examples/official-storybook/image-snapshots/__image_snapshots__
destination: official_storybook_image_snapshots
smoke-tests:
<<: *defaults
steps:
- checkout
- restore_cache:
name: "Restore core dependencies cache"
keys:
- core-dependencies-{{ checksum "yarn.lock" }}
- restore_cache:
name: "Restore core dist cache"
keys:
- core-dist-{{ .Revision }}
- run:
name: "Run react kitchen-sink (smoke test)"
command: |
@ -97,17 +139,15 @@ jobs:
cd examples/angular-cli
yarn storybook --smoke-test
- run:
name: "Run image snapshots"
command: yarn test --image
name: "Run polymer-cli (smoke test)"
command: |
cd examples/polymer-cli
yarn storybook --smoke-test
- run:
name: Integration Test - Kichen sinks
command: yarn test --integration
- store_artifacts:
path: integration/__image_snapshots__
destination: integration_image_snapshots
- store_artifacts:
path: examples/official-storybook/image-snapshots/__image_snapshots__
destination: official_storybook_image_snapshots
name: "Run official-storybook (smoke test)"
command: |
cd examples/official-storybook
yarn storybook --smoke-test
react-native:
<<: *defaults
steps:
@ -178,10 +218,6 @@ jobs:
name: "Restore core dist cache"
keys:
- core-dist-{{ .Revision }}
- run:
name: "Link packages"
command: |
yarn install
- run:
name: "Lint"
command: |
@ -198,35 +234,11 @@ jobs:
name: "Restore core dist cache"
keys:
- core-dist-{{ .Revision }}
- run:
name: "Link packages"
command: |
yarn install
- run:
name: "Run unit tests"
command: |
yarn test --coverage --runInBand --core
yarn coverage
storybook:
<<: *defaults
steps:
- checkout
- restore_cache:
name: "Restore core dependencies cache"
keys:
- core-dependencies-{{ checksum "yarn.lock" }}
- restore_cache:
name: "Restore core dist cache"
keys:
- core-dist-{{ .Revision }}
- run:
name: "Link packages"
command: |
yarn install
- run:
name: "Visually test storybook"
command: |
yarn chromatic
cli:
working_directory: /tmp/storybook
docker:
@ -279,22 +291,26 @@ workflows:
build_accept_deploy:
jobs:
- build
- danger:
requires:
- build
- example-kitchen-sinks:
requires:
- build
- smoke-tests:
requires:
- build
- react-native:
requires:
- build
- docs
- lint:
requires:
- docs
- build
- unit-test:
requires:
- build
- storybook:
requires:
- build
- cli:
requires:
- build

View File

@ -2,8 +2,6 @@ dist
build
coverage
node_modules
addons/**/example/**
app/**/demo/**
docs/public
lib/cli/test
*.bundle.js
@ -13,3 +11,4 @@ lib/cli/test
!.eslintrc.js
!.eslintrc-markdown.js
!.jest.config.js
!.storybook

View File

@ -1,59 +0,0 @@
const error = 2;
const warn = 1;
const ignore = 0;
module.exports = {
root: true,
extends: ['eslint-config-airbnb', 'plugin:jest/recommended', 'prettier'],
plugins: ['prettier', 'jest', 'react'],
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module',
},
env: {
es6: true,
node: true,
'jest/globals': true,
},
globals: {
storiesOf: true,
addonAPI: true,
__DEV__: true,
fetch: true,
},
rules: {
strict: [error, 'never'],
'prettier/prettier': [
warn,
{
printWidth: 100,
tabWidth: 2,
bracketSpacing: true,
trailingComma: 'es5',
singleQuote: true,
},
],
'no-console': ignore,
'global-require': ignore,
quotes: [warn, 'single'],
'no-unused-vars': ignore,
'class-methods-use-this': ignore,
'arrow-parens': [warn, 'as-needed'],
'space-before-function-paren': ignore,
'import/no-unresolved': ignore,
'import/extensions': ignore,
'import/no-extraneous-dependencies': ignore,
'import/prefer-default-export': ignore,
'react/prop-types': ignore,
'react/jsx-wrap-multilines': ignore,
'react/jsx-uses-react': error,
'react/jsx-uses-vars': error,
'react/react-in-jsx-scope': ignore,
'react/jsx-filename-extension': ignore,
'jsx-a11y/accessible-emoji': ignore,
'jsx-a11y/href-no-hash': ignore,
'jsx-a11y/label-has-for': ignore,
'jsx-a11y/anchor-is-valid': ['warn', { aspects: ['invalidHref'] }],
'react/no-unescaped-entities': ignore,
},
};

View File

@ -4,8 +4,14 @@ const ignore = 0;
module.exports = {
root: true,
extends: ['eslint-config-airbnb', 'plugin:jest/recommended', 'prettier'],
plugins: ['prettier', 'jest', 'react', 'json'],
extends: [
'airbnb',
'plugin:jest/recommended',
'plugin:import/react-native',
'prettier',
'prettier/react',
],
plugins: ['prettier', 'jest', 'import', 'react', 'jsx-a11y', 'json'],
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module',
@ -18,9 +24,13 @@ module.exports = {
settings: {
'import/core-modules': ['enzyme'],
'import/ignore': ['node_modules\\/(?!@storybook)'],
'import/resolver': {
node: {
extensions: ['.js', '.ts'],
},
},
},
rules: {
strict: [error, 'never'],
'prettier/prettier': [
warn,
{
@ -32,16 +42,13 @@ module.exports = {
},
],
'no-debugger': process.env.NODE_ENV === 'production' ? error : ignore,
quotes: [warn, 'single', { avoidEscape: true }],
'class-methods-use-this': ignore,
'arrow-parens': [warn, 'as-needed'],
'space-before-function-paren': ignore,
'import/no-unresolved': error,
'import/extensions': [
error,
'always',
{
js: 'never',
json: 'always',
ts: 'never',
},
],
'import/no-extraneous-dependencies': [
@ -56,6 +63,7 @@ module.exports = {
'**/scripts/*.js',
'**/stories/**/*.js',
'**/__tests__/**/*.js',
'**/.storybook/**/*.js',
],
peerDependencies: true,
},
@ -64,24 +72,47 @@ module.exports = {
'import/default': error,
'import/named': error,
'import/namespace': error,
'react/jsx-wrap-multilines': ignore,
'react/jsx-indent': ignore,
'react/jsx-indent-props': ignore,
'react/jsx-closing-bracket-location': ignore,
'react/jsx-uses-react': error,
'react/jsx-uses-vars': error,
'react/react-in-jsx-scope': error,
'react/jsx-filename-extension': [
warn,
{
extensions: ['.js', '.jsx'],
},
],
'jsx-a11y/accessible-emoji': ignore,
'jsx-a11y/href-no-hash': ignore,
'jsx-a11y/label-has-for': ignore,
'jsx-a11y/click-events-have-key-events': error,
'jsx-a11y/anchor-is-valid': [warn, { aspects: ['invalidHref'] }],
'react/no-unescaped-entities': ignore,
'jsx-a11y/label-has-for': [
error,
{
required: {
some: ['nesting', 'id'],
},
},
],
'jsx-a11y/anchor-is-valid': [
error,
{
components: ['RoutedLink', 'MenuLink', 'LinkTo', 'Link'],
specialLink: ['overrideParams', 'kind', 'story', 'to'],
},
],
'no-underscore-dangle': [
error,
{
allow: ['__STORYBOOK_CLIENT_API__', '__STORYBOOK_ADDONS_CHANNEL__'],
},
],
},
overrides: [
{
files: ['**/react-native*/**', '**/REACT_NATIVE*/**', '**/crna*/**'],
rules: {
'jsx-a11y/accessible-emoji': ignore,
},
},
{
files: '**/.storybook/config.js',
rules: {
'global-require': ignore,
},
},
],
};

View File

@ -1,4 +1,5 @@
### Issue details
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?_
@ -24,3 +25,23 @@ _(A screencast can be useful for visual bugs, but it is not a substitute for a t
```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

7
.github/stale.yml vendored
View File

@ -1,7 +1,7 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 45
daysUntilStale: 21
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 15
daysUntilClose: 30
# Issues with these labels will never be considered stale
exemptLabels:
- todo
@ -10,6 +10,7 @@ exemptLabels:
- 'do not merge'
- 'needs review'
- 'high priority'
- dependencies:update
# Label to use when marking an issue as stale
staleLabel: inactive
@ -19,7 +20,7 @@ markComment: >
If there are still questions, comments, or bugs, please feel free to continue
the discussion. Unfortunately, we don't have time to get to every issue. We
are always open to contributions so please send us a pull request if you would
like to help. Inactive issues will be closed after 60 days. Thanks!
like to help. Inactive issues will be closed after 30 days. Thanks!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Hey there, it's me again! I am going close this issue to help our maintainers

View File

@ -12,7 +12,7 @@ Aaron Mc Adam <aaron@aaronmcadam.com>
Aruna Herath <aruna@kadira.io> <arunabherath@gmail.com>
Arunoda Susiripala <arunoda.susiripala@gmail.com> Arunoda Susiripala <arunoda.susiripala@gmail.com>
Benedikt D Valdez <benediktvaldez@users.noreply.github.com> Benedikt D Valdez <benediktvaldez@users.noreply.github.com>
Daniel Duan <dduan@squarespace.com> <dduan@yahoo.com>
Daniel Duan <dduan@squarespace.com> <dduan@yahoo.com> # github:danielduan, npm:danielduan, twitter:danduan
Daniel James <daniel@thzinc.com> <djames@syncromatics.com>
Danny Andrews <danny-andrews@users.noreply.github.com> danny@ownlocal.com>
Dustin Kane <dkane@athenahealth.com> <dustinpkane@gmail.com>

View File

@ -1,18 +1,3 @@
module.exports = {
plugins: [
'remark-preset-lint-recommended',
['remark-lint-list-item-indent', false],
[
'remark-lint-code',
{
js: {
module: 'node_modules/remark-lint-code-eslint',
options: {
fix: true,
configFile: '.eslintrc-markdown.js',
},
},
},
],
],
plugins: ['remark-preset-lint-recommended', ['remark-lint-list-item-indent', false]],
};

19
ADDONS_SUPPORT.md Normal file
View File

@ -0,0 +1,19 @@
## Addon / Framework Support Table
| |[React](app/react)|[React Native](app/react-native)|[Vue](app/vue)|[Angular](app/angular)| [Polymer](app/polymer)|
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|[a11y](addons/a11y) |+| | | | |
|[actions](addons/actions) |+|+|+|+|+|
|[background](addons/background) |+| | | | |
|[centered](addons/centered) |+| |+| | |
|[events](addons/events) |+| | | | |
|[graphql](addons/graphql) |+| | | | |
|[info](addons/info) |+| | | | |
|[jest](addons/jest) |+| | | | |
|[knobs](addons/knobs) |+|+|+|+|+|
|[links](addons/links) |+|+|+|+|+|
|[notes](addons/notes) |+| |+|+|+|
|[options](addons/options) |+|+|+|+|+|
|[storyshots](addons/storyshots) |+|+|+|+| |
|[storysource](addons/storysource)|+| |+|+|+|
|[viewport](addons/viewport) |+| |+|+|+|

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@ You can also pick directly from CLI:
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.
This command will list all the suites and options for running tests.
```sh
yarn test
@ -58,7 +58,7 @@ You can also pick suites from CLI. Suites available are listed below.
`yarn test --core`
This option executes test from `<rootdir>/app/react`, `<rootdir>/app/vue`, and `<rootdir>/lib`.
Before the tests are ran, the project must be bootstrapped with core. You can accomplish this with `yarn bootstrap --core`
Before the tests are ran, the project must be bootstrapped with core. You can accomplish this with `yarn bootstrap --core`
##### React-Native example Tests
@ -67,15 +67,6 @@ Before the tests are ran, the project must be bootstrapped with core. You can ac
This option executes tests from `<rootdir>/app/react-native`.
Before these tests are ran, the project must be bootstrapped with the React Native example enabled. You can accomplish this by running `yarn bootstrap --reactnative`
##### Integration Tests (Screenshots of running apps)
`yarn test --integration`
This option executes tests from `<rootdir>/integration`.
In order for the snapshot-integration tests to be executed properly, examples being tested must be running on their defaults ports, as declared in `integration/examples.test.js`
Puppeteer is used to launch and grab screenshots of example pages, while jest is used to assert matching images.
##### CRA-kitchen-sink - Image snapshots using Storyshots
`yarn test --image`
@ -273,7 +264,7 @@ _Make sure `yarn dev` is running_
##### 1. Setup storybook in your project
First we are going to install storyboook, 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.
First we are going to install storyboook, 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`
@ -283,7 +274,7 @@ First we are going to install storyboook, then we are going to link `@storybook/
**_Note_**: This process is the same for `@storybook/vue`, `@storybook/addon-foo`, etc
1. Go to your storybook _root_ directory
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

View File

@ -2,8 +2,8 @@
[![Build Status on CircleCI](https://circleci.com/gh/storybooks/storybook.svg?style=shield)](https://circleci.com/gh/storybooks/storybook)
[![CodeFactor](https://www.codefactor.io/repository/github/storybooks/storybook/badge)](https://www.codefactor.io/repository/github/storybooks/storybook)
[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847/badge.svg)](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/badge.svg)](https://snyk.io/test/github/storybooks/storybook)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](https://now-examples-slackin-rrirkqohko.now.sh/)
[![Backers on Open Collective](https://opencollective.com/storybook/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/storybook/sponsors/badge.svg)](#sponsors)
@ -26,9 +26,10 @@ Storybook comes with a lot of [addons](https://storybook.js.org/addons/introduct
- [Getting Started](#getting-started)
- [Projects](#projects)
- [Main Projects](#main-projects)
- [Supported Frameworks](#supported-frameworks)
- [Sub Projects](#sub-projects)
- [Addons](#addons)
- [Live Examples](#live-examples) 💪
- [Contributing](#contributing)
- [Development scripts](#development-scripts)
- [Backers](#backers)
@ -64,12 +65,13 @@ For additional help, join us [in our Slack](https://now-examples-slackin-rrirkqo
## Projects
### Main Projects
### Supported Frameworks
- [Storybook for react](app/react) - Storybook for React components
- [Storybook for vue](app/vue) - Storybook for Vue components
- [Storybook for angular](app/angular) - Storybook for Angular components
- [Storybook for react-native](app/react-native) - Storybook for React-Native components
- [React](app/react)
- [React Native](app/react-native)
- [Vue](app/vue)
- [Angular](app/angular)
- [Polymer](app/polymer) <sup>alpha</sup>
### Sub Projects
@ -78,15 +80,44 @@ For additional help, join us [in our Slack](https://now-examples-slackin-rrirkqo
### Addons
- [storyshots](addons/storyshots) - Easy snapshot testing for storybook
- [actions](addons/actions/) - Log actions as users interact with components in storybook
- [links](addons/links/) - Create links between stories
- [comments](addons/comments/) - Comment on storybook stories
- [a11y](addons/a11y/) - Test components for user accessibility in Storybook
- [actions](addons/actions/) - Log actions as users interact with components in the Storybook UI
- [background](addons/background/) - Let users choose backgrounds in the Storybook UI
- [centered](addons/centered/) - Center the alignment of your components within the Storybook UI
- [events](addons/events/) - Interactively fire events to components that respond to EventEmitter
- [graphql](addons/graphql/) - Query a GraphQL server within Storybook stories
- [info](addons/info/) - Annotate stories with extra component usage information
- [jest](addons/jest/) - View the results of components' unit tests in Storybook
- [knobs](addons/knobs/) - Interactively edit component prop data in the Storybook UI
- [notes](addons/notes/) - Annotate storybook stories with notes
- [options](addons/options/) - Customize the storybook UI in code
- [links](addons/links/) - Create links between stories
- [notes](addons/notes/) - Annotate Storybook stories with notes
- [options](addons/options/) - Customize the Storybook UI in code
- [storyshots](addons/storyshots/) - Easy 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)
## Live Examples
### 4.0.alpha
> Note, this is an Alpha version. Some of the features still might not be released
- [React Official](https://storybooks-official.netlify.com)
- [Vue](https://storybooks-vue.netlify.com/)
- [Angular](https://storybooks-angular.netlify.com/)
- [Polymer](https://storybooks-polymer.netlify.com/)
### 3.4
- [React Official](https://release-3-4--storybooks-official.netlify.com)
- [Vue](https://release-3-4--storybooks-vue.netlify.com/)
- [Angular](https://release-3-4--storybooks-angular.netlify.com/)
- [Polymer](https://release-3-4--storybooks-polymer.netlify.com/)
### 3.3
- [React Official](https://release-3-3--storybooks-official.netlify.com)
- [Vue](https://release-3-3--storybooks-vue.netlify.com/)
- [Angular](https://release-3-3--storybooks-angular.netlify.com/)
## Contributing

View File

@ -86,7 +86,7 @@ addressed before a release can go out by adding them to the milestone. For examp
Adding bugs to the milestone helps people looking for good ways to contribute,
or to understand what is blocking the release so they can actually do something
about it. Discussion about which bugs are critical happens in the `#maintenance`
channel [in our Slack](https://now-examples-slackin-rrirkqohko.now.sh/) [![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)].(https://now-examples-slackin-rrirkqohko.now.sh/)
channel [in our Slack](https://now-examples-slackin-rrirkqohko.now.sh/) [![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](https://now-examples-slackin-rrirkqohko.now.sh/)
If you're experiencing a bug, the best way to make sure that it gets attention

View File

@ -2,22 +2,22 @@
## Table of contents
* [New features](#new-features)
+ [Responsive + multi-device viewports preview.](#responsive--multi-device-viewports-preview)
+ [Automatic story detection](#automatic-story-detection)
+ [Theme ability and override core UI components](#theme-ability-and-override-core-ui-components)
+ [Add a playground addon](#add-a-playground-addon)
+ [See multiple (or all) stories in 1 preview.](#see-multiple--or-all--stories-in-1-preview)
* [Supporting other frameworks and libraries](#supporting-other-frameworks-and-libraries)
+ [Polymer & Webcomponents](#polymer---webcomponents)
+ [Aurelia](#aurelia)
+ [Ember](#ember)
* [Breaking changes](#breaking-changes)
+ [Addon API](#addon-api)
+ [API for adding stories](#api-for-adding-stories)
* [Documentation](#documentation)
+ [Better design](#better-design)
+ [Record videos and write blog post on how to use, tweak & develop storybook](#record-videos-and-write-blog-post-on-how-to-use--tweak---develop-storybook)
* [New features](#new-features)
+ [Responsive + multi-device viewports preview.](#responsive--multi-device-viewports-preview)
+ [Automatic story detection](#automatic-story-detection)
+ [Theme ability and override core UI components](#theme-ability-and-override-core-ui-components)
+ [Add a playground addon](#add-a-playground-addon)
+ [See multiple (or all) stories in 1 preview.](#see-multiple--or-all--stories-in-1-preview)
* [Supporting other frameworks and libraries](#supporting-other-frameworks-and-libraries)
+ [Polymer & Webcomponents](#polymer---webcomponents)
+ [Aurelia](#aurelia)
+ [Ember](#ember)
* [Breaking changes](#breaking-changes)
+ [Addon API](#addon-api)
+ [API for adding stories](#api-for-adding-stories)
* [Documentation](#documentation)
+ [Better design](#better-design)
+ [Record videos and write blog post on how to use, tweak & develop storybook](#record-videos-and-write-blog-post-on-how-to-use--tweak---develop-storybook)
## New features
@ -57,7 +57,7 @@ That way you can write your stories how they are best, and preview them how you
We believe in the power of react, and think it's the right choice for a lot of projects.
But it's up to you and your team to decide your stack.
Unfortunately if you choose anything other then React or React-Native you can not use storybook.
Unfortunately if you choose anything not from the list of [supported frameworks](README.md#supported-frameworks) you can not use storybook.
We want you to be able to use storybook with the framework / library of your choice.

View File

@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import { storiesOf } from '@storybook/angular';
@Component({
selector: 'storybook-with-ng-content',
template: `<div style="color: #1e88e5;"><ng-content></ng-content></div>`,
})
class WithNgContentComponent {}
storiesOf('Custom|ng-content', module).add('Default', () => ({
template: `<storybook-with-ng-content><h1>This is rendered in ng-content</h1></storybook-with-ng-content>`,
moduleMetadata: {
declarations: [WithNgContentComponent],
},
}));

View File

@ -0,0 +1,3 @@
while(true) {
console.log("it's a kind of magic");
}

View File

@ -0,0 +1,153 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import { action } from '@storybook/addon-actions';
import DocgenButton from '../components/DocgenButton';
import FlowTypeButton from '../components/FlowTypeButton';
import BaseButton from '../components/BaseButton';
import TableComponent from '../components/TableComponent';
storiesOf('Addons|Info.React Docgen', module)
.add(
'Comments from PropType declarations',
withInfo(
'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <DocgenButton onClick={action('clicked')} label="Docgen Button" />)
)
.add(
'Comments from Flow declarations',
withInfo(
'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
)(() => <FlowTypeButton onClick={action('clicked')} label="Flow Typed Button" />)
)
.add(
'Comments from component declaration',
withInfo(
'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading'
)(() => <BaseButton onClick={action('clicked')} label="Button" />)
);
const markdownDescription = `
#### You can use markdown in your withInfo() description.
Sometimes you might want to manually include some code examples:
~~~js
const Button = () => <button />;
~~~
Maybe include a [link](http://storybook.js.org) to your project as well.
`;
storiesOf('Addons|Info.Markdown', module).add(
'Displays Markdown in description',
withInfo(markdownDescription)(() => <BaseButton onClick={action('clicked')} label="Button" />)
);
storiesOf('Addons|Info.Options.inline', module).add(
'Inlines component inside story',
withInfo({
text: 'Component should be inlined between description and PropType table',
inline: true, // Displays info inline vs click button to view
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Options.header', module).add(
'Shows or hides Info Addon header',
withInfo({
text: 'The Info Addon header should be hidden',
header: false, // Toggles display of header with component name and description
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Options.source', module).add(
'Shows or hides Info Addon source',
withInfo({
text: 'The Info Addon source section should be hidden',
source: false, // Displays the source of story Component
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Options.propTables', module).add(
'Shows additional component prop tables',
withInfo({
text: 'There should be a prop table added for a component not included in the story',
propTables: [FlowTypeButton],
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Options.propTablesExclude', module).add(
'Exclude component from prop tables',
withInfo({
text: 'This can exclude extraneous components from being displayed in prop tables.',
propTablesExclude: [FlowTypeButton],
})(() => (
<div>
<BaseButton label="Button" />
<FlowTypeButton label="Flow Typed Button" />
</div>
))
);
storiesOf('Addons|Info.Options.styles', module)
.add(
'Extend info styles with an object',
withInfo({
styles: {
button: {
base: {
background: 'purple',
},
},
header: {
h1: {
color: 'green',
},
},
},
})(() => <BaseButton label="Button" />)
)
.add(
'Full control over styles using a function',
withInfo({
styles: stylesheet => ({
...stylesheet,
header: {
...stylesheet.header,
h1: {
...stylesheet.header.h1,
color: 'red',
},
},
}),
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Options.TableComponent', module).add(
'Use a custom component for the table',
withInfo({
TableComponent,
})(() => <BaseButton label="Button" />)
);
storiesOf('Addons|Info.Decorator', module)
.addDecorator((story, context) =>
withInfo('Info could be used as a global or local decorator as well.')(story)(context)
)
.add('Use Info as story decorator', () => <BaseButton label="Button" />);
const hoc = WrapComponent => ({ ...props }) => <WrapComponent {...props} />;
const Input = hoc(() => <input type="text" />);
const TextArea = hoc(({ children }) => <textarea>{children}</textarea>);
storiesOf('Addons|Info.GitHub issues', module).add(
'#1814',
withInfo('Allow Duplicate DisplayNames for HOC #1814')(() => (
<div>
<Input />
<TextArea />
</div>
))
);

View File

@ -0,0 +1,32 @@
import { Component } from '@angular/core';
import { Store, StoreModule } from '@ngrx/store';
import { storiesOf, moduleMetadata } from '@storybook/angular';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
constructor(store: Store<any>) {
this.store = store;
}
getSotreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}
storiesOf('ngrx|Store', module)
.addDecorator(
moduleMetadata({
imports: [StoreModule.forRoot({})],
declarations: [WithStoreComponent],
})
)
.add('With component', () => {
return {
component: WithStoreComponent,
};
});

View File

@ -0,0 +1,39 @@
/* global window */
/* eslint-disable global-require, import/no-dynamic-require */
import React from 'react';
@Component({
selector: 'storybook-comp-with-store',
template: '<div>{{this.getSotreState()}}</div>',
})
class WithStoreComponent {
private store: Store<any>;
constructor(store: Store<any>) {
this.store = store;
}
getSotreState(): string {
return this.store === undefined ? 'Store is NOT injected' : 'Store is injected';
}
}
/*
eslint-disable some kind
of multi line ignore, though
I'm not sure it's possible.
*/
import { storiesOf } from '@storybook/react';
/* eslint-disable-line */ const x = 0;
// eslint-disable-line
storiesOf('Foo', module)
.add('bar', () => <div>baz</div>);
/*
This is actually a good comment that will help
users to understand what's going on here.
*/

View File

@ -0,0 +1,23 @@
/* global window */
/* eslint-disable global-require, import/no-dynamic-require */
import React from 'react';
/*
eslint-disable some kind
of multi line ignore, though
I'm not sure it's possible.
*/
import { storiesOf } from '@storybook/react';
/* eslint-disable-line */ const x = 0;
// eslint-disable-line
storiesOf('Foo', module)
.add('bar', () => <div>baz</div>);
/*
This is actually a good comment that will help
users to understand what's going on here.
*/

View File

@ -15,10 +15,10 @@ const styles = {
wrong: {
color: '#ffffff',
backgroundColor: '#4caf50',
}
}
},
};
function Button({ label, content, disabled, contrast }) {
function Button({ content, disabled, contrast }) {
return (
<button
style={{
@ -27,19 +27,19 @@ function Button({ label, content, disabled, contrast }) {
}}
disabled={disabled}
>
{ content }
{content}
</button>
)
);
}
Button.propTypes = {
label: PropTypes.string,
content: PropTypes.string,
disabled: PropTypes.bool,
contrast: PropTypes.oneOf(['ok', 'wrong'])
contrast: PropTypes.oneOf(['ok', 'wrong']),
};
Button.defaultProps = {
content: 'null',
disabled: false,
contrast: 'ok',
};

View File

@ -1,34 +1,17 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import Faker from 'faker';
import { checkA11y } from './../../../src';
import Button from './component';
import Faker from 'faker';
const text = Faker.lorem.words();
storiesOf('<Button />', module)
.addDecorator(checkA11y)
.add('Default', () => (
<Button />
))
.add('Content', () => (
<Button content={text} />
))
.add('Label', () => (
<Button label={text} />
))
.add('Disabled', () => (
<Button
disabled
content={text}
/>
))
.add('Invalid contrast', () => (
<Button
contrast="wrong"
content={Faker.lorem.words()}
/>
));
.add('Default', () => <Button />)
.add('Content', () => <Button content={text} />)
.add('Label', () => <Button label={text} />)
.add('Disabled', () => <Button disabled content={text} />)
.add('Invalid contrast', () => <Button contrast="wrong" content={Faker.lorem.words()} />);

View File

@ -2,14 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
function Input({ id, value, type, placeholder }) {
return (
<input
id={id}
value={value}
placeholder={placeholder}
type={type}
/>
);
return <input id={id} value={value} placeholder={placeholder} type={type} />;
}
Input.propTypes = {
@ -17,6 +10,13 @@ Input.propTypes = {
id: PropTypes.string,
value: PropTypes.string,
placeholder: PropTypes.string,
}
};
Input.defaultProps = {
type: null,
id: null,
value: null,
placeholder: null,
};
export default Input;

View File

@ -5,22 +5,19 @@ const styles = {
label: {
padding: '0 6px',
},
}
};
function Label({ id, content }) {
return (
<label
style={styles.label}
htmlFor={id}
>
{ content }
<label style={styles.label} htmlFor={id}>
{content}
</label>
)
);
}
Label.propTypes = {
content: PropTypes.string,
id: PropTypes.string,
content: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
};
export default Label;

View File

@ -15,7 +15,11 @@ function Row({ label, input }) {
Row.propTypes = {
label: PropTypes.instanceOf(Label),
input: PropTypes.instanceOf(Input),
}
input: PropTypes.instanceOf(Input).isRequired,
};
Row.defaultProps = {
label: null,
};
export default Row;

View File

@ -2,8 +2,4 @@ import Input from './Input';
import Label from './Label';
import Row from './Row';
export {
Input,
Label,
Row,
};
export { Input, Label, Row };

View File

@ -1,36 +1,20 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import Faker from 'faker';
import * as Form from './components';
import { storiesOf } from '@storybook/react';
import { checkA11y } from './../../../src';
import Faker from 'faker';
const label = Faker.lorem.word();
const placeholder = Faker.lorem.word();
storiesOf('<Form />', module)
.addDecorator(checkA11y)
.add('Without Label', () => (
<Form.Row
input={<Form.Input />}
/>
))
.add ('With label', () => (
<Form.Row
label={<Form.Label
content={label}
id="1"
/>}
input={<Form.Input id="1" />}
/>
))
.add ('With placeholder', () => (
<Form.Row
input={<Form.Input
id="1"
placeholder={placeholder}
/>}
/>
.add('Without Label', () => <Form.Row input={<Form.Input />} />)
.add('With label', () => (
<Form.Row label={<Form.Label content={label} id="1" />} input={<Form.Input id="1" />} />
))
.add('With placeholder', () => (
<Form.Row input={<Form.Input id="1" placeholder={placeholder} />} />
));

View File

@ -2,13 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
function Image({ src, alt, presentation }) {
return (
<img
src={src}
alt={alt}
role={presentation && 'presentation'}
/>
);
return <img src={src} alt={alt} role={presentation && 'presentation'} />;
}
Image.propTypes = {
@ -17,4 +11,9 @@ Image.propTypes = {
presentation: PropTypes.bool,
};
Image.defaultProps = {
alt: null,
presentation: false,
};
export default Image;

View File

@ -1,29 +1,16 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import Faker from 'faker';
import { checkA11y } from './../../../src';
import Image from './component';
import Faker from 'faker';
const image = Faker.image.animals();
const alt = Faker.lorem.words();
storiesOf('<Image />', module)
.addDecorator(checkA11y)
.add('Without alt', () => (
<Image src={image} />
))
.add('With alt', () => (
<Image
src={image}
alt={alt}
/>
))
.add('Presentation', () => (
<Image
presentation
src={image}
/>
));
.add('Without alt', () => <Image src={image} />)
.add('With alt', () => <Image src={image} alt={alt} />)
.add('Presentation', () => <Image presentation src={image} />);

View File

@ -1,20 +1,20 @@
import React, { cloneElement } from 'react';
import { createElement } from 'react';
import PropTypes from 'prop-types';
const headings = {
1: (<h1 />),
2: (<h2 />),
3: (<h3 />),
4: (<h4 />),
1: 'h1',
2: 'h2',
3: 'h3',
4: 'h4',
};
function Heading({ level, children }) {
return cloneElement(headings[level], {}, children)
return createElement(headings[level], {}, children);
}
Heading.propTypes = {
level: PropTypes.oneOf([1, 2, 3, 4]),
children: PropTypes.any,
children: PropTypes.node,
};
Heading.defaultProps = {

View File

@ -2,16 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
function Link({ href, content }) {
return (
<a href={href}>
{ content }
</a>
);
return <a href={href}>{content}</a>;
}
Link.propTypes = {
href: PropTypes.string,
content: PropTypes.string,
href: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
};
export default Link;

View File

@ -2,15 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
function Text({ children }) {
return (
<p>
{children}
</p>
);
return <p>{children}</p>;
}
Text.propTypes = {
children: PropTypes.any,
children: PropTypes.node.isRequired,
};
export default Text;

View File

@ -2,8 +2,4 @@ import Heading from './Heading';
import Link from './Link';
import Text from './Text';
export {
Heading,
Link,
Text,
};
export { Heading, Link, Text };

View File

@ -1,41 +1,26 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import Faker from 'faker';
import { checkA11y } from './../../../src';
import * as Typography from './components';
import Faker from 'faker';
const href = "javascript:void 0";
// eslint-disable-next-line no-script-url
const href = 'javascript:void 0';
storiesOf('<Typography />', module)
.addDecorator(checkA11y)
.add('Correct', () => (
<div>
<Typography.Heading level={1}>
{Faker.lorem.sentence()}
</Typography.Heading>
<Typography.Heading level={1}>{Faker.lorem.sentence()}</Typography.Heading>
<Typography.Text>
{Faker.lorem.paragraph()}
</Typography.Text>
<Typography.Text>{Faker.lorem.paragraph()}</Typography.Text>
<Typography.Link
content={`${Faker.lorem.words(4)}...`}
href={href}
/>
<Typography.Link content={`${Faker.lorem.words(4)}...`} href={href} />
</div>
))
.add('Empty Heading', () => (
<Typography.Heading level={2} />
))
.add('Empty Paragraph', () => (
<Typography.Text />
))
.add('Empty Link', () => (
<Typography.Link href={href} />
))
.add('Link without href', () => (
<Typography.Link content={`${Faker.lorem.words(4)}...`} />
));
.add('Empty Heading', () => <Typography.Heading level={2} />)
.add('Empty Paragraph', () => <Typography.Text />)
.add('Empty Link', () => <Typography.Link href={href} />)
.add('Link without href', () => <Typography.Link content={`${Faker.lorem.words(4)}...`} />);

View File

@ -1,9 +1,7 @@
import * as storybook from '@storybook/react';
const req = require.context('./components/', true, /stories\.js$/)
const req = require.context('./components/', true, /stories\.js$/);
const loadStories = () =>
req.keys().forEach(req);
const loadStories = () => req.keys().forEach(req);
storybook.configure(loadStories, module)
storybook.configure(loadStories, module);

View File

@ -2,6 +2,8 @@
This storybook addon can be helpfull to make your UI components more accessibile.
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![](docs/screenshot.png)
## Getting started

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-a11y",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "a11y addon for storybook",
"keywords": [
"a11y",
@ -25,10 +25,16 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/components": "^3.4.0-alpha.5",
"@storybook/components": "3.4.0-rc.3",
"axe-core": "^2.6.1",
"glamorous": "^4.11.4",
"prop-types": "^15.6.0"
"babel-runtime": "^6.26.0",
"glamor": "^2.20.40",
"glamorous": "^4.12.1",
"prop-types": "^15.6.1"
},
"devDependencies": {
"@storybook/react": "3.4.0-rc.3",
"faker": "^4.1.0"
},
"peerDependencies": {
"@storybook/addons": "^3.3.0",

View File

@ -11,10 +11,7 @@
Storybook Addon Actions can be used to display data received by event handlers in [Storybook](https://storybook.js.org).
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
- [React Native](https://github.com/storybooks/storybook/tree/master/app/react-native)
- [Vue](https://github.com/storybooks/storybook/tree/master/app/vue).
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![Screenshot](docs/screenshot.png)

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-actions",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "Action Logger addon for storybook",
"keywords": [
"storybook"
@ -20,10 +20,14 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/components": "3.4.0-rc.3",
"babel-runtime": "^6.26.0",
"deep-equal": "^1.0.1",
"glamor": "^2.20.40",
"glamorous": "^4.12.1",
"global": "^4.3.2",
"make-error": "^1.3.2",
"prop-types": "^15.6.0",
"make-error": "^1.3.4",
"prop-types": "^15.6.1",
"react-inspector": "^2.2.2",
"uuid": "^3.2.1"
},

View File

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

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Inspector from 'react-inspector';
import style from './style';
import { Actions, Action, Button, Wrapper, InspectorContainer, Countwrap, Counter } from './style';
class ActionLogger extends Component {
getActionData() {
@ -9,30 +9,28 @@ class ActionLogger extends Component {
}
renderAction(action) {
const counter = <div style={style.counter}>{action.count}</div>;
const counter = <Counter>{action.count}</Counter>;
return (
<div key={action.id} style={style.action}>
<div style={style.countwrap}>{action.count > 1 && counter}</div>
<div style={style.inspector}>
<Action key={action.id}>
<Countwrap>{action.count > 1 && counter}</Countwrap>
<InspectorContainer>
<Inspector
sortObjectKeys
showNonenumerable={false}
name={action.data.name}
data={action.data.args || action.data}
/>
</div>
</div>
</InspectorContainer>
</Action>
);
}
render() {
return (
<div style={style.wrapper}>
<pre style={style.actions}>{this.getActionData()}</pre>
<button style={style.button} onClick={this.props.onClear}>
CLEAR
</button>
</div>
<Wrapper>
<Actions>{this.getActionData()}</Actions>
<Button onClick={this.props.onClear}>Clear</Button>
</Wrapper>
);
}
}

View File

@ -1,49 +1,53 @@
export default {
wrapper: {
flex: 1,
display: 'flex',
position: 'relative',
},
actions: {
flex: 1,
margin: 0,
padding: '8px 2px 20px 0',
overflowY: 'auto',
color: '#666',
},
action: {
display: 'flex',
padding: '3px 3px 3px 0',
borderLeft: '5px solid white',
borderBottom: '1px solid #fafafa',
transition: 'all 0.1s',
alignItems: 'start',
},
countwrap: {
paddingBottom: 2,
},
counter: {
margin: '0 5px 0 5px',
backgroundColor: '#777777',
color: '#ffffff',
padding: '1px 5px',
borderRadius: '20px',
},
inspector: {
flex: 1,
padding: '0 0 0 5px',
},
button: {
position: 'absolute',
bottom: 0,
right: 0,
border: 'none',
borderTop: 'solid 1px rgba(0, 0, 0, 0.2)',
borderLeft: 'solid 1px rgba(0, 0, 0, 0.2)',
background: 'rgba(255, 255, 255, 0.5)',
padding: '5px 10px',
borderRadius: '4px 0 0 0',
color: 'rgba(0, 0, 0, 0.5)',
outline: 'none',
},
};
import glamorous from 'glamorous';
import { Button as BaseButton } from '@storybook/components';
export const Actions = glamorous.pre({
flex: 1,
margin: 0,
padding: '8px 2px 20px 0',
overflowY: 'auto',
color: '#666',
});
export const Action = glamorous.div({
display: 'flex',
padding: '3px 3px 3px 0',
borderLeft: '5px solid white',
borderBottom: '1px solid #fafafa',
transition: 'all 0.1s',
alignItems: 'start',
});
export const Button = glamorous(BaseButton)({
position: 'absolute',
bottom: 0,
right: 0,
borderRadius: '4px 0 0 0',
textTransform: 'uppercase',
letterSpacing: 1,
paddingTop: 5,
paddingBootom: 5,
});
export const Counter = glamorous.div({
margin: '0 5px 0 5px',
backgroundColor: '#777777',
color: '#ffffff',
padding: '1px 5px',
borderRadius: '20px',
});
export const Countwrap = glamorous.div({
paddingBottom: 2,
});
export const InspectorContainer = glamorous.div({
flex: 1,
padding: '0 0 0 5px',
});
export const Wrapper = glamorous.div({
flex: 1,
display: 'flex',
position: 'relative',
});

View File

@ -3,5 +3,4 @@ export const ADDON_ID = 'storybook/actions';
export const PANEL_ID = `${ADDON_ID}/actions-panel`;
export const EVENT_ID = `${ADDON_ID}/action-event`;
export { register } from './manager';
export { action, decorateAction } from './preview';

View File

@ -2,6 +2,7 @@ import reviver from './reviver';
import { muteProperty } from './util';
import { CYCLIC_KEY } from './';
// eslint-disable-next-line no-control-regex
const pathReg = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\([\\"/bfnrt]|u[0-9a-zA-Z]{4}))*")])*$/;
export default function retrocycle(json) {

View File

@ -1,7 +1,7 @@
export canConfigureName from './canConfigureName.js';
export getPropertiesList from './getPropertiesList.js';
export isObject from './isObject.js';
export muteProperty from './muteProperty.js';
export canConfigureName from './canConfigureName';
export getPropertiesList from './getPropertiesList';
export isObject from './isObject';
export muteProperty from './muteProperty';
export prepareArguments from './prepareArguments';
export typeReviver from './typeReviver.js';
export typeReplacer from './typeReplacer.js';
export typeReviver from './typeReviver';
export typeReplacer from './typeReplacer';

View File

@ -1,5 +1,3 @@
/* eslint-disable no-underscore-dangle */
import addons from '@storybook/addons';
import uuid from 'uuid/v1';
import { EVENT_ID } from './';
@ -24,11 +22,9 @@ export function action(name) {
}
export function decorateAction(decorators) {
// eslint-disable-next-line no-unused-vars, func-names
return function(name) {
return name => {
const callAction = action(name);
// eslint-disable-next-line no-unused-vars, func-names
return function(..._args) {
return (..._args) => {
const decorated = decorators.reduce((args, fn) => fn(args), _args);
callAction(...decorated);
};

View File

@ -3,24 +3,22 @@
[![Build Status on CircleCI](https://circleci.com/gh/storybooks/storybook.svg?style=shield)](https://circleci.com/gh/storybooks/storybook)
[![CodeFactor](https://www.codefactor.io/repository/github/storybooks/storybook/badge)](https://www.codefactor.io/repository/github/storybooks/storybook)
[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847/badge.svg)](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](https://now-examples-slackin-rrirkqohko.now.sh/)
[![Backers on Open Collective](https://opencollective.com/storybook/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/storybook/sponsors/badge.svg)](#sponsors)
* * *
Storybook Centered Decorator can be used to center components inside the preview in [Storybook](https://storybook.js.org).
Storybook Background Addon can be used to change background colors inside the preview in [Storybook](https://storybook.js.org).
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![React Storybook Screenshot](https://storybook.js.org/img/addon-backgrounds.gif)
## Installation
```sh
npm i --save @storybook/addon-backgrounds
npm i -D @storybook/addon-backgrounds
```
## Configuration

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-backgrounds",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "A storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -24,7 +24,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"prop-types": "^15.6.0"
"babel-runtime": "^6.26.0",
"global": "^4.3.2",
"prop-types": "^15.6.1"
},
"peerDependencies": {
"@storybook/addons": "^3.3.0",

View File

@ -1,15 +1,21 @@
import { document } from 'global';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import addons from '@storybook/addons';
import Swatch from './Swatch';
const storybookIframe = 'storybook-preview-iframe';
const style = {
font: {
fontFamily:
"-apple-system,'.SFNSText-Regular', 'San Francisco', Roboto, 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif",
fontSize: '14px',
},
iframe: {
transition: 'background 0.25s ease-in-out',
},
};
const defaultBackground = {
@ -73,26 +79,41 @@ export default class BackgroundPanel extends Component {
const currentBackground = api.getQueryParam('background');
if (currentBackground) {
this.setBackgroundInPreview(currentBackground);
this.updateIframe(currentBackground);
} else if (backgrounds.filter(x => x.default).length) {
const defaultBgs = backgrounds.filter(x => x.default);
this.setBackgroundInPreview(defaultBgs[0].value);
this.updateIframe(defaultBgs[0].value);
}
});
this.channel.on('background-unset', () => {
this.setState({ backgrounds: [] });
api.setQueryParams({ background: null });
this.updateIframe('none');
});
}
setBackgroundInPreview = background => this.channel.emit('background', background);
componentDidMount() {
this.iframe = document.getElementById(storybookIframe);
if (!this.iframe) {
throw new Error('Cannot find Storybook iframe');
}
Object.keys(style.iframe).forEach(prop => {
this.iframe.style[prop] = style.iframe[prop];
});
}
setBackgroundFromSwatch = background => {
this.setBackgroundInPreview(background);
this.updateIframe(background);
this.props.api.setQueryParams({ background });
};
updateIframe(background) {
this.iframe.style.background = background;
}
render() {
const backgrounds = [...this.state.backgrounds];

View File

@ -17,6 +17,16 @@ const mockedApi = {
};
const channel = new EventEmitter();
jest.mock('global', () => ({
document: {
getElementById() {
return {
style: {},
};
},
},
}));
describe('Background Panel', () => {
it('should exist', () => {
const backgroundPanel = shallow(<BackgroundPanel channel={channel} api={mockedApi} />);
@ -105,19 +115,18 @@ describe('Background Panel', () => {
expect(backgroundPanel.state('backgrounds')).toHaveLength(0);
});
it('should pass the event from swatch clicks through the provided channel', () => {
it('should set iframe background', () => {
const SpiedChannel = new EventEmitter();
const backgroundPanel = mount(<BackgroundPanel channel={SpiedChannel} api={mockedApi} />);
backgroundPanel.setState({ backgrounds }); // force re-render
const spy = jest.fn();
SpiedChannel.on('background', spy);
backgroundPanel
.find('h4')
.first()
.simulate('click');
expect(spy).toBeCalledWith(backgrounds[0].value);
expect(backgroundPanel.instance().iframe.style).toMatchObject({
background: backgrounds[0].value,
});
});
});

View File

@ -16,34 +16,6 @@ describe('Background Decorator', () => {
expect(backgroundDecorator).toBeDefined();
});
it('should initially have a transparent background state', () => {
const SpiedChannel = new EventEmitter();
const backgroundDecorator = shallow(
<BackgroundDecorator story={testStory} channel={SpiedChannel} />
);
expect(backgroundDecorator.state().background).toBe('transparent');
});
it('should have a background matching its state', () => {
const SpiedChannel = new EventEmitter();
const backgroundDecorator = shallow(
<BackgroundDecorator story={testStory} channel={SpiedChannel} />
);
expect(backgroundDecorator.html().match(/background:transparent/gim)).toHaveLength(1);
});
it('should set internal state when background event called', () => {
const SpiedChannel = new EventEmitter();
const backgroundDecorator = shallow(
<BackgroundDecorator story={testStory} channel={SpiedChannel} />
);
SpiedChannel.emit('background', '#123456');
expect(backgroundDecorator.state().background).toBe('#123456');
});
it('should send background-unset event when the component unmounts', () => {
const SpiedChannel = new EventEmitter();
const backgroundDecorator = shallow(

View File

@ -3,21 +3,6 @@ import PropTypes from 'prop-types';
import addons from '@storybook/addons';
const style = {
wrapper: {
overflow: 'auto',
position: 'fixed',
top: 0,
bottom: 0,
right: 0,
left: 0,
transition: 'background 0.25s ease-in-out',
backgroundPosition: 'center',
backgroundSize: 'cover',
background: 'transparent',
},
};
export class BackgroundDecorator extends React.Component {
constructor(props) {
super(props);
@ -31,13 +16,10 @@ export class BackgroundDecorator extends React.Component {
this.channel = addons.getChannel();
}
this.state = { background: 'transparent' };
this.story = story();
}
componentWillMount() {
this.channel.on('background', this.setBackground);
this.channel.emit('background-set', this.props.backgrounds);
}
@ -48,16 +30,11 @@ export class BackgroundDecorator extends React.Component {
}
componentWillUnmount() {
this.channel.removeListener('background', this.setBackground);
this.channel.emit('background-unset');
}
setBackground = background => this.setState({ background });
render() {
const styles = style.wrapper;
styles.background = this.state.background;
return <div style={Object.assign({}, styles)}>{this.story}</div>;
return this.story;
}
}
BackgroundDecorator.propTypes = {

View File

@ -11,10 +11,7 @@
Storybook Centered Decorator can be used to center components inside the preview in [Storybook](https://storybook.js.org).
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
- [Vue](https://github.com/storybooks/storybook/tree/master/app/vue)
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
### Usage

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import decorator from '../index';
import { document } from 'global';
import decorator from '../src';
const content = decorator(() => 'Hello World!');
const wrapper = document.querySelector('#content');

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-centered",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "Storybook decorator to center components",
"license": "MIT",
"author": "Muhammed Thanish <mnmtanish@gmail.com>",
@ -10,8 +10,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"babel-runtime": "^6.26.0",
"global": "^4.3.2"
},
"devDependencies": {
"react-dom": "^16.2.0"
},
"peerDependencies": {
"react": "*"
}

View File

@ -11,6 +11,8 @@
This [storybook](https://storybooks.js.org) ([source](https://github.com/storybooks/storybook)) addon allows you to add events for your stories.
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![Storybook Addon Events Example](docs/demo1.png)
[Storybook Addon Events Live Demo](https://z4o4z.github.io/storybook-addon-events/index.html)
@ -25,8 +27,8 @@ Then create a file called `addons.js` in your storybook config.
Add following content to it:
```js
import '@storybook/addon-actions';
import '@storybook/addon-links';
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-events/register';
```

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-events",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "Add events to your Storybook stories.",
"keywords": [
"addon",
@ -23,9 +23,8 @@
"dependencies": {
"babel-runtime": "^6.26.0",
"format-json": "^1.0.3",
"prop-types": "^15.6.0",
"react-textarea-autosize": "^5.2.1",
"uuid": "^3.2.1"
"prop-types": "^15.6.1",
"react-textarea-autosize": "^5.2.1"
},
"peerDependencies": {
"@storybook/addons": "^3.3.0",

View File

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

View File

@ -145,7 +145,9 @@ export default class Item extends Component {
disabled={failed}
title="Submit event"
>
📢
<span role="img" aria-label="loudspeaker">
📢
</span>
</button>
<Textarea
id={`addon-event-${name}`}
@ -155,11 +157,15 @@ export default class Item extends Component {
/>
{isTextAreaShowed ? (
<button style={styles.button} onClick={this.onToggleEditClick} title="Close editing">
<span role="img" aria-label="cross">
</span>
</button>
) : (
<button style={styles.button} onClick={this.onToggleEditClick} title="Edit event payload">
<span role="img" aria-label="pencil">
</span>
</button>
)}
</div>

View File

@ -66,7 +66,7 @@ export default class Events extends Component {
const { events } = this.state;
return (
<div style={styles.wrapper}>
{events.map(event => <Event key={event.id} {...event} onEmit={this.onEmit} />)}
{events.map(event => <Event key={event.name} {...event} onEmit={this.onEmit} />)}
</div>
);
}

View File

@ -1,3 +1 @@
export default from './preview';
export { register } from './manager';

View File

@ -1,2 +1,3 @@
import * as storybook from '@storybook/react';
storybook.configure(() => require('./stories'), module);

View File

@ -1,13 +1,14 @@
import React from 'react';
import { storiesOf } from '@storybook/react'
import { setupGraphiQL } from '../src'
import { storiesOf } from '@storybook/react';
import { setupGraphiQL } from '../src';
// setup the graphiql helper which can be used with the add method later
const graphiql = setupGraphiQL({ url: 'http://localhost:3000/graphql' });
storiesOf('GraphQL Demo', module)
.add('get user info', graphiql(`{
storiesOf('GraphQL Demo', module).add(
'get user info',
graphiql(`{
user(id: "1") {
name
}
}`));
}`)
);

View File

@ -11,8 +11,7 @@
Storybook GraphQL Addon can be used to display the GraphiQL IDE with example queries in [Storybook](https://storybook.js.org).
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![Screenshot](docs/screenshot.png)

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-graphql",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "Storybook addon to display the GraphiQL IDE",
"keywords": [
"storybook"
@ -22,10 +22,14 @@
"storybook": "start-storybook -p 9001"
},
"dependencies": {
"babel-runtime": "^6.26.0",
"global": "^4.3.2",
"graphiql": "^0.11.11",
"graphql": "^0.12.3",
"prop-types": "^15.6.0"
"graphql": "^0.13.2",
"prop-types": "^15.6.1"
},
"devDependencies": {
"@storybook/react": "3.4.0-rc.3"
},
"peerDependencies": {
"react": "*"

View File

@ -2,14 +2,10 @@ import React from 'react';
import { configure, setAddon, addDecorator } from '@storybook/react';
import InfoAddon from '../src/';
addDecorator((story) => (
<div style={{padding: 20}}>
{story()}
</div>
));
addDecorator(story => <div style={{ padding: 20 }}>{story()}</div>);
setAddon(InfoAddon);
configure(function () {
configure(() => {
require('../example/story');
}, module);

View File

@ -1,4 +1,4 @@
module.exports = function (config) {
module.exports = () => {
// This is the default webpack config defined in the `../webpack.config.js`
// modify as you need.
};

View File

@ -19,7 +19,7 @@ const config = {
test: /\.json?$/,
loaders: ['json'],
include: path.resolve(__dirname, '../'),
}
},
],
},
};

View File

@ -12,8 +12,7 @@
Storybook Info Addon will show additional information for your stories in [Storybook](https://storybook.js.org).
Useful when you want to display usage or other types of documentation alongside your story.
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
![Screenshot](docs/home-screenshot.png)

View File

@ -1,20 +1,21 @@
import PropTypes from 'prop-types';
import React from 'react';
const Button = ({ disabled, label, style, onClick }) => (
const Button = ({ disabled, label, onClick }) => (
<button disabled={disabled} onClick={onClick}>
{label}
</button>
);
Object.assign(Button, {
displayName: 'Button',
propTypes: {
label: PropTypes.string.isRequired,
style: PropTypes.object,
disabled: PropTypes.bool,
onClick: PropTypes.func,
},
});
Button.propTypes = {
label: PropTypes.string.isRequired,
disabled: PropTypes.bool,
onClick: PropTypes.func,
};
Button.defaultProps = {
disabled: false,
onClick() {},
};
export default Button;

View File

@ -1,21 +1,18 @@
import React from 'react';
import Button from './Button';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
storiesOf(
'Button'
).addWithInfo(
import Button from './Button';
storiesOf('Button').addWithInfo(
'simple usage',
'This is the basic usage with the button with providing a label to show the text.',
() => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
<p>
Click the "?" mark at top-right to view the info.
</p>
<p>Click the "?" mark at top-right to view the info.</p>
</div>
)
);
@ -88,27 +85,22 @@ storiesOf('Button').addWithInfo(
<div>
<h2>This is a JSX info section</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed ornare massa rutrum metus commodo, a mattis velit dignissim.
Fusce vestibulum turpis sed massa egestas pharetra. Sed at libero
nulla.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ornare massa rutrum metus
commodo, a mattis velit dignissim. Fusce vestibulum turpis sed massa egestas pharetra. Sed at
libero nulla.
</p>
<p>
<a href="https://github.com/storybooks/react-storybook-addon-info">
This is a link
</a>
<a href="https://github.com/storybooks/react-storybook-addon-info">This is a link</a>
</p>
<p>
<img src="http://placehold.it/350x150" />
<img alt="350x150" src="http://placehold.it/350x150" />
</p>
</div>,
() => (
<div>
<Button label="The Button" onClick={action('onClick')} />
<br />
<p>
Click the "?" mark at top-right to view the info.
</p>
<p>Click the "?" mark at top-right to view the info.</p>
</div>
)
);
@ -118,18 +110,15 @@ storiesOf('Button').addWithInfo(
<div>
<h2>This is a JSX info section</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed ornare massa rutrum metus commodo, a mattis velit dignissim.
Fusce vestibulum turpis sed massa egestas pharetra. Sed at libero
nulla.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ornare massa rutrum metus
commodo, a mattis velit dignissim. Fusce vestibulum turpis sed massa egestas pharetra. Sed at
libero nulla.
</p>
<p>
<a href="https://github.com/storybooks/react-storybook-addon-info">
This is a link
</a>
<a href="https://github.com/storybooks/react-storybook-addon-info">This is a link</a>
</p>
<p>
<img src="http://placehold.it/350x150" />
<img alt="350x150" src="http://placehold.it/350x150" />
</p>
</div>,
() => <Button label="The Button" onClick={action('onClick')} />,
@ -182,11 +171,11 @@ storiesOf('Button').addWithInfo(
() => <Button label="The Button" onClick={action('onClick')} />,
{
inline: true,
styles: stylesheet => {
stylesheet.infoPage = {
styles: stylesheet => ({
...stylesheet,
infoPage: {
backgroundColor: '#ccc',
};
return stylesheet;
},
},
}),
}
);

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-info",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "A Storybook addon to show additional information for your stories.",
"license": "MIT",
"main": "dist/index.js",
@ -15,18 +15,21 @@
"storybook": "start-storybook -p 9010"
},
"dependencies": {
"@storybook/client-logger": "^3.4.0-alpha.5",
"@storybook/components": "^3.4.0-alpha.5",
"@storybook/client-logger": "3.4.0-rc.3",
"@storybook/components": "3.4.0-rc.3",
"babel-runtime": "^6.26.0",
"glamorous": "^4.11.4",
"glamor": "^2.20.40",
"glamorous": "^4.12.1",
"global": "^4.3.2",
"marksy": "^6.0.3",
"nested-object-assign": "^1.0.1",
"prop-types": "^15.6.0",
"prop-types": "^15.6.1",
"react-addons-create-fragment": "^15.5.3",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"@storybook/addon-actions": "3.4.0-rc.3",
"@storybook/react": "3.4.0-rc.3",
"react-test-renderer": "^16.1.0"
},
"peerDependencies": {

View File

@ -1,5 +1,3 @@
/* eslint-disable no-underscore-dangle */
import PropTypes from 'prop-types';
import React from 'react';

View File

@ -1,13 +1,13 @@
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const ArrayOf = ({ propType }) => (
<span>
<span>[</span>
<span>
<PrettyPropType propType={propType.value} />
<PrettyPropType propType={getPropTypes(propType)} />
</span>
<span>]</span>
</span>

View File

@ -1,7 +1,13 @@
import React from 'react';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const Enum = ({ propType }) => <span>{propType.value.map(({ value }) => value).join(' | ')}</span>;
const Enum = ({ propType }) => (
<span>
{getPropTypes(propType)
.map(({ value }) => value)
.join(' | ')}
</span>
);
Enum.propTypes = {
propType: TypeInfo.isRequired,

View File

@ -1,7 +1,7 @@
import React from 'react';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const InstanceOf = ({ propType }) => <span>{propType.value}</span>;
const InstanceOf = ({ propType }) => <span>{getPropTypes(propType)}</span>;
InstanceOf.propTypes = {
propType: TypeInfo.isRequired,

View File

@ -0,0 +1,10 @@
import React from 'react';
import { TypeInfo } from './proptypes';
const Literal = ({ propType }) => <span>{propType.value}</span>;
Literal.propTypes = {
propType: TypeInfo.isRequired,
};
export default Literal;

View File

@ -1,5 +0,0 @@
import React from 'react';
const ObjectType = () => <span>{}</span>;
export default ObjectType;

View File

@ -1,12 +1,12 @@
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const ObjectOf = ({ propType }) => (
<span>
{'{[<key>]: '}
<PrettyPropType propType={propType.value} />
<PrettyPropType propType={getPropTypes(propType)} />
{'}'}
</span>
);

View File

@ -1,5 +0,0 @@
import React from 'react';
const ObjectType = () => <span>{}</span>;
export default ObjectType;

View File

@ -1,7 +1,12 @@
import React from 'react';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const OneOf = ({ propType }) => <span>{propType.value.map(({ value }) => value).join(' | ')}</span>;
const joinValues = propTypes => propTypes.map(({ value }) => value).join(' | ');
const OneOf = ({ propType }) => {
const propTypes = getPropTypes(propType);
return <span>{`oneOf ${Array.isArray(propTypes) ? joinValues(propTypes) : propTypes}`}</span>;
};
OneOf.propTypes = {
propType: TypeInfo.isRequired,

View File

@ -1,18 +1,18 @@
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const OneOfType = ({ propType }) => {
const { length } = propType.value;
const propTypes = getPropTypes(propType);
return (
<span>
{propType.value
{propTypes
.map((value, i) => {
const key = `${value.name}${value.value ? `-${value.value}` : ''}`;
return [
<PrettyPropType key={key} propType={value} />,
i < length - 1 ? <span key={`${key}-separator`}> | </span> : null,
i < propTypes.length - 1 ? <span key={`${key}-separator`}> | </span> : null,
];
})
.reduce((acc, tuple) => acc.concat(tuple), [])}

View File

@ -1,7 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import ObjectType from './ObjectType';
import Shape from './Shape';
import OneOfType from './OneOfType';
import ArrayOf from './ArrayOf';
@ -9,6 +8,7 @@ import ObjectOf from './ObjectOf';
import OneOf from './OneOf';
import InstanceOf from './InstanceOf';
import Signature from './Signature';
import Literal from './Literal';
import { TypeInfo } from './proptypes';
@ -19,7 +19,7 @@ const propTypeComponentMap = new Map([
['arrayOf', ArrayOf],
['objectOf', ObjectOf],
// Might be overkill to have below proptypes as separate components *shrug*
['object', ObjectType],
['literal', Literal],
['enum', OneOf],
['instanceOf', InstanceOf],
['signature', Signature],

View File

@ -5,7 +5,7 @@ import { HighlightButton } from '@storybook/components';
import PrettyPropType from './PrettyPropType';
import PropertyLabel from './PropertyLabel';
import { TypeInfo } from './proptypes';
import { TypeInfo, getPropTypes } from './proptypes';
const MARGIN_SIZE = 15;
@ -33,6 +33,7 @@ class Shape extends React.Component {
render() {
const { propType, depth } = this.props;
const propTypes = getPropTypes(propType);
return (
<span>
<HighlightButton
@ -45,13 +46,13 @@ class Shape extends React.Component {
</HighlightButton>
<HighlightButton onClick={this.handleToggle}>...</HighlightButton>
{!this.state.minimized &&
Object.keys(propType.value).map(childProperty => (
Object.keys(propTypes).map(childProperty => (
<div key={childProperty} style={{ marginLeft: depth * MARGIN_SIZE }}>
<PropertyLabel
property={childProperty}
required={propType.value[childProperty].required}
required={propTypes[childProperty].required}
/>
<PrettyPropType depth={depth + 1} propType={propType.value[childProperty]} />
<PrettyPropType depth={depth + 1} propType={propTypes[childProperty]} />
,
</div>
))}

View File

@ -7,3 +7,5 @@ export const TypeInfo = oneOfType([
}),
PropTypes.string,
]);
export const getPropTypes = propType => propType.value || propType.elements;

View File

@ -2,6 +2,8 @@
Brings Jest results in storybook.
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
[![Storybook Jest Addon Demo](@storybook/addon-jest.gif)](https://storybook-addon-jest-example.herokuapp.com/)
> Checkout the above [Live Storybook](https://storybook-addon-jest-example.herokuapp.com/).
@ -113,8 +115,8 @@ storiesOf('MyComponent', module)
### withTests(options)
- **options.results**: [OBJECT] jest output results. *mandatory*
- **filteExt**: [STRING] test file extention. *optionnal*. This allow you to write "MyComponent" and not "MyComponent.test.js". It will be used as regex to find your file results. Default value is `((\\.specs?)|(\\.tests?))?(\\.js)?$`. That mean it will match: MyComponent.js, MyComponent.test.js, MyComponent.tests.js, MyComponent.spec.js, MyComponent.specs.js...
- **options.results**: OBJECT jest output results. *mandatory*
- **filteExt**: STRING test file extention. *optionnal*. This allow you to write "MyComponent" and not "MyComponent.test.js". It will be used as regex to find your file results. Default value is `((\\.specs?)|(\\.tests?))?(\\.js)?$`. That mean it will match: MyComponent.js, MyComponent.test.js, MyComponent.tests.js, MyComponent.spec.js, MyComponent.specs.js...
## TODO

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",
@ -25,11 +25,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/components": "^3.4.0-alpha.5",
"@storybook/components": "3.4.0-rc.3",
"babel-runtime": "^6.26.0",
"glamor": "^2.20.40",
"glamorous": "^4.11.4",
"glamorous": "^4.12.1",
"global": "^4.3.2",
"prop-types": "^15.6.0"
"prop-types": "^15.6.1"
},
"peerDependencies": {
"@storybook/addons": "^3.3.0",

View File

@ -98,7 +98,7 @@ const Content = glamorous(({ tests, className }) => (
<div className={className}>
{tests.map(({ name, result }) => {
if (!result) {
return <NoTests>This story has tests configured, but no file was found</NoTests>;
return <NoTests key={name}>This story has tests configured, but no file was found</NoTests>;
}
const successNumber = result.assertionResults.filter(({ status }) => status === 'passed')

View File

@ -35,7 +35,8 @@ const StackTrace = glamorous(({ trace, className }) => (
.join('')
.trim()
.split(/\n/)
.map(i => <div>{i.trim()}</div>)}
// eslint-disable-next-line react/no-array-index-key
.map((traceLine, traceLineIndex) => <div key={traceLineIndex}>{traceLine.trim()}</div>)}
</details>
))({
background: 'silver',
@ -80,6 +81,11 @@ const createSubgroup = (acc, item, i, list) => {
if (!acc.grouped) {
acc.grouped = [];
}
if (!('grouperIndex' in acc)) {
acc.grouperIndex = 0;
} else {
acc.grouperIndex += 1;
}
// start or stop extraction
if (acc.startTrigger(item)) {
@ -100,15 +106,22 @@ const createSubgroup = (acc, item, i, list) => {
// on last iteration inject at detected injectionpoint, and group
if (i === list.length - 1) {
// Provide a "safety net" when Jest returns a partially recognized "group"
// (recognized by acc.startTrigger but acc.endTrigger was never found) and
// it's the only group in output for a test result. In that case, acc.list
// will be empty, so return whatever was found, even if it will be unstyled
// and prevent next createSubgroup calls from throwing due to empty lists.
acc.list.push(null);
return acc.list.reduce((eacc, el, ei) => {
switch (true) {
case acc.injectionPoint === 0 && ei === 0: {
// at index 0, inject before
return eacc.concat(acc.grouper(acc.grouped)).concat(el);
return eacc.concat(acc.grouper(acc.grouped, acc.grouperIndex)).concat(el);
}
case acc.injectionPoint > 0 && acc.injectionPoint === ei + 1: {
// at index > 0, and next index WOULD BE injectionPoint, inject after
return eacc.concat(el).concat(acc.grouper(acc.grouped));
return eacc.concat(el).concat(acc.grouper(acc.grouped, acc.grouperIndex));
}
default: {
// do not inject
@ -150,7 +163,7 @@ const Message = ({ msg }) => {
.reduce(createSubgroup, {
startTrigger: e => typeof e === 'string' && e.indexOf('Error: ') === 0,
endTrigger: e => typeof e === 'string' && e.match('Expected '),
grouper: list => <Main msg={list} />,
grouper: (list, key) => <Main key={key} msg={list} />,
})
.reduce(
(acc, it) =>
@ -161,12 +174,12 @@ const Message = ({ msg }) => {
.reduce(createSubgroup, {
startTrigger: e => typeof e === 'string' && e.indexOf('Expected ') !== -1,
endTrigger: e => typeof e === 'string' && e.match(/^at/),
grouper: list => <Sub msg={list} />,
grouper: (list, key) => <Sub key={key} msg={list} />,
})
.reduce(createSubgroup, {
startTrigger: e => typeof e === 'string' && e.match(/at(.|\n)+\d+:\d+\)/),
endTrigger: () => false,
grouper: list => <StackTrace trace={list} />,
grouper: (list, key) => <StackTrace key={key} trace={list} />,
});
return <Pre>{data}</Pre>;

View File

@ -1,4 +1,3 @@
/* eslint-disable react/no-danger */
import React from 'react';
import addons from '@storybook/addons';

View File

@ -1,4 +1,4 @@
module.exports = function (config) {
module.exports = () => {
// This is the default webpack config defined in the `../webpack.config.js`
// modify as you need.
};

View File

@ -7,20 +7,12 @@
[![Storybook Slack](https://now-examples-slackin-rrirkqohko.now.sh/badge.svg)](https://now-examples-slackin-rrirkqohko.now.sh/)
[![Backers on Open Collective](https://opencollective.com/storybook/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/storybook/sponsors/badge.svg)](#sponsors)
This addon works with Storybook for:
[React](https://github.com/storybooks/storybook/tree/master/app/react).
[React Native](https://github.com/storybooks/storybook/tree/master/app/react-native).
[Vue](https://github.com/storybooks/storybook/tree/master/app/vue).
* * *
Storybook Addon Knobs allow you to edit React props dynamically using the Storybook UI.
You can also use Knobs as a dynamic variable inside stories in [Storybook](https://storybook.js.org).
This addon works with Storybook for:
- [React](https://github.com/storybooks/storybook/tree/master/app/react)
- [React Native](https://github.com/storybooks/storybook/tree/master/app/react-native)
- [Vue](https://github.com/storybooks/storybook/tree/master/app/vue)
[Framework Support](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md)
This is how Knobs look like:
@ -44,6 +36,7 @@ import '@storybook/addon-knobs/register'
Now, write your stories with knobs.
### With React
```js
import { storiesOf } from '@storybook/react';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/react';
@ -71,6 +64,40 @@ stories.add('as dynamic variables', () => {
});
```
### With Angular
```js
import { storiesOf } from '@storybook/angular';
import { boolean, number, text, withKnobs } from '@storybook/addon-knobs/angular';
import { Button } from '@storybook/angular/demo';
const stories = storiesOf('Storybook Knobs', module);
// "withKnobs" decorator should be applied before the stories using knobs
stories.addDecorator(withKnobs);
// Knobs for Angular props
stories.add('with text', () => ({
component: Button,
props: {
text: text('text', 'Hello Button'), // The first param of the knob function has to be exactly the same as the component input.
},
}));
```
Categorize your knobs by assigning them a `groupId`. When a `groupId` exists, tabs will appear in the knobs storybook panel to filter between the groups. Knobs without a `groupId` are automatically categorized into the `ALL` group.
```
// Knob assigned a groupId.
stories.add('as dynamic variables', () => {
const groupId = 'GROUP-ID1'
const name = text('Name', 'Arunoda Susiripala', groupId);
const content = `My name is ${name}.`;
return (<div>{content}</div>);
});
```
> In the case of Vue, use these imports:
>
> ```js
@ -123,10 +150,10 @@ import { text } from '@storybook/addon-knobs/react';
const label = 'Your Name';
const defaultValue = 'Arunoda Susiripala';
const groupId = 'GROUP-ID1';
const value = text(label, defaultValue);
const value = text(label, defaultValue, groupId);
```
### boolean
Allows you to get a boolean value from the user.
@ -136,10 +163,10 @@ import { boolean } from '@storybook/addon-knobs/react';
const label = 'Agree?';
const defaultValue = false;
const groupId = 'GROUP-ID1';
const value = boolean(label, defaultValue);
const value = boolean(label, defaultValue, groupId);
```
### number
Allows you to get a number from the user.
@ -149,10 +176,15 @@ import { number } from '@storybook/addon-knobs/react';
const label = 'Age';
const defaultValue = 78;
const groupId = 'GROUP-ID1';
const value = number(label, defaultValue);
```
For use with `groupId`, pass the default `options` as the third argument
```
const value = number(label, defaultValue, {}, groupId);
```
### number bound by range
Allows you to get a number from the user using a range slider.
@ -168,8 +200,9 @@ const options = {
max: 90,
step: 1,
};
const groupId = 'GROUP-ID1';
const value = number(label, defaultValue, options);
const value = number(label, defaultValue, options, groupId);
```
### color
@ -181,8 +214,9 @@ import { color } from '@storybook/addon-knobs/react';
const label = 'Color';
const defaultValue = '#ff00ff';
const groupId = 'GROUP-ID1';
const value = color(label, defaultValue);
const value = color(label, defaultValue, groupId);
```
### object
@ -196,8 +230,9 @@ const label = 'Styles';
const defaultValue = {
backgroundColor: 'red'
};
const groupId = 'GROUP-ID1';
const value = object(label, defaultValue);
const value = object(label, defaultValue, groupId);
```
> Make sure to enter valid JSON syntax while editing values inside the knob.
@ -210,7 +245,8 @@ Allows you to get an array of strings from the user.
import { array } from '@storybook/addon-knobs/react';
const label = 'Styles';
const defaultValue = ['Red']
const defaultValue = ['Red'];
const groupId = 'GROUP-ID1';
const value = array(label, defaultValue);
```
@ -227,6 +263,11 @@ const value = array(label, defaultValue);
> const value = array(label, defaultValue, separator);
> ```
For use with `groupId`, pass the default `separator` as the third argument
```
const value = array(label, defaultValue, ',', groupId);
```
### select
Allows you to get a value from a select box from the user.
@ -241,12 +282,34 @@ const options = {
yellow: 'Yellow',
};
const defaultValue = 'red';
const groupId = 'GROUP-ID1';
const value = select(label, options, defaultValue);
const value = select(label, options, defaultValue, groupId);
```
> You can also provide options as an array like this: `['red', 'blue', 'yellow']`
### selectV2
In v4 this will replace `select`. The value from the select now uses the values from the `options` object.
```js
import { selectV2 } from '@storybook/addon-knobs';
const label = 'Colors';
const options = {
Red: 'red',
Blue: 'blue',
Yellow: 'yellow',
Rainbow: ['red', 'orange', 'etc'],
None: null,
};
const defaultValue = 'Red';
const groupId = 'GROUP-ID1';
const value = selectV2(label, options, defaultValue, groupId);
```
### files
Allows you to get a value from a file input from the user.
@ -271,7 +334,9 @@ import { date } from '@storybook/addon-knobs/react';
const label = 'Event Date';
const defaultValue = new Date('Jan 20 2017');
const value = date(label, defaultValue);
const groupId = 'GROUP-ID1';
const value = date(label, defaultValue, groupId);
```
> Note: the default value must not change - e.g., do not do `date('Label', new Date())` or `date('Label')`
@ -279,7 +344,7 @@ const value = date(label, defaultValue);
The `date` knob returns the selected date as stringified Unix timestamp (e.g. `"1510913096516"`).
If your component needs the date in a different form you can wrap the `date` function:
```
```js
function myDateKnob(name, defaultValue) {
const stringTimestamp = date(name, defaultValue)
return new Date(stringTimestamp)
@ -295,7 +360,9 @@ import { button } from '@storybook/addon-knobs';
const label = 'Do Something';
const handler = () => doSomething('foobar');
button(label, handler);
const groupId = 'GROUP-ID1';
button(label, handler, groupId);
```
### withKnobs vs withKnobsOptions

View File

@ -36,8 +36,8 @@ stories.add('with all knobs', () => {
return (
<div style={style}>
I'm {name} and I was born on "{moment(dob).format('DD MMM YYYY')}"
I like: <ul>{passions.map((p, i) => <li key={i}>{p}</li>)}</ul>
I'm {name} and I was born on "{moment(dob).format('DD MMM YYYY')}" I like:{' '}
<ul>{passions.map(p => <li key={p}>{p}</li>)}</ul>
<p>My favorite number is {favoriteNumber}.</p>
<p>My most comfortable room temperature is {comfortTemp} degrees Fahrenheit.</p>
<p>When I am happy I look like this: <img src={images[0]} /></p>
@ -53,25 +53,33 @@ stories.add('dates Knob', () => {
return (
<ul style={{ listStyleType: 'none', listStyle: 'none', paddingLeft: '15px' }}>
<li>
<p><b>Javascript Date</b> default value, passes date value</p>
<p>
<b>Javascript Date</b> default value, passes date value
</p>
<blockquote>
<code>const myDob = date('My DOB', new Date('July 07 1993'));</code>
<pre>// I was born in: "{moment(myDob).format('DD MMM YYYY')}"</pre>
<pre>{`// I was born in: "${moment(myDob).format('DD MMM YYYY')}"`}</pre>
</blockquote>
</li>
<li>
<p><b>undefined</b> default value passes today's date</p>
<p>
<b>undefined</b> default value passes today's date
</p>
<blockquote>
<code>const today = date('today');</code>
<pre>// Today's date is: "{moment(today).format('DD MMM YYYY')}"</pre>
<pre>{`// Today's date is: "${moment(today).format('DD MMM YYYY')}"`}</pre>
</blockquote>
</li>
<li>
<p><b>null</b> default value passes null value</p>
<p>
<b>null</b> default value passes null value
</p>
<blockquote>
<code>const dob = date('DOB', null);</code>
<pre>
// You were born in: "{dob ? moment(dob).format('DD MMM YYYY') : 'Please select date.'}"
{`// You were born in: "${
dob ? moment(dob).format('DD MMM YYYY') : 'Please select date.'
}"`}
</pre>
</blockquote>
</li>
@ -83,9 +91,7 @@ stories.add('dynamic knobs', () => {
const showOptional = select('Show optional', ['yes', 'no'], 'yes');
return (
<div>
<div>
{text('compulsary', 'I must be here')}
</div>
<div>{text('compulsary', 'I must be here')}</div>
{showOptional === 'yes' ? <div>{text('optional', 'I can disapear')}</div> : null}
</div>
);

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-knobs",
"version": "3.4.0-alpha.5",
"version": "3.4.0-rc.3",
"description": "Storybook Addon Prop Editor Component",
"license": "MIT",
"main": "dist/index.js",
@ -15,22 +15,24 @@
"storybook": "start-storybook -p 9010"
},
"dependencies": {
"@storybook/components": "3.4.0-rc.3",
"babel-runtime": "^6.26.0",
"deep-equal": "^1.0.1",
"global": "^4.3.2",
"insert-css": "^2.0.0",
"lodash.debounce": "^4.0.8",
"moment": "^2.20.1",
"prop-types": "^15.6.0",
"react-color": "^2.11.4",
"react-datetime": "^2.11.1",
"moment": "^2.21.0",
"prop-types": "^15.6.1",
"react-color": "^2.14.0",
"react-datetime": "^2.14.0",
"react-textarea-autosize": "^5.2.1",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"@storybook/react": "3.4.0-rc.3",
"raw-loader": "^0.5.1",
"style-loader": "^0.19.1",
"vue": "^2.5.13"
"style-loader": "^0.20.3",
"vue": "^2.5.16"
},
"peerDependencies": {
"@storybook/addons": "^3.3.0",

View File

@ -11,6 +11,7 @@ export default class KnobStore {
set(key, value) {
this.store[key] = value;
this.store[key].used = true;
this.store[key].groupId = value.groupId;
this.callbacks.forEach(cb => cb());
}

View File

@ -86,11 +86,12 @@ const getAnnotatedComponent = ({ componentMeta, component, params, knobStore, ch
return KnobWrapperComponent;
};
const createComponentFromTemplate = template => {
const createComponentFromTemplate = (template, styles) => {
const componentClass = class DynamicComponent {};
return Component({
template,
styles,
})(componentClass);
};
@ -106,10 +107,10 @@ export function prepareComponent({ getStory, context, channel, knobStore }) {
resetKnobs(knobStore, channel);
const story = getStory(context);
let { component } = story;
const { template } = story;
const { template, styles } = story;
if (!component) {
component = createComponentFromTemplate(template);
component = createComponentFromTemplate(template, styles);
}
const { componentMeta, props, params, moduleMetadata } = getComponentMetadata({

View File

@ -12,12 +12,13 @@ import {
array,
date,
select,
selectV2,
button,
files,
manager,
} from '../base';
export { knob, text, boolean, number, color, object, array, date, select, button, files };
export { knob, text, boolean, number, color, object, array, date, select, selectV2, button, files };
export const angularHandler = (channel, knobStore) => getStory => context =>
prepareComponent({ getStory, context, channel, knobStore });

View File

@ -1,3 +1,4 @@
import deprecate from 'util-deprecate';
import KnobManager from './KnobManager';
export const manager = new KnobManager();
@ -6,15 +7,15 @@ export function knob(name, options) {
return manager.knob(name, options);
}
export function text(name, value) {
return manager.knob(name, { type: 'text', value });
export function text(name, value, groupId) {
return manager.knob(name, { type: 'text', value, groupId });
}
export function boolean(name, value) {
return manager.knob(name, { type: 'boolean', value });
export function boolean(name, value, groupId) {
return manager.knob(name, { type: 'boolean', value, groupId });
}
export function number(name, value, options = {}) {
export function number(name, value, options = {}, groupId) {
const rangeDefaults = {
min: 0,
max: 10,
@ -32,34 +33,41 @@ export function number(name, value, options = {}) {
...mergedOptions,
type: 'number',
value,
groupId,
};
return manager.knob(name, finalOptions);
}
export function color(name, value) {
return manager.knob(name, { type: 'color', value });
export function color(name, value, groupId) {
return manager.knob(name, { type: 'color', value, groupId });
}
export function object(name, value) {
return manager.knob(name, { type: 'object', value });
export function object(name, value, groupId) {
return manager.knob(name, { type: 'object', value, groupId });
}
export function select(name, options, value) {
return manager.knob(name, { type: 'select', options, value });
export const select = deprecate(
(name, options, value, groupId) =>
manager.knob(name, { type: 'select', options, value, groupId }),
'in v4 keys/values of the options argument are reversed'
);
export function selectV2(name, options, value, groupId) {
return manager.knob(name, { type: 'select', selectV2: true, options, value, groupId });
}
export function array(name, value, separator = ',') {
return manager.knob(name, { type: 'array', value, separator });
export function array(name, value, separator = ',', groupId) {
return manager.knob(name, { type: 'array', value, separator, groupId });
}
export function date(name, value = new Date()) {
export function date(name, value = new Date(), groupId) {
const proxyValue = value ? value.getTime() : null;
return manager.knob(name, { type: 'date', value: proxyValue });
return manager.knob(name, { type: 'date', value: proxyValue, groupId });
}
export function button(name, callback) {
return manager.knob(name, { type: 'button', callback, hideLabel: true });
export function button(name, callback, groupId) {
return manager.knob(name, { type: 'button', callback, hideLabel: true, groupId });
}
export function files(name, accept, value = []) {

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