From 6bf401b3bb64596ae976e309948c3ba8384c1be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Ramos?= Date: Tue, 25 Oct 2016 20:15:33 +0100 Subject: [PATCH] react and a11y linting --- frontend/.eslintrc | 83 ++++++++++++++++++++-- frontend/package.json | 1 + frontend/src/containers/app.js | 15 +++- frontend/src/containers/home/index.js | 4 +- frontend/src/containers/not-found/index.js | 4 +- frontend/yarn.lock | 14 +++- ui/.eslintignore | 3 +- ui/.eslintrc | 83 ++++++++++++++++++++-- ui/docs/containers/app/index.js | 13 +++- ui/docs/containers/app/navigation.js | 4 +- ui/docs/containers/home.js | 2 +- ui/docs/containers/item/index.js | 8 ++- ui/package.json | 1 + ui/src/components/base/index.js | 14 ++-- ui/src/components/button/index.js | 14 +++- ui/src/components/column/index.js | 14 ++-- ui/src/components/container/index.js | 12 ++-- ui/src/components/row/index.js | 12 ++-- ui/webpack/base.js | 7 +- ui/webpack/production.js | 1 - ui/webpack/test.js | 4 +- ui/yarn.lock | 14 +++- 22 files changed, 270 insertions(+), 57 deletions(-) diff --git a/frontend/.eslintrc b/frontend/.eslintrc index 55e4244d..0f6b9fe1 100644 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -10,19 +10,90 @@ }, "plugins": [ "babel", - "react" + "react", + "jsx-a11y" ], "rules": { + "react/jsx-no-undef": 2, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/no-deprecated": 2, + "react/no-direct-mutation-state": 2, + "react/no-find-dom-node": 2, + "react/no-is-mounted": 2, + "react/no-unknown-property": 2, + "react/no-render-return-value": 2, + "react/prop-types": 2, + "react/react-in-jsx-scope": 2, + "react/require-render-return": 2, + "react/no-children-prop": 2, + "react/no-danger": 2, + "react/no-danger-with-children": 2, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-multi-comp": 1, + "react/no-string-refs": 2, + "react/no-unescaped-entities": 2, + "react/no-unused-prop-types": 2, + "react/prefer-stateless-function": 2, + "react/self-closing-comp": 2, + "react/sort-comp": 2, + "react/sort-prop-types": 2, + "react/style-prop-object": 2, + "react/jsx-boolean-value": [2, "never"], + "react/jsx-closing-bracket-location": 2, + "react/jsx-curly-spacing": [2, "never"], + "react/jsx-equals-spacing": [2, "never"], + "react/jsx-filename-extension": [2, { + "extensions": [".js"] + }], + "react/jsx-first-prop-new-line": [2, "multiline"], + "react/jsx-handler-names": 2, + "react/jsx-indent": [2, 2], + "react/jsx-indent-props": [2, 2], + "react/jsx-key": 2, + "react/jsx-max-props-per-line": [2, { + "maximum": 2 + }], + "react/jsx-no-bind": 2, + "react/jsx-no-comment-textnodes": 2, + "react/jsx-no-duplicate-props": 2, + "react/jsx-no-target-blank": 2, + "react/jsx-pascal-case": 2, + "react/jsx-sort-props": 2, + "react/jsx-space-before-closing": 2, + "react/jsx-wrap-multilines": 2, + "jsx-a11y/anchor-has-content": 2, + "jsx-a11y/href-no-hash": 2, + "jsx-a11y/aria-props": 2, + "jsx-a11y/aria-proptypes": 2, + "jsx-a11y/aria-role": 2, + "jsx-a11y/aria-unsupported-elements": 2, + "jsx-a11y/click-events-have-key-events": 2, + "jsx-a11y/mouse-events-have-key-events": 2, + "jsx-a11y/heading-has-content": 2, + "jsx-a11y/html-has-lang": 2, + "jsx-a11y/img-has-alt": 2, + "jsx-a11y/img-redundant-alt": 2, + "jsx-a11y/label-has-for": 2, + "jsx-a11y/lang": 2, + "jsx-a11y/no-access-key": 2, + "jsx-a11y/no-marquee": 2, + "jsx-a11y/no-onchange": 2, + "jsx-a11y/no-static-element-interactions": 2, + "jsx-a11y/onclick-has-focus": 2, + "jsx-a11y/onclick-has-role": 2, + "jsx-a11y/role-has-required-aria-props": 2, + "jsx-a11y/role-supports-aria-props": 2, + "jsx-a11y/scope": 2, + "jsx-a11y/tabindex-no-positive": 2, "generator-star-spacing": 0, "babel/generator-star-spacing": 1, "space-before-function-paren": [2, "never"], - "react/jsx-uses-react": 2, - "react/jsx-uses-vars": 2, - "react/react-in-jsx-scope": 2, - "object-curly-newline": ["error", { + "object-curly-newline": [2, { "minProperties": 1 }], - "sort-vars": ["error", { + "sort-vars": [2, { "ignoreCase": true }] } diff --git a/frontend/package.json b/frontend/package.json index c7cae2ac..a23f964d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -62,6 +62,7 @@ "eslint-config-semistandard": "^7.0.0", "eslint-config-standard": "^6.2.0", "eslint-plugin-babel": "^3.3.0", + "eslint-plugin-jsx-a11y": "^2.2.3", "eslint-plugin-promise": "^3.3.0", "eslint-plugin-react": "^6.4.1", "eslint-plugin-standard": "^2.0.1", diff --git a/frontend/src/containers/app.js b/frontend/src/containers/app.js index b2145cce..1b317608 100644 --- a/frontend/src/containers/app.js +++ b/frontend/src/containers/app.js @@ -20,6 +20,11 @@ const { } = ReactRouter; const App = connect()(React.createClass({ + propTypes: { + children: React.PropTypes.node, + dispatch: React.PropTypes.func, + router: React.PropTypes.object + }, componentWillMount: function() { const { router, @@ -51,8 +56,14 @@ const App = connect()(React.createClass({ module.exports = (props) => { return ( - - + + ); }; diff --git a/frontend/src/containers/home/index.js b/frontend/src/containers/home/index.js index fedce866..5a51cb1d 100644 --- a/frontend/src/containers/home/index.js +++ b/frontend/src/containers/home/index.js @@ -6,7 +6,7 @@ const { FormattedMessage } = ReactIntl; -module.exports = () => { +const Home = () => { return (

@@ -15,3 +15,5 @@ module.exports = () => {

); }; + +module.exports = Home; diff --git a/frontend/src/containers/not-found/index.js b/frontend/src/containers/not-found/index.js index b5ae7b4d..6e90fd6a 100644 --- a/frontend/src/containers/not-found/index.js +++ b/frontend/src/containers/not-found/index.js @@ -1,6 +1,6 @@ const React = require('react'); -module.exports = () => { +const NotFound = () => { return (

404

@@ -8,3 +8,5 @@ module.exports = () => {
); }; + +module.exports = NotFound; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 88e1a905..fd8da6be 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1928,6 +1928,10 @@ d@^0.1.1, d@~0.1.1: dependencies: es5-ext "~0.10.2" +damerau-levenshtein@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.3.tgz#ae4f4ce0b62acae10ff63a01bb08f652f5213af2" + dashdash@^1.12.0: version "1.14.0" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.0.tgz#29e486c5418bf0f356034a993d51686a33e84141" @@ -2279,6 +2283,14 @@ eslint-plugin-babel@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-3.3.0.tgz#2f494aedcf6f4aa4e75b9155980837bc1fbde193" +eslint-plugin-jsx-a11y: + version "2.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-2.2.3.tgz#4e35cb71b8a7db702ac415c806eb8e8d9ea6c65d" + dependencies: + damerau-levenshtein "^1.0.0" + jsx-ast-utils "^1.0.0" + object-assign "^4.0.1" + eslint-plugin-promise@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.3.0.tgz#20a1ef58b4243ffdaef82ee9360a02353a7cca89" @@ -3443,7 +3455,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -jsx-ast-utils@^1.3.1: +jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.2.tgz#dff658782705352111f9865d40471bc4a955961e" dependencies: diff --git a/ui/.eslintignore b/ui/.eslintignore index a6f80ca9..c49b1f00 100644 --- a/ui/.eslintignore +++ b/ui/.eslintignore @@ -2,4 +2,5 @@ coverage .nyc_output docs/static -static \ No newline at end of file +static +webpack/embed-markdown-loader \ No newline at end of file diff --git a/ui/.eslintrc b/ui/.eslintrc index 55e4244d..0f6b9fe1 100644 --- a/ui/.eslintrc +++ b/ui/.eslintrc @@ -10,19 +10,90 @@ }, "plugins": [ "babel", - "react" + "react", + "jsx-a11y" ], "rules": { + "react/jsx-no-undef": 2, + "react/jsx-uses-react": 2, + "react/jsx-uses-vars": 2, + "react/no-deprecated": 2, + "react/no-direct-mutation-state": 2, + "react/no-find-dom-node": 2, + "react/no-is-mounted": 2, + "react/no-unknown-property": 2, + "react/no-render-return-value": 2, + "react/prop-types": 2, + "react/react-in-jsx-scope": 2, + "react/require-render-return": 2, + "react/no-children-prop": 2, + "react/no-danger": 2, + "react/no-danger-with-children": 2, + "react/no-did-mount-set-state": 2, + "react/no-did-update-set-state": 2, + "react/no-multi-comp": 1, + "react/no-string-refs": 2, + "react/no-unescaped-entities": 2, + "react/no-unused-prop-types": 2, + "react/prefer-stateless-function": 2, + "react/self-closing-comp": 2, + "react/sort-comp": 2, + "react/sort-prop-types": 2, + "react/style-prop-object": 2, + "react/jsx-boolean-value": [2, "never"], + "react/jsx-closing-bracket-location": 2, + "react/jsx-curly-spacing": [2, "never"], + "react/jsx-equals-spacing": [2, "never"], + "react/jsx-filename-extension": [2, { + "extensions": [".js"] + }], + "react/jsx-first-prop-new-line": [2, "multiline"], + "react/jsx-handler-names": 2, + "react/jsx-indent": [2, 2], + "react/jsx-indent-props": [2, 2], + "react/jsx-key": 2, + "react/jsx-max-props-per-line": [2, { + "maximum": 2 + }], + "react/jsx-no-bind": 2, + "react/jsx-no-comment-textnodes": 2, + "react/jsx-no-duplicate-props": 2, + "react/jsx-no-target-blank": 2, + "react/jsx-pascal-case": 2, + "react/jsx-sort-props": 2, + "react/jsx-space-before-closing": 2, + "react/jsx-wrap-multilines": 2, + "jsx-a11y/anchor-has-content": 2, + "jsx-a11y/href-no-hash": 2, + "jsx-a11y/aria-props": 2, + "jsx-a11y/aria-proptypes": 2, + "jsx-a11y/aria-role": 2, + "jsx-a11y/aria-unsupported-elements": 2, + "jsx-a11y/click-events-have-key-events": 2, + "jsx-a11y/mouse-events-have-key-events": 2, + "jsx-a11y/heading-has-content": 2, + "jsx-a11y/html-has-lang": 2, + "jsx-a11y/img-has-alt": 2, + "jsx-a11y/img-redundant-alt": 2, + "jsx-a11y/label-has-for": 2, + "jsx-a11y/lang": 2, + "jsx-a11y/no-access-key": 2, + "jsx-a11y/no-marquee": 2, + "jsx-a11y/no-onchange": 2, + "jsx-a11y/no-static-element-interactions": 2, + "jsx-a11y/onclick-has-focus": 2, + "jsx-a11y/onclick-has-role": 2, + "jsx-a11y/role-has-required-aria-props": 2, + "jsx-a11y/role-supports-aria-props": 2, + "jsx-a11y/scope": 2, + "jsx-a11y/tabindex-no-positive": 2, "generator-star-spacing": 0, "babel/generator-star-spacing": 1, "space-before-function-paren": [2, "never"], - "react/jsx-uses-react": 2, - "react/jsx-uses-vars": 2, - "react/react-in-jsx-scope": 2, - "object-curly-newline": ["error", { + "object-curly-newline": [2, { "minProperties": 1 }], - "sort-vars": ["error", { + "sort-vars": [2, { "ignoreCase": true }] } diff --git a/ui/docs/containers/app/index.js b/ui/docs/containers/app/index.js index 52afa66f..3af5438e 100644 --- a/ui/docs/containers/app/index.js +++ b/ui/docs/containers/app/index.js @@ -7,7 +7,7 @@ const Item = require('../item/'); const { Base -} = require('../../../src');; +} = require('../../../src'); const { Match @@ -17,8 +17,15 @@ module.exports = () => { return ( - - + + ); }; diff --git a/ui/docs/containers/app/navigation.js b/ui/docs/containers/app/navigation.js index c29070ba..1d701027 100644 --- a/ui/docs/containers/app/navigation.js +++ b/ui/docs/containers/app/navigation.js @@ -39,7 +39,7 @@ const getList = (items, parent) => { ); }; -module.exports = () => { +const Navigation = () => { return (
@@ -50,3 +50,5 @@ module.exports = () => {
); }; + +module.exports = Navigation; diff --git a/ui/docs/containers/home.js b/ui/docs/containers/home.js index edc65ecf..fa61e1d2 100644 --- a/ui/docs/containers/home.js +++ b/ui/docs/containers/home.js @@ -1,5 +1,5 @@ const React = require('react'); module.exports = () => { - return (
); + return (
); }; diff --git a/ui/docs/containers/item/index.js b/ui/docs/containers/item/index.js index ef59be97..3fe4ad2e 100644 --- a/ui/docs/containers/item/index.js +++ b/ui/docs/containers/item/index.js @@ -6,7 +6,7 @@ const titleCase = require('title-case'); const Docs = require('../../../src/docs'); -module.exports = ({ +const Item = ({ params }) => { const path = (params.parent !== 'undefined') @@ -26,3 +26,9 @@ module.exports = ({
); }; + +Item.propTypes = { + params: React.PropTypes.object +}; + +module.exports = Item; diff --git a/ui/package.json b/ui/package.json index 5588634c..d619426b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -35,6 +35,7 @@ "eslint-config-semistandard": "^7.0.0", "eslint-config-standard": "^6.2.0", "eslint-plugin-babel": "^3.3.0", + "eslint-plugin-jsx-a11y": "^2.2.3", "eslint-plugin-promise": "^3.3.0", "eslint-plugin-react": "^6.4.1", "eslint-plugin-standard": "^2.0.1", diff --git a/ui/src/components/base/index.js b/ui/src/components/base/index.js index 9021ba18..547cb440 100644 --- a/ui/src/components/base/index.js +++ b/ui/src/components/base/index.js @@ -2,10 +2,10 @@ const React = require('react'); const classNames = require('classnames'); const styles = require('./style.css'); -const Base = module.exports = ({ +const Base = ({ + children, className, - style, - children + style }) => { const cn = classNames( className, @@ -13,14 +13,16 @@ const Base = module.exports = ({ ); return ( -
+
{children}
); }; Base.propTypes = { + children: React.PropTypes.node, className: React.PropTypes.string, - style: React.PropTypes.object, - children: React.PropTypes.node + style: React.PropTypes.object }; + +module.exports = Base; diff --git a/ui/src/components/button/index.js b/ui/src/components/button/index.js index d53e09a0..f00df8e0 100644 --- a/ui/src/components/button/index.js +++ b/ui/src/components/button/index.js @@ -1,16 +1,28 @@ const React = require('react'); const styles = require('./style.css'); -module.exports = ({ +const Button = ({ disabled = false, + className, + style, children }) => { return ( ); }; + +Button.propTypes = { + children: React.PropTypes.node, + className: React.PropTypes.string, + disabled: React.PropTypes.bool, + style: React.PropTypes.object +}; + +module.exports = Button; diff --git a/ui/src/components/column/index.js b/ui/src/components/column/index.js index d3bd8623..22985c24 100644 --- a/ui/src/components/column/index.js +++ b/ui/src/components/column/index.js @@ -27,11 +27,11 @@ const getClasses = (props) => { })).filter(Boolean); }; -const Column = module.exports = (props) => { +const Column = (props) => { const { + children, className, reverse, - children, style } = props; @@ -43,20 +43,22 @@ const Column = module.exports = (props) => { ); return ( -
+
{children}
); }; Column.propTypes = { - reverse: React.PropTypes.bool, - className: React.PropTypes.string, - style: React.PropTypes.object, children: React.PropTypes.node, + className: React.PropTypes.string, + reverse: React.PropTypes.bool, + style: React.PropTypes.object, ...breakpoints.reduce((all, bp) => ({ ...all, [`${bp}Offset`]: React.PropTypes.number, [bp]: React.PropTypes.number }), {}) }; + +module.exports = Column; diff --git a/ui/src/components/container/index.js b/ui/src/components/container/index.js index 537fdd6d..53258846 100644 --- a/ui/src/components/container/index.js +++ b/ui/src/components/container/index.js @@ -7,7 +7,7 @@ const React = require('react'); const classNames = require('classnames'); const styles = require('./style.css'); -const Container = module.exports = ({ +const Container = ({ fluid = false, className, children, @@ -19,15 +19,17 @@ const Container = module.exports = ({ ); return ( -
+
{children}
); }; Container.propTypes = { - fluid: React.PropTypes.bool, + children: React.PropTypes.node, className: React.PropTypes.string, - style: React.PropTypes.object, - children: React.PropTypes.node + fluid: React.PropTypes.bool, + style: React.PropTypes.object }; + +module.exports = Container; diff --git a/ui/src/components/row/index.js b/ui/src/components/row/index.js index 6250eb74..52042aef 100644 --- a/ui/src/components/row/index.js +++ b/ui/src/components/row/index.js @@ -52,7 +52,7 @@ const getClasses = (props) => { })).filter(Boolean); }; -const Row = module.exports = (props) => { +const Row = (props) => { const { className, reverse, @@ -68,7 +68,7 @@ const Row = module.exports = (props) => { ); return ( -
+
{children}
); @@ -81,12 +81,14 @@ const ModificatorType = React.PropTypes.oneOfType([ ]); Row.propTypes = { - reverse: React.PropTypes.bool, - className: React.PropTypes.string, - style: React.PropTypes.object, children: React.PropTypes.node, + className: React.PropTypes.string, + reverse: React.PropTypes.bool, + style: React.PropTypes.object, ...modifiers.reduce((all, m) => ({ ...all, [m]: ModificatorType }), {}) }; + +module.exports = Row; diff --git a/ui/webpack/base.js b/ui/webpack/base.js index c93d4c83..309f08d1 100644 --- a/ui/webpack/base.js +++ b/ui/webpack/base.js @@ -16,13 +16,10 @@ const plugins = { require('postcss-modules-values'), require('postcss-mixins')(), require('postcss-for'), - require('postcss-cssnext')(), + require('postcss-cssnext')() ] }, - 'embed-markdown-loader': { - // don't detach yet (has a bug in the production config) - // webpackConfigFullpath: path.join(__dirname, 'index.js') - } + 'embed-markdown-loader': {} } }) }; diff --git a/ui/webpack/production.js b/ui/webpack/production.js index 4b3d924a..02c90d18 100644 --- a/ui/webpack/production.js +++ b/ui/webpack/production.js @@ -2,7 +2,6 @@ const WebpackShellPlugin = require('webpack-shell-plugin'); const base = require('./base.js'); const webpack = require('webpack'); const entries = require('./entrypoints'); -const path = require('path'); module.exports = Object.assign(base.config, { entry: entries.reduce((all, entry) => { diff --git a/ui/webpack/test.js b/ui/webpack/test.js index 3de7c020..af6bef85 100644 --- a/ui/webpack/test.js +++ b/ui/webpack/test.js @@ -1,6 +1,4 @@ const base = require('./base'); -const webpack = require('webpack'); -const path = require('path'); module.exports = { output: { @@ -16,4 +14,4 @@ module.exports = { loader: 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader' }] } -}; \ No newline at end of file +}; diff --git a/ui/yarn.lock b/ui/yarn.lock index 9934b15b..f6b29439 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1856,6 +1856,10 @@ d@^0.1.1, d@~0.1.1: dependencies: es5-ext "~0.10.2" +damerau-levenshtein@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.3.tgz#ae4f4ce0b62acae10ff63a01bb08f652f5213af2" + dangerously-set-inner-html@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dangerously-set-inner-html/-/dangerously-set-inner-html-2.0.0.tgz#da73f26330c9ef0b6b12ca443091904406ddb73a" @@ -2249,6 +2253,14 @@ eslint-plugin-babel@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-3.3.0.tgz#2f494aedcf6f4aa4e75b9155980837bc1fbde193" +eslint-plugin-jsx-a11y: + version "2.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-2.2.3.tgz#4e35cb71b8a7db702ac415c806eb8e8d9ea6c65d" + dependencies: + damerau-levenshtein "^1.0.0" + jsx-ast-utils "^1.0.0" + object-assign "^4.0.1" + eslint-plugin-promise@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.3.0.tgz#20a1ef58b4243ffdaef82ee9360a02353a7cca89" @@ -3328,7 +3340,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -jsx-ast-utils@^1.3.1: +jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.2.tgz#dff658782705352111f9865d40471bc4a955961e" dependencies: