mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-05 01:31:06 +08:00
Merge branch 'release/3.3' into 269-angular-support
This commit is contained in:
commit
1a8dbdd09f
@ -6,7 +6,7 @@ defaults: &defaults
|
||||
version: 2
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
- yarn global add npm
|
||||
jobs:
|
||||
validate:
|
||||
<<: *defaults
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
- run:
|
||||
name: "Bootstrapping"
|
||||
command: |
|
||||
npm run bootstrap -- --all
|
||||
yarn bootstrap -- --all
|
||||
- save_cache:
|
||||
key: package-dependencies-{{ checksum "package.json" }}
|
||||
paths:
|
||||
@ -63,15 +63,19 @@ jobs:
|
||||
- run:
|
||||
name: "Bootstrapping"
|
||||
command: |
|
||||
npm run bootstrap -- --core
|
||||
yarn bootstrap -- --core
|
||||
- run:
|
||||
name: "Build react kitchen-sink"
|
||||
command: |
|
||||
cd examples/cra-kitchen-sink && npm run build-storybook
|
||||
cd examples/cra-kitchen-sink
|
||||
yarn build-storybook
|
||||
yarn storybook -- --smoke-test
|
||||
- run:
|
||||
name: "Build vue kitchen-sink"
|
||||
command: |
|
||||
cd examples/vue-kitchen-sink && npm run build-storybook
|
||||
cd examples/vue-kitchen-sink
|
||||
yarn build-storybook
|
||||
yarn storybook -- --smoke-test
|
||||
example-react-native:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@ -87,11 +91,17 @@ jobs:
|
||||
- run:
|
||||
name: "Bootstrapping packages"
|
||||
command: |
|
||||
npm run bootstrap -- --core --reactnative
|
||||
yarn bootstrap -- --core --reactnative --reactnativeapp
|
||||
- run:
|
||||
name: "Running react-native"
|
||||
name: "Running React-Native example"
|
||||
command: |
|
||||
echo "TODO"
|
||||
cd examples/react-native-vanilla
|
||||
yarn storybook -- --smoke-test
|
||||
- run:
|
||||
name: "Running React-Native-App example"
|
||||
command: |
|
||||
cd examples/crna-kitchen-sink
|
||||
yarn storybook -- --smoke-test
|
||||
docs:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@ -107,11 +117,11 @@ jobs:
|
||||
- run:
|
||||
name: "Bootstrapping"
|
||||
command: |
|
||||
npm run bootstrap -- --docs
|
||||
yarn bootstrap -- --docs
|
||||
- run:
|
||||
name: "Running docs"
|
||||
command: |
|
||||
npm run docs:build
|
||||
yarn docs:build
|
||||
lint:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@ -127,7 +137,7 @@ jobs:
|
||||
- run:
|
||||
name: "Linting"
|
||||
command: |
|
||||
npm run lint
|
||||
yarn lint
|
||||
unit-test:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@ -143,12 +153,12 @@ jobs:
|
||||
- run:
|
||||
name: "Bootstrapping"
|
||||
command: |
|
||||
npm run bootstrap -- --core --reactnative
|
||||
yarn bootstrap -- --core --reactnative
|
||||
- run:
|
||||
name: "Unit testing"
|
||||
command: |
|
||||
npm run test -- --coverage -i
|
||||
npm run coverage
|
||||
yarn test -- --all --coverage --runInBand
|
||||
yarn coverage
|
||||
deploy:
|
||||
<<: *defaults
|
||||
steps:
|
||||
|
@ -72,7 +72,7 @@ module.exports = {
|
||||
'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'] }],
|
||||
'jsx-a11y/anchor-is-valid': [warn, { aspects: ['invalidHref'] }],
|
||||
'react/no-unescaped-entities': ignore,
|
||||
},
|
||||
};
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,7 +12,6 @@ coverage/
|
||||
*.lerna_backup
|
||||
build
|
||||
packages/examples/automated-*
|
||||
yarn.lock
|
||||
/**/LICENSE
|
||||
docs/public
|
||||
packs/*.tgz
|
||||
|
50
.mailmap
Normal file
50
.mailmap
Normal file
@ -0,0 +1,50 @@
|
||||
# --- instructions --- #
|
||||
|
||||
# Add your account in this format:
|
||||
Your name here <yourname@example.com> # github:my-github-account, npm:my-npm-account, twitter:my-twitter-handle
|
||||
|
||||
# supported:
|
||||
# github, npm, twitter, website
|
||||
|
||||
# --- list ----------- #
|
||||
|
||||
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 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>
|
||||
Eli Sherer <eli.sherer@gmail.com> elish <elish@payoneer.com>
|
||||
Evgeny Kochetkov <evgeny.kochetkov@me.com> Evgeny Kochetkov <evgenykochetkov@users.noreply.github.com>
|
||||
Fabien Bernard <fabien0102@hotmail.com> Fabien BERNARD <fabien0102@hotmail.com>
|
||||
Fernando Daciuk <f.daciuk@gmail.com> <fdaciuk@users.noreply.github.com>
|
||||
Greenkeeper <support@greenkeeper.io> greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>
|
||||
Greenkeeper <support@greenkeeper.io> greenkeeperio-bot <support@greenkeeper.io>
|
||||
Jason Schloer <jschloer@Jasons-Mac-Pro.local> jschloer <jschloer@terragotech.com>
|
||||
Jean-Michel Francois <jmfrancois@talend.com> Jean-Michel FRANCOIS <jmfrancois@talend.com>
|
||||
Jeff Carbonella <jeff.carbonella@gmail.com> <jeff@contactually.com>
|
||||
Jeff Knaggs <jeef3@users.noreply.github.com> <mail@jeef3.com>
|
||||
Jordan Gensler <jordan.gensler@airbnb.com> <jordangens@gmail.com>
|
||||
Kanitkorn Sujautra <k.sujautra@gmail.com> Kanitkorn S <lukyth@users.noreply.github.com>
|
||||
Kent C. Dodds <kent@doddsfamily.us> <kent+github@doddsfamily.us>
|
||||
larry <bshy522@gmail.com> <larry@yunify.com>
|
||||
Madushan Nishantha <j.l.madushan@gmail.com> <madushan1000@users.noreply.github.com>
|
||||
Marie-Laure Thuret <mthuret@users.noreply.github.com> mthuret <marielaure.thuret@algolia.com>
|
||||
Max Hodges <max@whiterabbitjapan.com> MaxHodges <max@whiterabbitjapan.com>
|
||||
Michael Shilman <shilman@lab80.co> <shilman@users.noreply.github.com>
|
||||
Michael Shilman <shilman@lab80.co> <michael@lab80.co>
|
||||
Muhammed Thanish <mnmtanish@gmail.com> <mnmtanish@users.noreply.github.com>
|
||||
Ned Schwartz <ned@theinterned.net> Ned Schwartz <ned@theinterned.net>
|
||||
Joe Nelson <Joe.Nelson@regence.com> Nelson, Joe <Joe.Nelson@regence.com>
|
||||
Nikolay Kozhuharenko <Nikolay.Kozhuharenko@gmail.com> Nikolay <Nikolay.Kozhuharenko@gmail.com>
|
||||
Norbert de Langen <ndelangen@me.com> # github:ndelangen, npm:ndelangen, twitter:norbertdelangen
|
||||
Oleg Proskurin <regx@usul.su> UsulPro <regx@usul.su>
|
||||
Orta <orta.therox@gmail.com> orta <orta.therox@gmail.com>
|
||||
Ritesh Kumar <ritz078@users.noreply.github.com> Ritesh Kumar <rkritesh078@gmail.com>
|
||||
Sylvain Bannier <sylvain.bannier@smile.fr> Sylvain BANNIER <sylvain.bannier@smile.fr>
|
||||
Tom Coleman <tom@percolatestudio.com> Tom Coleman <tom@thesnail.org>
|
||||
Trevor Eyre <trevoreyre@gmail.com> # github:TrevorEyre, twitter:trevor_eyre
|
||||
William Castandet <wcastand@gmail.com> wcastand <wcastand@gmail.com>
|
||||
Xavier Cazalot <xavier.cazalot@gmail.com> xavcz <xavier.cazalot@gmail.com>
|
20
CHANGELOG.md
20
CHANGELOG.md
@ -1,3 +1,23 @@
|
||||
# 3.2.9
|
||||
|
||||
2017-August-26
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
- Fix getstorybook CLI for React Native projects [#1741](https://github.com/storybooks/storybook/pull/1741)
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Improve `addon-info` README options documentation [#1732](https://github.com/storybooks/storybook/pull/1732)
|
||||
|
||||
#### Maintenance
|
||||
|
||||
- ADD a CLI for bootstrapping [#1216](https://github.com/storybooks/storybook/pull/1216)
|
||||
|
||||
#### Dependency Upgrades
|
||||
|
||||
- Update lerna to the latest version 🚀 [#1727](https://github.com/storybooks/storybook/pull/1727)
|
||||
|
||||
# 3.2.8
|
||||
|
||||
2017-August-23
|
||||
|
@ -4,6 +4,8 @@ Thanks for your interest in improving Storybook! We are a community-driven proje
|
||||
|
||||
Please review this document to help to streamline the process and save everyone's precious time.
|
||||
|
||||
This guide assumes you're using `yarn` as package manager. You may have some success using `npm` as well, but there are chances you'll get wrong versions of root dependencies in that case (we only commit `yarn.lock` to the repo).
|
||||
|
||||
## Issues
|
||||
|
||||
No software is bug free. So, if you got an issue, follow these steps:
|
||||
@ -18,29 +20,57 @@ No software is bug free. So, if you got an issue, follow these steps:
|
||||
|
||||
### Testing against `master`
|
||||
|
||||
To test your project against the current latest version of storybook, you can clone the repository and link it with `npm`. Try following these steps:
|
||||
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:
|
||||
#### 1. Download the latest version of this project, and build it:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/storybooks/storybook.git
|
||||
cd storybook
|
||||
npm install
|
||||
npm run bootstrap -- --core
|
||||
yarn install
|
||||
yarn bootstrap
|
||||
```
|
||||
|
||||
2. Link `storybook` and any other required dependencies:
|
||||
The bootstrap command will 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:
|
||||
|
||||
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
|
||||
cd app/react
|
||||
npm link
|
||||
|
||||
cd <your-project>
|
||||
npm link @storybook/react
|
||||
|
||||
# repeat with whichever other parts of the monorepo you are using.
|
||||
yarn test
|
||||
```
|
||||
|
||||
_Note that in order to run the tests fro ReactNative, you must have bootstrapped with ReactNative enabled_
|
||||
|
||||
You can also pick suites from CLI:
|
||||
|
||||
```sh
|
||||
yarn test -- --core
|
||||
```
|
||||
|
||||
In order to run ALL unit tests, you must have bootstrapped the react-native
|
||||
|
||||
#### 2b. Link `storybook` and any other required dependencies:
|
||||
|
||||
If you want to test your own existing project using the github version of storybook, you need to `link` the packages you use in your project.
|
||||
|
||||
```sh
|
||||
cd app/react
|
||||
yarn link
|
||||
|
||||
cd <your-project>
|
||||
yarn link @storybook/react
|
||||
|
||||
# repeat with whichever other parts of the monorepo you are using.
|
||||
```
|
||||
|
||||
### Reproductions
|
||||
|
||||
The best way to help figure out an issue you are having is to produce a minimal reproduction against the `master` branch.
|
||||
@ -51,13 +81,12 @@ A good way to do that is using the example `cra-kitchen-sink` app embedded in th
|
||||
# Download and build this repository:
|
||||
git clone https://github.com/storybooks/storybook.git
|
||||
cd storybook
|
||||
npm install
|
||||
npm run bootstrap -- --core
|
||||
|
||||
cd examples/cra-kitchen-sink
|
||||
yarn install
|
||||
yarn bootstrap
|
||||
|
||||
# make changes to try and reproduce the problem, such as adding components + stories
|
||||
npm start storybook
|
||||
cd examples/cra-kitchen-sink
|
||||
yarn storybook
|
||||
|
||||
# see if you can see the problem, if so, commit it:
|
||||
git checkout "branch-describing-issue"
|
||||
@ -71,7 +100,7 @@ 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/storybooks/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 storybook. Alternatively, use `npm run eject` in the CRA app to get a modifiable webpack config.
|
||||
**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 storybook. Alternatively, use `yarn eject` in the CRA app to get a modifiable webpack config.
|
||||
|
||||
## Pull Requests (PRs)
|
||||
|
||||
@ -82,7 +111,7 @@ We welcome your contributions. There are many ways you can help us. This is few
|
||||
- Work on [API](https://github.com/storybooks/storybook/labels/enhancement%3A%20api), [Addons](https://github.com/storybooks/storybook/labels/enhancement%3A%20addons), [UI](https://github.com/storybooks/storybook/labels/enhancement%3A%20ui) or [Webpack](https://github.com/storybooks/storybook/labels/enhancement%3A%20webpack) use enhancements and new [features](https://github.com/storybooks/storybook/labels/feature%20request).
|
||||
- Add more [tests](https://codecov.io/gh/storybooks/storybook/tree/master/packages) (specially for the [UI](https://codecov.io/gh/storybooks/storybook/tree/master/packages/storybook-ui/src)).
|
||||
|
||||
Before you submit a new PR, make you to run `npm test`. Do not submit a PR if tests are failing. If you need any help, create an issue and ask.
|
||||
Before you submit a new PR, make you to run `yarn test`. Do not submit a PR if tests are failing. If you need any help, create an issue and ask.
|
||||
|
||||
### Reviewing PRs
|
||||
|
||||
@ -136,7 +165,7 @@ This project written in ES2016+ syntax so, we need to transpile it before use.
|
||||
So run the following command:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
yarn dev
|
||||
```
|
||||
|
||||
This will watch files and transpile in watch mode.
|
||||
@ -146,14 +175,14 @@ This will watch files and transpile in watch mode.
|
||||
First of all link this repo with:
|
||||
|
||||
```sh
|
||||
npm link
|
||||
yarn link
|
||||
```
|
||||
|
||||
In order to test features you add, you may need to link the local copy of this repo.
|
||||
For that we need a sample project. Let's create it.
|
||||
|
||||
```sh
|
||||
npm install --global create-react-app getstorybook
|
||||
yarn global add create-react-app getstorybook
|
||||
create-react-app my-demo-app
|
||||
cd my-demo-app
|
||||
getstorybook
|
||||
@ -165,12 +194,12 @@ getstorybook
|
||||
Then link storybook inside the sample project with:
|
||||
|
||||
```sh
|
||||
npm link @storybook/react
|
||||
yarn link @storybook/react
|
||||
```
|
||||
|
||||
### Getting Changes
|
||||
|
||||
After you've done any change, you need to run the `npm run storybook` command every time to see those changes.
|
||||
After you've done any change, you need to run the `yarn storybook` command every time to see those changes.
|
||||
|
||||
## Release Guide
|
||||
|
||||
@ -193,12 +222,9 @@ First, build the release:
|
||||
git checkout master
|
||||
git status
|
||||
|
||||
# clean out extra files
|
||||
# clean out extra files & build all the packages
|
||||
# WARNING: destructive if you have extra files lying around!
|
||||
git clean -fdx && yarn
|
||||
|
||||
# build all the packages
|
||||
npm run bootstrap -- --all
|
||||
yarn bootstrap -- --reset --all
|
||||
```
|
||||
|
||||
From here there are different procedures for prerelease (e.g. alpha/beta/rc) and proper release.
|
||||
@ -209,7 +235,7 @@ From here there are different procedures for prerelease (e.g. alpha/beta/rc) and
|
||||
|
||||
```sh
|
||||
# publish and tag the release
|
||||
npm run publish -- --concurrency 1 --npm-tag=alpha
|
||||
yarn run publish -- --concurrency 1 --npm-tag=alpha
|
||||
|
||||
# push the tags
|
||||
git push --tags
|
||||
@ -219,14 +245,14 @@ git push --tags
|
||||
|
||||
```sh
|
||||
# publish but don't commit to git
|
||||
npm run publish -- --concurrency 1 --skip-git
|
||||
yarn publish -- --concurrency 1 --skip-git
|
||||
|
||||
# Update `CHANGELOG.md`
|
||||
# - Edit PR titles/labels on github until output is good
|
||||
# - Optionally, edit a handwritten description in `CHANGELOG.md`
|
||||
npm run changelog
|
||||
yarn changelog
|
||||
|
||||
# tag the release and push `CHANGELOG.md` and tags
|
||||
# FIXME: not end-to-end tested!
|
||||
npm run github-release
|
||||
yarn github-release
|
||||
```
|
||||
|
@ -37,7 +37,7 @@ Here's an example of using Notes and Info in 3.2 with the new API.
|
||||
storiesOf('composition', module)
|
||||
.add('new addons api',
|
||||
withInfo('see Notes panel for composition info')(
|
||||
withNotes({ notes: 'Composition: Info(Notes())' })(context =>
|
||||
withNotes({ text: 'Composition: Info(Notes())' })(context =>
|
||||
<MyComponent name={context.story} />
|
||||
)
|
||||
)
|
||||
|
18
README.md
18
README.md
@ -92,30 +92,30 @@ We welcome contributions to Storybook!
|
||||
|
||||
### Development scripts
|
||||
|
||||
#### `npm run bootstrap`
|
||||
#### `yarn bootstrap`
|
||||
|
||||
> Installs package dependencies and links packages together - using lerna
|
||||
|
||||
#### `npm run publish`
|
||||
#### `yarn publish`
|
||||
|
||||
> Push a release to git and npm
|
||||
> will ask for version in interactive mode - using lerna.
|
||||
|
||||
#### `npm run lint`
|
||||
#### `yarn lint`
|
||||
|
||||
> boolean check if code conforms to linting rules - uses remark & eslint
|
||||
|
||||
- `npm run lint:js` - will check js
|
||||
- `npm run lint:md` - will check markdown + code samples
|
||||
- `yarn lint:js` - will check js
|
||||
- `yarn lint:md` - will check markdown + code samples
|
||||
|
||||
- `npm run lint:js -- --fix` - will automatically fix js
|
||||
- `npm run lint:md -- -o` - will automatically fix markdown
|
||||
- `yarn lint:js -- --fix` - will automatically fix js
|
||||
- `yarn lint:md -- -o` - will automatically fix markdown
|
||||
|
||||
#### `npm run test`
|
||||
#### `yarn test`
|
||||
|
||||
> boolean check if unit tests all pass - uses jest
|
||||
|
||||
- `npm run test:watch` - will run tests in watch-mode
|
||||
- `yarn test:watch` - will run tests in watch-mode
|
||||
|
||||
### Backers
|
||||
|
||||
|
23
__mocks__/fs.js
Normal file
23
__mocks__/fs.js
Normal file
@ -0,0 +1,23 @@
|
||||
const fs = jest.genMockFromModule('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
|
||||
// `fs` APIs are used.
|
||||
let mockFiles = Object.create(null);
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
function __setMockFiles(newMockFiles) {
|
||||
mockFiles = newMockFiles;
|
||||
}
|
||||
|
||||
// A custom version of `readdirSync` that reads from the special mocked out
|
||||
// file list set via __setMockFiles
|
||||
const readFileSync = (filePath = '') => mockFiles[filePath];
|
||||
const existsSync = filePath => !!mockFiles[filePath];
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
fs.__setMockFiles = __setMockFiles;
|
||||
fs.readFileSync = readFileSync;
|
||||
fs.existsSync = existsSync;
|
||||
|
||||
module.exports = fs;
|
@ -41,7 +41,7 @@ storiesOf('Component', module)
|
||||
|
||||
## Usage with options
|
||||
|
||||
`withInfo` can also take an options object in case you want to configure how
|
||||
`withInfo` can also take an [options object](#global-options) in case you want to configure how
|
||||
the info panel looks on a per-story basis:
|
||||
|
||||
```js
|
||||
@ -50,10 +50,8 @@ import { withInfo } from '@storybook/addon-info';
|
||||
storiesOf('Component', module)
|
||||
.add('simple info',
|
||||
withInfo({
|
||||
text: 'doc string about my component',
|
||||
maxPropsIntoLine: 1,
|
||||
maxPropObjectKeys: 10,
|
||||
maxPropArrayLength: 10,
|
||||
text: 'String or React Element with docs about my component', // Warning! This option's name will be likely renamed to "summary" in 3.3 release. Follow this PR #1501 for details
|
||||
// other possible options see in Global options section below
|
||||
)(() =>
|
||||
<Component>Click the "?" mark at top-right to view the info.</Component>
|
||||
)
|
||||
@ -78,8 +76,14 @@ import { setDefaults } from '@storybook/addon-info';
|
||||
|
||||
// addon-info
|
||||
setDefaults({
|
||||
inline: true,
|
||||
maxPropsIntoLine: 1,
|
||||
header: false, // Toggles display of header with component name and description
|
||||
inline: true, // Displays info inline vs click button to view
|
||||
source: true, // Displays the source of story Component
|
||||
propTables: [/* Components used in story */], // displays Prop Tables with this components
|
||||
propTablesExclude: [], // Exclude Components from being shoun in Prop Tables section
|
||||
styles: {}, // Overrides styles of addon
|
||||
marksyConf: {}, // Overrides components used to display markdown. Warning! This option's name will be likely deprecated in favor to "components" with the same API in 3.3 release. Follow this PR #1501 for details
|
||||
maxPropsIntoLine: 1, // Max props to display per line in source code
|
||||
maxPropObjectKeys: 10,
|
||||
maxPropArrayLength: 10,
|
||||
maxPropStringLength: 100,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-info",
|
||||
"version": "3.2.7",
|
||||
"version": "3.2.9",
|
||||
"description": "A Storybook addon to show additional information for your stories.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
|
@ -158,7 +158,7 @@ const value = color(label, defaultValue);
|
||||
|
||||
### object
|
||||
|
||||
Allows you to get a JSON object from the user.
|
||||
Allows you to get a JSON object or array from the user.
|
||||
|
||||
```js
|
||||
import { object } from '@storybook/addon-knobs';
|
||||
@ -175,7 +175,7 @@ const value = object(label, defaultValue);
|
||||
|
||||
### array
|
||||
|
||||
Allows you to get an array from the user.
|
||||
Allows you to get an array of strings from the user.
|
||||
|
||||
```js
|
||||
import { array } from '@storybook/addon-knobs';
|
||||
|
@ -88,7 +88,7 @@ ObjectType.defaultProps = {
|
||||
ObjectType.propTypes = {
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.object,
|
||||
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
@ -36,12 +36,14 @@ Have a look at the linkTo function:
|
||||
import { linkTo } from '@storybook/addon-links'
|
||||
|
||||
linkTo('Toggle', 'off')
|
||||
linkTo(() => 'Toggle', () => 'off')
|
||||
linkTo('Toggle') // Links to the first story in the 'Toggle' kind
|
||||
```
|
||||
|
||||
With that, you can link an event in a component to any story in the Storybook.
|
||||
|
||||
- First parameter is the the story kind name (what you named with `storiesOf`).
|
||||
- Second parameter is the story name (what you named with `.add`).
|
||||
- Second (optional) parameter is the story name (what you named with `.add`). If the second parameter is omitted, the link will point to the first story in the given kind.
|
||||
|
||||
> You can also pass a function instead for any of above parameter. That function accepts arguments emitted by the event and it should return a string. <br/>
|
||||
> Have a look at [PR86](https://github.com/kadirahq/react-storybook/pull/86) for more information.
|
||||
|
@ -5,10 +5,10 @@ setOptions({
|
||||
name: 'CUSTOM-OPTIONS',
|
||||
url: 'https://github.com/storybooks/storybook',
|
||||
// goFullScreen: false,
|
||||
// showLeftPanel: true,
|
||||
showDownPanel: false,
|
||||
// showStoriesPanel: true,
|
||||
showAddonPanel: false,
|
||||
// showSearchBox: false,
|
||||
// downPanelInRight: false,
|
||||
// addonPanelInRight: false,
|
||||
});
|
||||
|
||||
storybook.configure(() => require('./stories'), module);
|
||||
|
@ -53,25 +53,25 @@ setOptions({
|
||||
*/
|
||||
goFullScreen: false,
|
||||
/**
|
||||
* display left panel that shows a list of stories
|
||||
* display panel that shows a list of stories
|
||||
* @type {Boolean}
|
||||
*/
|
||||
showLeftPanel: true,
|
||||
showStoriesPanel: true,
|
||||
/**
|
||||
* display horizontal panel that displays addon configurations
|
||||
* display panel that shows addon configurations
|
||||
* @type {Boolean}
|
||||
*/
|
||||
showDownPanel: true,
|
||||
showAddonPanel: true,
|
||||
/**
|
||||
* display floating search box to search through stories
|
||||
* @type {Boolean}
|
||||
*/
|
||||
showSearchBox: false,
|
||||
/**
|
||||
* show horizontal addons panel as a vertical panel on the right
|
||||
* show addon panel as a vertical panel on the right
|
||||
* @type {Boolean}
|
||||
*/
|
||||
downPanelInRight: false,
|
||||
addonPanelInRight: false,
|
||||
/**
|
||||
* sorts stories
|
||||
* @type {Boolean}
|
||||
@ -98,7 +98,7 @@ setOptions({
|
||||
* id to select an addon panel
|
||||
* @type {String}
|
||||
*/
|
||||
selectedAddonPanel: undefined, // The order of addons in the "Addons Panel" is the same as you import them in 'addons.js'. The first panel will be opened by default as you run Storybook
|
||||
selectedAddonPanel: undefined, // The order of addons in the "Addon panel" is the same as you import them in 'addons.js'. The first panel will be opened by default as you run Storybook
|
||||
});
|
||||
|
||||
storybook.configure(() => require('./stories'), module);
|
||||
|
@ -145,6 +145,10 @@ Just render the story, don't check the output at all (useful if you just want to
|
||||
|
||||
Like the default, but allows you to specify a set of options for the test renderer. [See for example here](https://github.com/storybooks/storybook/blob/b915b5439786e0edb17d7f5ab404bba9f7919381/examples/test-cra/src/storyshots.test.js#L14-L16).
|
||||
|
||||
### `multiSnapshotWithOptions(options)`
|
||||
|
||||
Like `snapshotWithOptions`, but generate a separate snapshot file for each stories file rather than a single monolithic file (as is the convention in Jest). This makes it dramatically easier to review changes.
|
||||
|
||||
### `shallowSnapshot`
|
||||
|
||||
Take a snapshot of a shallow-rendered version of the component.
|
||||
|
@ -11,11 +11,14 @@
|
||||
"scripts": {
|
||||
"build-storybook": "build-storybook",
|
||||
"prepublish": "babel ./src --out-dir ./dist",
|
||||
"storybook": "start-storybook -p 6006"
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"example": "jest storyshot.test"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"glob": "^7.1.2",
|
||||
"global": "^4.3.2",
|
||||
"jest-specific-snapshot": "^0.2.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"read-pkg-up": "^2.0.0"
|
||||
},
|
||||
@ -23,18 +26,21 @@
|
||||
"@storybook/addons": "^3.2.6",
|
||||
"@storybook/channels": "^3.2.0",
|
||||
"@storybook/react": "^3.2.8",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-jest": "^20.0.3",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1"
|
||||
"react-dom": "^15.6.1",
|
||||
"jest": "^20.0.4",
|
||||
"jest-cli": "^20.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@storybook/addons": "^3.2.6",
|
||||
"@storybook/channels": "^3.2.0",
|
||||
"@storybook/react": "^3.2.8",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"react": "*",
|
||||
"react-test-renderer": "*"
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import glob from 'glob';
|
||||
import global, { describe, it } from 'global';
|
||||
import readPkgUp from 'read-pkg-up';
|
||||
import addons from '@storybook/addons';
|
||||
@ -6,8 +8,15 @@ import addons from '@storybook/addons';
|
||||
import runWithRequireContext from './require_context';
|
||||
import createChannel from './storybook-channel-mock';
|
||||
import { snapshot } from './test-bodies';
|
||||
import { getPossibleStoriesFiles } from './utils';
|
||||
|
||||
export { snapshotWithOptions, snapshot, shallowSnapshot, renderOnly } from './test-bodies';
|
||||
export {
|
||||
snapshot,
|
||||
multiSnapshotWithOptions,
|
||||
snapshotWithOptions,
|
||||
shallowSnapshot,
|
||||
renderOnly,
|
||||
} from './test-bodies';
|
||||
|
||||
let storybook;
|
||||
let configPath;
|
||||
@ -48,6 +57,7 @@ export default function testStorySnapshots(options = {}) {
|
||||
runWithRequireContext(content, contextOpts);
|
||||
} else if (isRNStorybook) {
|
||||
storybook = require.requireActual('@storybook/react-native');
|
||||
|
||||
configPath = path.resolve(options.configPath || 'storybook');
|
||||
require.requireActual(configPath);
|
||||
} else {
|
||||
@ -70,13 +80,15 @@ export default function testStorySnapshots(options = {}) {
|
||||
|
||||
// eslint-disable-next-line
|
||||
for (const group of stories) {
|
||||
if (options.storyKindRegex && !group.kind.match(options.storyKindRegex)) {
|
||||
const { fileName, kind } = group;
|
||||
|
||||
if (options.storyKindRegex && !kind.match(options.storyKindRegex)) {
|
||||
// eslint-disable-next-line
|
||||
continue;
|
||||
}
|
||||
|
||||
describe(suite, () => {
|
||||
describe(group.kind, () => {
|
||||
describe(kind, () => {
|
||||
// eslint-disable-next-line
|
||||
for (const story of group.stories) {
|
||||
if (options.storyNameRegex && !story.name.match(options.storyNameRegex)) {
|
||||
@ -85,7 +97,7 @@ export default function testStorySnapshots(options = {}) {
|
||||
}
|
||||
|
||||
it(story.name, () => {
|
||||
const context = { kind: group.kind, story: story.name };
|
||||
const context = { fileName, kind, story: story.name };
|
||||
options.test({ story, context });
|
||||
});
|
||||
}
|
||||
@ -93,3 +105,16 @@ export default function testStorySnapshots(options = {}) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('Storyshots Integrity', () => {
|
||||
describe('Abandoned Storyshots', () => {
|
||||
const storyshots = glob.sync('**/*.storyshot');
|
||||
|
||||
const abandonedStoryshots = storyshots.filter(fileName => {
|
||||
const possibleStoriesFiles = getPossibleStoriesFiles(fileName);
|
||||
return !possibleStoriesFiles.some(fs.existsSync);
|
||||
});
|
||||
|
||||
expect(abandonedStoryshots).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,40 @@
|
||||
import renderer from 'react-test-renderer';
|
||||
import shallow from 'react-test-renderer/shallow';
|
||||
import 'jest-specific-snapshot';
|
||||
import { getStoryshotFile } from './utils';
|
||||
|
||||
function getRenderedTree(story, context, options) {
|
||||
const storyElement = story.render(context);
|
||||
return renderer.create(storyElement, options).toJSON();
|
||||
}
|
||||
|
||||
function getSnapshotFileName(context) {
|
||||
const fileName = context.fileName;
|
||||
|
||||
if (!fileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getStoryshotFile(fileName);
|
||||
}
|
||||
|
||||
export const snapshotWithOptions = options => ({ story, context }) => {
|
||||
const storyElement = story.render(context);
|
||||
const tree = renderer.create(storyElement, options).toJSON();
|
||||
const tree = getRenderedTree(story, context, options);
|
||||
expect(tree).toMatchSnapshot();
|
||||
};
|
||||
|
||||
export const multiSnapshotWithOptions = options => ({ story, context }) => {
|
||||
const tree = getRenderedTree(story, context, options);
|
||||
const snapshotFileName = getSnapshotFileName(context);
|
||||
|
||||
if (!snapshotFileName) {
|
||||
expect(tree).toMatchSnapshot();
|
||||
return;
|
||||
}
|
||||
|
||||
expect(tree).toMatchSpecificSnapshot(snapshotFileName);
|
||||
};
|
||||
|
||||
export const snapshot = snapshotWithOptions({});
|
||||
|
||||
export function shallowSnapshot({ story, context }) {
|
||||
|
15
addons/storyshots/src/utils.js
Normal file
15
addons/storyshots/src/utils.js
Normal file
@ -0,0 +1,15 @@
|
||||
import path from 'path';
|
||||
|
||||
export function getStoryshotFile(fileName) {
|
||||
const { dir, name } = path.parse(fileName);
|
||||
return path.format({ dir: path.join(dir, '__snapshots__'), name, ext: '.storyshot' });
|
||||
}
|
||||
|
||||
export function getPossibleStoriesFiles(storyshotFile) {
|
||||
const { dir, name } = path.parse(storyshotFile);
|
||||
|
||||
return [
|
||||
path.format({ dir: path.dirname(dir), name, ext: '.js' }),
|
||||
path.format({ dir: path.dirname(dir), name, ext: '.jsx' }),
|
||||
];
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Another Button with some emoji 1`] = `
|
||||
<button
|
||||
className="css-1yjiefr"
|
||||
onClick={[Function]}
|
||||
>
|
||||
😀 😎 👍 💯
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Another Button with text 1`] = `
|
||||
<button
|
||||
className="css-1yjiefr"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Hello Button
|
||||
</button>
|
||||
`;
|
@ -0,0 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Button with some emoji 1`] = `
|
||||
<button
|
||||
className="css-1yjiefr"
|
||||
onClick={[Function]}
|
||||
>
|
||||
😀 😎 👍 💯
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Button with text 1`] = `
|
||||
<button
|
||||
className="css-1yjiefr"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Hello Button
|
||||
</button>
|
||||
`;
|
@ -0,0 +1,104 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Welcome to Storybook 1`] = `
|
||||
<article
|
||||
className="css-1fqbdip"
|
||||
>
|
||||
<h1
|
||||
className="css-nil"
|
||||
>
|
||||
Welcome to storybook
|
||||
</h1>
|
||||
<p>
|
||||
This is a UI component dev environment for your app.
|
||||
</p>
|
||||
<p>
|
||||
We've added some basic stories inside the
|
||||
|
||||
<code
|
||||
className="css-mteq83"
|
||||
>
|
||||
src/stories
|
||||
</code>
|
||||
|
||||
directory.
|
||||
<br />
|
||||
A story is a single state of one or more UI components. You can have as many stories as you want.
|
||||
<br />
|
||||
(Basically a story is like a visual test case.)
|
||||
</p>
|
||||
<p>
|
||||
See these sample
|
||||
|
||||
<a
|
||||
className="css-ca0824"
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
>
|
||||
stories
|
||||
</a>
|
||||
|
||||
for a component called
|
||||
|
||||
<code
|
||||
className="css-mteq83"
|
||||
>
|
||||
Button
|
||||
</code>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
Just like that, you can add your own components as stories.
|
||||
<br />
|
||||
You can also edit those components and see changes right away.
|
||||
<br />
|
||||
(Try editing the
|
||||
<code
|
||||
className="css-mteq83"
|
||||
>
|
||||
Button
|
||||
</code>
|
||||
stories located at
|
||||
<code
|
||||
className="css-mteq83"
|
||||
>
|
||||
src/stories/index.js
|
||||
</code>
|
||||
.)
|
||||
</p>
|
||||
<p>
|
||||
Usually we create stories with smaller UI components in the app.
|
||||
<br />
|
||||
Have a look at the
|
||||
|
||||
<a
|
||||
className="css-ca0824"
|
||||
href="https://storybook.js.org/basics/writing-stories"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Writing Stories
|
||||
</a>
|
||||
|
||||
section in our documentation.
|
||||
</p>
|
||||
<p
|
||||
className="css-bwdon3"
|
||||
>
|
||||
<b>
|
||||
NOTE:
|
||||
</b>
|
||||
<br />
|
||||
Have a look at the
|
||||
|
||||
<code
|
||||
className="css-mteq83"
|
||||
>
|
||||
.storybook/webpack.config.js
|
||||
</code>
|
||||
|
||||
to add webpack loaders and plugins you are using in this project.
|
||||
</p>
|
||||
</article>
|
||||
`;
|
8
addons/storyshots/stories/storyshot.test.js
Normal file
8
addons/storyshots/stories/storyshot.test.js
Normal file
@ -0,0 +1,8 @@
|
||||
import path from 'path';
|
||||
import initStoryshots, { multiSnapshotWithOptions } from '../src';
|
||||
|
||||
initStoryshots({
|
||||
framework: 'react',
|
||||
configPath: path.join(__dirname, '..', '.storybook'),
|
||||
test: multiSnapshotWithOptions({}),
|
||||
});
|
@ -30,7 +30,7 @@
|
||||
"@storybook/channel-websocket": "^3.2.0",
|
||||
"@storybook/ui": "^3.2.7",
|
||||
"autoprefixer": "^7.1.1",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.22.0",
|
||||
@ -39,14 +39,14 @@
|
||||
"babel-plugin-transform-react-constant-elements": "^6.23.0",
|
||||
"babel-plugin-transform-regenerator": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-minify": "^0.2.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.0.0",
|
||||
"commander": "^2.9.0",
|
||||
"commander": "^2.11.0",
|
||||
"css-loader": "^0.28.1",
|
||||
"events": "^1.1.1",
|
||||
"express": "^4.15.3",
|
||||
@ -70,7 +70,7 @@
|
||||
"ws": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-cli": "^6.26.0",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-native": "^0.43.3"
|
||||
|
4
app/react-native/src/bin/storybook-start.js
vendored
4
app/react-native/src/bin/storybook-start.js
vendored
@ -15,6 +15,7 @@ program
|
||||
.option('-r, --reset-cache', 'reset react native packager')
|
||||
.option('--skip-packager', 'run only storybook server')
|
||||
.option('-i, --manual-id', 'allow multiple users to work with same storybook')
|
||||
.option('--smoke-test', 'Exit after successful start')
|
||||
.parse(process.argv);
|
||||
|
||||
const projectDir = path.resolve();
|
||||
@ -38,6 +39,9 @@ server.listen(...listenAddr, err => {
|
||||
}
|
||||
const address = `http://${program.host || 'localhost'}:${program.port}/`;
|
||||
console.info(`\nReact Native Storybook started on => ${address}\n`); // eslint-disable-line no-console
|
||||
if (program.smokeTest) {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
if (!program.skipPackager) {
|
||||
|
10
app/react-native/src/preview/index.js
vendored
10
app/react-native/src/preview/index.js
vendored
@ -23,7 +23,10 @@ export default class Preview {
|
||||
if (module && module.hot) {
|
||||
// TODO remove the kind on dispose
|
||||
}
|
||||
return new StoryKindApi(this._stories, this._addons, this._decorators, kind);
|
||||
|
||||
const fileName = module ? module.filename : null;
|
||||
|
||||
return new StoryKindApi(this._stories, this._addons, this._decorators, kind, fileName);
|
||||
}
|
||||
|
||||
setAddon(addon) {
|
||||
@ -44,11 +47,14 @@ export default class Preview {
|
||||
|
||||
getStorybook() {
|
||||
return this._stories.getStoryKinds().map(kind => {
|
||||
const fileName = this._stories.getStoryFileName(kind);
|
||||
|
||||
const stories = this._stories.getStories(kind).map(name => {
|
||||
const render = this._stories.getStory(kind, name);
|
||||
return { name, render };
|
||||
});
|
||||
return { kind, stories };
|
||||
|
||||
return { kind, fileName, stories };
|
||||
});
|
||||
}
|
||||
|
||||
|
5
app/react-native/src/preview/story_kind.js
vendored
5
app/react-native/src/preview/story_kind.js
vendored
@ -1,10 +1,11 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
export default class StoryKindApi {
|
||||
constructor(stories, addons, decorators, kind) {
|
||||
constructor(stories, addons, decorators, kind, fileName) {
|
||||
this.kind = kind;
|
||||
this._stories = stories;
|
||||
this._decorators = decorators.slice();
|
||||
this._fileName = fileName;
|
||||
Object.assign(this, addons);
|
||||
}
|
||||
|
||||
@ -15,7 +16,7 @@ export default class StoryKindApi {
|
||||
|
||||
add(story, fn) {
|
||||
const decorated = this._decorate(fn);
|
||||
this._stories.addStory(this.kind, story, decorated);
|
||||
this._stories.addStory(this.kind, story, decorated, this._fileName);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
12
app/react-native/src/preview/story_store.js
vendored
12
app/react-native/src/preview/story_store.js
vendored
@ -9,11 +9,12 @@ export default class StoryStore extends EventEmitter {
|
||||
this._data = {};
|
||||
}
|
||||
|
||||
addStory(kind, name, fn) {
|
||||
addStory(kind, name, fn, fileName) {
|
||||
count += 1;
|
||||
if (!this._data[kind]) {
|
||||
this._data[kind] = {
|
||||
kind,
|
||||
fileName,
|
||||
index: count,
|
||||
stories: {},
|
||||
};
|
||||
@ -46,6 +47,15 @@ export default class StoryStore extends EventEmitter {
|
||||
.map(info => info.name);
|
||||
}
|
||||
|
||||
getStoryFileName(kind) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return storiesKind.fileName;
|
||||
}
|
||||
|
||||
getStory(kind, name) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
|
@ -9,15 +9,6 @@ export default function(publicPath, options) {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Storybook for React</title>
|
||||
<style>
|
||||
/*
|
||||
When resizing panels, the drag event breaks if the cursor
|
||||
moves over the iframe. Add the 'dragging' class to the body
|
||||
at drag start and remove it when the drag ends.
|
||||
*/
|
||||
.dragging iframe {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Styling the fuzzy search box placeholders */
|
||||
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||
color: #ddd;
|
||||
|
@ -29,7 +29,7 @@
|
||||
"@storybook/ui": "^3.2.7",
|
||||
"airbnb-js-shims": "^1.1.1",
|
||||
"autoprefixer": "^7.1.1",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-react-docgen": "^1.6.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
@ -39,8 +39,8 @@
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.0.0",
|
||||
"chalk": "^2.0.1",
|
||||
"commander": "^2.9.0",
|
||||
"chalk": "^2.1.0",
|
||||
"commander": "^2.11.0",
|
||||
"common-tags": "^1.4.0",
|
||||
"configstore": "^3.1.0",
|
||||
"css-loader": "^0.28.1",
|
||||
@ -73,9 +73,8 @@
|
||||
"webpack-hot-middleware": "^2.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"mock-fs": "^4.3.0",
|
||||
"nodemon": "^1.11.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"nodemon": "^1.12.0",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1"
|
||||
},
|
||||
|
@ -76,8 +76,10 @@ export default class ClientApi {
|
||||
getStory
|
||||
);
|
||||
|
||||
const fileName = m ? m.filename : null;
|
||||
|
||||
// Add the fully decorated getStory function.
|
||||
this._storyStore.addStory(kind, storyName, fn);
|
||||
this._storyStore.addStory(kind, storyName, fn, fileName);
|
||||
return api;
|
||||
};
|
||||
|
||||
@ -91,11 +93,14 @@ export default class ClientApi {
|
||||
|
||||
getStorybook() {
|
||||
return this._storyStore.getStoryKinds().map(kind => {
|
||||
const fileName = this._storyStore.getStoryFileName(kind);
|
||||
|
||||
const stories = this._storyStore.getStories(kind).map(name => {
|
||||
const render = this._storyStore.getStory(kind, name);
|
||||
return { name, render };
|
||||
});
|
||||
return { kind, stories };
|
||||
|
||||
return { kind, fileName, stories };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ class StoryStore {
|
||||
this.stories = [];
|
||||
}
|
||||
|
||||
addStory(kind, story, fn) {
|
||||
this.stories.push({ kind, story, fn });
|
||||
addStory(kind, story, fn, fileName) {
|
||||
this.stories.push({ kind, story, fn, fileName });
|
||||
}
|
||||
|
||||
getStoryKinds() {
|
||||
@ -29,6 +29,11 @@ class StoryStore {
|
||||
}, []);
|
||||
}
|
||||
|
||||
getStoryFileName(kind) {
|
||||
const story = this.stories.find(info => info.kind === kind);
|
||||
return story ? story.fileName : null;
|
||||
}
|
||||
|
||||
getStory(kind, name) {
|
||||
return this.stories.reduce((fn, info) => {
|
||||
if (!fn && info.kind === kind && info.story === name) {
|
||||
@ -55,7 +60,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa();
|
||||
api.storiesOf('none', module).aa();
|
||||
expect(data).toBe('foo');
|
||||
});
|
||||
|
||||
@ -75,7 +80,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa().bb();
|
||||
api.storiesOf('none', module).aa().bb();
|
||||
expect(data).toEqual(['foo', 'bar']);
|
||||
});
|
||||
|
||||
@ -89,7 +94,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa();
|
||||
api.storiesOf('none', module).aa();
|
||||
expect(data).toBe('function');
|
||||
});
|
||||
|
||||
@ -109,7 +114,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').bb();
|
||||
api.storiesOf('none', module).bb();
|
||||
expect(data).toBe('foo');
|
||||
});
|
||||
|
||||
@ -124,7 +129,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf(kind).aa();
|
||||
api.storiesOf(kind, module).aa();
|
||||
expect(data).toBe(kind);
|
||||
});
|
||||
});
|
||||
@ -133,7 +138,7 @@ describe('preview.client_api', () => {
|
||||
it('should add local decorators', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator(fn => `aa-${fn()}`);
|
||||
|
||||
localApi.add('storyName', () => 'Hello');
|
||||
@ -144,7 +149,7 @@ describe('preview.client_api', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
api.addDecorator(fn => `bb-${fn()}`);
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
|
||||
localApi.add('storyName', () => 'Hello');
|
||||
expect(storyStore.stories[0].fn()).toBe('bb-Hello');
|
||||
@ -153,7 +158,7 @@ describe('preview.client_api', () => {
|
||||
it('should utilize both decorators at once', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
|
||||
api.addDecorator(fn => `aa-${fn()}`);
|
||||
localApi.addDecorator(fn => `bb-${fn()}`);
|
||||
@ -165,7 +170,7 @@ describe('preview.client_api', () => {
|
||||
it('should pass the context', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator(fn => `aa-${fn()}`);
|
||||
|
||||
localApi.add('storyName', ({ kind, story }) => `${kind}-${story}`);
|
||||
@ -180,7 +185,7 @@ describe('preview.client_api', () => {
|
||||
it('should have access to the context', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator((fn, { kind, story }) => `${kind}-${story}-${fn()}`);
|
||||
|
||||
localApi.add('storyName', () => 'Hello');
|
||||
@ -219,16 +224,17 @@ describe('preview.client_api', () => {
|
||||
'story-2.1': () => 'story-2.1',
|
||||
'story-2.2': () => 'story-2.2',
|
||||
};
|
||||
const kind1 = api.storiesOf('kind-1');
|
||||
const kind1 = api.storiesOf('kind-1', { filename: 'kind1.js' });
|
||||
kind1.add('story-1.1', functions['story-1.1']);
|
||||
kind1.add('story-1.2', functions['story-1.2']);
|
||||
const kind2 = api.storiesOf('kind-2');
|
||||
const kind2 = api.storiesOf('kind-2', { filename: 'kind2.js' });
|
||||
kind2.add('story-2.1', functions['story-2.1']);
|
||||
kind2.add('story-2.2', functions['story-2.2']);
|
||||
const book = api.getStorybook();
|
||||
expect(book).toEqual([
|
||||
{
|
||||
kind: 'kind-1',
|
||||
fileName: 'kind1.js',
|
||||
stories: [
|
||||
{ name: 'story-1.1', render: functions['story-1.1'] },
|
||||
{ name: 'story-1.2', render: functions['story-1.2'] },
|
||||
@ -236,6 +242,43 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
{
|
||||
kind: 'kind-2',
|
||||
fileName: 'kind2.js',
|
||||
stories: [
|
||||
{ name: 'story-2.1', render: functions['story-2.1'] },
|
||||
{ name: 'story-2.2', render: functions['story-2.2'] },
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return storybook with file names when module with file name provided', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const functions = {
|
||||
'story-1.1': () => 'story-1.1',
|
||||
'story-1.2': () => 'story-1.2',
|
||||
'story-2.1': () => 'story-2.1',
|
||||
'story-2.2': () => 'story-2.2',
|
||||
};
|
||||
const kind1 = api.storiesOf('kind-1', { filename: 'foo' });
|
||||
kind1.add('story-1.1', functions['story-1.1']);
|
||||
kind1.add('story-1.2', functions['story-1.2']);
|
||||
const kind2 = api.storiesOf('kind-2', { filename: 'bar' });
|
||||
kind2.add('story-2.1', functions['story-2.1']);
|
||||
kind2.add('story-2.2', functions['story-2.2']);
|
||||
const book = api.getStorybook();
|
||||
expect(book).toEqual([
|
||||
{
|
||||
kind: 'kind-1',
|
||||
fileName: 'foo',
|
||||
stories: [
|
||||
{ name: 'story-1.1', render: functions['story-1.1'] },
|
||||
{ name: 'story-1.2', render: functions['story-1.2'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'kind-2',
|
||||
fileName: 'bar',
|
||||
stories: [
|
||||
{ name: 'story-2.1', render: functions['story-2.1'] },
|
||||
{ name: 'story-2.2', render: functions['story-2.2'] },
|
||||
|
@ -12,10 +12,11 @@ export default class StoryStore {
|
||||
this._data = {};
|
||||
}
|
||||
|
||||
addStory(kind, name, fn) {
|
||||
addStory(kind, name, fn, fileName) {
|
||||
if (!this._data[kind]) {
|
||||
this._data[kind] = {
|
||||
kind,
|
||||
fileName,
|
||||
index: getId(),
|
||||
stories: {},
|
||||
};
|
||||
@ -47,6 +48,15 @@ export default class StoryStore {
|
||||
.map(info => info.name);
|
||||
}
|
||||
|
||||
getStoryFileName(kind) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return storiesKind.fileName;
|
||||
}
|
||||
|
||||
getStory(kind, name) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
|
@ -1,11 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import JSON5 from 'json5';
|
||||
import { console as logger } from 'global';
|
||||
import defaultConfig from './config/babel';
|
||||
|
||||
// avoid ESLint errors
|
||||
const logger = console;
|
||||
|
||||
function removeReactHmre(presets) {
|
||||
const index = presets.indexOf('react-hmre');
|
||||
if (index > -1) {
|
||||
|
@ -1,6 +1,16 @@
|
||||
import mock from 'mock-fs';
|
||||
import loadBabelConfig from './babel_config';
|
||||
|
||||
// eslint-disable-next-line global-require
|
||||
jest.mock('fs', () => require('../../../../__mocks__/fs'));
|
||||
jest.mock('path', () => ({
|
||||
resolve: () => '.babelrc',
|
||||
}));
|
||||
|
||||
const setup = ({ files }) => {
|
||||
// eslint-disable-next-line no-underscore-dangle, global-require
|
||||
require('fs').__setMockFiles(files);
|
||||
};
|
||||
|
||||
describe('babel_config', () => {
|
||||
// As the 'fs' is going to be mocked, let's call require.resolve
|
||||
// so the require.cache has the correct route to the file.
|
||||
@ -8,9 +18,9 @@ describe('babel_config', () => {
|
||||
const babelPluginReactDocgenPath = require.resolve('babel-plugin-react-docgen');
|
||||
|
||||
it('should return the config with the extra plugins when `plugins` is an array.', () => {
|
||||
// Mock a simple `.babelrc` config file.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
@ -19,72 +29,82 @@ describe('babel_config', () => {
|
||||
"foo-plugin"
|
||||
]
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.foo');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
'foo-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
'foo-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the config with the extra plugins when `plugins` is not an array.', () => {
|
||||
// Mock a `.babelrc` config file with plugins key not being an array.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
],
|
||||
"plugins": "bar-plugin"
|
||||
}`,
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
],
|
||||
"plugins": "bar-plugin"
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.bar');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
'bar-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
'bar-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the config only with the extra plugins when `plugins` is not present.', () => {
|
||||
// Mock a `.babelrc` config file with no plugins key.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
]
|
||||
}`,
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
]
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.biz');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -16,6 +16,12 @@ module.exports = {
|
||||
require.resolve('babel-preset-minify'),
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-react-docgen'),
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
require.resolve('babel-plugin-transform-regenerator'),
|
||||
[
|
||||
require.resolve('babel-plugin-transform-runtime'),
|
||||
|
@ -40,15 +40,6 @@ export default function({ assets, publicPath, headHtml }) {
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||
<title>Storybook</title>
|
||||
<style>
|
||||
/*
|
||||
When resizing panels, the drag event breaks if the cursor
|
||||
moves over the iframe. Add the 'dragging' class to the body
|
||||
at drag start and remove it when the drag ends.
|
||||
*/
|
||||
.dragging iframe {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Styling the fuzzy search box placeholders */
|
||||
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||
color: #ddd;
|
||||
|
@ -33,6 +33,7 @@ program
|
||||
)
|
||||
.option('--ssl-cert <cert>', 'Provide an SSL certificate. (Required with --https)')
|
||||
.option('--ssl-key <key>', 'Provide an SSL key. (Required with --https)')
|
||||
.option('--smoke-test', 'Exit after successful start')
|
||||
.option('-d, --db-path [db-file]', 'DEPRECATED!')
|
||||
.option('--enable-db', 'DEPRECATED!')
|
||||
.parse(process.argv);
|
||||
@ -153,5 +154,8 @@ Promise.all([webpackValid, serverListening])
|
||||
.then(() => {
|
||||
const address = `http://${program.host || 'localhost'}:${program.port}/`;
|
||||
logger.info(`Storybook started on => ${chalk.cyan(address)}\n`);
|
||||
if (program.smokeTest) {
|
||||
process.exit(0);
|
||||
}
|
||||
})
|
||||
.catch(error => logger.error(error));
|
||||
|
@ -1,42 +1,69 @@
|
||||
import mock from 'mock-fs';
|
||||
import { getPreviewHeadHtml } from './utils';
|
||||
import { getPreviewHeadHtml, getManagerHeadHtml } from './utils';
|
||||
|
||||
const HEAD_HTML_CONTENTS = '<script>console.log("custom script!");</script>';
|
||||
// eslint-disable-next-line global-require
|
||||
jest.mock('fs', () => require('../../../../__mocks__/fs'));
|
||||
jest.mock('path', () => ({
|
||||
resolve: (a, p) => p,
|
||||
}));
|
||||
|
||||
describe('server.getPreviewHeadHtml', () => {
|
||||
describe('when .storybook/head.html does not exist', () => {
|
||||
beforeEach(() => {
|
||||
mock({
|
||||
config: {},
|
||||
});
|
||||
const setup = ({ files }) => {
|
||||
// eslint-disable-next-line no-underscore-dangle, global-require
|
||||
require('fs').__setMockFiles(files);
|
||||
};
|
||||
|
||||
const HEAD_HTML_CONTENTS = 'UNITTEST_HEAD_HTML_CONTENTS';
|
||||
|
||||
describe('getPreviewHeadHtml', () => {
|
||||
it('returns an empty string without head.html present', () => {
|
||||
setup({
|
||||
files: {},
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('return an empty string', () => {
|
||||
const result = getPreviewHeadHtml('./config');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
const result = getPreviewHeadHtml('first');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
|
||||
describe('when .storybook/head.html exists', () => {
|
||||
beforeEach(() => {
|
||||
mock({
|
||||
config: {
|
||||
'head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
it('return contents of head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
const result = getPreviewHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
|
||||
it('returns contents of preview-head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'preview-head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
it('return the contents of the file', () => {
|
||||
const result = getPreviewHeadHtml('./config');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
const result = getPreviewHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getManagerHeadHtml', () => {
|
||||
it('returns an empty string without manager-head.html present', () => {
|
||||
setup({
|
||||
files: {},
|
||||
});
|
||||
|
||||
const result = getManagerHeadHtml('first');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
|
||||
it('returns contents of manager-head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'manager-head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
const result = getManagerHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +29,7 @@
|
||||
"@storybook/ui": "^3.2.7",
|
||||
"airbnb-js-shims": "^1.1.1",
|
||||
"autoprefixer": "^7.1.1",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-react-docgen": "^1.6.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
@ -39,8 +39,8 @@
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.0.0",
|
||||
"chalk": "^2.0.1",
|
||||
"commander": "^2.9.0",
|
||||
"chalk": "^2.1.0",
|
||||
"commander": "^2.11.0",
|
||||
"common-tags": "^1.4.0",
|
||||
"configstore": "^3.1.0",
|
||||
"css-loader": "^0.28.1",
|
||||
@ -77,8 +77,7 @@
|
||||
"webpack-hot-middleware": "^2.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"mock-fs": "^4.3.0",
|
||||
"nodemon": "^1.11.0"
|
||||
"babel-cli": "^6.26.0",
|
||||
"nodemon": "^1.12.0"
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +89,10 @@ export default class ClientApi {
|
||||
getStory
|
||||
);
|
||||
|
||||
const fileName = m ? m.filename : null;
|
||||
|
||||
// Add the fully decorated getStory function.
|
||||
this._storyStore.addStory(kind, storyName, getDecoratedStory);
|
||||
this._storyStore.addStory(kind, storyName, getDecoratedStory, fileName);
|
||||
return api;
|
||||
};
|
||||
|
||||
@ -104,11 +106,14 @@ export default class ClientApi {
|
||||
|
||||
getStorybook() {
|
||||
return this._storyStore.getStoryKinds().map(kind => {
|
||||
const fileName = this._storyStore.getStoryFileName(kind);
|
||||
|
||||
const stories = this._storyStore.getStories(kind).map(name => {
|
||||
const render = this._storyStore.getStory(kind, name);
|
||||
return { name, render };
|
||||
});
|
||||
return { kind, stories };
|
||||
|
||||
return { kind, fileName, stories };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ class StoryStore {
|
||||
this.stories = [];
|
||||
}
|
||||
|
||||
addStory(kind, story, fn) {
|
||||
this.stories.push({ kind, story, fn });
|
||||
addStory(kind, story, fn, fileName) {
|
||||
this.stories.push({ kind, story, fn, fileName });
|
||||
}
|
||||
|
||||
getStoryKinds() {
|
||||
@ -29,6 +29,11 @@ class StoryStore {
|
||||
}, []);
|
||||
}
|
||||
|
||||
getStoryFileName(kind) {
|
||||
const story = this.stories.find(info => info.kind === kind);
|
||||
return story ? story.fileName : null;
|
||||
}
|
||||
|
||||
getStory(kind, name) {
|
||||
return this.stories.reduce((fn, info) => {
|
||||
if (!fn && info.kind === kind && info.story === name) {
|
||||
@ -55,7 +60,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa();
|
||||
api.storiesOf('none', module).aa();
|
||||
expect(data).toBe('foo');
|
||||
});
|
||||
|
||||
@ -75,7 +80,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa().bb();
|
||||
api.storiesOf('none', module).aa().bb();
|
||||
expect(data).toEqual(['foo', 'bar']);
|
||||
});
|
||||
|
||||
@ -89,7 +94,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').aa();
|
||||
api.storiesOf('none', module).aa();
|
||||
expect(data).toBe('function');
|
||||
});
|
||||
|
||||
@ -109,7 +114,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf('none').bb();
|
||||
api.storiesOf('none', module).bb();
|
||||
expect(data).toBe('foo');
|
||||
});
|
||||
|
||||
@ -124,7 +129,7 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
});
|
||||
|
||||
api.storiesOf(kind).aa();
|
||||
api.storiesOf(kind, module).aa();
|
||||
expect(data).toBe(kind);
|
||||
});
|
||||
});
|
||||
@ -133,7 +138,7 @@ describe('preview.client_api', () => {
|
||||
it('should add local decorators', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator(fn => ({ template: `<div>aa${fn().template}</div>` }));
|
||||
|
||||
localApi.add('storyName', () => ({ template: '<p>hello</p>' }));
|
||||
@ -144,7 +149,7 @@ describe('preview.client_api', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
api.addDecorator(fn => ({ template: `<div>bb${fn().template}</div>` }));
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
|
||||
localApi.add('storyName', () => ({ template: '<p>hello</p>' }));
|
||||
expect(storyStore.stories[0].fn().template).toBe('<div>bb<p>hello</p></div>');
|
||||
@ -153,7 +158,7 @@ describe('preview.client_api', () => {
|
||||
it('should utilize both decorators at once', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
|
||||
api.addDecorator(fn => ({ template: `<div>aa${fn().template}</div>` }));
|
||||
localApi.addDecorator(fn => ({ template: `<div>bb${fn().template}</div>` }));
|
||||
@ -165,7 +170,7 @@ describe('preview.client_api', () => {
|
||||
it('should pass the context', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator(fn => ({ template: `<div>aa${fn().template}</div>` }));
|
||||
|
||||
localApi.add('storyName', ({ kind, story }) => ({ template: `<p>${kind}-${story}</p>` }));
|
||||
@ -180,7 +185,7 @@ describe('preview.client_api', () => {
|
||||
it('should have access to the context', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const localApi = api.storiesOf('none');
|
||||
const localApi = api.storiesOf('none', module);
|
||||
localApi.addDecorator((fn, { kind, story }) => ({
|
||||
template: `<div>${kind}-${story}-${fn().template}</div>`,
|
||||
}));
|
||||
@ -221,16 +226,17 @@ describe('preview.client_api', () => {
|
||||
'story-2.1': () => 'story-2.1',
|
||||
'story-2.2': () => 'story-2.2',
|
||||
};
|
||||
const kind1 = api.storiesOf('kind-1');
|
||||
const kind1 = api.storiesOf('kind-1', { filename: 'kind1.js' });
|
||||
kind1.add('story-1.1', functions['story-1.1']);
|
||||
kind1.add('story-1.2', functions['story-1.2']);
|
||||
const kind2 = api.storiesOf('kind-2');
|
||||
const kind2 = api.storiesOf('kind-2', { filename: 'kind2.js' });
|
||||
kind2.add('story-2.1', functions['story-2.1']);
|
||||
kind2.add('story-2.2', functions['story-2.2']);
|
||||
const book = api.getStorybook();
|
||||
expect(book).toEqual([
|
||||
{
|
||||
kind: 'kind-1',
|
||||
fileName: 'kind1.js',
|
||||
stories: [
|
||||
{ name: 'story-1.1', render: functions['story-1.1'] },
|
||||
{ name: 'story-1.2', render: functions['story-1.2'] },
|
||||
@ -238,6 +244,43 @@ describe('preview.client_api', () => {
|
||||
},
|
||||
{
|
||||
kind: 'kind-2',
|
||||
fileName: 'kind2.js',
|
||||
stories: [
|
||||
{ name: 'story-2.1', render: functions['story-2.1'] },
|
||||
{ name: 'story-2.2', render: functions['story-2.2'] },
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return storybook with file names when module with file name provided', () => {
|
||||
const storyStore = new StoryStore();
|
||||
const api = new ClientAPI({ storyStore });
|
||||
const functions = {
|
||||
'story-1.1': () => 'story-1.1',
|
||||
'story-1.2': () => 'story-1.2',
|
||||
'story-2.1': () => 'story-2.1',
|
||||
'story-2.2': () => 'story-2.2',
|
||||
};
|
||||
const kind1 = api.storiesOf('kind-1', { filename: 'foo' });
|
||||
kind1.add('story-1.1', functions['story-1.1']);
|
||||
kind1.add('story-1.2', functions['story-1.2']);
|
||||
const kind2 = api.storiesOf('kind-2', { filename: 'bar' });
|
||||
kind2.add('story-2.1', functions['story-2.1']);
|
||||
kind2.add('story-2.2', functions['story-2.2']);
|
||||
const book = api.getStorybook();
|
||||
expect(book).toEqual([
|
||||
{
|
||||
kind: 'kind-1',
|
||||
fileName: 'foo',
|
||||
stories: [
|
||||
{ name: 'story-1.1', render: functions['story-1.1'] },
|
||||
{ name: 'story-1.2', render: functions['story-1.2'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'kind-2',
|
||||
fileName: 'bar',
|
||||
stories: [
|
||||
{ name: 'story-2.1', render: functions['story-2.1'] },
|
||||
{ name: 'story-2.2', render: functions['story-2.2'] },
|
||||
|
@ -12,10 +12,11 @@ export default class StoryStore {
|
||||
this._data = {};
|
||||
}
|
||||
|
||||
addStory(kind, name, fn) {
|
||||
addStory(kind, name, fn, fileName) {
|
||||
if (!this._data[kind]) {
|
||||
this._data[kind] = {
|
||||
kind,
|
||||
fileName,
|
||||
index: getId(),
|
||||
stories: {},
|
||||
};
|
||||
@ -47,6 +48,15 @@ export default class StoryStore {
|
||||
.map(info => info.name);
|
||||
}
|
||||
|
||||
getStoryFileName(kind) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return storiesKind.fileName;
|
||||
}
|
||||
|
||||
getStory(kind, name) {
|
||||
const storiesKind = this._data[kind];
|
||||
if (!storiesKind) {
|
||||
|
@ -1,6 +1,16 @@
|
||||
import mock from 'mock-fs';
|
||||
import loadBabelConfig from './babel_config';
|
||||
|
||||
// eslint-disable-next-line global-require
|
||||
jest.mock('fs', () => require('../../../../__mocks__/fs'));
|
||||
jest.mock('path', () => ({
|
||||
resolve: () => '.babelrc',
|
||||
}));
|
||||
|
||||
const setup = ({ files }) => {
|
||||
// eslint-disable-next-line no-underscore-dangle, global-require
|
||||
require('fs').__setMockFiles(files);
|
||||
};
|
||||
|
||||
describe('babel_config', () => {
|
||||
// As the 'fs' is going to be mocked, let's call require.resolve
|
||||
// so the require.cache has the correct route to the file.
|
||||
@ -8,9 +18,9 @@ describe('babel_config', () => {
|
||||
const babelPluginReactDocgenPath = require.resolve('babel-plugin-react-docgen');
|
||||
|
||||
it('should return the config with the extra plugins when `plugins` is an array.', () => {
|
||||
// Mock a simple `.babelrc` config file.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
@ -19,72 +29,82 @@ describe('babel_config', () => {
|
||||
"foo-plugin"
|
||||
]
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.foo');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
'foo-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
'foo-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the config with the extra plugins when `plugins` is not an array.', () => {
|
||||
// Mock a `.babelrc` config file with plugins key not being an array.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
],
|
||||
"plugins": "bar-plugin"
|
||||
}`,
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
],
|
||||
"plugins": "bar-plugin"
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.bar');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
'bar-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
'bar-plugin',
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the config only with the extra plugins when `plugins` is not present.', () => {
|
||||
// Mock a `.babelrc` config file with no plugins key.
|
||||
mock({
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
]
|
||||
}`,
|
||||
setup({
|
||||
files: {
|
||||
'.babelrc': `{
|
||||
"presets": [
|
||||
"env",
|
||||
"foo-preset"
|
||||
]
|
||||
}`,
|
||||
},
|
||||
});
|
||||
|
||||
const config = loadBabelConfig('.biz');
|
||||
|
||||
expect(config.plugins).toEqual([
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
expect(config).toEqual({
|
||||
babelrc: false,
|
||||
plugins: [
|
||||
[
|
||||
babelPluginReactDocgenPath,
|
||||
{
|
||||
DOC_GEN_COLLECTION_NAME: 'STORYBOOK_REACT_CLASSES',
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
mock.restore();
|
||||
presets: ['env', 'foo-preset'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -40,15 +40,6 @@ export default function({ assets, publicPath, headHtml }) {
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||
<title>Storybook</title>
|
||||
<style>
|
||||
/*
|
||||
When resizing panels, the drag event breaks if the cursor
|
||||
moves over the iframe. Add the 'dragging' class to the body
|
||||
at drag start and remove it when the drag ends.
|
||||
*/
|
||||
.dragging iframe {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Styling the fuzzy search box placeholders */
|
||||
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
|
||||
color: #ddd;
|
||||
|
@ -33,6 +33,7 @@ program
|
||||
)
|
||||
.option('--ssl-cert <cert>', 'Provide an SSL certificate. (Required with --https)')
|
||||
.option('--ssl-key <key>', 'Provide an SSL key. (Required with --https)')
|
||||
.option('--smoke-test', 'Exit after successful start')
|
||||
.option('-d, --db-path [db-file]', 'DEPRECATED!')
|
||||
.option('--enable-db', 'DEPRECATED!')
|
||||
.parse(process.argv);
|
||||
@ -153,5 +154,8 @@ Promise.all([webpackValid, serverListening])
|
||||
.then(() => {
|
||||
const address = `http://${program.host || 'localhost'}:${program.port}/`;
|
||||
logger.info(`Storybook started on => ${chalk.cyan(address)}\n`);
|
||||
if (program.smokeTest) {
|
||||
process.exit(0);
|
||||
}
|
||||
})
|
||||
.catch(error => logger.error(error));
|
||||
|
@ -1,42 +1,69 @@
|
||||
import mock from 'mock-fs';
|
||||
import { getPreviewHeadHtml } from './utils';
|
||||
import { getPreviewHeadHtml, getManagerHeadHtml } from './utils';
|
||||
|
||||
const HEAD_HTML_CONTENTS = '<script>console.log("custom script!");</script>';
|
||||
// eslint-disable-next-line global-require
|
||||
jest.mock('fs', () => require('../../../../__mocks__/fs'));
|
||||
jest.mock('path', () => ({
|
||||
resolve: (a, p) => p,
|
||||
}));
|
||||
|
||||
describe('server.getPreviewHeadHtml', () => {
|
||||
describe('when .storybook/head.html does not exist', () => {
|
||||
beforeEach(() => {
|
||||
mock({
|
||||
config: {},
|
||||
});
|
||||
const setup = ({ files }) => {
|
||||
// eslint-disable-next-line no-underscore-dangle, global-require
|
||||
require('fs').__setMockFiles(files);
|
||||
};
|
||||
|
||||
const HEAD_HTML_CONTENTS = 'UNITTEST_HEAD_HTML_CONTENTS';
|
||||
|
||||
describe('getPreviewHeadHtml', () => {
|
||||
it('returns an empty string without head.html present', () => {
|
||||
setup({
|
||||
files: {},
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('return an empty string', () => {
|
||||
const result = getPreviewHeadHtml('./config');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
const result = getPreviewHeadHtml('first');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
|
||||
describe('when .storybook/head.html exists', () => {
|
||||
beforeEach(() => {
|
||||
mock({
|
||||
config: {
|
||||
'head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
it('return contents of head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
const result = getPreviewHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
|
||||
it('returns contents of preview-head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'preview-head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
it('return the contents of the file', () => {
|
||||
const result = getPreviewHeadHtml('./config');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
const result = getPreviewHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getManagerHeadHtml', () => {
|
||||
it('returns an empty string without manager-head.html present', () => {
|
||||
setup({
|
||||
files: {},
|
||||
});
|
||||
|
||||
const result = getManagerHeadHtml('first');
|
||||
expect(result).toEqual('');
|
||||
});
|
||||
|
||||
it('returns contents of manager-head.html when present', () => {
|
||||
setup({
|
||||
files: {
|
||||
'manager-head.html': HEAD_HTML_CONTENTS,
|
||||
},
|
||||
});
|
||||
|
||||
const result = getManagerHeadHtml('second');
|
||||
expect(result).toEqual(HEAD_HTML_CONTENTS);
|
||||
});
|
||||
});
|
||||
|
283
dependencies.yml
Normal file
283
dependencies.yml
Normal file
@ -0,0 +1,283 @@
|
||||
collectors:
|
||||
|
||||
- type: js-npm
|
||||
path: docs
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
# Temporarily disabled
|
||||
# - type: js-npm
|
||||
# path: examples/crna-kitchen-sink
|
||||
# actors:
|
||||
# # pull requests for updates to our major version
|
||||
# - type: js-npm
|
||||
# versions: "L.Y.Y"
|
||||
# # create issues for new major versions
|
||||
# - type: repo-issue
|
||||
# versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: examples/vue-kitchen-sink
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
# Temporarily disabled
|
||||
# - type: js-npm
|
||||
# path: examples/react-native-vanilla
|
||||
# actors:
|
||||
# # pull requests for updates to our major version
|
||||
# - type: js-npm
|
||||
# versions: "L.Y.Y"
|
||||
# # create issues for new major versions
|
||||
# - type: repo-issue
|
||||
# versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: examples/cra-kitchen-sink
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: app/react-native
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: app/vue
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: app/react
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: /
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/channel-websocket
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/channel-postmessage
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/components
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/codemod
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/ui
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/cli
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/channels
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: lib/addons
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/info
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/centered
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/knobs
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/graphql
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/notes
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/comments
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/actions
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/storyshots
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/options
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/links
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
||||
|
||||
- type: js-npm
|
||||
path: addons/events
|
||||
actors:
|
||||
# pull requests for updates to our major version
|
||||
- type: js-npm
|
||||
versions: "L.Y.Y"
|
||||
# create issues for new major versions
|
||||
- type: repo-issue
|
||||
versions: "Y.0.0"
|
@ -5,9 +5,9 @@ This is the source for [storybook.js.org](https://storybook.js.org). It document
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
npm i
|
||||
npm run develop
|
||||
npm run storybook
|
||||
yarn
|
||||
yarn develop
|
||||
yarn storybook
|
||||
```
|
||||
|
||||
### Edit Documentation
|
||||
|
@ -21,10 +21,10 @@
|
||||
"@storybook/addon-links": "latest",
|
||||
"@storybook/addons": "latest",
|
||||
"@storybook/react": "latest",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
|
@ -101,3 +101,7 @@ This addon lets you navigate different versions of static Storybook builds. As s
|
||||
### [Apollo](https://github.com/abhiaiyer91/apollo-storybook-decorator)
|
||||
|
||||
Wrap your stories with the Apollo client for mocking GraphQL queries/mutations.
|
||||
|
||||
### [Screenshot](https://github.com/tsuyoshiwada/storybook-chrome-screenshot)
|
||||
|
||||
Save the screenshot image of your stories. via [Puppeteer](https://github.com/GoogleChrome/puppeteer).
|
||||
|
@ -1,16 +1,17 @@
|
||||
---
|
||||
* * *
|
||||
|
||||
id: 'guide-vue'
|
||||
title: 'Storybook for Vue'
|
||||
---
|
||||
|
||||
## title: 'Storybook for Vue'
|
||||
|
||||
You may have tried to use our quick start guide to setup your project for Storybook. If you want to set up Storybook manually, this is the guide for you.
|
||||
|
||||
> This will also help you to understand how Storybook works.
|
||||
> This will also help you understand how Storybook works.
|
||||
|
||||
## Starter Guide Vue
|
||||
|
||||
Storybook has its own Webpack setup and a dev server.
|
||||
Webpack setup is very similar to [Vue CLI](https://github.com/vuejs/vue-cli), but allows you to [configure as you want](/configurations/custom-webpack-config/).
|
||||
The Webpack setup is very similar to [Vue CLI's](https://github.com/vuejs/vue-cli), but allows you to [configure it however you want](/configurations/custom-webpack-config/).
|
||||
|
||||
In this guide, we are trying to set up Storybook for your Vue project.
|
||||
|
||||
@ -43,13 +44,13 @@ npm i --save vue
|
||||
Storybook can be configured in several different ways.
|
||||
That’s why we need a config directory. We've added a `-c` option to the above NPM script mentioning `.storybook` as the config directory.
|
||||
|
||||
There's 3 things you need to tell Storybook to do:
|
||||
There are 3 things you need to tell Storybook to do:
|
||||
|
||||
1. In stories, if use the custom components without Vue `components` option, you need to register these with `Vue.component`.
|
||||
2. In stories, if use the Vue plugins (e.g. `vuex`), you need to install these with `Vue.use`.
|
||||
1. Import and globally register with [`Vue.component()`](https://vuejs.org/v2/api/#Vue-component) any global custom components just like you did with your project. (Note: [components registered locally](https://vuejs.org/v2/guide/components.html#Local-Registration) will be brought in automatically).
|
||||
2. For any required Vue plugins (e.g. `vuex`), you'll also need to [install these with `Vue.use`](https://vuejs.org/v2/api/#Vue-use).
|
||||
3. Require your stories.
|
||||
|
||||
To do that, simply create a file at `.storybook/config.js` with the following content:
|
||||
Here's an example `.storybook/config.js` to get you started:
|
||||
|
||||
```js
|
||||
import { configure } from '@storybook/vue';
|
||||
@ -74,9 +75,9 @@ function loadStories() {
|
||||
configure(loadStories, module);
|
||||
```
|
||||
|
||||
That'll register all your custom components, install all Vue plugins and load stories in `../stories/index.js`.
|
||||
This example registered your custom `Button.vue` component, installed the Vuex plugin, and loaded you Storybook stories defined in `../stories/index.js`.
|
||||
|
||||
All custom components and All Vue plugins should be registered before calling `configure`.
|
||||
All custom components and Vue plugins should be registered before calling `configure()`.
|
||||
|
||||
> This stories folder is just an example, you can load stories from wherever you want to.
|
||||
> We think stories are best located close to the source files.
|
||||
|
@ -1,3 +1,10 @@
|
||||
carbon:
|
||||
thumbnail: ./thumbnails/carbon.png
|
||||
title: Carbon Components
|
||||
description: IBM's Carbon Design System implemented in React.
|
||||
source: https://github.com/carbon-design-system/carbon-components-react
|
||||
demo: http://react.carbondesignsystem.com
|
||||
site: http://carbondesignsystem.com
|
||||
airbnb:
|
||||
thumbnail: ./thumbnails/airbnb.jpg
|
||||
title: Airbnb Dates
|
||||
|
BIN
docs/pages/examples/thumbnails/carbon.png
Normal file
BIN
docs/pages/examples/thumbnails/carbon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
8607
docs/yarn.lock
Normal file
8607
docs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,10 +6,10 @@ setOptions({
|
||||
name: 'CRA Kitchen Sink',
|
||||
url: 'https://github.com/storybooks/storybook/tree/master/examples/cra-kitchen-sink',
|
||||
goFullScreen: false,
|
||||
showLeftPanel: true,
|
||||
showDownPanel: true,
|
||||
showStoriesPanel: true,
|
||||
showAddonsPanel: true,
|
||||
showSearchBox: false,
|
||||
downPanelInRight: true,
|
||||
addonsPanelInRight: true,
|
||||
sortStoriesByKind: false,
|
||||
hierarchySeparator: /\/|\./,
|
||||
});
|
||||
|
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import DocgenButton from './DocgenButton';
|
||||
|
||||
/** Button component description */
|
||||
const ImportedPropsButton = ({ disabled, label, onClick }) =>
|
||||
<button disabled={disabled} onClick={onClick}>
|
||||
{label}
|
||||
</button>;
|
||||
|
||||
ImportedPropsButton.defaultProps = DocgenButton.defaultProps;
|
||||
|
||||
ImportedPropsButton.propTypes = DocgenButton.propTypes;
|
||||
|
||||
export default ImportedPropsButton;
|
@ -150,7 +150,7 @@ exports[`Storyshots AddonInfo.DocgenButton DocgenButton 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
Some Description
|
||||
Button with PropTypes and doc comments
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -618,7 +618,7 @@ exports[`Storyshots AddonInfo.FlowTypeButton FlowTypeButton 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
Some Description
|
||||
Button with Flow type documentation comments
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -838,6 +838,474 @@ exports[`Storyshots AddonInfo.FlowTypeButton FlowTypeButton 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots AddonInfo.ImportedPropsButton ImportedPropsButton 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"position": "relative",
|
||||
"zIndex": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<button
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
Docgen Button
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
style={
|
||||
Object {
|
||||
"background": "#28c",
|
||||
"borderRadius": "0 0 0 5px",
|
||||
"color": "#fff",
|
||||
"cursor": "pointer",
|
||||
"display": "block",
|
||||
"fontFamily": "sans-serif",
|
||||
"fontSize": "12px",
|
||||
"padding": "5px 15px",
|
||||
"position": "fixed",
|
||||
"right": 0,
|
||||
"textDecoration": "none",
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
Show Info
|
||||
</a>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"background": "white",
|
||||
"bottom": 0,
|
||||
"display": "none",
|
||||
"left": 0,
|
||||
"overflow": "auto",
|
||||
"padding": "0 40px",
|
||||
"position": "fixed",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
"zIndex": 99999,
|
||||
}
|
||||
}
|
||||
>
|
||||
<a
|
||||
onClick={[Function]}
|
||||
role="button"
|
||||
style={
|
||||
Object {
|
||||
"background": "#28c",
|
||||
"borderRadius": "0 0 0 5px",
|
||||
"color": "#fff",
|
||||
"cursor": "pointer",
|
||||
"display": "block",
|
||||
"fontFamily": "sans-serif",
|
||||
"fontSize": "12px",
|
||||
"padding": "5px 15px",
|
||||
"position": "fixed",
|
||||
"right": 0,
|
||||
"textDecoration": "none",
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
tabIndex="0"
|
||||
>
|
||||
×
|
||||
</a>
|
||||
<div
|
||||
style={undefined}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"backgroundColor": "#fff",
|
||||
"border": "1px solid #eee",
|
||||
"borderRadius": "2px",
|
||||
"boxShadow": "0px 2px 3px rgba(0, 0, 0, 0.05)",
|
||||
"color": "#444",
|
||||
"fontFamily": "-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", \\"Arial\\", sans-serif",
|
||||
"fontSize": "15px",
|
||||
"fontWeight": 300,
|
||||
"lineHeight": 1.45,
|
||||
"marginTop": "50px",
|
||||
"padding": "20px 40px 40px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"borderBottom": "1px solid #eee",
|
||||
"marginBottom": 10,
|
||||
"paddingTop": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<h1
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "35px",
|
||||
"margin": 0,
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
AddonInfo.ImportedPropsButton
|
||||
</h1>
|
||||
<h2
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "22px",
|
||||
"fontWeight": 400,
|
||||
"margin": "0 0 10px 0",
|
||||
"padding": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
ImportedPropsButton
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<p
|
||||
style={
|
||||
Object {
|
||||
"WebkitFontSmoothing": "antialiased",
|
||||
"color": "#444",
|
||||
"fontFamily": "-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", \\"Arial\\", sans-serif",
|
||||
"fontSize": "15px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Button with PropTypes imported from another file. Should fallback to using PropTypes for data.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h1
|
||||
style={
|
||||
Object {
|
||||
"borderBottom": "1px solid #EEE",
|
||||
"fontSize": "25px",
|
||||
"margin": "20px 0 0 0",
|
||||
"padding": "0 0 5px 0",
|
||||
}
|
||||
}
|
||||
>
|
||||
Story Source
|
||||
</h1>
|
||||
<pre
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#fafafa",
|
||||
"fontFamily": "Menlo, Monaco, \\"Courier New\\", monospace",
|
||||
"fontSize": ".88em",
|
||||
"lineHeight": 1.5,
|
||||
"overflowX": "scroll",
|
||||
"padding": ".5rem",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"paddingLeft": 18,
|
||||
"paddingRight": 3,
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#777",
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
ImportedPropsButton
|
||||
</span>
|
||||
<span>
|
||||
<span>
|
||||
|
||||
<span
|
||||
style={Object {}}
|
||||
>
|
||||
onClick
|
||||
</span>
|
||||
<span>
|
||||
=
|
||||
<span
|
||||
style={Object {}}
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#170",
|
||||
}
|
||||
}
|
||||
>
|
||||
clicked()
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
|
||||
<span
|
||||
style={Object {}}
|
||||
>
|
||||
label
|
||||
</span>
|
||||
<span>
|
||||
=
|
||||
<span
|
||||
style={Object {}}
|
||||
>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#22a",
|
||||
"wordBreak": "break-word",
|
||||
}
|
||||
}
|
||||
>
|
||||
"
|
||||
Docgen Button
|
||||
"
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#777",
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<h1
|
||||
style={
|
||||
Object {
|
||||
"borderBottom": "1px solid #EEE",
|
||||
"fontSize": "25px",
|
||||
"margin": "20px 0 0 0",
|
||||
"padding": "0 0 5px 0",
|
||||
}
|
||||
}
|
||||
>
|
||||
Prop Types
|
||||
</h1>
|
||||
<div>
|
||||
<h2
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px 0 0 0",
|
||||
}
|
||||
}
|
||||
>
|
||||
"
|
||||
ImportedPropsButton
|
||||
" Component
|
||||
</h2>
|
||||
<table
|
||||
style={
|
||||
Object {
|
||||
"borderCollapse": "separate",
|
||||
"borderSpacing": "10px 5px",
|
||||
"marginLeft": -10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
property
|
||||
</th>
|
||||
<th>
|
||||
propType
|
||||
</th>
|
||||
<th>
|
||||
required
|
||||
</th>
|
||||
<th>
|
||||
default
|
||||
</th>
|
||||
<th>
|
||||
description
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
disabled
|
||||
</td>
|
||||
<td>
|
||||
bool
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#a11",
|
||||
}
|
||||
}
|
||||
>
|
||||
false
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
label
|
||||
</td>
|
||||
<td>
|
||||
string
|
||||
</td>
|
||||
<td>
|
||||
yes
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
onClick
|
||||
</td>
|
||||
<td>
|
||||
func
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
<span
|
||||
style={
|
||||
Object {
|
||||
"color": "#170",
|
||||
}
|
||||
}
|
||||
>
|
||||
onClick()
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
one
|
||||
</td>
|
||||
<td>
|
||||
other
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
two
|
||||
</td>
|
||||
<td>
|
||||
other
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
msg
|
||||
</td>
|
||||
<td>
|
||||
other
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
enm
|
||||
</td>
|
||||
<td>
|
||||
other
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
union
|
||||
</td>
|
||||
<td>
|
||||
other
|
||||
</td>
|
||||
<td>
|
||||
no
|
||||
</td>
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots App full app 1`] = `
|
||||
<div
|
||||
className="App"
|
||||
@ -1285,6 +1753,25 @@ exports[`Storyshots Button with knobs 1`] = `
|
||||
My birthday is:
|
||||
January 20, 2017
|
||||
</p>
|
||||
<p>
|
||||
I have
|
||||
2
|
||||
children:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Jane
|
||||
,
|
||||
13
|
||||
years old
|
||||
</li>
|
||||
<li>
|
||||
John
|
||||
,
|
||||
8
|
||||
years old
|
||||
</li>
|
||||
</ol>
|
||||
<p>
|
||||
My wallet contains: $
|
||||
12.50
|
||||
@ -2489,29 +2976,6 @@ exports[`Storyshots Cells/Molecules/Atoms.more with text2 1`] = `
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Navigation Menu link 1`] = `
|
||||
<div
|
||||
className="css-t9df35"
|
||||
>
|
||||
<a
|
||||
className="css-1enjukp"
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Menu link item
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Navigation Routed link 1`] = `
|
||||
<a
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Try clicking with different mouse buttons and modifier keys (shift/ctrl/alt/cmd)
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Some really long story kind description with text 1`] = `
|
||||
<div
|
||||
style={
|
||||
@ -2654,9 +3118,9 @@ exports[`Storyshots WithEvents Logger 1`] = `
|
||||
Object {
|
||||
"color": "rgb(51, 51, 51)",
|
||||
"fontFamily": "
|
||||
-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", \\"Roboto\\",
|
||||
\\"Segoe UI\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", sans-serif
|
||||
",
|
||||
-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", \\"Roboto\\",
|
||||
\\"Segoe UI\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", sans-serif
|
||||
",
|
||||
"padding": 20,
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots Navigation Menu link 1`] = `
|
||||
<div
|
||||
className="css-t9df35"
|
||||
>
|
||||
<a
|
||||
className="css-1enjukp"
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Menu link item
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Navigation Routed link 1`] = `
|
||||
<a
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Try clicking with different mouse buttons and modifier keys (shift/ctrl/alt/cmd)
|
||||
</a>
|
||||
`;
|
@ -28,6 +28,7 @@ import Logger from './Logger';
|
||||
import Container from './Container';
|
||||
import DocgenButton from '../components/DocgenButton';
|
||||
import FlowTypeButton from '../components/FlowTypeButton';
|
||||
import ImportedPropsButton from '../components/ImportedPropsButton';
|
||||
|
||||
const EVENTS = {
|
||||
TEST_EVENT_1: 'test-event-1',
|
||||
@ -99,6 +100,16 @@ storiesOf('Button', module)
|
||||
padding: '10px',
|
||||
});
|
||||
const nice = boolean('Nice', true);
|
||||
const children = object('Children', [
|
||||
{
|
||||
name: 'Jane',
|
||||
age: 13,
|
||||
},
|
||||
{
|
||||
name: 'John',
|
||||
age: 8,
|
||||
},
|
||||
]);
|
||||
|
||||
// NOTE: put this last because it currently breaks everything after it :D
|
||||
const birthday = date('Birthday', new Date('Jan 20 2017'));
|
||||
@ -116,6 +127,16 @@ storiesOf('Button', module)
|
||||
<p>
|
||||
My birthday is: {new Date(birthday).toLocaleDateString('en-US', dateOptions)}
|
||||
</p>
|
||||
<p>
|
||||
I have {children.length} children:
|
||||
</p>
|
||||
<ol>
|
||||
{children.map(child =>
|
||||
<li key={child.name}>
|
||||
{child.name}, {child.age} years old
|
||||
</li>
|
||||
)}
|
||||
</ol>
|
||||
<p>
|
||||
My wallet contains: ${dollars.toFixed(2)}
|
||||
</p>
|
||||
@ -168,6 +189,15 @@ storiesOf('AddonInfo.DocgenButton', module).addWithInfo('DocgenButton', 'Some De
|
||||
<DocgenButton onClick={action('clicked')} label="Docgen Button" />
|
||||
);
|
||||
|
||||
storiesOf(
|
||||
'AddonInfo.ImportedPropsButton',
|
||||
module
|
||||
).addWithInfo(
|
||||
'ImportedPropsButton',
|
||||
'Button with PropTypes imported from another file. Should fallback to using PropTypes for data.',
|
||||
() => <ImportedPropsButton onClick={action('clicked')} label="Docgen Button" />
|
||||
);
|
||||
|
||||
storiesOf(
|
||||
'AddonInfo.FlowTypeButton',
|
||||
module
|
||||
|
@ -1,4 +1,4 @@
|
||||
import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots';
|
||||
import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots';
|
||||
import path from 'path';
|
||||
|
||||
function createNodeMock(element) {
|
||||
@ -11,7 +11,7 @@ function createNodeMock(element) {
|
||||
initStoryshots({
|
||||
framework: 'react',
|
||||
configPath: path.join(__dirname, '..', '.storybook'),
|
||||
test: snapshotWithOptions({
|
||||
test: multiSnapshotWithOptions({
|
||||
createNodeMock,
|
||||
}),
|
||||
});
|
||||
|
@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "file:../../packs/storybook-addon-actions.tgz",
|
||||
"@storybook/addon-knobs": "file:../../packs/storybook-addon-knobs.tgz",
|
||||
"@storybook/addon-links": "file:../../packs/storybook-addon-links.tgz",
|
||||
"@storybook/addon-options": "file:../../packs/storybook-addon-options.tgz",
|
||||
"@storybook/addon-storyshots": "file:../../packs/storybook-addon-storyshots.tgz",
|
||||
@ -14,7 +15,7 @@
|
||||
"@storybook/react-native": "file:../../packs/storybook-react-native.tgz",
|
||||
"@storybook/ui": "file:../../packs/storybook-ui.tgz",
|
||||
"react-native-scripts": "1.1.0",
|
||||
"jest-expo": "~19.0.0",
|
||||
"jest-expo": "19.0.0",
|
||||
"react-test-renderer": "16.0.0-alpha.12"
|
||||
},
|
||||
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
|
||||
@ -30,9 +31,9 @@
|
||||
"preset": "jest-expo"
|
||||
},
|
||||
"dependencies": {
|
||||
"expo": "^19.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"expo": "19.0.0",
|
||||
"prop-types": "15.5.10",
|
||||
"react": "16.0.0-alpha.12",
|
||||
"react-native": "^0.46.1"
|
||||
"react-native": "0.46.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-links/register';
|
||||
import '@storybook/addon-options/register';
|
||||
import '@storybook/addon-knobs/register';
|
||||
|
59
examples/crna-kitchen-sink/storybook/stories/Knobs/index.js
Normal file
59
examples/crna-kitchen-sink/storybook/stories/Knobs/index.js
Normal file
@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
|
||||
import { text, number, boolean, color, select, array, date, object } from '@storybook/addon-knobs';
|
||||
|
||||
export default () => {
|
||||
const name = text('Name', 'Storyteller');
|
||||
const age = number('Age', 70, { range: true, min: 0, max: 90, step: 5 });
|
||||
const fruits = {
|
||||
apple: 'Apple',
|
||||
banana: 'Banana',
|
||||
cherry: 'Cherry',
|
||||
};
|
||||
const fruit = select('Fruit', fruits, 'apple');
|
||||
const dollars = number('Dollars', 12.5);
|
||||
|
||||
// NOTE: color picker is currently broken
|
||||
const backgroundColor = color('background', '#ffff00');
|
||||
const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
|
||||
const otherStyles = object('Styles', {
|
||||
borderWidth: 3,
|
||||
borderColor: '#ff00ff',
|
||||
padding: 10,
|
||||
});
|
||||
const nice = boolean('Nice', true);
|
||||
|
||||
// NOTE: put this last because it currently breaks everything after it :D
|
||||
const birthday = date('Birthday', new Date('Jan 20 2017'));
|
||||
|
||||
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
|
||||
const style = { backgroundColor, ...otherStyles };
|
||||
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
|
||||
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' };
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<Text>
|
||||
{intro}
|
||||
</Text>
|
||||
<Text>
|
||||
My birthday is: {new Date(birthday).toLocaleDateString('en-US', dateOptions)}
|
||||
</Text>
|
||||
<Text>
|
||||
My wallet contains: ${dollars.toFixed(2)}
|
||||
</Text>
|
||||
<Text>In my backpack, I have:</Text>
|
||||
<View>
|
||||
{items.map(item =>
|
||||
<Text key={item}>
|
||||
{item}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text>
|
||||
{salutation}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
@ -4,7 +4,9 @@ import { Text } from 'react-native';
|
||||
import { storiesOf } from '@storybook/react-native';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
import { withKnobs } from '@storybook/addon-knobs';
|
||||
|
||||
import knobsWrapper from './Knobs';
|
||||
import Button from './Button';
|
||||
import CenterView from './CenterView';
|
||||
import Welcome from './Welcome';
|
||||
@ -27,3 +29,5 @@ storiesOf('Button', module)
|
||||
<Text>😀 😎 👍 💯</Text>
|
||||
</Button>
|
||||
);
|
||||
|
||||
storiesOf('Knobs', module).addDecorator(withKnobs).add('with knobs', knobsWrapper);
|
||||
|
@ -96,6 +96,80 @@ exports[`Storyshots Button with text 1`] = `
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Knobs with knobs 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#ffff00",
|
||||
"borderColor": "#ff00ff",
|
||||
"borderWidth": 3,
|
||||
"padding": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
My name is Storyteller, I'm 70 years old, and my favorite fruit is apple.
|
||||
</Text>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
My birthday is:
|
||||
January 20, 2017
|
||||
</Text>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
My wallet contains: $
|
||||
12.50
|
||||
</Text>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
In my backpack, I have:
|
||||
</Text>
|
||||
<View>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
Laptop
|
||||
</Text>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
Book
|
||||
</Text>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
Whiskey
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
accessible={true}
|
||||
allowFontScaling={true}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
Nice to meet you!
|
||||
</Text>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Welcome to Storybook 1`] = `
|
||||
<View
|
||||
style={
|
||||
|
@ -8,16 +8,17 @@
|
||||
"storybook": "storybook start -p 7007"
|
||||
},
|
||||
"dependencies": {
|
||||
"prop-types": "^15.5.10",
|
||||
"prop-types": "15.5.10",
|
||||
"react": "16.0.0-alpha.6",
|
||||
"react-native": "0.44.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "20.0.3",
|
||||
"babel-jest": "21.0.0",
|
||||
"babel-preset-react-native": "1.9.2",
|
||||
"jest": "^20.0.4",
|
||||
"jest": "^21.0.1",
|
||||
"react-test-renderer": "16.0.0-alpha.6",
|
||||
"@storybook/addon-actions": "file:../../packs/storybook-addon-actions.tgz",
|
||||
"@storybook/addon-knobs": "file:../../packs/storybook-addon-knobs.tgz",
|
||||
"@storybook/addon-links": "file:../../packs/storybook-addon-links.tgz",
|
||||
"@storybook/addon-options": "file:../../packs/storybook-addon-options.tgz",
|
||||
"@storybook/addon-storyshots": "file:../../packs/storybook-addon-storyshots.tgz",
|
||||
@ -27,6 +28,6 @@
|
||||
"@storybook/components": "file:../../packs/storybook-components.tgz",
|
||||
"@storybook/react-native": "file:../../packs/storybook-react-native.tgz",
|
||||
"@storybook/ui": "file:../../packs/storybook-ui.tgz",
|
||||
"react-dom": "^15.6.1"
|
||||
"react-dom": "15.6.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-links/register';
|
||||
import '@storybook/addon-options/register';
|
||||
import '@storybook/addon-knobs/register';
|
||||
|
59
examples/react-native-vanilla/storybook/stories/Knobs/index.js
vendored
Normal file
59
examples/react-native-vanilla/storybook/stories/Knobs/index.js
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
|
||||
import { text, number, boolean, color, select, array, date, object } from '@storybook/addon-knobs';
|
||||
|
||||
export default () => {
|
||||
const name = text('Name', 'Storyteller');
|
||||
const age = number('Age', 70, { range: true, min: 0, max: 90, step: 5 });
|
||||
const fruits = {
|
||||
apple: 'Apple',
|
||||
banana: 'Banana',
|
||||
cherry: 'Cherry',
|
||||
};
|
||||
const fruit = select('Fruit', fruits, 'apple');
|
||||
const dollars = number('Dollars', 12.5);
|
||||
|
||||
// NOTE: color picker is currently broken
|
||||
const backgroundColor = color('background', '#ffff00');
|
||||
const items = array('Items', ['Laptop', 'Book', 'Whiskey']);
|
||||
const otherStyles = object('Styles', {
|
||||
borderWidth: 3,
|
||||
borderColor: '#ff00ff',
|
||||
padding: 10,
|
||||
});
|
||||
const nice = boolean('Nice', true);
|
||||
|
||||
// NOTE: put this last because it currently breaks everything after it :D
|
||||
const birthday = date('Birthday', new Date('Jan 20 2017'));
|
||||
|
||||
const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}.`;
|
||||
const style = { backgroundColor, ...otherStyles };
|
||||
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
|
||||
const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' };
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<Text>
|
||||
{intro}
|
||||
</Text>
|
||||
<Text>
|
||||
My birthday is: {new Date(birthday).toLocaleDateString('en-US', dateOptions)}
|
||||
</Text>
|
||||
<Text>
|
||||
My wallet contains: ${dollars.toFixed(2)}
|
||||
</Text>
|
||||
<Text>In my backpack, I have:</Text>
|
||||
<View>
|
||||
{items.map(item =>
|
||||
<Text key={item}>
|
||||
{item}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text>
|
||||
{salutation}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
@ -4,7 +4,9 @@ import { Text } from 'react-native';
|
||||
import { storiesOf } from '@storybook/react-native';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
import { withKnobs } from '@storybook/addon-knobs';
|
||||
|
||||
import knobsWrapper from './Knobs';
|
||||
import Button from './Button';
|
||||
import CenterView from './CenterView';
|
||||
import Welcome from './Welcome';
|
||||
@ -27,3 +29,5 @@ storiesOf('Button', module)
|
||||
<Text>😀 😎 👍 💯</Text>
|
||||
</Button>
|
||||
);
|
||||
|
||||
storiesOf('Knobs', module).addDecorator(withKnobs).add('with knobs', knobsWrapper);
|
||||
|
@ -10,7 +10,7 @@
|
||||
"@storybook/addon-centered": "^3.2.1",
|
||||
"@storybook/addon-notes": "^3.2.0",
|
||||
"@storybook/addon-knobs": "^3.2.0",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"cross-env": "^3.0.0",
|
||||
|
@ -174,7 +174,7 @@ storiesOf('Addon Notes', module)
|
||||
.add(
|
||||
'Note with HTML',
|
||||
withNotes({
|
||||
notes: `
|
||||
text: `
|
||||
<h2>My notes on emojies</h2>
|
||||
|
||||
<em>It's not all that important to be honest, but..</em>
|
||||
|
@ -24,5 +24,5 @@
|
||||
"examples/*"
|
||||
],
|
||||
"concurrency": 1,
|
||||
"version": "3.2.8"
|
||||
"version": "3.2.9"
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export default class Channel {
|
||||
|
||||
_handleEvent(event) {
|
||||
const listeners = this._listeners[event.type];
|
||||
if (event.from !== this._sender && listeners) {
|
||||
if (listeners) {
|
||||
listeners.forEach(fn => fn(...event.args));
|
||||
}
|
||||
}
|
||||
|
@ -177,12 +177,12 @@ describe('Channel', () => {
|
||||
});
|
||||
|
||||
describe('_miscellaneous', () => {
|
||||
it('should ignore if event came from itself', () => {
|
||||
it('should not ignore if event came from itself', () => {
|
||||
const received = [];
|
||||
channel.on('type-1', n => received.push(n));
|
||||
channel._handleEvent({ type: 'type-1', args: [11] });
|
||||
channel._handleEvent({ type: 'type-1', args: [12], from: channel._sender });
|
||||
expect(received).toEqual([11]);
|
||||
expect(received).toEqual([11, 12]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ const chalk = require('chalk');
|
||||
const helpers = require('../../lib/helpers');
|
||||
|
||||
module.exports = Promise.all([
|
||||
latestVersion('@storybook/react'),
|
||||
latestVersion('@storybook/react-native'),
|
||||
latestVersion('@storybook/addon-actions'),
|
||||
latestVersion('@storybook/addon-links'),
|
||||
latestVersion('prop-types'),
|
||||
@ -33,7 +33,7 @@ module.exports = Promise.all([
|
||||
packageJson.dependencies = packageJson.dependencies || {};
|
||||
packageJson.devDependencies = packageJson.devDependencies || {};
|
||||
|
||||
packageJson.devDependencies['@storybook/react'] = `^${storybookVersion}`;
|
||||
packageJson.devDependencies['@storybook/react-native'] = `^${storybookVersion}`;
|
||||
packageJson.devDependencies['@storybook/addon-actions'] = `^${actionsVersion}`;
|
||||
packageJson.devDependencies['@storybook/addon-links'] = `^${linksVersion}`;
|
||||
|
||||
|
@ -4,7 +4,7 @@ const path = require('path');
|
||||
const latestVersion = require('latest-version');
|
||||
|
||||
module.exports = Promise.all([
|
||||
latestVersion('@storybook/react'),
|
||||
latestVersion('@storybook/react-native'),
|
||||
latestVersion('@storybook/addon-actions'),
|
||||
latestVersion('@storybook/addon-links'),
|
||||
latestVersion('prop-types'),
|
||||
@ -17,7 +17,7 @@ module.exports = Promise.all([
|
||||
packageJson.dependencies = packageJson.dependencies || {};
|
||||
packageJson.devDependencies = packageJson.devDependencies || {};
|
||||
|
||||
packageJson.devDependencies['@storybook/react'] = `^${storybookVersion}`;
|
||||
packageJson.devDependencies['@storybook/react-native'] = `^${storybookVersion}`;
|
||||
packageJson.devDependencies['@storybook/addon-actions'] = `^${actionsVersion}`;
|
||||
packageJson.devDependencies['@storybook/addon-links'] = `^${linksVersion}`;
|
||||
|
||||
|
@ -40,7 +40,8 @@ module.exports = function detect(options) {
|
||||
|
||||
if (
|
||||
(packageJson.dependencies && packageJson.dependencies.vue) ||
|
||||
(packageJson.devDependencies && packageJson.devDependencies.vue)
|
||||
(packageJson.devDependencies && packageJson.devDependencies.vue) ||
|
||||
(packageJson.devDependencies && packageJson.devDependencies.nuxt)
|
||||
) {
|
||||
return types.VUE;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/cli",
|
||||
"version": "3.2.8",
|
||||
"version": "3.2.9",
|
||||
"description": "Storybook's CLI - easiest method of adding storybook to your projects",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@ -25,9 +25,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/codemod": "^3.2.6",
|
||||
"chalk": "^2.0.1",
|
||||
"chalk": "^2.1.0",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"commander": "^2.9.0",
|
||||
"commander": "^2.11.0",
|
||||
"cross-spawn": "^5.0.1",
|
||||
"jscodeshift": "^0.3.30",
|
||||
"json5": "^0.5.1",
|
||||
|
@ -6,7 +6,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^7.2.2",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
|
@ -1,15 +1,15 @@
|
||||
import keycode from 'keycode';
|
||||
|
||||
export const features = {
|
||||
FULLSCREEN: 1,
|
||||
DOWN_PANEL: 2,
|
||||
LEFT_PANEL: 3,
|
||||
SHORTCUTS_HELP: 4,
|
||||
ESCAPE: 5,
|
||||
NEXT_STORY: 6,
|
||||
PREV_STORY: 7,
|
||||
SHOW_SEARCH: 8,
|
||||
DOWN_PANEL_IN_RIGHT: 9,
|
||||
FULLSCREEN: 'FULLSCREEN',
|
||||
ADDON_PANEL: 'ADDON_PANEL',
|
||||
STORIES_PANEL: 'STORIES_PANEL',
|
||||
SHORTCUTS_HELP: 'SHORTCUTS_HELP',
|
||||
ESCAPE: 'ESCAPE',
|
||||
NEXT_STORY: 'NEXT_STORY',
|
||||
PREV_STORY: 'PREV_STORY',
|
||||
SHOW_SEARCH: 'SHOW_SEARCH',
|
||||
ADDON_PANEL_IN_RIGHT: 'ADDON_PANEL_IN_RIGHT',
|
||||
};
|
||||
|
||||
export function isModifierPressed(e) {
|
||||
@ -42,10 +42,10 @@ export default function handle(e) {
|
||||
return features.FULLSCREEN;
|
||||
case keycode('D'):
|
||||
e.preventDefault();
|
||||
return features.DOWN_PANEL;
|
||||
return features.ADDON_PANEL;
|
||||
case keycode('L'):
|
||||
e.preventDefault();
|
||||
return features.LEFT_PANEL;
|
||||
return features.STORIES_PANEL;
|
||||
case keycode('right'):
|
||||
e.preventDefault();
|
||||
return features.NEXT_STORY;
|
||||
@ -57,7 +57,7 @@ export default function handle(e) {
|
||||
return features.SHOW_SEARCH;
|
||||
case keycode('J'):
|
||||
e.preventDefault();
|
||||
return features.DOWN_PANEL_IN_RIGHT;
|
||||
return features.ADDON_PANEL_IN_RIGHT;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ describe('manager.api.actions.api', () => {
|
||||
expect(stateUpdates.selectedAddonPanel).toEqual('storybooks/storybook-addon-knobs');
|
||||
});
|
||||
|
||||
it('should keep current downPanel and output panel IDs', () => {
|
||||
it('should keep current addonPanel and output panel IDs', () => {
|
||||
const clientStore = new MockClientStore();
|
||||
actions.setOptions({ clientStore, provider }, { selectedAddonPanel: null });
|
||||
|
||||
|
@ -2,23 +2,32 @@ import pick from 'lodash.pick';
|
||||
import { features } from '../../../libs/key_events';
|
||||
import apiActions from '../../api/actions';
|
||||
|
||||
const deprecationMessage = (oldName, newName) =>
|
||||
`The ${oldName} option has been renamed to ${newName} and will not be available in the next major Storybook release. Please update your config.`;
|
||||
|
||||
export function keyEventToOptions(currentOptions, event) {
|
||||
switch (event) {
|
||||
case features.FULLSCREEN:
|
||||
return { goFullScreen: !currentOptions.goFullScreen };
|
||||
case features.DOWN_PANEL:
|
||||
return { showDownPanel: !currentOptions.showDownPanel };
|
||||
case features.LEFT_PANEL:
|
||||
return { showLeftPanel: !currentOptions.showLeftPanel };
|
||||
case features.ADDON_PANEL:
|
||||
return { showAddonPanel: !currentOptions.showAddonPanel };
|
||||
case features.STORIES_PANEL:
|
||||
return { showStoriesPanel: !currentOptions.showStoriesPanel };
|
||||
case features.SHOW_SEARCH:
|
||||
return { showSearchBox: true };
|
||||
case features.DOWN_PANEL_IN_RIGHT:
|
||||
return { downPanelInRight: !currentOptions.downPanelInRight };
|
||||
case features.ADDON_PANEL_IN_RIGHT:
|
||||
return { addonPanelInRight: !currentOptions.addonPanelInRight };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const renamedOptions = {
|
||||
showLeftPanel: 'showStoriesPanel',
|
||||
showDownPanel: 'showAddonPanel',
|
||||
downPanelInRight: 'addonPanelInRight',
|
||||
};
|
||||
|
||||
export default {
|
||||
handleEvent(context, event) {
|
||||
const { clientStore } = context;
|
||||
@ -51,8 +60,26 @@ export default {
|
||||
...pick(options, Object.keys(state.shortcutOptions)),
|
||||
};
|
||||
|
||||
const withNewNames = Object.keys(renamedOptions).reduce((acc, oldName) => {
|
||||
const newName = renamedOptions[oldName];
|
||||
|
||||
if (oldName in options && !(newName in options)) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(deprecationMessage(oldName, newName));
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[newName]: options[oldName],
|
||||
};
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, updatedOptions);
|
||||
|
||||
return {
|
||||
shortcutOptions: updatedOptions,
|
||||
shortcutOptions: withNewNames,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
@ -35,5 +35,34 @@ describe('manager.shortcuts.actions.shortcuts', () => {
|
||||
shortcutOptions: { bbc: 50, abc: 10 },
|
||||
});
|
||||
});
|
||||
|
||||
test('should warn about deprecated option names', () => {
|
||||
const clientStore = new MockClientStore();
|
||||
const spy = jest.spyOn(console, 'warn');
|
||||
actions.setOptions(
|
||||
{ clientStore },
|
||||
{
|
||||
showLeftPanel: 1,
|
||||
showDownPanel: 2,
|
||||
downPanelInRight: 3,
|
||||
}
|
||||
);
|
||||
|
||||
const state = {
|
||||
shortcutOptions: {},
|
||||
};
|
||||
const stateUpdates = clientStore.updateCallback(state);
|
||||
expect(spy).toHaveBeenCalledTimes(3);
|
||||
expect(stateUpdates).toEqual({
|
||||
shortcutOptions: {
|
||||
showStoriesPanel: 1,
|
||||
showAddonPanel: 2,
|
||||
addonPanelInRight: 3,
|
||||
},
|
||||
});
|
||||
|
||||
spy.mockReset();
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,10 +5,10 @@ export default {
|
||||
defaultState: {
|
||||
shortcutOptions: {
|
||||
goFullScreen: false,
|
||||
showLeftPanel: true,
|
||||
showDownPanel: true,
|
||||
showStoriesPanel: true,
|
||||
showAddonPanel: true,
|
||||
showSearchBox: false,
|
||||
downPanelInRight: false,
|
||||
addonPanelInRight: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ export default {
|
||||
clientStore.toggle('showShortcutsHelp');
|
||||
},
|
||||
|
||||
selectDownPanel({ clientStore }, panelName) {
|
||||
selectAddonPanel({ clientStore }, panelName) {
|
||||
clientStore.set('selectedAddonPanel', panelName);
|
||||
},
|
||||
};
|
||||
|
@ -24,13 +24,13 @@ describe('manager.ui.actions.ui', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectDownPanel', () => {
|
||||
describe('selectAddonPanel', () => {
|
||||
it('should set the given panel name', () => {
|
||||
const clientStore = {
|
||||
set: jest.fn(),
|
||||
};
|
||||
const panelName = 'kkkind';
|
||||
actions.selectDownPanel({ clientStore }, panelName);
|
||||
actions.selectAddonPanel({ clientStore }, panelName);
|
||||
|
||||
expect(clientStore.set).toHaveBeenCalledWith('selectedAddonPanel', panelName);
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import style from './style';
|
||||
|
||||
class DownPanel extends Component {
|
||||
class AddonPanel extends Component {
|
||||
renderTab(name, panel) {
|
||||
let tabStyle = style.tablink;
|
||||
if (this.props.selectedPanel === name) {
|
||||
@ -69,16 +69,16 @@ class DownPanel extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
DownPanel.defaultProps = {
|
||||
AddonPanel.defaultProps = {
|
||||
panels: {},
|
||||
onPanelSelect: () => {},
|
||||
selectedPanel: null,
|
||||
};
|
||||
|
||||
DownPanel.propTypes = {
|
||||
AddonPanel.propTypes = {
|
||||
panels: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
onPanelSelect: PropTypes.func,
|
||||
selectedPanel: PropTypes.string,
|
||||
};
|
||||
|
||||
export default DownPanel;
|
||||
export default AddonPanel;
|
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import DownPanel from './index';
|
||||
import AddonPanel from './index';
|
||||
|
||||
describe('manager.ui.components.down_panel.index', () => {
|
||||
describe('manager.ui.components.addon_panel.index', () => {
|
||||
test('should render only the selected panel with display set other than "none"', () => {
|
||||
const panels = {
|
||||
test1: {
|
||||
@ -20,7 +20,7 @@ describe('manager.ui.components.down_panel.index', () => {
|
||||
const onPanelSelect = () => 'onPanelSelect';
|
||||
|
||||
const wrapper = shallow(
|
||||
<DownPanel panels={panels} onPanelSelect={onPanelSelect} selectedPanel={'test2'} />
|
||||
<AddonPanel panels={panels} onPanelSelect={onPanelSelect} selectedPanel={'test2'} />
|
||||
);
|
||||
|
||||
expect(wrapper.find('#test1').parent()).toHaveStyle('display', 'none');
|
||||
@ -38,7 +38,7 @@ describe('manager.ui.components.down_panel.index', () => {
|
||||
const onPanelSelect = jest.fn();
|
||||
const preventDefault = jest.fn();
|
||||
const wrapper = shallow(
|
||||
<DownPanel panels={panels} onPanelSelect={onPanelSelect} selectedPanel={'test1'} />
|
||||
<AddonPanel panels={panels} onPanelSelect={onPanelSelect} selectedPanel={'test1'} />
|
||||
);
|
||||
wrapper.find('a').simulate('click', { preventDefault });
|
||||
|
||||
@ -50,7 +50,7 @@ describe('manager.ui.components.down_panel.index', () => {
|
||||
test('should render "no panels available"', () => {
|
||||
const panels = {};
|
||||
const onPanelSelect = () => 'onPanelSelect';
|
||||
const wrapper = shallow(<DownPanel panels={panels} onPanelSelect={onPanelSelect} />);
|
||||
const wrapper = shallow(<AddonPanel panels={panels} onPanelSelect={onPanelSelect} />);
|
||||
|
||||
expect(wrapper.contains('no panels available')).toBe(true);
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import { document, localStorage, window } from 'global';
|
||||
import { localStorage, window } from 'global';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import SplitPane from 'react-split-pane';
|
||||
@ -11,48 +11,48 @@ const rootStyle = {
|
||||
backgroundColor: '#F7F7F7',
|
||||
};
|
||||
|
||||
const leftPanelStyle = leftPanelOnTop => ({
|
||||
const storiesPanelStyle = storiesPanelOnTop => ({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: leftPanelOnTop ? 'column' : 'row',
|
||||
flexDirection: storiesPanelOnTop ? 'column' : 'row',
|
||||
alignItems: 'stretch',
|
||||
paddingRight: leftPanelOnTop ? 10 : 0,
|
||||
paddingRight: storiesPanelOnTop ? 10 : 0,
|
||||
});
|
||||
|
||||
const downPanelStyle = downPanelInRight => ({
|
||||
const addonPanelStyle = addonPanelInRight => ({
|
||||
display: 'flex',
|
||||
flexDirection: downPanelInRight ? 'row' : 'column',
|
||||
flexDirection: addonPanelInRight ? 'row' : 'column',
|
||||
alignItems: 'stretch',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: downPanelInRight ? '5px 10px 10px 0' : '0px 10px 10px 0',
|
||||
padding: addonPanelInRight ? '5px 10px 10px 0' : '0px 10px 10px 0',
|
||||
boxSizing: 'border-box',
|
||||
});
|
||||
|
||||
const resizerCursor = isVert => (isVert ? 'col-resize' : 'row-resize');
|
||||
|
||||
const storiesResizerStyle = (showLeftPanel, leftPanelOnTop) => ({
|
||||
cursor: showLeftPanel ? resizerCursor(!leftPanelOnTop) : undefined,
|
||||
height: leftPanelOnTop ? 10 : 'auto',
|
||||
width: leftPanelOnTop ? '100%' : 10,
|
||||
const storiesResizerStyle = (showStoriesPanel, storiesPanelOnTop) => ({
|
||||
cursor: showStoriesPanel ? resizerCursor(!storiesPanelOnTop) : undefined,
|
||||
height: storiesPanelOnTop ? 10 : 'auto',
|
||||
width: storiesPanelOnTop ? '100%' : 10,
|
||||
zIndex: 1,
|
||||
});
|
||||
|
||||
const addonResizerStyle = (showDownPanel, downPanelInRight) => ({
|
||||
cursor: showDownPanel ? resizerCursor(downPanelInRight) : undefined,
|
||||
height: downPanelInRight ? '100%' : 10,
|
||||
width: downPanelInRight ? 10 : '100%',
|
||||
const addonResizerStyle = (showAddonPanel, addonPanelInRight) => ({
|
||||
cursor: showAddonPanel ? resizerCursor(addonPanelInRight) : undefined,
|
||||
height: addonPanelInRight ? '100%' : 10,
|
||||
width: addonPanelInRight ? 10 : '100%',
|
||||
zIndex: 1,
|
||||
});
|
||||
|
||||
const contentPanelStyle = (downPanelInRight, leftPanelOnTop) => ({
|
||||
const contentPanelStyle = (addonPanelInRight, storiesPanelOnTop) => ({
|
||||
position: 'absolute',
|
||||
boxSizing: 'border-box',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: downPanelInRight ? '10px 2px 10px 0' : '10px 10px 2px 0',
|
||||
paddingTop: leftPanelOnTop ? 0 : 10,
|
||||
padding: addonPanelInRight ? '10px 2px 10px 0' : '10px 10px 2px 0',
|
||||
paddingTop: storiesPanelOnTop ? 0 : 10,
|
||||
});
|
||||
|
||||
const normalPreviewStyle = {
|
||||
@ -78,6 +78,15 @@ const fullScreenPreviewStyle = {
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
const overlayStyle = isDragging => ({
|
||||
display: isDragging ? 'block' : 'none',
|
||||
position: 'absolute',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px',
|
||||
});
|
||||
|
||||
const defaultSizes = {
|
||||
addonPanel: {
|
||||
down: 200,
|
||||
@ -89,10 +98,6 @@ const defaultSizes = {
|
||||
},
|
||||
};
|
||||
|
||||
const onDragStart = () => document.body.classList.add('dragging');
|
||||
|
||||
const onDragEnd = () => document.body.classList.remove('dragging');
|
||||
|
||||
const saveSizes = sizes => {
|
||||
try {
|
||||
localStorage.setItem('panelSizes', JSON.stringify(sizes));
|
||||
@ -129,9 +134,12 @@ class Layout extends React.Component {
|
||||
height: 0,
|
||||
width: 0,
|
||||
},
|
||||
isDragging: false,
|
||||
};
|
||||
|
||||
this.onResize = this.onResize.bind(this);
|
||||
this.onDragStart = this.onDragStart.bind(this);
|
||||
this.onDragEnd = this.onDragEnd.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -142,6 +150,14 @@ class Layout extends React.Component {
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
}
|
||||
|
||||
onDragStart() {
|
||||
this.setState({ isDragging: true });
|
||||
}
|
||||
|
||||
onDragEnd() {
|
||||
this.setState({ isDragging: false });
|
||||
}
|
||||
|
||||
onResize(pane, mode) {
|
||||
return size => {
|
||||
this.layerSizes[pane][mode] = size;
|
||||
@ -161,16 +177,16 @@ class Layout extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
goFullScreen,
|
||||
showLeftPanel,
|
||||
showDownPanel,
|
||||
downPanelInRight,
|
||||
downPanel,
|
||||
leftPanel,
|
||||
showStoriesPanel,
|
||||
showAddonPanel,
|
||||
addonPanelInRight,
|
||||
addonPanel,
|
||||
storiesPanel,
|
||||
preview,
|
||||
} = this.props;
|
||||
const { previewPanelDimensions } = this.state;
|
||||
|
||||
const leftPanelOnTop = false;
|
||||
const storiesPanelOnTop = false;
|
||||
|
||||
let previewStyle = normalPreviewStyle;
|
||||
|
||||
@ -180,32 +196,36 @@ class Layout extends React.Component {
|
||||
|
||||
const sizes = getSavedSizes(this.layerSizes);
|
||||
|
||||
const leftPanelDefaultSize = !leftPanelOnTop ? sizes.storiesPanel.left : sizes.storiesPanel.top;
|
||||
const downPanelDefaultSize = !downPanelInRight ? sizes.addonPanel.down : sizes.addonPanel.right;
|
||||
const storiesPanelDefaultSize = !storiesPanelOnTop
|
||||
? sizes.storiesPanel.left
|
||||
: sizes.storiesPanel.top;
|
||||
const addonPanelDefaultSize = !addonPanelInRight
|
||||
? sizes.addonPanel.down
|
||||
: sizes.addonPanel.right;
|
||||
|
||||
const addonSplit = downPanelInRight ? 'vertical' : 'horizontal';
|
||||
const storiesSplit = leftPanelOnTop ? 'horizontal' : 'vertical';
|
||||
const addonSplit = addonPanelInRight ? 'vertical' : 'horizontal';
|
||||
const storiesSplit = storiesPanelOnTop ? 'horizontal' : 'vertical';
|
||||
|
||||
return (
|
||||
<div style={rootStyle}>
|
||||
<SplitPane
|
||||
split={storiesSplit}
|
||||
allowResize={showLeftPanel}
|
||||
allowResize={showStoriesPanel}
|
||||
minSize={150}
|
||||
maxSize={-400}
|
||||
size={showLeftPanel ? leftPanelDefaultSize : 1}
|
||||
defaultSize={leftPanelDefaultSize}
|
||||
resizerStyle={storiesResizerStyle(showLeftPanel, leftPanelOnTop)}
|
||||
onDragStarted={onDragStart}
|
||||
onDragFinished={onDragEnd}
|
||||
onChange={this.onResize('storiesPanel', leftPanelOnTop ? 'top' : 'left')}
|
||||
size={showStoriesPanel ? storiesPanelDefaultSize : 1}
|
||||
defaultSize={storiesPanelDefaultSize}
|
||||
resizerStyle={storiesResizerStyle(showStoriesPanel, storiesPanelOnTop)}
|
||||
onDragStarted={this.onDragStart}
|
||||
onDragFinished={this.onDragEnd}
|
||||
onChange={this.onResize('storiesPanel', storiesPanelOnTop ? 'top' : 'left')}
|
||||
>
|
||||
{conditionalRender(
|
||||
showLeftPanel,
|
||||
showStoriesPanel,
|
||||
() =>
|
||||
<div style={leftPanelStyle(leftPanelOnTop)}>
|
||||
<div style={storiesPanelStyle(storiesPanelOnTop)}>
|
||||
<div style={{ flexGrow: 1, height: '100%', width: '100%' }}>
|
||||
{leftPanel()}
|
||||
{storiesPanel()}
|
||||
</div>
|
||||
<USplit shift={5} split={storiesSplit} />
|
||||
</div>,
|
||||
@ -214,18 +234,24 @@ class Layout extends React.Component {
|
||||
|
||||
<SplitPane
|
||||
split={addonSplit}
|
||||
allowResize={showDownPanel}
|
||||
allowResize={showAddonPanel}
|
||||
primary="second"
|
||||
minSize={downPanelInRight ? 200 : 100}
|
||||
minSize={addonPanelInRight ? 200 : 100}
|
||||
maxSize={-200}
|
||||
size={showDownPanel ? downPanelDefaultSize : 1}
|
||||
defaultSize={downPanelDefaultSize}
|
||||
resizerStyle={addonResizerStyle(showDownPanel, downPanelInRight)}
|
||||
onDragStarted={onDragStart}
|
||||
onDragFinished={onDragEnd}
|
||||
onChange={this.onResize('addonPanel', downPanelInRight ? 'right' : 'down')}
|
||||
size={showAddonPanel ? addonPanelDefaultSize : 1}
|
||||
defaultSize={addonPanelDefaultSize}
|
||||
resizerStyle={addonResizerStyle(showAddonPanel, addonPanelInRight)}
|
||||
onDragStarted={this.onDragStart}
|
||||
onDragFinished={this.onDragEnd}
|
||||
onChange={this.onResize('addonPanel', addonPanelInRight ? 'right' : 'down')}
|
||||
>
|
||||
<div style={contentPanelStyle(downPanelInRight, leftPanelOnTop)}>
|
||||
<div style={contentPanelStyle(addonPanelInRight, storiesPanelOnTop)}>
|
||||
{/*
|
||||
When resizing panels, the drag event breaks if the cursor
|
||||
moves over the iframe. Show an overlay div over iframe
|
||||
at drag start and hide it when the drag ends.
|
||||
*/}
|
||||
<div style={overlayStyle(this.state.isDragging)} />
|
||||
<div
|
||||
style={previewStyle}
|
||||
ref={ref => {
|
||||
@ -237,11 +263,11 @@ class Layout extends React.Component {
|
||||
<Dimensions {...previewPanelDimensions} />
|
||||
</div>
|
||||
{conditionalRender(
|
||||
showDownPanel,
|
||||
showAddonPanel,
|
||||
() =>
|
||||
<div style={downPanelStyle(downPanelInRight)}>
|
||||
<div style={addonPanelStyle(addonPanelInRight)}>
|
||||
<USplit shift={-5} split={addonSplit} />
|
||||
{downPanel()}
|
||||
{addonPanel()}
|
||||
</div>,
|
||||
() => <span />
|
||||
)}
|
||||
@ -253,13 +279,13 @@ class Layout extends React.Component {
|
||||
}
|
||||
|
||||
Layout.propTypes = {
|
||||
showLeftPanel: PropTypes.bool.isRequired,
|
||||
showDownPanel: PropTypes.bool.isRequired,
|
||||
showStoriesPanel: PropTypes.bool.isRequired,
|
||||
showAddonPanel: PropTypes.bool.isRequired,
|
||||
goFullScreen: PropTypes.bool.isRequired,
|
||||
leftPanel: PropTypes.func.isRequired,
|
||||
storiesPanel: PropTypes.func.isRequired,
|
||||
preview: PropTypes.func.isRequired,
|
||||
downPanel: PropTypes.func.isRequired,
|
||||
downPanelInRight: PropTypes.bool.isRequired,
|
||||
addonPanel: PropTypes.func.isRequired,
|
||||
addonPanelInRight: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
|
@ -7,18 +7,19 @@ describe('manager.ui.components.layout.index', () => {
|
||||
test('should render provided components', () => {
|
||||
const wrap = shallow(
|
||||
<Layout
|
||||
showLeftPanel
|
||||
showDownPanel
|
||||
showStoriesPanel
|
||||
showAddonPanel
|
||||
goFullScreen={false}
|
||||
leftPanel={() => 'LeftPanel'}
|
||||
downPanel={() => 'DownPanel'}
|
||||
addonPanelInRight={false}
|
||||
storiesPanel={() => 'StoriesPanel'}
|
||||
addonPanel={() => 'AddonPanel'}
|
||||
preview={() => 'Preview'}
|
||||
/>
|
||||
);
|
||||
|
||||
const markup = wrap.html();
|
||||
expect(markup).toMatch(/LeftPanel/);
|
||||
expect(markup).toMatch(/DownPanel/);
|
||||
expect(markup).toMatch(/StoriesPanel/);
|
||||
expect(markup).toMatch(/AddonPanel/);
|
||||
expect(markup).toMatch(/Preview/);
|
||||
});
|
||||
});
|
||||
@ -27,56 +28,61 @@ describe('manager.ui.components.layout.index', () => {
|
||||
test('should only render preview', () => {
|
||||
const wrap = shallow(
|
||||
<Layout
|
||||
showStoriesPanel={false}
|
||||
showAddonPanel={false}
|
||||
goFullScreen
|
||||
leftPanel={() => 'LeftPanel'}
|
||||
downPanel={() => 'DownPanel'}
|
||||
addonPanelInRight={false}
|
||||
storiesPanel={() => 'StoriesPanel'}
|
||||
addonPanel={() => 'AddonPanel'}
|
||||
preview={() => 'Preview'}
|
||||
/>
|
||||
);
|
||||
|
||||
const markup = wrap.html();
|
||||
expect(markup).not.toMatch(/LeftPanel/);
|
||||
expect(markup).not.toMatch(/DownPanel/);
|
||||
expect(markup).not.toMatch(/StoriesPanel/);
|
||||
expect(markup).not.toMatch(/AddonPanel/);
|
||||
expect(markup).toMatch(/Preview/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with showLeftPanel=false', () => {
|
||||
test('should hide the leftPanel', () => {
|
||||
describe('with showStoriesPanel=false', () => {
|
||||
test('should hide the storiesPanel', () => {
|
||||
const wrap = shallow(
|
||||
<Layout
|
||||
showLeftPanel={false}
|
||||
showDownPanel
|
||||
showStoriesPanel={false}
|
||||
showAddonPanel
|
||||
goFullScreen={false}
|
||||
leftPanel={() => 'LeftPanel'}
|
||||
downPanel={() => 'DownPanel'}
|
||||
addonPanelInRight={false}
|
||||
storiesPanel={() => 'StoriesPanel'}
|
||||
addonPanel={() => 'AddonPanel'}
|
||||
preview={() => 'Preview'}
|
||||
/>
|
||||
);
|
||||
|
||||
const markup = wrap.html();
|
||||
expect(markup).not.toMatch(/LeftPanel/);
|
||||
expect(markup).toMatch(/DownPanel/);
|
||||
expect(markup).not.toMatch(/StoriesPanel/);
|
||||
expect(markup).toMatch(/AddonPanel/);
|
||||
expect(markup).toMatch(/Preview/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with showDownPanel=false', () => {
|
||||
test('should hide the downPanel', () => {
|
||||
describe('with showAddonPanel=false', () => {
|
||||
test('should hide the addonPanel', () => {
|
||||
const wrap = shallow(
|
||||
<Layout
|
||||
showLeftPanel
|
||||
showDownPanel={false}
|
||||
showStoriesPanel
|
||||
showAddonPanel={false}
|
||||
goFullScreen={false}
|
||||
leftPanel={() => 'LeftPanel'}
|
||||
downPanel={() => 'DownPanel'}
|
||||
addonPanelInRight={false}
|
||||
storiesPanel={() => 'StoriesPanel'}
|
||||
addonPanel={() => 'AddonPanel'}
|
||||
preview={() => 'Preview'}
|
||||
/>
|
||||
);
|
||||
|
||||
const markup = wrap.html();
|
||||
expect(markup).toMatch(/LeftPanel/);
|
||||
expect(markup).not.toMatch(/DownPanel/);
|
||||
expect(markup).toMatch(/StoriesPanel/);
|
||||
expect(markup).not.toMatch(/AddonPanel/);
|
||||
expect(markup).toMatch(/Preview/);
|
||||
});
|
||||
});
|
||||
|
@ -40,13 +40,12 @@ const spanStyle = {
|
||||
}),
|
||||
};
|
||||
|
||||
//eslint-disable-next-line
|
||||
const USplit = ({ shift, split }) =>
|
||||
<div style={wrapStyle[split](shift)}>
|
||||
<span style={spanStyle[split]()} />
|
||||
</div>;
|
||||
|
||||
USplit.PropTypes = {
|
||||
USplit.propTypes = {
|
||||
shift: PropTypes.number,
|
||||
split: PropTypes.oneOf(['vertical', 'horizontal']),
|
||||
};
|
||||
|
@ -7,7 +7,11 @@ const keyCodeEnter = 13;
|
||||
describe('manager.ui.components.menu_item', () => {
|
||||
describe('render', () => {
|
||||
test('should use "a" tag', () => {
|
||||
const wrap = shallow(<MenuItem title="title">Content</MenuItem>);
|
||||
const wrap = shallow(
|
||||
<MenuItem title="title" onClick={() => undefined}>
|
||||
Content
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrap.matchesElement(
|
||||
@ -25,7 +29,7 @@ describe('manager.ui.components.menu_item', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
onClick = jest.fn();
|
||||
wrap = shallow(<MenuItem onClick={onClick} />);
|
||||
wrap = shallow(<MenuItem onClick={onClick}>Content</MenuItem>);
|
||||
});
|
||||
|
||||
test('should call onClick on a click', () => {
|
||||
|
@ -6,9 +6,14 @@ import FuzzySearch from 'react-fuzzy';
|
||||
import SearchBox from './search_box';
|
||||
|
||||
describe('manager.ui.components.search_box', () => {
|
||||
const defaultProps = {
|
||||
showSearchBox: false,
|
||||
onSelectStory: () => undefined,
|
||||
onClose: () => undefined,
|
||||
};
|
||||
describe('render', () => {
|
||||
test('should render FuzzySearch inside ReactModal', () => {
|
||||
const wrap = shallow(<SearchBox showSearchBox />);
|
||||
const wrap = shallow(<SearchBox {...defaultProps} showSearchBox />);
|
||||
|
||||
const modal = wrap.find(ReactModal);
|
||||
expect(modal).toBePresent();
|
||||
@ -28,7 +33,7 @@ describe('manager.ui.components.search_box', () => {
|
||||
stories: ['b', 'c'],
|
||||
},
|
||||
];
|
||||
const wrap = shallow(<SearchBox stories={stories} />);
|
||||
const wrap = shallow(<SearchBox {...defaultProps} stories={stories} />);
|
||||
const search = wrap.find(FuzzySearch);
|
||||
|
||||
const expectedList = [
|
||||
@ -57,7 +62,7 @@ describe('manager.ui.components.search_box', () => {
|
||||
describe('events', () => {
|
||||
test('should call the onClose prop when modal requests it', () => {
|
||||
const onClose = jest.fn();
|
||||
const wrap = shallow(<SearchBox onClose={onClose} />);
|
||||
const wrap = shallow(<SearchBox {...defaultProps} onClose={onClose} />);
|
||||
|
||||
const modal = wrap.find(ReactModal);
|
||||
modal.simulate('requestClose');
|
||||
@ -68,7 +73,9 @@ describe('manager.ui.components.search_box', () => {
|
||||
test('should handle selecting a kind', () => {
|
||||
const onSelectStory = jest.fn();
|
||||
const onClose = jest.fn();
|
||||
const wrap = shallow(<SearchBox onSelectStory={onSelectStory} onClose={onClose} />);
|
||||
const wrap = shallow(
|
||||
<SearchBox {...defaultProps} onSelectStory={onSelectStory} onClose={onClose} />
|
||||
);
|
||||
|
||||
const modal = wrap.find(FuzzySearch);
|
||||
modal.simulate('select', {
|
||||
@ -83,7 +90,9 @@ describe('manager.ui.components.search_box', () => {
|
||||
test('should handle selecting a story', () => {
|
||||
const onSelectStory = jest.fn();
|
||||
const onClose = jest.fn();
|
||||
const wrap = shallow(<SearchBox onSelectStory={onSelectStory} onClose={onClose} />);
|
||||
const wrap = shallow(
|
||||
<SearchBox {...defaultProps} onSelectStory={onSelectStory} onClose={onClose} />
|
||||
);
|
||||
|
||||
const modal = wrap.find(FuzzySearch);
|
||||
modal.simulate('select', {
|
||||
|
@ -40,10 +40,10 @@ export function getShortcuts(platform) {
|
||||
if (platform && platform.indexOf('mac') !== -1) {
|
||||
return [
|
||||
{ name: 'Show Search Box', keys: ['⌘ ⇧ P', '⌃ ⇧ P'] },
|
||||
{ name: 'Toggle Action Logger position', keys: ['⌘ ⇧ J', '⌃ ⇧ J'] },
|
||||
{ name: 'Toggle Addon panel position', keys: ['⌘ ⇧ J', '⌃ ⇧ J'] },
|
||||
{ name: 'Toggle Fullscreen Mode', keys: ['⌘ ⇧ F', '⌃ ⇧ F'] },
|
||||
{ name: 'Toggle Left Panel', keys: ['⌘ ⇧ L', '⌃ ⇧ L'] },
|
||||
{ name: 'Toggle Down Panel', keys: ['⌘ ⇧ D', '⌃ ⇧ D'] },
|
||||
{ name: 'Toggle Stories Panel', keys: ['⌘ ⇧ L', '⌃ ⇧ L'] },
|
||||
{ name: 'Toggle Addon panel', keys: ['⌘ ⇧ D', '⌃ ⇧ D'] },
|
||||
{ name: 'Next Story', keys: ['⌘ ⇧ →', '⌃ ⇧ →'] },
|
||||
{ name: 'Previous Story', keys: ['⌘ ⇧ ←', '⌃ ⇧ ←'] },
|
||||
];
|
||||
@ -51,10 +51,10 @@ export function getShortcuts(platform) {
|
||||
|
||||
return [
|
||||
{ name: 'Show Search Box', keys: ['Ctrl + Shift + P'] },
|
||||
{ name: 'Toggle Action Logger position', keys: ['Ctrl + Shift + J'] },
|
||||
{ name: 'Toggle Addon panel position', keys: ['Ctrl + Shift + J'] },
|
||||
{ name: 'Toggle Fullscreen Mode', keys: ['Ctrl + Shift + F'] },
|
||||
{ name: 'Toggle Left Panel', keys: ['Ctrl + Shift + L'] },
|
||||
{ name: 'Toggle Down Panel', keys: ['Ctrl + Shift + D'] },
|
||||
{ name: 'Toggle Stories Panel', keys: ['Ctrl + Shift + L'] },
|
||||
{ name: 'Toggle Addon panel', keys: ['Ctrl + Shift + D'] },
|
||||
{ name: 'Next Story', keys: ['Ctrl + Shift + →'] },
|
||||
{ name: 'Previous Story', keys: ['Ctrl + Shift + ←'] },
|
||||
];
|
||||
|
@ -5,6 +5,8 @@ import { baseFonts } from '@storybook/components';
|
||||
const wrapperStyle = {
|
||||
background: '#F7F7F7',
|
||||
marginBottom: 10,
|
||||
display: 'flex',
|
||||
height: 27,
|
||||
};
|
||||
|
||||
const headingStyle = {
|
||||
@ -14,50 +16,49 @@ const headingStyle = {
|
||||
fontSize: '12px',
|
||||
fontWeight: 'bolder',
|
||||
color: '#828282',
|
||||
border: '1px solid #C1C1C1',
|
||||
textAlign: 'center',
|
||||
borderRadius: '2px',
|
||||
padding: '5px',
|
||||
cursor: 'pointer',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
float: 'none',
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
const shortcutIconStyle = {
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '3.5px',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bolder',
|
||||
color: 'rgb(130, 130, 130)',
|
||||
border: '1px solid rgb(193, 193, 193)',
|
||||
textAlign: 'center',
|
||||
borderRadius: 2,
|
||||
padding: 5,
|
||||
cursor: 'pointer',
|
||||
margin: 0,
|
||||
display: 'inlineBlock',
|
||||
paddingLeft: 8,
|
||||
float: 'right',
|
||||
marginLeft: 5,
|
||||
padding: 0,
|
||||
margin: '0 0 0 5px',
|
||||
backgroundColor: 'inherit',
|
||||
outline: 0,
|
||||
width: 30,
|
||||
};
|
||||
|
||||
const linkStyle = {
|
||||
textDecoration: 'none',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px solid rgb(193, 193, 193)',
|
||||
borderRadius: 2,
|
||||
};
|
||||
|
||||
const Header = ({ openShortcutsHelp, name, url }) =>
|
||||
<div style={wrapperStyle}>
|
||||
<button style={shortcutIconStyle} onClick={openShortcutsHelp}>
|
||||
⌘
|
||||
</button>
|
||||
<a style={linkStyle} href={url} target="_blank" rel="noopener noreferrer">
|
||||
<h3 style={headingStyle}>
|
||||
{name}
|
||||
</h3>
|
||||
</a>
|
||||
<button style={shortcutIconStyle} onClick={openShortcutsHelp}>
|
||||
⌘
|
||||
</button>
|
||||
</div>;
|
||||
|
||||
Header.defaultProps = {
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import Header from './header';
|
||||
|
||||
describe('manager.ui.components.left_panel.header', () => {
|
||||
describe('manager.ui.components.stories_panel.header', () => {
|
||||
test('should fire openShortcutsHelp when clicked on shortcut button', () => {
|
||||
const openShortcutsHelp = jest.fn();
|
||||
const wrap = shallow(<Header openShortcutsHelp={openShortcutsHelp} />);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user