mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-20 05:02:37 +08:00
Merge branch 'master' into add-events-addon
This commit is contained in:
commit
925c4d704d
@ -2,7 +2,14 @@ dist
|
||||
build
|
||||
coverage
|
||||
node_modules
|
||||
**/example/**
|
||||
**/demo/**
|
||||
docs/public
|
||||
|
||||
*.bundle.js
|
||||
*.js.map
|
||||
|
||||
!.remarkrc.js
|
||||
!.eslintrc.js
|
||||
!.eslintrc-markdown.js
|
||||
!.jest.config.js
|
||||
|
56
.eslintrc-markdown.js
Normal file
56
.eslintrc-markdown.js
Normal file
@ -0,0 +1,56 @@
|
||||
const error = 2;
|
||||
const warn = 1;
|
||||
const ignore = 0;
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['eslint-config-airbnb', 'plugin:jest/recommended', 'prettier'],
|
||||
plugins: ['prettier', 'jest', 'react'],
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
'jest/globals': true,
|
||||
},
|
||||
globals: {
|
||||
storiesOf: true,
|
||||
addonAPI: true,
|
||||
__DEV__: true,
|
||||
fetch: true,
|
||||
},
|
||||
rules: {
|
||||
strict: [error, 'never'],
|
||||
'prettier/prettier': [
|
||||
warn,
|
||||
{
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
trailingComma: 'es5',
|
||||
singleQuote: true,
|
||||
},
|
||||
],
|
||||
'no-console': ignore,
|
||||
'global-require': ignore,
|
||||
quotes: [warn, 'single'],
|
||||
'no-unused-vars': ignore,
|
||||
'class-methods-use-this': ignore,
|
||||
'arrow-parens': [warn, 'as-needed'],
|
||||
'space-before-function-paren': ignore,
|
||||
'import/no-unresolved': ignore,
|
||||
'import/extensions': ignore,
|
||||
'import/no-extraneous-dependencies': ignore,
|
||||
'import/prefer-default-export': ignore,
|
||||
'react/prop-types': ignore,
|
||||
'react/jsx-wrap-multilines': ignore,
|
||||
'react/jsx-uses-react': error,
|
||||
'react/jsx-uses-vars': error,
|
||||
'react/react-in-jsx-scope': ignore,
|
||||
'react/jsx-filename-extension': ignore,
|
||||
'jsx-a11y/accessible-emoji': ignore,
|
||||
'react/no-unescaped-entities': ignore,
|
||||
},
|
||||
};
|
73
.eslintrc.js
73
.eslintrc.js
@ -1,11 +1,11 @@
|
||||
const error = 2;
|
||||
const warn = 1;
|
||||
const ignore = 0;
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'./node_modules/eslint-config-airbnb-base/rules/es6.js',
|
||||
],
|
||||
plugins: [
|
||||
'prettier',
|
||||
],
|
||||
extends: ['eslint-config-airbnb', 'plugin:jest/recommended', 'prettier'],
|
||||
plugins: ['prettier', 'jest', 'react', 'json'],
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
@ -13,17 +13,56 @@ module.exports = {
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
'jest/globals': true,
|
||||
},
|
||||
rules: {
|
||||
strict: 0,
|
||||
'prettier/prettier': ['warn', {
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
trailingComma: 'es5',
|
||||
singleQuote: true,
|
||||
}],
|
||||
quotes: ['warn', 'single'],
|
||||
'arrow-parens': ['warn', 'as-needed'],
|
||||
strict: [error, 'never'],
|
||||
'prettier/prettier': [
|
||||
warn,
|
||||
{
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
trailingComma: 'es5',
|
||||
singleQuote: true,
|
||||
},
|
||||
],
|
||||
quotes: [warn, 'single'],
|
||||
'class-methods-use-this': ignore,
|
||||
'arrow-parens': [warn, 'as-needed'],
|
||||
'space-before-function-paren': ignore,
|
||||
'import/no-unresolved': warn,
|
||||
'import/extensions': [
|
||||
warn,
|
||||
{
|
||||
js: 'never',
|
||||
json: 'always',
|
||||
},
|
||||
],
|
||||
'import/no-extraneous-dependencies': [
|
||||
warn,
|
||||
{
|
||||
devDependencies: [
|
||||
'**/*.test.js',
|
||||
'**/scripts/*.js',
|
||||
'**/stories/*.js',
|
||||
'**/__tests__/*.js',
|
||||
],
|
||||
peerDependencies: true,
|
||||
},
|
||||
],
|
||||
'import/prefer-default-export': ignore,
|
||||
'react/jsx-wrap-multilines': ignore,
|
||||
'react/jsx-uses-react': error,
|
||||
'react/jsx-uses-vars': error,
|
||||
'react/react-in-jsx-scope': error,
|
||||
'react/jsx-filename-extension': [
|
||||
warn,
|
||||
{
|
||||
extensions: ['.js', '.jsx'],
|
||||
},
|
||||
],
|
||||
'jsx-a11y/accessible-emoji': ignore,
|
||||
'react/no-unescaped-entities': ignore,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
16
.remarkrc
16
.remarkrc
@ -1,16 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"remark-preset-lint-recommended",
|
||||
["remark-lint-list-item-indent", false],
|
||||
["remark-lint-code", {"js": {
|
||||
"module": "node_modules/remark-lint-code-eslint",
|
||||
"options": {
|
||||
"fix": true
|
||||
}
|
||||
}}],
|
||||
["remark-toc", {
|
||||
"tight": true,
|
||||
"maxDepth": 3
|
||||
}]
|
||||
]
|
||||
}
|
25
.remarkrc.js
Normal file
25
.remarkrc.js
Normal file
@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'remark-preset-lint-recommended',
|
||||
['remark-lint-list-item-indent', false],
|
||||
[
|
||||
'remark-lint-code',
|
||||
{
|
||||
js: {
|
||||
module: 'node_modules/remark-lint-code-eslint',
|
||||
options: {
|
||||
fix: true,
|
||||
configFile: '.eslintrc-markdown.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'remark-toc',
|
||||
{
|
||||
tight: true,
|
||||
maxDepth: 3,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
38
CHANGELOG.md
38
CHANGELOG.md
@ -1,3 +1,33 @@
|
||||
# 3.0.1
|
||||
|
||||
Minor bug fixes and documentation updates post 3.0.0 release.
|
||||
|
||||
2017-June-06
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
- Added error message for `addon-options` [#1194](https://github.com/storybooks/storybook/pull/1194)
|
||||
- Fix(react-native) add missing `ws` dependency [#1174](https://github.com/storybooks/storybook/pull/1174)
|
||||
- Fix terminal colors by reset console colors explicitly [#1184](https://github.com/storybooks/storybook/pull/1184)
|
||||
- Fix addon panel layout styling [#1170](https://github.com/storybooks/storybook/pull/1170)
|
||||
- ADD https import & remove tracking code remains [#1176](https://github.com/storybooks/storybook/pull/1176)
|
||||
- Fix incorrect babel config file reading [#1156](https://github.com/storybooks/storybook/pull/1156)
|
||||
- Fixed withKnobs definition. [#1164](https://github.com/storybooks/storybook/pull/1164)
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Fixed typo in react-native browser instructions [#1189](https://github.com/storybooks/storybook/pull/1189)
|
||||
- Add instruction for npm install with -D for development dependency [#1168](https://github.com/storybooks/storybook/pull/1168)
|
||||
- Fix broken link for [addons] in README [#1167](https://github.com/storybooks/storybook/pull/1167)
|
||||
- Refreshed logo in docs [#1149](https://github.com/storybooks/storybook/pull/1149)
|
||||
- fix addon broken links in documentation [#1165](https://github.com/storybooks/storybook/pull/1165)
|
||||
- start-storybook cli - expand commands descriptions [#1161](https://github.com/storybooks/storybook/pull/1161)
|
||||
- Fix typo in codemod readme [#1158](https://github.com/storybooks/storybook/pull/1158)
|
||||
|
||||
#### Dependency Upgrades
|
||||
|
||||
- Replaced deprecated `markdown-to-react-components` with `marksy` [#1188](https://github.com/storybooks/storybook/pull/1188)
|
||||
|
||||
# 3.0.0
|
||||
|
||||
Storybook 3.0 is our first fully community-driven release! Notable changes:
|
||||
@ -27,7 +57,7 @@ Storybook 3.0 is our first fully community-driven release! Notable changes:
|
||||
- FIX addon info and addon storyshots incompatibility [#1129](https://github.com/storybooks/storybook/pull/1129)
|
||||
- FIX postcss options missing in default webpack config && UPDATE dependencies [#1087](https://github.com/storybooks/storybook/pull/1087)
|
||||
- Fix CLI had a package version from storybook hardcoded - now queries npm registry [#1079](https://github.com/storybooks/storybook/pull/1079)
|
||||
- Fix semi broken __docgenInfo integration in addon info [#1030](https://github.com/storybooks/storybook/pull/1030)
|
||||
- Fix semi broken \_\_docgenInfo integration in addon info [#1030](https://github.com/storybooks/storybook/pull/1030)
|
||||
- Fix: build-storybook no longer supports relative paths [#1058](https://github.com/storybooks/storybook/pull/1058)
|
||||
- Fix for types `number` for addon knobs [#1001](https://github.com/storybooks/storybook/pull/1001)
|
||||
- Fix webpack overriding && Add an example with local file dependencies [#965](https://github.com/storybooks/storybook/pull/965)
|
||||
@ -86,12 +116,12 @@ Storybook 3.0 is our first fully community-driven release! Notable changes:
|
||||
- Added an upgrade mode to getstorybook [#1146](https://github.com/storybooks/storybook/pull/1146)
|
||||
- Update link to Storyshots addon [#1074](https://github.com/storybooks/storybook/pull/1074)
|
||||
- Added error message for missing or invalid storyName [#747](https://github.com/storybooks/storybook/pull/747)
|
||||
- Opened an Open Collective Account https://opencollective.com/storybook [#1065](https://github.com/storybooks/storybook/pull/1065)
|
||||
- Opened an Open Collective Account <https://opencollective.com/storybook> [#1065](https://github.com/storybooks/storybook/pull/1065)
|
||||
- Add propTablesExclude option [#924](https://github.com/storybooks/storybook/pull/924)
|
||||
- addon-info: make the info overlay be fixed [#914](https://github.com/storybooks/storybook/pull/914)
|
||||
- Handle null elements in getData [#926](https://github.com/storybooks/storybook/pull/926)
|
||||
- add description field from __docgenInfo for prop table for info plugin [#929](https://github.com/storybooks/storybook/pull/929)
|
||||
- #959 add a max-height and center element with alignItems: center [#961](https://github.com/storybooks/storybook/pull/961)
|
||||
- add description field from \_\_docgenInfo for prop table for info plugin [#929](https://github.com/storybooks/storybook/pull/929)
|
||||
- \#959 add a max-height and center element with alignItems: center [#961](https://github.com/storybooks/storybook/pull/961)
|
||||
- Switch to the only prepublish script [#903](https://github.com/storybooks/storybook/pull/903)
|
||||
- PR review policy [#923](https://github.com/storybooks/storybook/pull/923)
|
||||
- Add typescript definitions for getStorybook() [#753](https://github.com/storybooks/storybook/pull/753)
|
||||
|
@ -93,10 +93,10 @@ We welcome contributions to Storybook!
|
||||
> boolean check if code conforms to linting rules - uses remark & eslint
|
||||
|
||||
- `npm run lint:js` - will check js
|
||||
- `npm run lint:markdown` - will check markdown + code samples
|
||||
- `npm run lint:md` - will check markdown + code samples
|
||||
|
||||
- `npm run lint:js -- --fix` - will automatically fix js
|
||||
- `npm run lint:markdown -- -o` - will automatically fix markdown
|
||||
- `npm run lint:md -- -o` - will automatically fix markdown
|
||||
|
||||
#### `npm run test`
|
||||
|
||||
|
@ -18,6 +18,7 @@ This addon works with Storybook for:
|
||||
## Getting Started
|
||||
|
||||
Install:
|
||||
|
||||
```sh
|
||||
npm i -D @storybook/addon-actions
|
||||
```
|
||||
@ -27,8 +28,10 @@ Import the `action` function and use it to create actions handlers. When creatin
|
||||
> _Note: Make sure NOT to use reserved words as function names. [issues#29](https://github.com/storybooks/storybook-addon-actions/issues/29#issuecomment-288274794)_
|
||||
|
||||
```js
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import Button from './button';
|
||||
|
||||
storiesOf('Button', module)
|
||||
.add('default view', () => (
|
||||
@ -47,6 +50,8 @@ If you wish to process action data before sending them over to the logger, you c
|
||||
```js
|
||||
import { action, decorateAction } from '@storybook/addon-actions'
|
||||
|
||||
import Button from './button';
|
||||
|
||||
const firstArgAction = decorateAction([
|
||||
args => args.slice(0, 1)
|
||||
]);
|
||||
|
@ -1,31 +1,25 @@
|
||||
{
|
||||
"name": "@storybook/addon-actions",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "Action Logger addon for storybook",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/actions",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy-storybook": "storybook-to-ghpages",
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9001"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/actions",
|
||||
"devDependencies": {
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"deep-equal": "^1.0.1",
|
||||
@ -33,6 +27,12 @@
|
||||
"prop-types": "^15.5.8",
|
||||
"react-inspector": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*"
|
||||
|
@ -5,7 +5,7 @@ import style from './style';
|
||||
|
||||
class ActionLogger extends Component {
|
||||
componentDidUpdate() {
|
||||
const latest = this.refs.latest;
|
||||
const latest = this.ref.latest;
|
||||
if (latest) {
|
||||
const borderLeft = style.action.borderLeft;
|
||||
latest.style.borderLeft = 'solid 5px #aaa';
|
||||
@ -15,8 +15,12 @@ class ActionLogger extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
getActionData() {
|
||||
return this.props.actions.map((action, i) => this.renderAction(action, i));
|
||||
}
|
||||
|
||||
renderAction(action, i) {
|
||||
const ref = i ? '' : 'latest';
|
||||
const ref = () => (this.ref = i ? '' : 'latest');
|
||||
const counter = <div style={style.counter}>{action.count}</div>;
|
||||
return (
|
||||
<div ref={ref} key={action.id} style={style.action}>
|
||||
@ -34,10 +38,6 @@ class ActionLogger extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
getActionData() {
|
||||
return this.props.actions.map((action, i) => this.renderAction(action, i));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
@ -50,7 +50,11 @@ class ActionLogger extends Component {
|
||||
|
||||
ActionLogger.propTypes = {
|
||||
onClear: PropTypes.func,
|
||||
actions: PropTypes.array,
|
||||
actions: PropTypes.array, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
ActionLogger.defaultProps = {
|
||||
onClear: () => {},
|
||||
actions: [],
|
||||
};
|
||||
|
||||
export default ActionLogger;
|
||||
|
@ -1,5 +1,9 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
import ActionLoggerComponent from '../../components/ActionLogger/';
|
||||
import { EVENT_ID } from '../../';
|
||||
|
||||
@ -10,14 +14,22 @@ export default class ActionLogger extends React.Component {
|
||||
this._actionListener = action => this.addAction(action);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.channel.on(EVENT_ID, this._actionListener);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.channel.removeListener(EVENT_ID, this._actionListener);
|
||||
}
|
||||
|
||||
addAction(action) {
|
||||
action.data.args = action.data.args.map(arg => JSON.parse(arg));
|
||||
action.data.args = action.data.args.map(arg => JSON.parse(arg)); // eslint-disable-line
|
||||
const actions = [...this.state.actions];
|
||||
const previous = actions.length && actions[0];
|
||||
if (previous && deepEqual(previous.data, action.data, { strict: true })) {
|
||||
previous.count++;
|
||||
previous.count++; // eslint-disable-line
|
||||
} else {
|
||||
action.count = 1;
|
||||
action.count = 1; // eslint-disable-line
|
||||
actions.unshift(action);
|
||||
}
|
||||
this.setState({ actions });
|
||||
@ -27,14 +39,6 @@ export default class ActionLogger extends React.Component {
|
||||
this.setState({ actions: [] });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.channel.on(EVENT_ID, this._actionListener);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.channel.removeListener(EVENT_ID, this._actionListener);
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = {
|
||||
actions: this.state.actions,
|
||||
@ -43,3 +47,10 @@ export default class ActionLogger extends React.Component {
|
||||
return <ActionLoggerComponent {...props} />;
|
||||
}
|
||||
}
|
||||
|
||||
ActionLogger.propTypes = {
|
||||
channel: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
ActionLogger.defaultProps = {
|
||||
channel: {},
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// addons, panels and events get unique names using a prefix
|
||||
export const ADDON_ID = 'storybook/addon-actions';
|
||||
export const ADDON_ID = 'storybook/actions';
|
||||
export const PANEL_ID = `${ADDON_ID}/actions-panel`;
|
||||
export const EVENT_ID = `${ADDON_ID}/action-event`;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import ActionLogger from './containers/ActionLogger';
|
||||
import { ADDON_ID, PANEL_ID } from './';
|
||||
|
||||
export function register() {
|
||||
addons.register(ADDON_ID, api => {
|
||||
addons.register(ADDON_ID, () => {
|
||||
const channel = addons.getChannel();
|
||||
addons.addPanel(PANEL_ID, {
|
||||
title: 'Action Logger',
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
import addons from '@storybook/addons';
|
||||
import stringify from 'json-stringify-safe';
|
||||
import { EVENT_ID } from './';
|
||||
@ -10,6 +12,7 @@ function _format(arg) {
|
||||
}
|
||||
|
||||
export function action(name) {
|
||||
// eslint-disable-next-line no-unused-vars, func-names
|
||||
const handler = function(..._args) {
|
||||
const args = Array.from(_args).map(_format);
|
||||
const channel = addons.getChannel();
|
||||
@ -27,13 +30,16 @@ export function action(name) {
|
||||
//
|
||||
// Ref: https://bocoup.com/weblog/whats-in-a-function-name
|
||||
const fnName = name ? name.replace(/\W+/g, '_') : 'action';
|
||||
// eslint-disable-next-line no-eval
|
||||
const named = eval(`(function ${fnName}() { return handler.apply(this, arguments) })`);
|
||||
return named;
|
||||
}
|
||||
|
||||
export function decorateAction(decorators) {
|
||||
// eslint-disable-next-line no-unused-vars, func-names
|
||||
return function(name) {
|
||||
const callAction = action(name);
|
||||
// eslint-disable-next-line no-unused-vars, func-names
|
||||
return function(..._args) {
|
||||
const decorated = decorators.reduce((args, fn) => fn(args), _args);
|
||||
callAction(...decorated);
|
||||
|
@ -15,7 +15,7 @@ This addon works with Storybook for:
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
npm i @storybook/addon-centered
|
||||
npm install @storybook/addon-centered --save-dev
|
||||
```
|
||||
|
||||
#### As a decorator
|
||||
@ -23,11 +23,11 @@ npm i @storybook/addon-centered
|
||||
You can set the decorator locally:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import MyComponent from '../Component';
|
||||
import centered from '@storybook/addon-centered';
|
||||
|
||||
import MyComponent from '../Component';
|
||||
|
||||
storiesOf('MyComponent', module)
|
||||
.addDecorator(centered)
|
||||
.add('without props', () => (<MyComponent />))
|
||||
@ -52,7 +52,6 @@ configure(function () {
|
||||
1 - Configure the extension
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { configure, setAddon } from '@storybook/react';
|
||||
import centered from '@storybook/addon-centered';
|
||||
|
||||
@ -61,7 +60,7 @@ setAddon({
|
||||
this.add(storyName, (context) => (
|
||||
centered.call(context, storyFn)
|
||||
));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
configure(function () {
|
||||
@ -72,8 +71,8 @@ configure(function () {
|
||||
2 - Use it in your story
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
import Component from '../Component';
|
||||
|
||||
storiesOf('Component', module)
|
||||
|
@ -2,10 +2,13 @@
|
||||
"name": "@storybook/addon-centered",
|
||||
"version": "3.0.0",
|
||||
"description": "Storybook decorator to center components",
|
||||
"license": "MIT",
|
||||
"author": "Muhammed Thanish <mnmtanish@gmail.com>",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js"
|
||||
},
|
||||
"author": "Muhammed Thanish <mnmtanish@gmail.com>",
|
||||
"license": "MIT"
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
const manager = require('./dist/manager');
|
||||
|
||||
manager.init();
|
||||
|
@ -13,7 +13,7 @@
|
||||
"main": "preview.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
@ -27,6 +27,7 @@
|
||||
"babel-runtime": "^6.23.0",
|
||||
"deep-equal": "^1.0.1",
|
||||
"events": "^1.1.1",
|
||||
"global": "^4.3.2",
|
||||
"insert-css": "^1.0.0",
|
||||
"marked": "^0.3.6",
|
||||
"moment": "^2.18.1",
|
||||
|
@ -1,2 +1,3 @@
|
||||
const preview = require('./dist/preview');
|
||||
|
||||
preview.init();
|
||||
|
@ -10,16 +10,20 @@ const buttonStyles = {
|
||||
padding: '3px 10px',
|
||||
};
|
||||
|
||||
const Button = ({ children, onClick, style = {} }) => (
|
||||
const Button = ({ children, onClick, style = {} }) =>
|
||||
<button style={{ ...buttonStyles, ...style }} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
</button>;
|
||||
|
||||
Button.defaultProps = {
|
||||
onClick: () => {},
|
||||
style: {},
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
export default Button;
|
||||
|
@ -1,13 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { window } from 'global';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import marked from 'marked';
|
||||
import style from './style';
|
||||
|
||||
const renderer = new marked.Renderer();
|
||||
renderer.heading = function(text) {
|
||||
return text;
|
||||
};
|
||||
renderer.heading = text => text;
|
||||
|
||||
marked.setOptions({
|
||||
renderer,
|
||||
@ -43,8 +42,10 @@ export default class CommentForm extends Component {
|
||||
}
|
||||
|
||||
openLogin() {
|
||||
const signInUrl = `https://hub.getstorybook.io/sign-in?redirectUrl=${encodeURIComponent(location.href)}`;
|
||||
location.href = signInUrl;
|
||||
const signInUrl = `https://hub.getstorybook.io/sign-in?redirectUrl=${encodeURIComponent(
|
||||
window.location.href
|
||||
)}`;
|
||||
window.location.href = signInUrl;
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
@ -58,7 +59,7 @@ export default class CommentForm extends Component {
|
||||
if (!this.props.user) {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<Textarea style={style.input} disabled={true} />
|
||||
<Textarea style={style.input} disabled />
|
||||
<button style={style.submitButton} onClick={() => this.openLogin()}>
|
||||
Sign in with Storybook Hub
|
||||
</button>
|
||||
@ -84,6 +85,11 @@ export default class CommentForm extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
CommentForm.defaultProps = {
|
||||
user: null,
|
||||
addComment: () => {},
|
||||
};
|
||||
CommentForm.propTypes = {
|
||||
user: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
addComment: PropTypes.func,
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { window } from 'global';
|
||||
import moment from 'moment';
|
||||
import renderHTML from 'react-render-html';
|
||||
import insertCss from 'insert-css';
|
||||
@ -26,7 +27,7 @@ export default class CommentItem extends Component {
|
||||
}
|
||||
|
||||
deleteComment() {
|
||||
const confirmDelete = confirm('Are you sure you want to delete this comment?');
|
||||
const confirmDelete = window.confirm('Are you sure you want to delete this comment?');
|
||||
if (confirmDelete === true) {
|
||||
this.props.deleteComment();
|
||||
}
|
||||
@ -34,7 +35,7 @@ export default class CommentItem extends Component {
|
||||
|
||||
renderDelete() {
|
||||
return (
|
||||
<a href="#" style={style.commentDelete} onClick={this.deleteComment}>
|
||||
<a style={style.commentDelete} onClick={this.deleteComment} role="button" tabIndex="0">
|
||||
delete
|
||||
</a>
|
||||
);
|
||||
@ -69,8 +70,19 @@ export default class CommentItem extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
CommentItem.defaultProps = {
|
||||
comment: {},
|
||||
deleteComment: () => {},
|
||||
ownComment: false,
|
||||
};
|
||||
|
||||
CommentItem.propTypes = {
|
||||
deleteComment: PropTypes.func,
|
||||
comment: PropTypes.object,
|
||||
comment: PropTypes.shape({
|
||||
user: PropTypes.object,
|
||||
text: PropTypes.string,
|
||||
time: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
|
||||
loading: PropTypes.bool,
|
||||
}),
|
||||
ownComment: PropTypes.bool,
|
||||
};
|
||||
|
@ -37,21 +37,29 @@ export default class CommentList extends Component {
|
||||
}}
|
||||
style={style.wrapper}
|
||||
>
|
||||
{comments.map((comment, idx) => (
|
||||
{comments.map(comment =>
|
||||
<CommentItem
|
||||
key={`comment_${idx}`}
|
||||
key={comment.id}
|
||||
comment={comment}
|
||||
ownComment={comment.userId === (this.props.user && this.props.user.id)}
|
||||
deleteComment={() => this.props.deleteComment(comment.id)}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CommentList.defaultProps = {
|
||||
comments: [],
|
||||
user: null,
|
||||
deleteComment: () => {},
|
||||
};
|
||||
|
||||
CommentList.propTypes = {
|
||||
comments: PropTypes.array,
|
||||
user: PropTypes.object,
|
||||
comments: PropTypes.arrayOf(PropTypes.object),
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
}),
|
||||
deleteComment: PropTypes.func,
|
||||
};
|
||||
|
@ -1,41 +1,43 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import CommentList from '../CommentList';
|
||||
import CommentForm from '../CommentForm';
|
||||
import style from './style';
|
||||
|
||||
export default class CommentsPanel extends Component {
|
||||
render() {
|
||||
if (this.props.loading) {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<div style={style.message}>loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.appNotAvailable) {
|
||||
const appsUrl = 'https://hub.getstorybook.io/apps';
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<div style={style.message}>
|
||||
<a style={style.button} href={appsUrl}>Create an app for this repo on Storybook Hub</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CommentsPanel(props) {
|
||||
if (props.loading) {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<CommentList key="list" {...this.props} />
|
||||
<CommentForm key="form" {...this.props} />
|
||||
<div style={style.message}>loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.appNotAvailable) {
|
||||
const appsUrl = 'https://hub.getstorybook.io/apps';
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<div style={style.message}>
|
||||
<a style={style.button} href={appsUrl}>Create an app for this repo on Storybook Hub</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<CommentList key="list" {...props} />
|
||||
<CommentForm key="form" {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
CommentsPanel.defaultProps = {
|
||||
loading: false,
|
||||
appNotAvailable: false,
|
||||
};
|
||||
|
||||
CommentsPanel.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
appNotAvailable: PropTypes.bool,
|
||||
};
|
||||
|
@ -13,16 +13,16 @@ export default class DataStore {
|
||||
this.eventStore = new EventEmitter();
|
||||
}
|
||||
|
||||
_addToCache(currentStory, comments) {
|
||||
const key = this._getStoryKey(currentStory);
|
||||
addToCache(currentStory, comments) {
|
||||
const key = this.getStoryKey(currentStory);
|
||||
this.cache[key] = {
|
||||
comments,
|
||||
addedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
_getFromCache(currentStory) {
|
||||
const key = this._getStoryKey(currentStory);
|
||||
getFromCache(currentStory) {
|
||||
const key = this.getStoryKey(currentStory);
|
||||
const item = this.cache[key];
|
||||
|
||||
if (!item) {
|
||||
@ -44,14 +44,14 @@ export default class DataStore {
|
||||
return { comments, invalidated };
|
||||
}
|
||||
|
||||
_reloadCurrentComments() {
|
||||
if (this._stopReloading) {
|
||||
clearInterval(this._stopReloading);
|
||||
reloadCurrentComments() {
|
||||
if (this.stopReloading) {
|
||||
clearInterval(this.stopReloading);
|
||||
}
|
||||
|
||||
this._stopReloading = setInterval(
|
||||
this.stopReloading = setInterval(
|
||||
() => {
|
||||
this._loadUsers().then(() => this._loadComments());
|
||||
this.loadUsers().then(() => this.loadComments());
|
||||
},
|
||||
1000 * 60 // Reload for every minute
|
||||
);
|
||||
@ -63,14 +63,14 @@ export default class DataStore {
|
||||
// We don't need to do anything if the there's no loggedIn user.
|
||||
// if (!this.user) return;
|
||||
|
||||
this._reloadCurrentComments();
|
||||
const item = this._getFromCache(this.currentStory);
|
||||
this.reloadCurrentComments();
|
||||
const item = this.getFromCache(this.currentStory);
|
||||
|
||||
if (item) {
|
||||
this._fireComments(item.comments);
|
||||
this.fireComments(item.comments);
|
||||
// if the cache invalidated we need to load comments again.
|
||||
if (item.invalidated) {
|
||||
return this._loadUsers().then(() => this._loadComments());
|
||||
return this.loadUsers().then(() => this.loadComments());
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
@ -78,21 +78,23 @@ export default class DataStore {
|
||||
// load comments for the first time.
|
||||
// TODO: send a null and handle the loading part in the UI side.
|
||||
this.eventStore.emit('loading', true);
|
||||
this._fireComments([]);
|
||||
this._loadUsers().then(() => this._loadComments()).then(() => {
|
||||
this.fireComments([]);
|
||||
this.loadUsers().then(() => this.loadComments()).then(() => {
|
||||
this.eventStore.emit('loading', false);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
return this.currentStory;
|
||||
}
|
||||
|
||||
setCurrentUser(user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
_loadUsers() {
|
||||
loadUsers() {
|
||||
const query = {};
|
||||
const options = { limit: 1e6 };
|
||||
return this.db.persister._getAppInfo().then(info => {
|
||||
return this.db.persister.getAppInfo().then(info => {
|
||||
if (!info) {
|
||||
return null;
|
||||
}
|
||||
@ -108,38 +110,37 @@ export default class DataStore {
|
||||
});
|
||||
}
|
||||
|
||||
_loadComments() {
|
||||
loadComments() {
|
||||
const currentStory = { ...this.currentStory };
|
||||
const query = currentStory;
|
||||
const options = { limit: 1e6 };
|
||||
return this.db.persister._getAppInfo().then(info => {
|
||||
return this.db.persister.getAppInfo().then(info => {
|
||||
if (!info) {
|
||||
return null;
|
||||
}
|
||||
return this.db.getCollection('comments').get(query, options).then(comments => {
|
||||
// add to cache
|
||||
this._addToCache(currentStory, comments);
|
||||
this.addToCache(currentStory, comments);
|
||||
|
||||
// set comments only if we are on the relavant story
|
||||
if (deepEquals(currentStory, this.currentStory)) {
|
||||
this._fireComments(comments);
|
||||
this.fireComments(comments);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_getStoryKey(currentStory) {
|
||||
getStoryKey(currentStory) {
|
||||
return `${currentStory.sbKind}:::${currentStory.sbStory}`;
|
||||
}
|
||||
|
||||
_fireComments(comments) {
|
||||
fireComments(comments) {
|
||||
this.callbacks.forEach(callback => {
|
||||
// link user to the comment directly
|
||||
comments.forEach(comment => {
|
||||
comment.user = this.users[comment.userId];
|
||||
});
|
||||
|
||||
callback(comments);
|
||||
const commentsWithUser = comments.map(comment =>
|
||||
Object.assign({}, comment, { user: this.users[comment.userId] })
|
||||
);
|
||||
callback(commentsWithUser);
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,27 +154,27 @@ export default class DataStore {
|
||||
return stop;
|
||||
}
|
||||
|
||||
_addPendingComment(comment) {
|
||||
addPendingComment(comment) {
|
||||
// Add the pending comment.
|
||||
const pendingComment = { ...comment, loading: true };
|
||||
const { comments: existingComments } = this._getFromCache(this.currentStory);
|
||||
const { comments: existingComments } = this.getFromCache(this.currentStory);
|
||||
const updatedComments = existingComments.concat(pendingComment);
|
||||
|
||||
this._fireComments(updatedComments);
|
||||
this.fireComments(updatedComments);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
_setDeletedComment(commentId) {
|
||||
const { comments } = this._getFromCache(this.currentStory);
|
||||
setDeletedComment(commentId) {
|
||||
const { comments } = this.getFromCache(this.currentStory);
|
||||
const deleted = comments.find(c => c.id === commentId);
|
||||
if (deleted) {
|
||||
deleted.loading = true;
|
||||
}
|
||||
this._fireComments(comments);
|
||||
this.fireComments(comments);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
_addAuthorToTheDatabase() {
|
||||
addAuthorToTheDatabase() {
|
||||
if (this.users[this.user.id]) {
|
||||
// user exists in the DB.
|
||||
return Promise.resolve(null);
|
||||
@ -188,33 +189,34 @@ export default class DataStore {
|
||||
|
||||
// NOTE the "sbProtected" makes sure only the author can modify
|
||||
// or delete a comment after its saved on the cloud database.
|
||||
_addCommentToDatabase(comment) {
|
||||
addCommentToDatabase(comment) {
|
||||
const doc = {
|
||||
...comment,
|
||||
...this.currentStory,
|
||||
...this.currentStory,
|
||||
sbProtected: true,
|
||||
};
|
||||
|
||||
return this.db.getCollection('comments').set(doc);
|
||||
}
|
||||
|
||||
_deleteCommentOnDatabase(commentId) {
|
||||
deleteCommentOnDatabase(commentId) {
|
||||
const query = { id: commentId };
|
||||
return this.db.getCollection('comments').del(query);
|
||||
}
|
||||
|
||||
addComment(comment) {
|
||||
return this._addAuthorToTheDatabase()
|
||||
.then(() => this._addPendingComment(comment))
|
||||
.then(() => this._addCommentToDatabase(comment))
|
||||
.then(() => this._loadUsers())
|
||||
.then(() => this._loadComments());
|
||||
return this.addAuthorToTheDatabase()
|
||||
.then(() => this.addPendingComment(comment))
|
||||
.then(() => this.addCommentToDatabase(comment))
|
||||
.then(() => this.loadUsers())
|
||||
.then(() => this.loadComments());
|
||||
}
|
||||
|
||||
deleteComment(commentId) {
|
||||
return this._setDeletedComment(commentId)
|
||||
.then(() => this._deleteCommentOnDatabase(commentId))
|
||||
.then(() => this._loadComments());
|
||||
return this.setDeletedComment(commentId)
|
||||
.then(() => this.deleteCommentOnDatabase(commentId))
|
||||
.then(() => this.loadComments());
|
||||
}
|
||||
|
||||
onLoading(cb) {
|
||||
|
@ -5,7 +5,7 @@ const user = {
|
||||
name: 'user-name',
|
||||
};
|
||||
|
||||
const dbGetUsers = jest.fn(a => Promise.resolve([user]));
|
||||
const dbGetUsers = jest.fn(() => Promise.resolve([user]));
|
||||
const dbSetUsers = jest.fn(a => Promise.resolve(a));
|
||||
const dbGetComments = jest.fn(a => Promise.resolve(a));
|
||||
const dbSetComments = jest.fn(a => Promise.resolve(a));
|
||||
@ -16,7 +16,7 @@ const db = {
|
||||
case 'users': {
|
||||
return {
|
||||
get: dbGetUsers,
|
||||
set: dbGetUsers,
|
||||
set: dbSetUsers,
|
||||
};
|
||||
}
|
||||
case 'comments': {
|
||||
@ -25,10 +25,13 @@ const db = {
|
||||
set: dbSetComments,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
persister: {
|
||||
_getAppInfo() {
|
||||
getAppInfo() {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
},
|
||||
|
@ -41,23 +41,23 @@ export default class Container extends Component {
|
||||
this.stopListingStoreLoading();
|
||||
}
|
||||
|
||||
_getAppInfo(persister) {
|
||||
getAppInfo(persister) {
|
||||
return persister
|
||||
._getAppInfo()
|
||||
.then(appInfo => Promise.resolve(appInfo), err => Promise.resolve(null));
|
||||
.getAppInfo()
|
||||
.then(appInfo => Promise.resolve(appInfo), () => Promise.resolve(null));
|
||||
}
|
||||
|
||||
init() {
|
||||
const db = addons.getDatabase();
|
||||
|
||||
if (typeof db.persister._getUser !== 'function') {
|
||||
if (typeof db.persister.getUser !== 'function') {
|
||||
throw new Error('unable to get user info');
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
db.persister
|
||||
._getUser()
|
||||
.then(u => Promise.resolve(u), err => Promise.resolve(null))
|
||||
.getUser()
|
||||
.then(u => Promise.resolve(u), () => Promise.resolve(null))
|
||||
.then(user => {
|
||||
if (user) {
|
||||
this.store.setCurrentUser(user);
|
||||
@ -65,7 +65,7 @@ export default class Container extends Component {
|
||||
} else {
|
||||
this.setState({ user: null });
|
||||
}
|
||||
return this._getAppInfo(db.persister);
|
||||
return this.getAppInfo(db.persister);
|
||||
})
|
||||
.then(appInfo => {
|
||||
const updatedState = { loading: false };
|
||||
@ -108,5 +108,7 @@ export default class Container extends Component {
|
||||
}
|
||||
|
||||
Container.propTypes = {
|
||||
api: PropTypes.object,
|
||||
api: PropTypes.shape({
|
||||
onStory: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
@ -1,6 +1,9 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved */
|
||||
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import Button from '../index';
|
||||
import CommentForm from '../manager/components/CommentForm';
|
||||
import CommentList from '../manager/components/CommentList';
|
||||
@ -28,7 +31,8 @@ const commentsList = [
|
||||
name: 'User B',
|
||||
},
|
||||
time: 'Wed Oct 12 2016 13:38:46 GMT+0530 (IST)',
|
||||
text: 'Vivamus tortor nisi, <b>efficitur</b> in rutrum <em>ac</em>, tempor <code>et mauris</code>. In et rutrum enim',
|
||||
text:
|
||||
'Vivamus tortor nisi, <b>efficitur</b> in rutrum <em>ac</em>, tempor <code>et mauris</code>. In et rutrum enim',
|
||||
},
|
||||
{
|
||||
loading: true,
|
||||
@ -56,14 +60,14 @@ storiesOf('Button', module)
|
||||
storiesOf('Components', module)
|
||||
.add('CommentForm', () => <CommentForm addComment={action('addComment')} />)
|
||||
.add('CommentList - No Comments', () => <CommentList comments={[]} />)
|
||||
.add('CommentList - with comments', () => (
|
||||
.add('CommentList - with comments', () =>
|
||||
<CommentList user={userObj} comments={commentsList} deleteComment={action('deleteComment')} />
|
||||
))
|
||||
)
|
||||
.add('CommentPanel - not loggedIn', () => <CommentsPanel />)
|
||||
.add('CommentPanel - app not available', () => (
|
||||
.add('CommentPanel - app not available', () =>
|
||||
<CommentsPanel user={userObj} appNotAvailable={{}} />
|
||||
))
|
||||
.add('CommentPanel - loggedIn with no comments', () => (
|
||||
)
|
||||
.add('CommentPanel - loggedIn with no comments', () =>
|
||||
<CommentsPanel
|
||||
user={userObj}
|
||||
loading={false}
|
||||
@ -71,8 +75,8 @@ storiesOf('Components', module)
|
||||
addComment={action('addComment')}
|
||||
deleteComment={action('deleteComment')}
|
||||
/>
|
||||
))
|
||||
.add('CommentPanel - loggedIn with has comments', () => (
|
||||
)
|
||||
.add('CommentPanel - loggedIn with has comments', () =>
|
||||
<CommentsPanel
|
||||
user={userObj}
|
||||
loading={false}
|
||||
@ -80,4 +84,4 @@ storiesOf('Components', module)
|
||||
addComment={action('addComment')}
|
||||
deleteComment={action('deleteComment')}
|
||||
/>
|
||||
));
|
||||
);
|
||||
|
@ -49,7 +49,9 @@ The `setupGraphiQL` function also accepts a fetcher parameter which can be used
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import { setupGraphiQL } from '@storybook/addon-graphql'
|
||||
|
||||
const fetcher = function (params) {
|
||||
import { url } from './settings';
|
||||
|
||||
const fetcher = params => {
|
||||
const options = {
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
@ -2,33 +2,35 @@
|
||||
"name": "@storybook/addon-graphql",
|
||||
"version": "3.0.0",
|
||||
"description": "Storybook addon to display the GraphiQL IDE",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/graphql",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy-storybook": "storybook-to-ghpages",
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9001"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"dependencies": {
|
||||
"global": "^4.3.2",
|
||||
"graphiql": "^0.7.8",
|
||||
"graphql": "^0.7.0",
|
||||
"prop-types": "^15.5.10"
|
||||
},
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/graphql",
|
||||
"devDependencies": {
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"graphiql": "^0.7.8",
|
||||
"graphql": "^0.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import style from './style';
|
||||
|
||||
export default class FullScreen extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default function FullScreen(props) {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
FullScreen.defaultProps = { children: null };
|
||||
FullScreen.propTypes = { children: PropTypes.node };
|
||||
|
@ -1,15 +1,17 @@
|
||||
import React from 'react';
|
||||
import GraphiQL from 'graphiql';
|
||||
import FullScreen from './components/FullScreen';
|
||||
import { fetch } from 'global';
|
||||
import 'graphiql/graphiql.css';
|
||||
|
||||
import FullScreen from './components/FullScreen';
|
||||
|
||||
const FETCH_OPTIONS = {
|
||||
method: 'post',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
};
|
||||
|
||||
function getDefautlFetcher(url) {
|
||||
return function(params) {
|
||||
return params => {
|
||||
const body = JSON.stringify(params);
|
||||
const options = Object.assign({ body }, FETCH_OPTIONS);
|
||||
return fetch(url, options).then(res => res.json());
|
||||
@ -23,13 +25,12 @@ function reIndentQuery(query) {
|
||||
}
|
||||
|
||||
export function setupGraphiQL(config) {
|
||||
return function(_query, variables = '{}') {
|
||||
return (_query, variables = '{}') => {
|
||||
const query = reIndentQuery(_query);
|
||||
const fetcher = config.fetcher || getDefautlFetcher(config.url);
|
||||
return () => (
|
||||
return () =>
|
||||
<FullScreen>
|
||||
<GraphiQL query={query} variables={variables} fetcher={fetcher} />
|
||||
</FullScreen>
|
||||
);
|
||||
</FullScreen>;
|
||||
};
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ Then create your stories with the `.addWithInfo` API.
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import Component from './Component';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import Component from './Component';
|
||||
|
||||
storiesOf('Component')
|
||||
.addWithInfo(
|
||||
|
@ -1,16 +1,25 @@
|
||||
{
|
||||
"name": "@storybook/addon-info",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "A Storybook addon to show additional information for your stories.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9010",
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh"
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh",
|
||||
"storybook": "start-storybook -p 9010"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"global": "^4.3.2",
|
||||
"marksy": "^2.0.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-addons-create-fragment": "^15.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"git-url-parse": "^6.2.2",
|
||||
@ -21,13 +30,5 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"markdown-to-react-components": "^0.2.1",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-addons-create-fragment": "^15.5.3"
|
||||
},
|
||||
"main": "dist/index.js"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Props from './Props';
|
||||
|
||||
const stylesheet = {
|
||||
@ -8,62 +9,6 @@ const stylesheet = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class Node extends React.Component {
|
||||
render() {
|
||||
const { node, depth } = this.props;
|
||||
const { tagStyle, containerStyle } = stylesheet;
|
||||
|
||||
const leftPad = {
|
||||
paddingLeft: 3 + (depth + 1) * 15,
|
||||
paddingRight: 3,
|
||||
};
|
||||
|
||||
Object.assign(containerStyle, leftPad);
|
||||
|
||||
const { name, text, children } = getData(node);
|
||||
|
||||
// Just text
|
||||
if (!name) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}>{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Single-line tag
|
||||
if (!children) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<Props node={node} singleLine />
|
||||
<span style={tagStyle}>/></span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Keep a copy so that further mutations to containerStyle don't impact us:
|
||||
const containerStyleCopy = Object.assign({}, containerStyle);
|
||||
|
||||
// tag with children
|
||||
return (
|
||||
<div>
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<Props node={node} />
|
||||
<span style={tagStyle}>></span>
|
||||
</div>
|
||||
{React.Children.map(children, childElement => (
|
||||
<Node node={childElement} depth={depth + 1} />
|
||||
))}
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}></{name}></span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getData(element) {
|
||||
const data = {
|
||||
name: null,
|
||||
@ -71,11 +16,11 @@ function getData(element) {
|
||||
children: null,
|
||||
};
|
||||
|
||||
if (typeof element === 'null') {
|
||||
if (element === null) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (typeof element == 'string') {
|
||||
if (typeof element === 'string') {
|
||||
data.text = element;
|
||||
return data;
|
||||
}
|
||||
@ -96,3 +41,65 @@ function getData(element) {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export default function Node(props) {
|
||||
const { node, depth } = props;
|
||||
const { tagStyle, containerStyle } = stylesheet;
|
||||
|
||||
const leftPad = {
|
||||
paddingLeft: 3 + (depth + 1) * 15,
|
||||
paddingRight: 3,
|
||||
};
|
||||
|
||||
Object.assign(containerStyle, leftPad);
|
||||
|
||||
const { name, text, children } = getData(node);
|
||||
|
||||
// Just text
|
||||
if (!name) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}>{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Single-line tag
|
||||
if (!children) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<Props node={node} singleLine />
|
||||
<span style={tagStyle}>/></span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Keep a copy so that further mutations to containerStyle don't impact us:
|
||||
const containerStyleCopy = Object.assign({}, containerStyle);
|
||||
|
||||
// tag with children
|
||||
return (
|
||||
<div>
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<Props node={node} />
|
||||
<span style={tagStyle}>></span>
|
||||
</div>
|
||||
{React.Children.map(children, childElement => <Node node={childElement} depth={depth + 1} />)}
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}></{name}></span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Node.defaultProps = {
|
||||
node: null,
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
Node.propTypes = {
|
||||
node: PropTypes.node,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
@ -1,11 +1,13 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import PropVal from './PropVal';
|
||||
|
||||
const PropTypesMap = new Map();
|
||||
for (const typeName in PropTypes) {
|
||||
if (!PropTypes.hasOwnProperty(typeName)) {
|
||||
continue;
|
||||
for (const typeName in PropTypes) { // eslint-disable-line
|
||||
if (!PropTypes.hasOwnProperty(typeName)) { // eslint-disable-line
|
||||
continue; // eslint-disable-line
|
||||
}
|
||||
const type = PropTypes[typeName];
|
||||
PropTypesMap.set(type, typeName);
|
||||
@ -20,93 +22,92 @@ const stylesheet = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class PropTable extends React.Component {
|
||||
render() {
|
||||
const type = this.props.type;
|
||||
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const props = {};
|
||||
|
||||
if (type.propTypes) {
|
||||
for (const property in type.propTypes) {
|
||||
if (!type.propTypes.hasOwnProperty(property)) {
|
||||
continue;
|
||||
}
|
||||
const typeInfo = type.propTypes[property];
|
||||
let propType = PropTypesMap.get(typeInfo) || 'other';
|
||||
const required = typeInfo.isRequired === undefined ? 'yes' : 'no';
|
||||
const description = type.__docgenInfo &&
|
||||
type.__docgenInfo.props &&
|
||||
type.__docgenInfo.props[property]
|
||||
? type.__docgenInfo.props[property].description
|
||||
: null;
|
||||
if (propType === 'other') {
|
||||
if (
|
||||
type.__docgenInfo &&
|
||||
type.__docgenInfo.props &&
|
||||
type.__docgenInfo.props[property] &&
|
||||
type.__docgenInfo.props[property].type
|
||||
) {
|
||||
propType = type.__docgenInfo.props[property].type.name;
|
||||
}
|
||||
}
|
||||
props[property] = { property, propType, required, description };
|
||||
}
|
||||
}
|
||||
|
||||
if (type.defaultProps) {
|
||||
for (const property in type.defaultProps) {
|
||||
if (!type.defaultProps.hasOwnProperty(property)) {
|
||||
continue;
|
||||
}
|
||||
const value = type.defaultProps[property];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!props[property]) {
|
||||
props[property] = { property };
|
||||
}
|
||||
props[property].defaultValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
const array = Object.values(props);
|
||||
if (!array.length) {
|
||||
return <small>No propTypes defined!</small>;
|
||||
}
|
||||
array.sort((a, b) => a.property > b.property);
|
||||
|
||||
return (
|
||||
<table style={stylesheet.propTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>property</th>
|
||||
<th>propType</th>
|
||||
<th>required</th>
|
||||
<th>default</th>
|
||||
<th>description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{array.map(row => (
|
||||
<tr key={row.property}>
|
||||
<td>{row.property}</td>
|
||||
<td>{row.propType}</td>
|
||||
<td>{row.required}</td>
|
||||
<td>{row.defaultValue === undefined ? '-' : <PropVal val={row.defaultValue} />}</td>
|
||||
<td>{row.description}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
const PropTable = ({ type }) => {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const props = {};
|
||||
|
||||
if (type.propTypes) {
|
||||
for (const property in type.propTypes) { // eslint-disable-line
|
||||
if (!type.propTypes.hasOwnProperty(property)) { // eslint-disable-line
|
||||
continue; // eslint-disable-line
|
||||
}
|
||||
const typeInfo = type.propTypes[property];
|
||||
let propType = PropTypesMap.get(typeInfo) || 'other';
|
||||
const required = typeInfo.isRequired === undefined ? 'yes' : 'no';
|
||||
const description = type.__docgenInfo &&
|
||||
type.__docgenInfo.props &&
|
||||
type.__docgenInfo.props[property]
|
||||
? type.__docgenInfo.props[property].description
|
||||
: null;
|
||||
if (propType === 'other') {
|
||||
if (
|
||||
type.__docgenInfo &&
|
||||
type.__docgenInfo.props &&
|
||||
type.__docgenInfo.props[property] &&
|
||||
type.__docgenInfo.props[property].type
|
||||
) {
|
||||
propType = type.__docgenInfo.props[property].type.name;
|
||||
}
|
||||
}
|
||||
props[property] = { property, propType, required, description };
|
||||
}
|
||||
}
|
||||
|
||||
if (type.defaultProps) {
|
||||
for (const property in type.defaultProps) { // eslint-disable-line
|
||||
if (!type.defaultProps.hasOwnProperty(property)) { // eslint-disable-line
|
||||
continue; // eslint-disable-line
|
||||
}
|
||||
const value = type.defaultProps[property];
|
||||
if (value === undefined) {
|
||||
continue; // eslint-disable-line
|
||||
}
|
||||
if (!props[property]) {
|
||||
props[property] = { property };
|
||||
}
|
||||
props[property].defaultValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
const array = Object.values(props);
|
||||
if (!array.length) {
|
||||
return <small>No propTypes defined!</small>;
|
||||
}
|
||||
array.sort((a, b) => a.property > b.property);
|
||||
|
||||
return (
|
||||
<table style={stylesheet.propTable}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>property</th>
|
||||
<th>propType</th>
|
||||
<th>required</th>
|
||||
<th>default</th>
|
||||
<th>description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{array.map(row =>
|
||||
<tr key={row.property}>
|
||||
<td>{row.property}</td>
|
||||
<td>{row.propType}</td>
|
||||
<td>{row.required}</td>
|
||||
<td>{row.defaultValue === undefined ? '-' : <PropVal val={row.defaultValue} />}</td>
|
||||
<td>{row.description}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
PropTable.displayName = 'PropTable';
|
||||
PropTable.defaultProps = {
|
||||
type: null,
|
||||
};
|
||||
PropTable.propTypes = {
|
||||
type: PropTypes.func,
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ function previewProp(val) {
|
||||
content = <span style={valueStyles.number}>{val}</span>;
|
||||
} else if (typeof val === 'string') {
|
||||
if (val.length > 50) {
|
||||
val = `${val.slice(0, 50)}…`;
|
||||
val = `${val.slice(0, 50)}…`; // eslint-disable-line
|
||||
}
|
||||
content = <span style={valueStyles.string}>"{val}"</span>;
|
||||
braceWrap = false;
|
||||
@ -112,7 +112,7 @@ function previewProp(val) {
|
||||
|
||||
export default class PropVal extends React.Component {
|
||||
render() {
|
||||
return previewProp(this.props.val);
|
||||
return previewProp(this.props.val); // eslint-disable-line react/prop-types
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import PropVal from './PropVal';
|
||||
|
||||
const stylesheet = {
|
||||
@ -7,48 +8,54 @@ const stylesheet = {
|
||||
propValueStyle: {},
|
||||
};
|
||||
|
||||
export default class Props extends React.Component {
|
||||
render() {
|
||||
const props = this.props.node.props;
|
||||
const defaultProps = this.props.node.type.defaultProps;
|
||||
if (!props || typeof props !== 'object') {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
const { propStyle, propValueStyle, propNameStyle } = stylesheet;
|
||||
|
||||
const names = Object.keys(props).filter(
|
||||
name =>
|
||||
name[0] !== '_' &&
|
||||
name !== 'children' &&
|
||||
(!defaultProps || props[name] != defaultProps[name])
|
||||
);
|
||||
|
||||
const breakIntoNewLines = names.length > 3;
|
||||
const endingSpace = this.props.singleLine ? ' ' : '';
|
||||
|
||||
const items = [];
|
||||
names.forEach((name, i) => {
|
||||
items.push(
|
||||
<span key={name}>
|
||||
{breakIntoNewLines
|
||||
? <span>
|
||||
<br />
|
||||
</span>
|
||||
: ' '}
|
||||
<span style={propNameStyle}>{name}</span>
|
||||
{/* Use implicit true: */}
|
||||
{(!props[name] || typeof props[name] !== 'boolean') &&
|
||||
<span>
|
||||
=
|
||||
<span style={propValueStyle}><PropVal val={props[name]} /></span>
|
||||
</span>}
|
||||
|
||||
{i === names.length - 1 && (breakIntoNewLines ? <br /> : endingSpace)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return <span>{items}</span>;
|
||||
export default function Props(props) {
|
||||
const nodeProps = props.node.props;
|
||||
const defaultProps = props.node.type.defaultProps;
|
||||
if (!nodeProps || typeof nodeProps !== 'object') {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
const { propValueStyle, propNameStyle } = stylesheet;
|
||||
|
||||
const names = Object.keys(props).filter(
|
||||
name =>
|
||||
name[0] !== '_' &&
|
||||
name !== 'children' &&
|
||||
(!defaultProps || props[name] !== defaultProps[name])
|
||||
);
|
||||
|
||||
const breakIntoNewLines = names.length > 3;
|
||||
const endingSpace = props.singleLine ? ' ' : '';
|
||||
|
||||
const items = [];
|
||||
names.forEach((name, i) => {
|
||||
items.push(
|
||||
<span key={name}>
|
||||
{breakIntoNewLines ? <span><br /> </span> : ' '}
|
||||
<span style={propNameStyle}>{name}</span>
|
||||
{/* Use implicit true: */}
|
||||
{(!nodeProps[name] || typeof nodeProps[name] !== 'boolean') &&
|
||||
<span>
|
||||
=
|
||||
<span style={propValueStyle}><PropVal val={nodeProps[name]} /></span>
|
||||
</span>}
|
||||
|
||||
{i === names.length - 1 && (breakIntoNewLines ? <br /> : endingSpace)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return <span>{items}</span>;
|
||||
}
|
||||
|
||||
Props.defaultProps = {
|
||||
singleLine: false,
|
||||
};
|
||||
|
||||
Props.propTypes = {
|
||||
node: PropTypes.shape({
|
||||
props: PropTypes.object,
|
||||
type: PropTypes.object.isRequired,
|
||||
}).isRequired,
|
||||
singleLine: PropTypes.bool,
|
||||
};
|
||||
|
@ -1,11 +1,19 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import React from 'react';
|
||||
import MTRC from 'markdown-to-react-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import global from 'global';
|
||||
|
||||
import marksy from 'marksy';
|
||||
|
||||
import PropTable from './PropTable';
|
||||
import Node from './Node';
|
||||
import { baseFonts } from './theme';
|
||||
import { Pre } from './markdown';
|
||||
|
||||
global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || [];
|
||||
const STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES;
|
||||
|
||||
const stylesheet = {
|
||||
link: {
|
||||
base: {
|
||||
@ -90,7 +98,7 @@ export default class Story extends React.Component {
|
||||
open: false,
|
||||
stylesheet: this.props.styles(JSON.parse(JSON.stringify(stylesheet))),
|
||||
};
|
||||
MTRC.configure(this.props.mtrcConf);
|
||||
this.marksy = marksy(this.props.marksyConf);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@ -156,9 +164,9 @@ export default class Story extends React.Component {
|
||||
<div style={this.state.stylesheet.children}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
<a style={linkStyle} onClick={openOverlay}>Show Info</a>
|
||||
<a style={linkStyle} onClick={openOverlay} role="button" tabIndex="0">Show Info</a>
|
||||
<div style={infoStyle}>
|
||||
<a style={linkStyle} onClick={closeOverlay}>×</a>
|
||||
<a style={linkStyle} onClick={closeOverlay} role="button" tabIndex="0">×</a>
|
||||
<div style={this.state.stylesheet.infoPage}>
|
||||
<div style={this.state.stylesheet.infoBody}>
|
||||
{this._getInfoHeader()}
|
||||
@ -211,7 +219,7 @@ export default class Story extends React.Component {
|
||||
const source = lines.map(s => s.slice(padding)).join('\n');
|
||||
return (
|
||||
<div style={this.state.stylesheet.infoContent}>
|
||||
{MTRC(source).tree}
|
||||
{this.marksy(source).tree}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -220,7 +228,7 @@ export default class Story extends React.Component {
|
||||
let retDiv = null;
|
||||
|
||||
if (Object.keys(STORYBOOK_REACT_CLASSES).length) {
|
||||
Object.keys(STORYBOOK_REACT_CLASSES).forEach((key, index) => {
|
||||
Object.keys(STORYBOOK_REACT_CLASSES).forEach(key => {
|
||||
if (STORYBOOK_REACT_CLASSES[key].name === this.props.context.kind) {
|
||||
retDiv = (
|
||||
<div>
|
||||
@ -243,9 +251,9 @@ export default class Story extends React.Component {
|
||||
<div>
|
||||
<h1 style={this.state.stylesheet.source.h1}>Story Source</h1>
|
||||
<Pre>
|
||||
{React.Children.map(this.props.children, (root, idx) => (
|
||||
{React.Children.map(this.props.children, (root, idx) =>
|
||||
<Node key={idx} depth={0} node={root} />
|
||||
))}
|
||||
)}
|
||||
</Pre>
|
||||
</div>
|
||||
);
|
||||
@ -284,7 +292,7 @@ export default class Story extends React.Component {
|
||||
typeof children === 'string' ||
|
||||
typeof children.type === 'string' ||
|
||||
(Array.isArray(this.props.propTablesExclude) && // also ignore excluded types
|
||||
~this.props.propTablesExclude.indexOf(children.type))
|
||||
~this.props.propTablesExclude.indexOf(children.type)) // eslint-disable-line no-bitwise
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -299,14 +307,14 @@ export default class Story extends React.Component {
|
||||
const array = Array.from(types.keys());
|
||||
array.sort((a, b) => (a.displayName || a.name) > (b.displayName || b.name));
|
||||
|
||||
const propTables = array.map((type, idx) => (
|
||||
<div key={idx}>
|
||||
const propTables = array.map(type =>
|
||||
<div key={type.name}>
|
||||
<h2 style={this.state.stylesheet.propTableHead}>
|
||||
"{type.displayName || type.name}" Component
|
||||
</h2>
|
||||
<PropTable type={type} />
|
||||
</div>
|
||||
));
|
||||
);
|
||||
|
||||
if (!propTables || propTables.length === 0) {
|
||||
return null;
|
||||
@ -330,8 +338,12 @@ export default class Story extends React.Component {
|
||||
}
|
||||
|
||||
Story.displayName = 'Story';
|
||||
|
||||
Story.propTypes = {
|
||||
context: PropTypes.object,
|
||||
context: PropTypes.shape({
|
||||
kind: PropTypes.string,
|
||||
story: PropTypes.string,
|
||||
}),
|
||||
info: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
propTables: PropTypes.arrayOf(PropTypes.func),
|
||||
propTablesExclude: PropTypes.arrayOf(PropTypes.func),
|
||||
@ -340,12 +352,16 @@ Story.propTypes = {
|
||||
showSource: PropTypes.bool,
|
||||
styles: PropTypes.func.isRequired,
|
||||
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
mtrcConf: PropTypes.object,
|
||||
marksyConf: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
||||
Story.defaultProps = {
|
||||
context: null,
|
||||
info: '',
|
||||
children: null,
|
||||
propTables: null,
|
||||
propTablesExclude: [],
|
||||
showInline: false,
|
||||
showHeader: true,
|
||||
showSource: true,
|
||||
mtrcConf: {},
|
||||
marksyConf: {},
|
||||
};
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Prism } from 'global';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export class Code extends React.Component {
|
||||
componentDidMount() {
|
||||
@ -41,30 +43,39 @@ export class Code extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export class Pre extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: '.88em',
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
backgroundColor: '#fafafa',
|
||||
padding: '.5rem',
|
||||
lineHeight: 1.5,
|
||||
overflowX: 'scroll',
|
||||
};
|
||||
Code.propTypes = {
|
||||
language: PropTypes.string,
|
||||
code: PropTypes.node,
|
||||
};
|
||||
Code.defaultProps = {
|
||||
language: null,
|
||||
code: null,
|
||||
};
|
||||
|
||||
return <pre style={style}>{this.props.children}</pre>;
|
||||
}
|
||||
export function Pre(props) {
|
||||
const style = {
|
||||
fontSize: '.88em',
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
backgroundColor: '#fafafa',
|
||||
padding: '.5rem',
|
||||
lineHeight: 1.5,
|
||||
overflowX: 'scroll',
|
||||
};
|
||||
return <pre style={style}>{props.children}</pre>;
|
||||
}
|
||||
|
||||
export class Blockquote extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: '1.88em',
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
borderLeft: '8px solid #fafafa',
|
||||
padding: '1rem',
|
||||
};
|
||||
Pre.propTypes = { children: PropTypes.node };
|
||||
Pre.defaultProps = { children: null };
|
||||
|
||||
return <blockquote style={style}>{this.props.children}</blockquote>;
|
||||
}
|
||||
export function Blockquote(props) {
|
||||
const style = {
|
||||
fontSize: '1.88em',
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
borderLeft: '8px solid #fafafa',
|
||||
padding: '1rem',
|
||||
};
|
||||
return <blockquote style={style}>{props.children}</blockquote>;
|
||||
}
|
||||
|
||||
Blockquote.propTypes = { children: PropTypes.node };
|
||||
Blockquote.defaultProps = { children: null };
|
||||
|
@ -1,88 +1,98 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { baseFonts } from '../theme';
|
||||
|
||||
export class H1 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
borderBottom: '1px solid #eee',
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '40px',
|
||||
};
|
||||
const defaultProps = {
|
||||
children: null,
|
||||
id: null,
|
||||
};
|
||||
const propTypes = {
|
||||
children: PropTypes.node,
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
return <h1 id={this.props.id} style={styles}>{this.props.children}</h1>;
|
||||
}
|
||||
export function H1(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
borderBottom: '1px solid #eee',
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '40px',
|
||||
};
|
||||
return <h1 id={props.id} style={styles}>{props.children}</h1>;
|
||||
}
|
||||
|
||||
export class H2 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '30px',
|
||||
};
|
||||
H1.defaultProps = defaultProps;
|
||||
H1.propTypes = propTypes;
|
||||
|
||||
return <h2 id={this.props.id} style={styles}>{this.props.children}</h2>;
|
||||
}
|
||||
export function H2(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '30px',
|
||||
};
|
||||
return <h2 id={props.id} style={styles}>{props.children}</h2>;
|
||||
}
|
||||
|
||||
export class H3 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '22px',
|
||||
textTransform: 'uppercase',
|
||||
};
|
||||
H2.defaultProps = defaultProps;
|
||||
H2.propTypes = propTypes;
|
||||
|
||||
return <h3 id={this.props.id} style={styles}>{this.props.children}</h3>;
|
||||
}
|
||||
export function H3(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '22px',
|
||||
textTransform: 'uppercase',
|
||||
};
|
||||
return <h3 id={props.id} style={styles}>{props.children}</h3>;
|
||||
}
|
||||
|
||||
export class H4 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '20px',
|
||||
};
|
||||
H3.defaultProps = defaultProps;
|
||||
H3.propTypes = propTypes;
|
||||
|
||||
return <h4 id={this.props.id} style={styles}>{this.props.children}</h4>;
|
||||
}
|
||||
export function H4(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '20px',
|
||||
};
|
||||
return <h4 id={props.id} style={styles}>{props.children}</h4>;
|
||||
}
|
||||
|
||||
export class H5 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
H4.defaultProps = defaultProps;
|
||||
H4.propTypes = propTypes;
|
||||
|
||||
return <h5 id={this.props.id} style={styles}>{this.props.children}</h5>;
|
||||
}
|
||||
export function H5(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 600,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
return <h5 id={props.id} style={styles}>{props.children}</h5>;
|
||||
}
|
||||
|
||||
export class H6 extends React.Component {
|
||||
render() {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 400,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
H5.defaultProps = defaultProps;
|
||||
H5.propTypes = propTypes;
|
||||
|
||||
return <h6 id={this.props.id} style={styles}>{this.props.children}</h6>;
|
||||
}
|
||||
export function H6(props) {
|
||||
const styles = {
|
||||
...baseFonts,
|
||||
fontWeight: 400,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
return <h6 id={props.id} style={styles}>{props.children}</h6>;
|
||||
}
|
||||
|
||||
H6.defaultProps = defaultProps;
|
||||
H6.propTypes = propTypes;
|
||||
|
@ -1,43 +1,49 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { baseFonts } from '../theme';
|
||||
|
||||
export class P extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <p style={style}>{this.props.children}</p>;
|
||||
}
|
||||
const defaultProps = { children: null };
|
||||
const propTypes = { children: PropTypes.node };
|
||||
|
||||
export function P(props) {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <p style={style}>{props.children}</p>;
|
||||
}
|
||||
|
||||
export class LI extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <li style={style}>{this.props.children}</li>;
|
||||
}
|
||||
P.defaultProps = defaultProps;
|
||||
P.propTypes = propTypes;
|
||||
|
||||
export function LI(props) {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <li style={style}>{props.children}</li>;
|
||||
}
|
||||
|
||||
export class UL extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
LI.defaultProps = defaultProps;
|
||||
LI.propTypes = propTypes;
|
||||
|
||||
return <ul style={style}>{this.props.children}</ul>;
|
||||
}
|
||||
export function UL(props) {
|
||||
const style = {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <ul style={style}>{props.children}</ul>;
|
||||
}
|
||||
|
||||
export class A extends React.Component {
|
||||
render() {
|
||||
const style = {
|
||||
color: '#3498db',
|
||||
};
|
||||
UL.defaultProps = defaultProps;
|
||||
UL.propTypes = propTypes;
|
||||
|
||||
return <a href={this.props.href} style={style}>{this.props.children}</a>;
|
||||
}
|
||||
export function A(props) {
|
||||
const style = {
|
||||
color: '#3498db',
|
||||
};
|
||||
return <a href={this.props.href} style={style}>{props.children}</a>;
|
||||
}
|
||||
|
||||
A.defaultProps = defaultProps;
|
||||
A.propTypes = propTypes;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import _Story from './components/Story';
|
||||
import { H1, H2, H3, H4, H5, H6, Code, P, UL, A, LI } from './components/markdown';
|
||||
|
||||
export const Story = _Story;
|
||||
|
||||
const defaultOptions = {
|
||||
@ -10,7 +11,7 @@ const defaultOptions = {
|
||||
propTables: [],
|
||||
};
|
||||
|
||||
const defaultMtrcConf = {
|
||||
const defaultMarksyConf = {
|
||||
h1: H1,
|
||||
h2: H2,
|
||||
h3: H3,
|
||||
@ -28,9 +29,9 @@ export default {
|
||||
addWithInfo(storyName, info, storyFn, _options) {
|
||||
if (typeof storyFn !== 'function') {
|
||||
if (typeof info === 'function') {
|
||||
_options = storyFn;
|
||||
storyFn = info;
|
||||
info = '';
|
||||
_options = storyFn; // eslint-disable-line
|
||||
storyFn = info; // eslint-disable-line
|
||||
info = ''; // eslint-disable-line
|
||||
} else {
|
||||
throw new Error('No story defining function has been specified');
|
||||
}
|
||||
@ -48,9 +49,9 @@ export default {
|
||||
options.propTables = null;
|
||||
}
|
||||
|
||||
const mtrcConf = { ...defaultMtrcConf };
|
||||
if (options && options.mtrcConf) {
|
||||
Object.assign(mtrcConf, options.mtrcConf);
|
||||
const marksyConf = { ...defaultMarksyConf };
|
||||
if (options && options.marksyConf) {
|
||||
Object.assign(marksyConf, options.marksyConf);
|
||||
}
|
||||
|
||||
return this.add(storyName, context => {
|
||||
@ -63,7 +64,7 @@ export default {
|
||||
propTables: options.propTables,
|
||||
propTablesExclude: options.propTablesExclude,
|
||||
styles: typeof options.styles === 'function' ? options.styles : s => s,
|
||||
mtrcConf,
|
||||
marksyConf,
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -24,7 +24,7 @@ This is how Knobs look like:
|
||||
First of all, you need to install knobs into your project as a dev dependency.
|
||||
|
||||
```sh
|
||||
npm i -D @storybook/addon-knobs
|
||||
npm install @storybook/addon-knobs --save-dev
|
||||
```
|
||||
|
||||
Then, configure it as an addon by adding it to your `addons.js` file (located in the Storybook config directory).
|
||||
@ -36,7 +36,6 @@ import '@storybook/addon-knobs/register'
|
||||
Now, write your stories with knobs.
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
|
||||
|
||||
@ -48,9 +47,7 @@ stories.addDecorator(withKnobs);
|
||||
|
||||
// Knobs for React props
|
||||
stories.add('with a button', () => (
|
||||
<button
|
||||
disabled={boolean('Disabled', false)}
|
||||
>
|
||||
<button disabled={boolean('Disabled', false)} >
|
||||
{text('Label', 'Hello Button')}
|
||||
</button>
|
||||
))
|
||||
@ -92,6 +89,8 @@ Just like that, you can import any other following Knobs:
|
||||
Allows you to get some text from the user.
|
||||
|
||||
```js
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Your Name';
|
||||
const defaultValue = 'Arunoda Susiripala';
|
||||
|
||||
@ -103,6 +102,8 @@ const value = text(label, defaultValue);
|
||||
Allows you to get a boolean value from the user.
|
||||
|
||||
```js
|
||||
import { boolean } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Agree?';
|
||||
const defaultValue = false;
|
||||
|
||||
@ -114,6 +115,8 @@ const value = boolean(label, defaultValue);
|
||||
Allows you to get a number from the user.
|
||||
|
||||
```js
|
||||
import { number } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Age';
|
||||
const defaultValue = 78;
|
||||
|
||||
@ -125,6 +128,8 @@ const value = number(label, defaultValue);
|
||||
Allows you to get a number from the user using a range slider.
|
||||
|
||||
```js
|
||||
import { number } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Temperature';
|
||||
const defaultValue = 73;
|
||||
const options = {
|
||||
@ -137,11 +142,13 @@ const options = {
|
||||
const value = number(label, defaultValue, options);
|
||||
```
|
||||
|
||||
### colour
|
||||
### color
|
||||
|
||||
Allows you to get a colour from the user.
|
||||
|
||||
```js
|
||||
import { color } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Color';
|
||||
const defaultValue = '#ff00ff';
|
||||
|
||||
@ -153,6 +160,8 @@ const value = color(label, defaultValue);
|
||||
Allows you to get a JSON object from the user.
|
||||
|
||||
```js
|
||||
import { object } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Styles';
|
||||
const defaultValue = {
|
||||
backgroundColor: 'red'
|
||||
@ -168,6 +177,8 @@ const value = object(label, defaultValue);
|
||||
Allows you to get an array from the user.
|
||||
|
||||
```js
|
||||
import { array } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Styles';
|
||||
const defaultValue = ['Red']
|
||||
|
||||
@ -178,6 +189,10 @@ const value = array(label, defaultValue);
|
||||
> By default it's a comma, but this can be override by passing a separator variable.
|
||||
>
|
||||
> ```js
|
||||
> import { array } from '@storybook/addon-knobs';
|
||||
>
|
||||
> const label = 'Styles';
|
||||
> const defaultValue = ['Red'];
|
||||
> const separator = ':';
|
||||
> const value = array(label, defaultValue, separator);
|
||||
> ```
|
||||
@ -187,6 +202,8 @@ const value = array(label, defaultValue);
|
||||
Allows you to get a value from a select box from the user.
|
||||
|
||||
```js
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Colors';
|
||||
const options = {
|
||||
red: 'Red',
|
||||
@ -205,6 +222,8 @@ const value = select(label, options, defaultValue);
|
||||
Allow you to get date (and time) from the user.
|
||||
|
||||
```js
|
||||
import { date } from '@storybook/addon-knobs';
|
||||
|
||||
const label = 'Event Date';
|
||||
const defaultValue = new Date('Jan 20 2017');
|
||||
const value = date(label, defaultValue);
|
||||
@ -217,4 +236,4 @@ If you are using typescript, make sure you have the type definitions installed f
|
||||
- node
|
||||
- react
|
||||
|
||||
You can install them using `npm i -S @types/node @types/react`, assuming you are using Typescript >2.0.
|
||||
You can install them using `npm install -save @types/node @types/react`, assuming you are using Typescript >2.0.
|
||||
|
@ -1,17 +1,32 @@
|
||||
{
|
||||
"name": "@storybook/addon-knobs",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "Storybook Addon Prop Editor Component",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"typings": "./storybook-addon-knobs.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "./example/prepublish.sh",
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9010",
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh"
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh",
|
||||
"start": "./example/prepublish.sh",
|
||||
"storybook": "start-storybook -p 9010"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "*",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"deep-equal": "^1.0.1",
|
||||
"global": "^4.3.2",
|
||||
"insert-css": "^1.0.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.18.1",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-color": "^2.11.4",
|
||||
"react-datetime": "^2.8.10",
|
||||
"react-textarea-autosize": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.12",
|
||||
@ -27,19 +42,5 @@
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "*",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"deep-equal": "^1.0.1",
|
||||
"insert-css": "^1.0.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.18.1",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-color": "^2.11.4",
|
||||
"react-datetime": "^2.8.10",
|
||||
"react-textarea-autosize": "^4.3.0"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"typings": "./storybook-addon-knobs.d.ts"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import React from 'react';
|
||||
import deepEqual from 'deep-equal';
|
||||
import WrapStory from './components/WrapStory';
|
||||
import KnobStore from './KnobStore';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
// This is used by _mayCallChannel to determine how long to wait to before triggering a panel update
|
||||
const PANEL_UPDATE_INTERVAL = 400;
|
||||
@ -41,7 +43,7 @@ export default class KnobManager {
|
||||
let knobStore = this.knobStoreMap[key];
|
||||
|
||||
if (!knobStore) {
|
||||
knobStore = this.knobStoreMap[key] = new KnobStore();
|
||||
knobStore = this.knobStoreMap[key] = new KnobStore(); // eslint-disable-line
|
||||
}
|
||||
|
||||
this.knobStore = knobStore;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { shallow } from 'enzyme'; // eslint-disable-line
|
||||
import KnobManager from './KnobManager';
|
||||
|
||||
describe('KnobManager', () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import PropForm from './PropForm';
|
||||
import Types from './types';
|
||||
|
||||
@ -147,7 +148,14 @@ export default class Panel extends React.Component {
|
||||
}
|
||||
|
||||
Panel.propTypes = {
|
||||
channel: PropTypes.object,
|
||||
onReset: PropTypes.object,
|
||||
api: PropTypes.object,
|
||||
channel: PropTypes.shape({
|
||||
emit: PropTypes.func,
|
||||
on: PropTypes.func,
|
||||
removeListener: PropTypes.func,
|
||||
}).isRequired,
|
||||
onReset: PropTypes.object, // eslint-disable-line
|
||||
api: PropTypes.shape({
|
||||
getQueryParam: PropTypes.func,
|
||||
setQueryParams: PropTypes.func,
|
||||
}).isRequired,
|
||||
};
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import TypeMap from './types';
|
||||
@ -60,6 +62,9 @@ export default class PropField extends React.Component {
|
||||
}
|
||||
|
||||
PropField.propTypes = {
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
knob: PropTypes.object,
|
||||
};
|
||||
|
@ -1,5 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import PropField from './PropField';
|
||||
|
||||
@ -33,16 +35,16 @@ export default class propForm extends React.Component {
|
||||
|
||||
return (
|
||||
<form style={stylesheet.propForm}>
|
||||
{knobs.map(knob => (
|
||||
{knobs.map(knob =>
|
||||
<PropField
|
||||
key={knob.name}
|
||||
name={knob.name}
|
||||
type={knob.type}
|
||||
value={knob.value}
|
||||
knob={knob}
|
||||
onChange={this._onFieldChange.bind(null, knob.name, knob.type)}
|
||||
onChange={() => this._onFieldChange(knob.name, knob.type)}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@ -50,7 +52,16 @@ export default class propForm extends React.Component {
|
||||
|
||||
propForm.displayName = 'propForm';
|
||||
|
||||
propForm.defaultProps = {
|
||||
knobs: [],
|
||||
};
|
||||
|
||||
propForm.propTypes = {
|
||||
knobs: PropTypes.array.isRequired,
|
||||
knobs: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
})
|
||||
),
|
||||
onFieldChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
@ -60,10 +62,27 @@ export default class WrapStory extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
WrapStory.propTypes = {
|
||||
context: PropTypes.object,
|
||||
storyFn: PropTypes.func,
|
||||
channel: PropTypes.object,
|
||||
knobStore: PropTypes.object,
|
||||
initialContent: PropTypes.object,
|
||||
WrapStory.defaultProps = {
|
||||
context: {},
|
||||
initialContent: {},
|
||||
storyFn: context => context,
|
||||
};
|
||||
|
||||
WrapStory.propTypes = {
|
||||
context: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
storyFn: PropTypes.func,
|
||||
channel: React.PropTypes.shape({
|
||||
on: PropTypes.func,
|
||||
removeListener: PropTypes.func,
|
||||
}).isRequired,
|
||||
knobStore: PropTypes.shape({
|
||||
channel: PropTypes.func,
|
||||
get: PropTypes.func,
|
||||
getAll: PropTypes.func,
|
||||
markAllUnused: PropTypes.func,
|
||||
reset: PropTypes.func,
|
||||
subscribe: PropTypes.func,
|
||||
unsubscribe: PropTypes.func,
|
||||
}).isRequired,
|
||||
initialContent: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { shallow } from 'enzyme'; // eslint-disable-line
|
||||
import Array from '../types/Array';
|
||||
|
||||
describe('Array', () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { shallow } from 'enzyme'; // eslint-disable-line
|
||||
import Panel from '../Panel';
|
||||
|
||||
describe('Panel', () => {
|
||||
|
@ -22,7 +22,9 @@ class ArrayType extends React.Component {
|
||||
return (
|
||||
<Textarea
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
value={knob.value.join(knob.separator)}
|
||||
onChange={e => onChange(e.target.value.split(knob.separator))}
|
||||
@ -31,17 +33,20 @@ class ArrayType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ArrayType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
ArrayType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
ArrayType.serialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
ArrayType.deserialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
ArrayType.serialize = value => value;
|
||||
ArrayType.deserialize = value => value;
|
||||
|
||||
export default ArrayType;
|
||||
|
@ -19,28 +19,32 @@ class BooleanType extends React.Component {
|
||||
return (
|
||||
<input
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
type="checkbox"
|
||||
onChange={() => onChange(this.refs.input.checked)}
|
||||
onChange={() => onChange(this.input.checked)}
|
||||
checked={knob.value}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
BooleanType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
BooleanType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
BooleanType.serialize = function(value) {
|
||||
return String(value);
|
||||
};
|
||||
|
||||
BooleanType.deserialize = function(value) {
|
||||
if (!value) return false;
|
||||
return value.trim() === 'true';
|
||||
};
|
||||
BooleanType.serialize = value => String(value);
|
||||
BooleanType.deserialize = value => (typeof value === 'string' ? value.match('true') : false);
|
||||
|
||||
export default BooleanType;
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { document } from 'global';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { SketchPicker } from 'react-color';
|
||||
|
||||
const conditionalRender = (condition, positive, negative) => (condition ? positive() : negative());
|
||||
|
||||
const styles = {
|
||||
swatch: {
|
||||
background: '#fff',
|
||||
@ -58,6 +61,7 @@ class ColorType extends React.Component {
|
||||
|
||||
render() {
|
||||
const { knob, onChange } = this.props;
|
||||
const { displayColorPicker } = this.state;
|
||||
const colorStyle = {
|
||||
width: 'auto',
|
||||
height: '20px',
|
||||
@ -67,35 +71,40 @@ class ColorType extends React.Component {
|
||||
};
|
||||
return (
|
||||
<div id={knob.name}>
|
||||
<div style={styles.swatch} onClick={this.handleClick}>
|
||||
<div style={styles.swatch} onClick={this.handleClick} role="button" tabIndex="0">
|
||||
<div style={colorStyle} />
|
||||
</div>
|
||||
{this.state.displayColorPicker
|
||||
? <div
|
||||
{conditionalRender(
|
||||
displayColorPicker,
|
||||
() =>
|
||||
<div
|
||||
style={styles.popover}
|
||||
ref={e => {
|
||||
this.popover = e;
|
||||
}}
|
||||
>
|
||||
<SketchPicker color={knob.value} onChange={color => onChange(color.hex)} />
|
||||
</div>
|
||||
: null}
|
||||
</div>,
|
||||
() => null
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ColorType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
ColorType.serialize = function(value) {
|
||||
return value;
|
||||
ColorType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
ColorType.deserialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
ColorType.serialize = value => value;
|
||||
ColorType.deserialize = value => value;
|
||||
|
||||
export default ColorType;
|
||||
|
@ -21,33 +21,30 @@ const customStyle = `
|
||||
insertCss(style);
|
||||
insertCss(customStyle);
|
||||
|
||||
class DateType extends React.Component {
|
||||
render() {
|
||||
const { knob, onChange } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Datetime
|
||||
id={knob.name}
|
||||
value={knob.value ? new Date(knob.value) : null}
|
||||
type="date"
|
||||
onChange={date => onChange(date.valueOf())}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
const DateType = ({ knob, onChange }) =>
|
||||
<div>
|
||||
<Datetime
|
||||
id={knob.name}
|
||||
value={knob.value ? new Date(knob.value) : null}
|
||||
type="date"
|
||||
onChange={date => onChange(date.valueOf())}
|
||||
/>
|
||||
</div>;
|
||||
|
||||
DateType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
DateType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
DateType.serialize = function(value) {
|
||||
return String(value);
|
||||
};
|
||||
|
||||
DateType.deserialize = function(value) {
|
||||
return parseFloat(value);
|
||||
};
|
||||
DateType.serialize = value => String(value);
|
||||
DateType.deserialize = value => parseFloat(value);
|
||||
|
||||
export default DateType;
|
||||
|
@ -28,11 +28,13 @@ class NumberType extends React.Component {
|
||||
return (
|
||||
<input
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
value={knob.value}
|
||||
type="number"
|
||||
onChange={() => onChange(parseFloat(this.refs.input.value))}
|
||||
onChange={() => onChange(parseFloat(this.input.value))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -43,14 +45,16 @@ class NumberType extends React.Component {
|
||||
return (
|
||||
<input
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
value={knob.value}
|
||||
type="range"
|
||||
min={knob.min}
|
||||
max={knob.max}
|
||||
step={knob.step}
|
||||
onChange={() => onChange(parseFloat(this.refs.input.value))}
|
||||
onChange={() => onChange(parseFloat(this.input.value))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -62,17 +66,20 @@ class NumberType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
NumberType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
NumberType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
NumberType.serialize = function(value) {
|
||||
return String(value);
|
||||
};
|
||||
|
||||
NumberType.deserialize = function(value) {
|
||||
return parseFloat(value);
|
||||
};
|
||||
NumberType.serialize = value => String(value);
|
||||
NumberType.deserialize = value => parseFloat(value);
|
||||
|
||||
export default NumberType;
|
||||
|
@ -69,7 +69,9 @@ class ObjectType extends React.Component {
|
||||
return (
|
||||
<Textarea
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={{ ...styles, ...extraStyle }}
|
||||
value={jsonString}
|
||||
onChange={e => this.handleChange(e)}
|
||||
@ -78,18 +80,20 @@ class ObjectType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ObjectType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
ObjectType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
ObjectType.serialize = function(object) {
|
||||
return JSON.stringify(object);
|
||||
};
|
||||
|
||||
ObjectType.deserialize = function(value) {
|
||||
if (!value) return {};
|
||||
return JSON.parse(value);
|
||||
};
|
||||
ObjectType.serialize = object => JSON.stringify(object);
|
||||
ObjectType.deserialize = value => (value ? JSON.parse(value) : {});
|
||||
|
||||
export default ObjectType;
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
@ -40,7 +42,9 @@ class SelectType extends React.Component {
|
||||
return (
|
||||
<select
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
value={knob.value}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
@ -51,17 +55,20 @@ class SelectType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
SelectType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
SelectType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
SelectType.serialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
SelectType.deserialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
SelectType.serialize = value => value;
|
||||
SelectType.deserialize = value => value;
|
||||
|
||||
export default SelectType;
|
||||
|
@ -23,7 +23,9 @@ class TextType extends React.Component {
|
||||
return (
|
||||
<Textarea
|
||||
id={knob.name}
|
||||
ref="input"
|
||||
ref={c => {
|
||||
this.input = c;
|
||||
}}
|
||||
style={styles}
|
||||
value={knob.value}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
@ -32,17 +34,20 @@ class TextType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
TextType.defaultProps = {
|
||||
knob: {},
|
||||
onChange: value => value,
|
||||
};
|
||||
|
||||
TextType.propTypes = {
|
||||
knob: PropTypes.object,
|
||||
knob: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
}),
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
TextType.serialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
TextType.deserialize = function(value) {
|
||||
return value;
|
||||
};
|
||||
TextType.serialize = value => value;
|
||||
TextType.deserialize = value => value;
|
||||
|
||||
export default TextType;
|
||||
|
@ -33,6 +33,8 @@ storiesOf('Button', module)
|
||||
Have a look at the linkTo function:
|
||||
|
||||
```js
|
||||
import { linkTo } from '@storybook/addon-links'
|
||||
|
||||
linkTo('Toggle', 'off')
|
||||
```
|
||||
|
||||
|
@ -2,35 +2,35 @@
|
||||
"name": "@storybook/addon-links",
|
||||
"version": "3.0.0",
|
||||
"description": "Story Links addon for storybook",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/links",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"typings": "./storybook-addon-links.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy-storybook": "storybook-to-ghpages",
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9001"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/links",
|
||||
"devDependencies": {
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*"
|
||||
},
|
||||
"typings": "./storybook-addon-links.d.ts"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const ADDON_ID = 'kadirahq/storybook-addon-links';
|
||||
export const ADDON_ID = 'storybook/links';
|
||||
export const EVENT_ID = `${ADDON_ID}/link-event`;
|
||||
|
||||
export { register } from './manager';
|
||||
|
@ -2,7 +2,7 @@ import addons from '@storybook/addons';
|
||||
import { EVENT_ID } from './';
|
||||
|
||||
export function linkTo(kind, story) {
|
||||
return function(...args) {
|
||||
return (...args) => {
|
||||
const resolvedKind = typeof kind === 'function' ? kind(...args) : kind;
|
||||
const resolvedStory = typeof story === 'function' ? story(...args) : story;
|
||||
|
||||
|
@ -2,15 +2,26 @@
|
||||
"name": "@storybook/addon-notes",
|
||||
"version": "3.0.0",
|
||||
"description": "Write notes for your Storybook stories.",
|
||||
"keywords": [
|
||||
"addon",
|
||||
"react",
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9010",
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh"
|
||||
"publish-storybook": "bash .scripts/publish_storybook.sh",
|
||||
"storybook": "start-storybook -p 9010"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "*",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"prop-types": "^15.5.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"git-url-parse": "^6.2.2",
|
||||
@ -21,18 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "*",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"prop-types": "^15.5.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@types/react": "^15.0.24"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"keywords": [
|
||||
"react",
|
||||
"storybook",
|
||||
"addon"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -18,3 +18,7 @@ WithNotes.propTypes = {
|
||||
children: PropTypes.node,
|
||||
notes: PropTypes.string,
|
||||
};
|
||||
WithNotes.defaultProps = {
|
||||
children: null,
|
||||
notes: '',
|
||||
};
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import addons from '@storybook/addons';
|
||||
@ -59,8 +61,12 @@ export class Notes extends React.Component {
|
||||
}
|
||||
|
||||
Notes.propTypes = {
|
||||
channel: PropTypes.object,
|
||||
api: PropTypes.object,
|
||||
channel: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
api: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
};
|
||||
Notes.defaultProps = {
|
||||
channel: {},
|
||||
api: {},
|
||||
};
|
||||
|
||||
// Register the addon with a unique name.
|
||||
|
@ -1,2 +1,3 @@
|
||||
const manager = require('./dist/manager');
|
||||
|
||||
manager.init();
|
||||
|
@ -1,36 +1,36 @@
|
||||
{
|
||||
"name": "@storybook/addon-options",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "Options addon for storybook",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/options",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "preview.js",
|
||||
"typings": "./storybook-addon-options.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js",
|
||||
"storybook": "start-storybook -p 9001"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/addons/options",
|
||||
"devDependencies": {
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-dom": "*"
|
||||
},
|
||||
"typings": "./storybook-addon-options.d.ts"
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
const preview = require('./dist/preview');
|
||||
|
||||
exports.setOptions = preview.setOptions;
|
||||
preview.init();
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import addons from '@storybook/addons';
|
||||
import { ADDON_ID, EVENT_ID } from '../shared';
|
||||
|
||||
|
@ -11,5 +11,10 @@ export function init() {
|
||||
// ready. If called before, options will be cached until it can be sent.
|
||||
export function setOptions(options) {
|
||||
const channel = addons.getChannel();
|
||||
if (!channel) {
|
||||
throw new Error(
|
||||
'Failed to find addon channel. This may be due to https://github.com/storybooks/storybook/issues/1192.'
|
||||
);
|
||||
}
|
||||
channel.emit(EVENT_ID, { options });
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ By default, Storyshots assumes the config directory path for your project as bel
|
||||
If you are using a different config directory path, you could change it like this:
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({
|
||||
configPath: '.my-storybook-config-dir'
|
||||
});
|
||||
@ -71,6 +73,8 @@ initStoryshots({
|
||||
By default, Storyshots groups stories inside a Jest test suit called "Storyshots". You could change it like this:
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({
|
||||
suit: 'MyStoryshots'
|
||||
});
|
||||
@ -81,6 +85,8 @@ initStoryshots({
|
||||
If you'd like to only run a subset of the stories for your snapshot tests based on the story's kind:
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({
|
||||
storyKindRegex: /^MyComponent$/
|
||||
});
|
||||
@ -93,6 +99,8 @@ This can be useful if you want to separate the snapshots in directories next to
|
||||
If you'd like to only run a subset of the stories for your snapshot tests based on the story's name:
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({
|
||||
storyNameRegex: /buttons/
|
||||
});
|
||||
|
@ -2,16 +2,22 @@
|
||||
"name": "@storybook/addon-storyshots",
|
||||
"version": "3.0.0",
|
||||
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build-storybook": "build-storybook",
|
||||
"prepublish": "babel ./src --out-dir ./dist",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
"storybook": "start-storybook -p 6006"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"global": "^4.3.2",
|
||||
"prop-types": "^15.5.8",
|
||||
"read-pkg-up": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
@ -21,13 +27,10 @@
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.23.0",
|
||||
"global": "^4.3.2",
|
||||
"prop-types": "^15.5.8",
|
||||
"read-pkg-up": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"@storybook/channels": "^3.0.0",
|
||||
"babel-core": "^6.24.1",
|
||||
"react": "*",
|
||||
"react-test-renderer": "*"
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import path from 'path';
|
||||
import global from 'global';
|
||||
import global, { describe, it } from 'global';
|
||||
import readPkgUp from 'read-pkg-up';
|
||||
import addons from '@storybook/addons';
|
||||
|
||||
import runWithRequireContext from './require_context';
|
||||
import createChannel from './storybook-channel-mock';
|
||||
import { snapshot } from './test-bodies';
|
||||
const { describe, it, expect } = global;
|
||||
|
||||
export { snapshotWithOptions, snapshot, renderOnly } from './test-bodies';
|
||||
|
||||
@ -17,12 +17,9 @@ const babel = require('babel-core');
|
||||
|
||||
const pkg = readPkgUp.sync().pkg;
|
||||
|
||||
const hasDependency = function(name) {
|
||||
return (
|
||||
(pkg.devDependencies && pkg.devDependencies[name]) ||
|
||||
(pkg.dependencies && pkg.dependencies[name])
|
||||
);
|
||||
};
|
||||
const hasDependency = name =>
|
||||
(pkg.devDependencies && pkg.devDependencies[name]) ||
|
||||
(pkg.dependencies && pkg.dependencies[name]);
|
||||
|
||||
export default function testStorySnapshots(options = {}) {
|
||||
addons.setChannel(createChannel());
|
||||
@ -33,16 +30,17 @@ export default function testStorySnapshots(options = {}) {
|
||||
|
||||
if (isStorybook) {
|
||||
storybook = require.requireActual('@storybook/react');
|
||||
// eslint-disable-next-line
|
||||
const loadBabelConfig = require('@storybook/react/dist/server/babel_config').default;
|
||||
const configDirPath = path.resolve(options.configPath || '.storybook');
|
||||
configPath = path.join(configDirPath, 'config.js');
|
||||
|
||||
const babelConfig = loadBabelConfig(configDirPath);
|
||||
const content = babel.transformFileSync(configPath, babelConfig).code;
|
||||
const contextOpts = {
|
||||
filename: configPath,
|
||||
dirname: configDirPath,
|
||||
};
|
||||
const babelConfig = loadBabelConfig(configDirPath);
|
||||
|
||||
runWithRequireContext(content, contextOpts);
|
||||
} else if (isRNStorybook) {
|
||||
@ -61,19 +59,24 @@ export default function testStorySnapshots(options = {}) {
|
||||
const stories = storybook.getStorybook();
|
||||
|
||||
// Added not to break existing storyshots configs (can be removed in a future major release)
|
||||
// eslint-disable-next-line
|
||||
options.storyNameRegex = options.storyNameRegex || options.storyRegex;
|
||||
|
||||
// eslint-disable-next-line
|
||||
options.test = options.test || snapshot;
|
||||
|
||||
// eslint-disable-next-line
|
||||
for (const group of stories) {
|
||||
if (options.storyKindRegex && !group.kind.match(options.storyKindRegex)) {
|
||||
// eslint-disable-next-line
|
||||
continue;
|
||||
}
|
||||
|
||||
describe(suit, () => {
|
||||
describe(group.kind, () => {
|
||||
// eslint-disable-next-line
|
||||
for (const story of group.stories) {
|
||||
if (options.storyNameRegex && !story.name.match(options.storyNameRegex)) {
|
||||
// eslint-disable-next-line
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ function requireModules(keys, root, directory, regExp, recursive) {
|
||||
// TODO: Check this in windows
|
||||
const entryKey = `./${path.join(directory, filename)}`;
|
||||
if (regExp.test(entryKey)) {
|
||||
keys[entryKey] = require(path.join(root, directory, filename));
|
||||
keys[entryKey] = require(path.join(root, directory, filename)); // eslint-disable-line
|
||||
return;
|
||||
}
|
||||
|
||||
@ -45,10 +45,10 @@ export default function runWithRequireContext(content, options) {
|
||||
|
||||
const newRequire = request => {
|
||||
if (isRelativeRequest(request)) {
|
||||
return require(path.resolve(dirname, request));
|
||||
return require(path.resolve(dirname, request)); // eslint-disable-line
|
||||
}
|
||||
|
||||
return require(request);
|
||||
return require(request); // eslint-disable-line
|
||||
};
|
||||
|
||||
newRequire.resolve = require.resolve;
|
||||
|
@ -10,5 +10,5 @@ export const snapshot = snapshotWithOptions({});
|
||||
|
||||
export function renderOnly({ story, context }) {
|
||||
const storyElement = story.render(context);
|
||||
const tree = renderer.create(storyElement);
|
||||
renderer.create(storyElement);
|
||||
}
|
||||
|
@ -11,11 +11,14 @@ const buttonStyles = {
|
||||
margin: 10,
|
||||
};
|
||||
|
||||
const Button = ({ children, onClick }) => (
|
||||
const Button = ({ children, onClick }) =>
|
||||
<button style={buttonStyles} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
</button>;
|
||||
|
||||
Button.defaultProps = {
|
||||
onClick: null,
|
||||
};
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.string.isRequired,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { storiesOf } from '@storybook/react'; // eslint-disable-line
|
||||
import { action } from '@storybook/addon-actions'; // eslint-disable-line
|
||||
import Button from './Button';
|
||||
|
||||
storiesOf('Another Button', module)
|
||||
|
@ -11,15 +11,17 @@ const buttonStyles = {
|
||||
margin: 10,
|
||||
};
|
||||
|
||||
const Button = ({ children, onClick }) => (
|
||||
const Button = ({ children, onClick }) =>
|
||||
<button style={buttonStyles} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
</button>;
|
||||
|
||||
Button.propTypes = {
|
||||
children: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
Button.defaultProps = {
|
||||
onClick: null,
|
||||
};
|
||||
|
||||
export default Button;
|
||||
|
@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
import { storiesOf } from '@storybook/react'; // eslint-disable-line
|
||||
import { action } from '@storybook/addon-actions'; // eslint-disable-line
|
||||
|
||||
import Button from './Button';
|
||||
import Welcome from './Welcome';
|
||||
|
||||
storiesOf('Button', module)
|
||||
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
|
||||
|
@ -1,4 +1,7 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const styles = {
|
||||
main: {
|
||||
@ -28,12 +31,24 @@ const styles = {
|
||||
backgroundColor: '#f3f2f2',
|
||||
color: '#3a3a3a',
|
||||
},
|
||||
|
||||
note: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const log = () => console.log('Welcome to storybook!');
|
||||
|
||||
export default class Welcome extends React.Component {
|
||||
showApp(e) {
|
||||
e.preventDefault();
|
||||
if (this.props.showApp) this.props.showApp();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.clickHandler = event => {
|
||||
event.preventDefault();
|
||||
|
||||
const { showApp } = this.props;
|
||||
showApp();
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -50,14 +65,15 @@ export default class Welcome extends React.Component {
|
||||
{' '}
|
||||
directory.
|
||||
<br />
|
||||
A story is a single state of one or more UI components. You can have as many stories as you want.
|
||||
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 style={styles.link} href="#" onClick={this.showApp.bind(this)}>stories</a>
|
||||
<a style={styles.link} onClick={this.clickHandler} role="button" tabIndex="0">stories</a>
|
||||
{' '}
|
||||
for a component called
|
||||
{' '}
|
||||
@ -70,20 +86,42 @@ export default class Welcome extends React.Component {
|
||||
You can also edit those components and see changes right away.
|
||||
<br />
|
||||
(Try editing the <code style={styles.code}>Button</code> component
|
||||
located at <code style={styles.code}>stories/Button.js</code>.)
|
||||
located at <code style={styles.code}>src/stories/Button.js</code>.)
|
||||
</p>
|
||||
<p>
|
||||
This is just one thing you can do with Storybook.
|
||||
<br />
|
||||
Have a look at the
|
||||
{' '}
|
||||
<a style={styles.link} href="https://github.com/storybooks/storybook" target="_blank">
|
||||
Storybook for React
|
||||
<a
|
||||
style={styles.link}
|
||||
href="https://github.com/storybooks/storybook"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Storybook
|
||||
</a>
|
||||
{' '}
|
||||
repo for more information.
|
||||
</p>
|
||||
<p style={styles.note}>
|
||||
<b>NOTE:</b>
|
||||
<br />
|
||||
Have a look at the
|
||||
{' '}
|
||||
<code style={styles.code}>.storybook/webpack.config.js</code>
|
||||
{' '}
|
||||
to add webpack
|
||||
loaders and plugins you are using in this project.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Welcome.propTypes = {
|
||||
showApp: PropTypes.function,
|
||||
};
|
||||
Welcome.defaultProps = {
|
||||
showApp: log,
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
import { storiesOf } from '@storybook/react'; // eslint-disable-line
|
||||
import { linkTo } from '@storybook/addon-links'; // eslint-disable-line
|
||||
|
||||
import Welcome from './Welcome';
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
First, install the `@storybook/react-native` module
|
||||
|
||||
```shell
|
||||
npm i -D @storybook/react-native
|
||||
```sh
|
||||
npm install @storybook/react-native --save-dev
|
||||
```
|
||||
|
||||
Create a new directory called `storybook` in your project root and create an entry file (index.ios.js or index.android.js) as given below. (Don't forget to replace "MyApplicationName" with your app name).
|
||||
@ -18,7 +18,10 @@ configure(function() {
|
||||
require('./stories');
|
||||
}, module);
|
||||
|
||||
const StorybookUI = getStorybookUI({port: 7007, host: 'localhost'});
|
||||
const StorybookUI = getStorybookUI({
|
||||
port: 7007,
|
||||
host: 'localhost',
|
||||
});
|
||||
AppRegistry.registerComponent('MyApplicationName', () => StorybookUI);
|
||||
```
|
||||
|
||||
@ -32,9 +35,6 @@ import '@storybook/addon-links';
|
||||
Then write your first story in the `stories` directory like this:
|
||||
|
||||
```js
|
||||
// index.js
|
||||
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
|
||||
@ -44,10 +44,9 @@ const style = {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF'
|
||||
};
|
||||
|
||||
const CenteredView = (props) => (
|
||||
const CenteredView = ({ children }) => (
|
||||
<View style={style}>
|
||||
{props.children}
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
|
@ -1,44 +1,34 @@
|
||||
{
|
||||
"name": "@storybook/react-native",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"description": "A better way to develop React Native Components for your app",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"storybook": "dist/bin/storybook.js"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/app/react-native",
|
||||
"bugs": {
|
||||
"url": "https://github.com/storybooks/storybook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/app/react-native",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "0.27.0 - 0.43.x"
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"storybook": "dist/bin/storybook.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-native": "^0.43.3"
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/storybooks/storybook.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "node ../../scripts/prepublish.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addon-actions": "^3.0.0",
|
||||
"@storybook/addon-actions": "^3.0.1",
|
||||
"@storybook/addon-links": "^3.0.0",
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"@storybook/channel-websocket": "^3.0.0",
|
||||
"@storybook/ui": "^3.0.0",
|
||||
"@storybook/ui": "^3.0.1",
|
||||
"autoprefixer": "^7.0.1",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-loader": "^7.0.0",
|
||||
@ -61,6 +51,7 @@
|
||||
"events": "^1.1.1",
|
||||
"express": "^4.15.2",
|
||||
"file-loader": "^0.11.1",
|
||||
"global": "^4.3.2",
|
||||
"json-loader": "^0.5.4",
|
||||
"json5": "^0.5.1",
|
||||
"postcss-loader": "^2.0.3",
|
||||
@ -71,6 +62,17 @@
|
||||
"uuid": "^3.0.1",
|
||||
"webpack": "^2.4.1",
|
||||
"webpack-dev-middleware": "^1.10.1",
|
||||
"webpack-hot-middleware": "^2.18.0"
|
||||
"webpack-hot-middleware": "^2.18.0",
|
||||
"ws": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-native": "^0.43.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "0.27.0 - 0.43.x"
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ There are multiple options here. for example, you can export conditionally:
|
||||
|
||||
```js
|
||||
import StorybookUI from './storybook';
|
||||
|
||||
import App from './app';
|
||||
|
||||
module.exports = __DEV__ ? StorybookUI : App;
|
||||
```
|
||||
|
||||
|
2
app/react-native/src/bin/storybook-start.js
vendored
2
app/react-native/src/bin/storybook-start.js
vendored
@ -36,7 +36,7 @@ server.listen(...listenAddr, err => {
|
||||
throw err;
|
||||
}
|
||||
const address = `http://${program.host || 'localhost'}:${program.port}/`;
|
||||
console.info(`\nReact Native Storybook started on => ${address}\n`);
|
||||
console.info(`\nReact Native Storybook started on => ${address}\n`); // eslint-disable-line no-console
|
||||
});
|
||||
|
||||
if (!program.skipPackager) {
|
||||
|
9
app/react-native/src/index.js
vendored
9
app/react-native/src/index.js
vendored
@ -1,4 +1,9 @@
|
||||
import deprecate from 'util-deprecate';
|
||||
|
||||
// NOTE export these to keep backwards compatibility
|
||||
import { action as deprecatedAction } from '@storybook/addon-actions';
|
||||
import { linkTo as deprecatedLinkTo } from '@storybook/addon-links';
|
||||
|
||||
import Preview from './preview';
|
||||
|
||||
const preview = new Preview();
|
||||
@ -10,10 +15,6 @@ export const configure = preview.configure.bind(preview);
|
||||
export const getStorybook = preview.getStorybook.bind(preview);
|
||||
export const getStorybookUI = preview.getStorybookUI.bind(preview);
|
||||
|
||||
// NOTE export these to keep backwards compatibility
|
||||
import { action as deprecatedAction } from '@storybook/addon-actions';
|
||||
import { linkTo as deprecatedLinkTo } from '@storybook/addon-links';
|
||||
|
||||
export const action = deprecate(
|
||||
deprecatedAction,
|
||||
'@storybook/react action is deprecated. See: https://github.com/storybooks/storybook/tree/master/addon/actions'
|
||||
|
@ -1,46 +1,4 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class PreviewHelp extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div style={styles.main}>
|
||||
<h1>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
|
||||
{' '}
|
||||
<span style={styles.code}>storybook/stories</span>
|
||||
{' '}
|
||||
directory.
|
||||
{' '}
|
||||
A story is a single state of one or more UI components. You can have as many stories as you want. Basically a story is like a visual test case.
|
||||
</p>
|
||||
<p>
|
||||
To see your Storybook stories on the device, you should start your mobile app for the
|
||||
{' '}
|
||||
<span style={styles.code}><platform></span>
|
||||
{' '}
|
||||
of your choice (typically ios or android).
|
||||
</p>
|
||||
<p>
|
||||
For <span style={styles.code}>create-react-native-app</span> apps:
|
||||
</p>
|
||||
<div style={styles.codeBlock}>
|
||||
<pre style={styles.instructionsCode}>npm run <platform></pre>
|
||||
</div>
|
||||
<p>
|
||||
For <span style={styles.code}>react-native init</span> apps:
|
||||
</p>
|
||||
<div style={styles.codeBlock}>
|
||||
<pre style={styles.instructionsCode}>npm run <platform></pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import React from 'react';
|
||||
|
||||
const styles = {
|
||||
main: {
|
||||
@ -67,4 +25,41 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default PreviewHelp;
|
||||
const PreviewHelp = () =>
|
||||
<div style={styles.main}>
|
||||
<h1>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
|
||||
{' '}
|
||||
<span style={styles.code}>storybook/stories</span>
|
||||
{' '}
|
||||
directory.
|
||||
{' '}
|
||||
A story is a single state of one or more UI components. You can have as many stories as you
|
||||
want. Basically a story is like a visual test case.
|
||||
</p>
|
||||
<p>
|
||||
To see your Storybook stories on the device, you should start your mobile app for the
|
||||
{' '}
|
||||
<span style={styles.code}><platform></span>
|
||||
{' '}
|
||||
of your choice (typically ios or android).
|
||||
</p>
|
||||
<p>
|
||||
For <span style={styles.code}>create-react-native-app</span> apps:
|
||||
</p>
|
||||
<div style={styles.codeBlock}>
|
||||
<pre style={styles.instructionsCode}>npm run <platform></pre>
|
||||
</div>
|
||||
<p>
|
||||
For <span style={styles.code}>react-native init</span> apps:
|
||||
</p>
|
||||
<div style={styles.codeBlock}>
|
||||
<pre style={styles.instructionsCode}>npm run <platform></pre>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
export { PreviewHelp as default };
|
||||
|
1
app/react-native/src/manager/index.js
vendored
1
app/react-native/src/manager/index.js
vendored
@ -1,3 +1,4 @@
|
||||
import { window, location, document } from 'global';
|
||||
import renderStorybookUI from '@storybook/ui';
|
||||
import Provider from './provider';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import style from './style';
|
||||
|
||||
@ -49,3 +49,11 @@ export default class StoryView extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
StoryView.propTypes = {
|
||||
events: PropTypes.shape({
|
||||
on: PropTypes.func.isRequired,
|
||||
removeListener: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
};
|
||||
|
5
app/react-native/src/preview/index.js
vendored
5
app/react-native/src/preview/index.js
vendored
@ -1,3 +1,5 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
import React from 'react';
|
||||
import addons from '@storybook/addons';
|
||||
import createChannel from '@storybook/channel-websocket';
|
||||
@ -66,10 +68,11 @@ export default class Preview {
|
||||
channel = createChannel({ url });
|
||||
addons.setChannel(channel);
|
||||
}
|
||||
channel.on('getStories', d => this._sendSetStories());
|
||||
channel.on('getStories', () => this._sendSetStories());
|
||||
channel.on('setCurrentStory', d => this._selectStory(d));
|
||||
this._sendSetStories();
|
||||
this._sendGetCurrentStory();
|
||||
|
||||
// finally return the preview component
|
||||
return <StoryView url={webUrl} events={this._events} />;
|
||||
};
|
||||
|
2
app/react-native/src/preview/story_kind.js
vendored
2
app/react-native/src/preview/story_kind.js
vendored
@ -1,3 +1,5 @@
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
|
||||
export default class StoryKindApi {
|
||||
constructor(stories, addons, decorators, kind) {
|
||||
this.kind = kind;
|
||||
|
8
app/react-native/src/preview/story_store.js
vendored
8
app/react-native/src/preview/story_store.js
vendored
@ -1,4 +1,5 @@
|
||||
let cnt = 0;
|
||||
/* eslint no-underscore-dangle: 0 */
|
||||
let count = 0;
|
||||
|
||||
export default class StoryStore {
|
||||
constructor() {
|
||||
@ -6,17 +7,18 @@ export default class StoryStore {
|
||||
}
|
||||
|
||||
addStory(kind, name, fn) {
|
||||
count += 1;
|
||||
if (!this._data[kind]) {
|
||||
this._data[kind] = {
|
||||
kind,
|
||||
index: cnt++,
|
||||
index: count,
|
||||
stories: {},
|
||||
};
|
||||
}
|
||||
|
||||
this._data[kind].stories[name] = {
|
||||
name,
|
||||
index: cnt++,
|
||||
index: count,
|
||||
fn,
|
||||
};
|
||||
}
|
||||
|
2
app/react-native/src/server/config.js
vendored
2
app/react-native/src/server/config.js
vendored
@ -101,7 +101,7 @@ export default function(configType, baseConfig, projectDir, configDir) {
|
||||
customConfigPath = path.resolve(__dirname, './config/defaults/webpack.config.js');
|
||||
}
|
||||
|
||||
const customConfig = require(customConfigPath);
|
||||
const customConfig = require(customConfigPath); // eslint-disable-line
|
||||
|
||||
if (typeof customConfig === 'function') {
|
||||
logger.info('=> Loading custom webpack config (full-control mode).');
|
||||
|
@ -23,7 +23,7 @@ const config = {
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
query: require('./babel.js'),
|
||||
query: require('./babel.js'), // eslint-disable-line
|
||||
include: includePaths,
|
||||
exclude: excludePaths,
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ const config = {
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
query: require('./babel.prod.js'),
|
||||
query: require('./babel.prod.js'), // eslint-disable-line
|
||||
include: includePaths,
|
||||
exclude: excludePaths,
|
||||
},
|
||||
|
6
app/react-native/src/server/index.js
vendored
6
app/react-native/src/server/index.js
vendored
@ -11,7 +11,7 @@ export default class Server {
|
||||
this.expressApp = express();
|
||||
this.expressApp.use(storybook(options));
|
||||
this.httpServer.on('request', this.expressApp);
|
||||
this.wsServer = ws.Server({ server: this.httpServer });
|
||||
this.wsServer = new ws.Server({ server: this.httpServer });
|
||||
this.wsServer.on('connection', s => this.handleWS(s));
|
||||
}
|
||||
|
||||
@ -22,14 +22,14 @@ export default class Server {
|
||||
: {};
|
||||
|
||||
if (params.pairedId) {
|
||||
socket.pairedId = params.pairedId;
|
||||
socket.pairedId = params.pairedId; // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('message', data => {
|
||||
this.wsServer.clients.forEach(c => {
|
||||
if (!this.options.manualId || (socket.pairedId && socket.pairedId === c.pairedId)) {
|
||||
return c.send(data);
|
||||
c.send(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
6
app/react-native/src/server/middleware.js
vendored
6
app/react-native/src/server/middleware.js
vendored
@ -12,13 +12,13 @@ import getIndexHtml from './index.html';
|
||||
function getMiddleware(configDir) {
|
||||
const middlewarePath = path.resolve(configDir, 'middleware.js');
|
||||
if (fs.existsSync(middlewarePath)) {
|
||||
let middlewareModule = require(middlewarePath);
|
||||
if (middlewareModule.__esModule) {
|
||||
let middlewareModule = require(middlewarePath); // eslint-disable-line
|
||||
if (middlewareModule.__esModule) { // eslint-disable-line
|
||||
middlewareModule = middlewareModule.default;
|
||||
}
|
||||
return middlewareModule;
|
||||
}
|
||||
return function() {};
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export default function({ projectDir, configDir, ...options }) {
|
||||
|
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