diff --git a/.eslintignore b/.eslintignore index f55dae391395..1e263e0ef99a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,11 @@ dist build coverage node_modules +**/example/** +**/demo/** docs/public + +!.remarkrc.js +!.eslintrc.js +!.eslintrc-markdown.js +!.jest.config.js diff --git a/.eslintrc-markdown.js b/.eslintrc-markdown.js new file mode 100644 index 000000000000..3896c2a97c0e --- /dev/null +++ b/.eslintrc-markdown.js @@ -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, + }, +}; diff --git a/.eslintrc.js b/.eslintrc.js index 30971bdb41c6..e30a1b5fde05 100644 --- a/.eslintrc.js +++ b/.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, }, -} +}; diff --git a/.remarkrc b/.remarkrc deleted file mode 100644 index 3c79efa3cdfa..000000000000 --- a/.remarkrc +++ /dev/null @@ -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 - }] - ] -} diff --git a/.remarkrc.js b/.remarkrc.js new file mode 100644 index 000000000000..078472d14052 --- /dev/null +++ b/.remarkrc.js @@ -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, + }, + ], + ], +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index 70db84882929..20b5a6f753cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,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) @@ -116,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 [#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) diff --git a/README.md b/README.md index be2f49534eba..7b2212e9250b 100644 --- a/README.md +++ b/README.md @@ -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` diff --git a/addons/actions/README.md b/addons/actions/README.md index 086946b23ed0..7038dd0aca34 100644 --- a/addons/actions/README.md +++ b/addons/actions/README.md @@ -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) ]); diff --git a/addons/actions/package.json b/addons/actions/package.json index 9e6f1e337b5c..4981952747d5 100644 --- a/addons/actions/package.json +++ b/addons/actions/package.json @@ -2,29 +2,23 @@ "name": "@storybook/addon-actions", "version": "3.0.1", "description": "Action Logger addon for storybook", - "main": "dist/index.js", - "scripts": { - "deploy-storybook": "storybook-to-ghpages", - "prepublish": "node ../../scripts/prepublish.js", - "storybook": "start-storybook -p 9001" - }, - "repository": { - "type": "git", - "url": "https://github.com/storybooks/storybook.git" - }, "keywords": [ "storybook" ], - "license": "MIT", + "homepage": "https://github.com/storybooks/storybook/tree/master/addons/actions", "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" + "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" }, "dependencies": { "@storybook/addons": "^3.0.0", @@ -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": "*" diff --git a/addons/actions/src/components/ActionLogger/index.js b/addons/actions/src/components/ActionLogger/index.js index 203d2f1bf4d8..5c2566495ff0 100644 --- a/addons/actions/src/components/ActionLogger/index.js +++ b/addons/actions/src/components/ActionLogger/index.js @@ -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 =
{action.count}
; return (
@@ -34,10 +38,6 @@ class ActionLogger extends Component { ); } - getActionData() { - return this.props.actions.map((action, i) => this.renderAction(action, i)); - } - render() { return (
@@ -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; diff --git a/addons/actions/src/containers/ActionLogger/index.js b/addons/actions/src/containers/ActionLogger/index.js index e67fe5425a80..f3c1e19a9af0 100644 --- a/addons/actions/src/containers/ActionLogger/index.js +++ b/addons/actions/src/containers/ActionLogger/index.js @@ -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 ; } } + +ActionLogger.propTypes = { + channel: PropTypes.object, // eslint-disable-line react/forbid-prop-types +}; +ActionLogger.defaultProps = { + channel: {}, +}; diff --git a/addons/actions/src/index.js b/addons/actions/src/index.js index a29e5c86279e..549b155e9c1c 100644 --- a/addons/actions/src/index.js +++ b/addons/actions/src/index.js @@ -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`; diff --git a/addons/actions/src/manager.js b/addons/actions/src/manager.js index 40b57852a912..a931da6d52cd 100644 --- a/addons/actions/src/manager.js +++ b/addons/actions/src/manager.js @@ -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', diff --git a/addons/actions/src/preview.js b/addons/actions/src/preview.js index e3b58189af73..0e8a0a85d7d2 100644 --- a/addons/actions/src/preview.js +++ b/addons/actions/src/preview.js @@ -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); diff --git a/addons/centered/README.md b/addons/centered/README.md index 3e2678fcf2e8..fd1524840e20 100644 --- a/addons/centered/README.md +++ b/addons/centered/README.md @@ -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', () => ()) @@ -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) diff --git a/addons/centered/package.json b/addons/centered/package.json index ebed5c19a930..02997e85f1aa 100644 --- a/addons/centered/package.json +++ b/addons/centered/package.json @@ -2,10 +2,13 @@ "name": "@storybook/addon-centered", "version": "3.0.0", "description": "Storybook decorator to center components", + "license": "MIT", + "author": "Muhammed Thanish ", "main": "dist/index.js", "scripts": { "prepublish": "node ../../scripts/prepublish.js" }, - "author": "Muhammed Thanish ", - "license": "MIT" + "peerDependencies": { + "react": "*" + } } diff --git a/addons/comments/manager.js b/addons/comments/manager.js index 68d13f4d0bf1..67d49f75b482 100644 --- a/addons/comments/manager.js +++ b/addons/comments/manager.js @@ -1,2 +1,3 @@ const manager = require('./dist/manager'); + manager.init(); diff --git a/addons/comments/package.json b/addons/comments/package.json index ebb872e62782..5de3651410db 100644 --- a/addons/comments/package.json +++ b/addons/comments/package.json @@ -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", diff --git a/addons/comments/preview.js b/addons/comments/preview.js index 06f2f2a7e5bd..80c4e66a8ee8 100644 --- a/addons/comments/preview.js +++ b/addons/comments/preview.js @@ -1,2 +1,3 @@ const preview = require('./dist/preview'); + preview.init(); diff --git a/addons/comments/src/index.js b/addons/comments/src/index.js index 4a7863a01254..3ac7f91f9a98 100644 --- a/addons/comments/src/index.js +++ b/addons/comments/src/index.js @@ -10,16 +10,20 @@ const buttonStyles = { padding: '3px 10px', }; -const Button = ({ children, onClick, style = {} }) => ( +const Button = ({ children, onClick, style = {} }) => -); + ; + +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; diff --git a/addons/comments/src/manager/components/CommentForm/index.js b/addons/comments/src/manager/components/CommentForm/index.js index c2259db8fdcb..4b09af023cdc 100644 --- a/addons/comments/src/manager/components/CommentForm/index.js +++ b/addons/comments/src/manager/components/CommentForm/index.js @@ -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 (
-