diff --git a/.eslintrc b/.eslintrc index b47a08e..659ee3e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,9 +8,35 @@ "plugin:prettier/recommended" ], "rules": { - "arrow-body-style": ["error", "as-needed"], + "arrow-body-style": [2, "as-needed"], + "unicorn/no-fn-reference-in-iterator": 0, - "unicorn/catch-error-name": ["error", { "name": "err" }], + "unicorn/catch-error-name": [2, { "name": "err" }], + + "import/no-unresolved": 2, + "import/named": 2, + "import/default": 2, + "import/namespace": 2, + "import/no-named-as-default": 2, + "import/no-named-as-default-member": 2, + "import/no-extraneous-dependencies": 2, + "import/newline-after-import": 2, + "import/no-named-default": 2, + "import/order": [ + 2, + { + "groups": [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index" + ], + "newlines-between": "always" + } + ], + "react/jsx-filename-extension": [ 2, { @@ -18,6 +44,7 @@ } ], "react/jsx-no-bind": 2, + "react/prefer-stateless-function": 2, "react/destructuring-assignment": [2, "always"], "react/prop-types": 2, @@ -25,8 +52,9 @@ "react/no-unused-prop-types": 2, "react/require-default-props": 2, "react/default-props-match-prop-types": 2, + "require-jsdoc": [ - "error", + 2, { "require": { "FunctionDeclaration": true, @@ -38,7 +66,7 @@ } ], "valid-jsdoc": [ - "error", + 2, { "prefer": { "arg": "param", @@ -60,6 +88,7 @@ "requireReturnDescription": true } ], + "standard/computed-property-even-spacing": 0 } } diff --git a/.travis.yml b/.travis.yml index 40f11ec..272faaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ cache: - node_modules yarn: true script: + - yarn run build:scss - yarn run lint - yarn test - 'yarn run test:coveralls' diff --git a/README.md b/README.md index 2366b79..97aaac9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![Build Status](https://travis-ci.org/kleros/dapp-front-boilerplate.svg?branch=master)](https://travis-ci.org/kleros/dapp-front-boilerplate) +[![Dependencies](https://david-dm.org/kleros/dapp-front-boilerplate.svg)](https://github.com/kleros/dapp-front-boilerplate/blob/master/package.json) [![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) [![Coverage Status](https://coveralls.io/repos/github/kleros/dapp-front-boilerplate/badge.svg?branch=master)](https://coveralls.io/github/kleros/dapp-front-boilerplate?branch=master) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) diff --git a/package.json b/package.json index deb5525..aef4516 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "eslint \"./*.js\" \"./{src,.storybook,stories}/**/*.js\" && stylelint \"./src/**/*.scss\" --syntax scss", "lint:fix": "eslint --fix \"./*.js\" \"./{src,.storybook,stories}/**/*.js\" && stylelint --fix \"./src/**/*.scss\" --syntax scss", - "test": "yarn run build:scss && react-scripts test --env=jsdom --coverage", + "test": "react-scripts test --env=jsdom --coverage", "test:coveralls": "coveralls < ./coverage/lcov.info", "commitmsg": "commitlint -e $GIT_PARAMS", "precommit": "lint-staged && yarn run lint && yarn test", @@ -52,6 +52,7 @@ "dependencies": { "ethjs": "^0.3.3", "ethjs-unit": "^0.1.6", + "history": "^4.7.2", "kleros-api": "^0.0.49", "normalize.css": "^7.0.0", "react": "^16.2.0", diff --git a/src/bootstrap/app.js b/src/bootstrap/app.js index dba1531..2d910ee 100644 --- a/src/bootstrap/app.js +++ b/src/bootstrap/app.js @@ -4,8 +4,11 @@ import { Helmet } from 'react-helmet' import { Provider } from 'react-redux' import { ConnectedRouter } from 'react-router-redux' import { Switch, Route } from 'react-router-dom' -import Initializer from './initializer' + import Balance from '../containers/balance' + +import Initializer from './initializer' + import './app.css' const App = ({ store, history, testElement }) => ( diff --git a/src/bootstrap/configure-store.js b/src/bootstrap/configure-store.js index e820cc0..764dca4 100644 --- a/src/bootstrap/configure-store.js +++ b/src/bootstrap/configure-store.js @@ -1,8 +1,9 @@ /* eslint-disable global-require */ import { applyMiddleware, compose, createStore } from 'redux' -import createHistory from 'history/createBrowserHistory' import { routerMiddleware } from 'react-router-redux' import createSagaMiddleware from 'redux-saga' +import createHistory from 'history/createBrowserHistory' + import rootReducer from '../reducers' import rootSaga from '../sagas' diff --git a/src/bootstrap/initializer.js b/src/bootstrap/initializer.js index 95280f8..134b36e 100644 --- a/src/bootstrap/initializer.js +++ b/src/bootstrap/initializer.js @@ -1,12 +1,14 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' + import * as walletActions from '../actions/wallet' import * as walletSelectors from '../reducers/wallet' -import { eth } from './dapp-api' import { renderIf } from '../utils/react-redux' import RequiresMetaMask from '../components/requires-meta-mask' +import { eth } from './dapp-api' + class Initializer extends PureComponent { static propTypes = { accounts: walletSelectors.accountsShape.isRequired, diff --git a/src/bootstrap/setup-integration-test.js b/src/bootstrap/setup-integration-test.js index 1958cf1..16b17d1 100644 --- a/src/bootstrap/setup-integration-test.js +++ b/src/bootstrap/setup-integration-test.js @@ -1,8 +1,8 @@ import React from 'react' import { mount } from 'enzyme' + import configureStore from './configure-store' import App from './app' -import { setTimeout } from 'timers' /** * Wait for all promises to resolve in a test environment. diff --git a/src/components/button/index.js b/src/components/button/index.js index a0cadbf..cd6d7a7 100644 --- a/src/components/button/index.js +++ b/src/components/button/index.js @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' + import './button.css' const Button = ({ children, onClick, className }) => ( diff --git a/src/components/requires-meta-mask/index.js b/src/components/requires-meta-mask/index.js index 97f69ab..1d18c05 100644 --- a/src/components/requires-meta-mask/index.js +++ b/src/components/requires-meta-mask/index.js @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' + import './require-meta-mask.css' const RequiresMetaMask = ({ needsUnlock }) => ( diff --git a/src/containers/balance/balance.test.js b/src/containers/balance/balance.test.js index 66a223f..970569a 100644 --- a/src/containers/balance/balance.test.js +++ b/src/containers/balance/balance.test.js @@ -1,6 +1,7 @@ import setupIntegrationTest, { flushPromises } from '../../bootstrap/setup-integration-test' + import Balance from '.' let integration = { diff --git a/src/containers/balance/index.js b/src/containers/balance/index.js index c26d0ab..408707f 100644 --- a/src/containers/balance/index.js +++ b/src/containers/balance/index.js @@ -1,10 +1,12 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' + import * as walletActions from '../../actions/wallet' import * as walletSelectors from '../../reducers/wallet' import { renderIf } from '../../utils/react-redux' import Identicon from '../../components/identicon' + import './balance.css' class Balance extends PureComponent { diff --git a/src/index.js b/src/index.js index d1a30e1..3018c19 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom' + import configureStore from './bootstrap/configure-store' import App from './bootstrap/app' import registerServiceWorker from './bootstrap/register-service-worker' diff --git a/src/reducers/index.js b/src/reducers/index.js index 4e24a8e..bd3c769 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,6 +1,7 @@ import { combineReducers } from 'redux' import { routerReducer as router } from 'react-router-redux' import { reducer as form } from 'redux-form' + import wallet from './wallet' // Export root reducer diff --git a/src/reducers/wallet.js b/src/reducers/wallet.js index 8599b4c..94ef7a1 100644 --- a/src/reducers/wallet.js +++ b/src/reducers/wallet.js @@ -1,4 +1,5 @@ import PropTypes from 'prop-types' + import createReducer from '../utils/create-reducer' import { createShape } from '../utils/react-redux' diff --git a/src/sagas/index.js b/src/sagas/index.js index 0f051f6..f49103b 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -1,5 +1,7 @@ import { delay } from 'redux-saga' + import { spawn, call, all } from 'redux-saga/effects' + import walletSaga from './wallet' /** diff --git a/src/sagas/index.test.js b/src/sagas/index.test.js index 60ef0f7..11d7cdb 100644 --- a/src/sagas/index.test.js +++ b/src/sagas/index.test.js @@ -1,7 +1,9 @@ -import { makeRestartable } from '.' import { delay } from 'redux-saga' + import { call } from 'redux-saga/effects' +import { makeRestartable } from '.' + it('Restarts terminated sagas.', async () => { const saga = function*() {} const gen = makeRestartable(saga)() diff --git a/src/sagas/wallet.js b/src/sagas/wallet.js index 48b38d7..78f7dc4 100644 --- a/src/sagas/wallet.js +++ b/src/sagas/wallet.js @@ -1,9 +1,11 @@ -import { takeLatest, call, put, select } from 'redux-saga/effects' import unit from 'ethjs-unit' -import { receiveAction, errorAction } from '../utils/actions' + +import { takeLatest, call, put, select } from 'redux-saga/effects' + import * as walletActions from '../actions/wallet' import * as walletSelectors from '../reducers/wallet' import { eth } from '../bootstrap/dapp-api' +import { receiveAction, errorAction } from '../utils/actions' import { ETH_NO_ACCOUNTS } from '../constants/errors' /** diff --git a/src/sagas/wallet.test.js b/src/sagas/wallet.test.js index 2c1ad81..23a94c2 100644 --- a/src/sagas/wallet.test.js +++ b/src/sagas/wallet.test.js @@ -1,12 +1,14 @@ -import { fetchAccounts, fetchBalance } from './wallet' import { call, put, select } from 'redux-saga/effects' -import { errorAction } from '../utils/actions' + import * as walletActions from '../actions/wallet' import * as walletSelectors from '../reducers/wallet' import { eth } from '../bootstrap/dapp-api' +import { errorAction } from '../utils/actions' import { ETH_NO_ACCOUNTS } from '../constants/errors' import { TEST_ACCOUNT } from '../constants/tests' +import { fetchAccounts, fetchBalance } from './wallet' + it('Handles cases when there are no accounts.', () => { const gen = fetchAccounts() expect(gen.next().value).toEqual(call(eth.accounts)) diff --git a/src/utils/create-form-generator.js b/src/utils/create-form-generator.js index fbfd9ac..d61eb8a 100644 --- a/src/utils/create-form-generator.js +++ b/src/utils/create-form-generator.js @@ -11,6 +11,7 @@ Docs URL: http://redux-form.com/6.8.0/docs/api/Field.md/ import React from 'react' import PropTypes from 'prop-types' import { reduxForm, Field, formValues, isInvalid, submit } from 'redux-form' + import { objMap } from './functional' import { camelToTitleCase } from './strings' diff --git a/src/utils/create-reducer.js b/src/utils/create-reducer.js index af0df9d..6750986 100644 --- a/src/utils/create-reducer.js +++ b/src/utils/create-reducer.js @@ -23,11 +23,11 @@ export default function createReducer(initialState, reducerMap) { ? reducerMap[action.type](state, action) : state - for (const actionTypePrefix of Object.keys(automaticActionPluginMap)) { - const l = actionTypePrefix.length - const a = action.type.slice(0, l) - if (actionTypePrefix === a) { - const resource = constantToCamelCase(action.type.slice(l), { + for (const typePrefix of Object.keys(automaticActionPluginMap)) { + const typePrefixLen = typePrefix.length + const actionTypePrefix = action.type.slice(0, typePrefixLen) + if (typePrefix === actionTypePrefix) { + const resource = constantToCamelCase(action.type.slice(typePrefixLen), { capitalizeFirst: true }) const lResource = resource.toLowerCase() @@ -36,11 +36,11 @@ export default function createReducer(initialState, reducerMap) { ...newState, [lResource]: { data: - actionTypePrefix === 'RECEIVE_' && + typePrefix === 'RECEIVE_' && (!reducerMap || !reducerMap[action.type]) ? action.payload[lResource] : state[lResource].data, - ...automaticActionPluginMap[actionTypePrefix] + ...automaticActionPluginMap[typePrefix] } } break diff --git a/src/utils/form-generator.js b/src/utils/form-generator.js index bbfaa79..15a6f0b 100644 --- a/src/utils/form-generator.js +++ b/src/utils/form-generator.js @@ -1,7 +1,8 @@ -import createFormGenerator from './create-form-generator' -import store from '..' +import store from '../' // eslint-disable-line unicorn/import-index import TextInput from '../components/text-input' +import createFormGenerator from './create-form-generator' + export default createFormGenerator( { text: TextInput, number: TextInput }, store diff --git a/src/utils/form-generator.test.js b/src/utils/form-generator.test.js index a2b1f90..a333253 100644 --- a/src/utils/form-generator.test.js +++ b/src/utils/form-generator.test.js @@ -1,8 +1,10 @@ import React from 'react' import { submit as reduxFormSubmit } from 'redux-form' + import setupIntegrationTest, { flushPromises } from '../bootstrap/setup-integration-test' + import formGenerator from './form-generator' import { required, number } from './validate' diff --git a/src/utils/react-redux.test.js b/src/utils/react-redux.test.js index 557909f..9d4d405 100644 --- a/src/utils/react-redux.test.js +++ b/src/utils/react-redux.test.js @@ -1,6 +1,7 @@ -import { createShape, renderIf } from './react-redux' import PropTypes from 'prop-types' +import { createShape, renderIf } from './react-redux' + jest.mock('prop-types', () => ({ shape: s => s, bool: { isRequired: 'bool', toString: () => 'bool' }, diff --git a/stories/button.js b/stories/button.js index d71ffd9..76ee288 100644 --- a/stories/button.js +++ b/stories/button.js @@ -1,6 +1,7 @@ import React from 'react' import { storiesOf } from '@storybook/react' import { action } from '@storybook/addon-actions' + import Button from '../src/components/button' storiesOf('Button', module) diff --git a/stories/identicon.js b/stories/identicon.js index 48e12c2..07a41c0 100644 --- a/stories/identicon.js +++ b/stories/identicon.js @@ -1,5 +1,6 @@ import React from 'react' import { storiesOf } from '@storybook/react' + import Identicon from '../src/components/identicon' storiesOf('Identicon', module).add('with placeholder seed', () => ( diff --git a/stories/requires-meta-mask.js b/stories/requires-meta-mask.js index 38edaad..9a000be 100644 --- a/stories/requires-meta-mask.js +++ b/stories/requires-meta-mask.js @@ -1,5 +1,6 @@ import React from 'react' import { storiesOf } from '@storybook/react' + import RequiresMetaMask from '../src/components/requires-meta-mask' storiesOf('RequiresMetaMask', module) diff --git a/stories/text-input.js b/stories/text-input.js index d93e67e..308d543 100644 --- a/stories/text-input.js +++ b/stories/text-input.js @@ -1,6 +1,7 @@ import React from 'react' import { storiesOf } from '@storybook/react' import { action } from '@storybook/addon-actions' + import TextInput from '../src/components/text-input' storiesOf('TextInput', module).add('default', () => (