From 9e76c02f61eee4e84fd992bf124e0dcca448d2f6 Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Wed, 23 Sep 2015 00:09:30 +0100 Subject: [PATCH 01/11] Alt integration plus simplifications --- app/actions/AppActions.js | 29 +++------- app/alt.js | 2 + app/components/App/App.jsx | 27 +++------ app/components/Menu/Menu.jsx | 4 +- app/constants/AppConstants.js | 7 --- app/dispatcher/AppDispatcher.js | 5 +- app/stores/BaseStore.js | 30 ---------- app/stores/ItemsStore.js | 47 ++++++---------- app/stores/__tests__/BaseStore-test.js | 78 -------------------------- package.json | 1 + 10 files changed, 38 insertions(+), 192 deletions(-) create mode 100644 app/alt.js delete mode 100644 app/constants/AppConstants.js delete mode 100644 app/stores/BaseStore.js delete mode 100644 app/stores/__tests__/BaseStore-test.js diff --git a/app/actions/AppActions.js b/app/actions/AppActions.js index 04ae62c..a518ae9 100644 --- a/app/actions/AppActions.js +++ b/app/actions/AppActions.js @@ -1,24 +1,9 @@ -import AppDispatcher from '../dispatcher/AppDispatcher'; -import WebAPI from '../util/WebAPI'; +import alt from '../alt'; -import { - ITEMS_GET_SUCCESS, - ITEMS_GET_ERROR -} from '../constants/AppConstants'; - -export default { - getItems() { - WebAPI.getItems() - .then((items) => { - AppDispatcher.dispatch({ - actionType: ITEMS_GET_SUCCESS, - items: items - }); - }) - .catch(() => { - AppDispatcher.dispatch({ - actionType: ITEMS_GET_ERROR - }); - }); +class AppActions { + constructor() { + this.generateActions('getItems'); } -}; +} + +export default alt.createActions(AppActions); diff --git a/app/alt.js b/app/alt.js new file mode 100644 index 0000000..bd2bdab --- /dev/null +++ b/app/alt.js @@ -0,0 +1,2 @@ +import Alt from 'alt'; +export default new Alt(); diff --git a/app/components/App/App.jsx b/app/components/App/App.jsx index 9435cdf..b5a146f 100644 --- a/app/components/App/App.jsx +++ b/app/components/App/App.jsx @@ -6,27 +6,14 @@ import ItemsStore from '../../stores/ItemsStore'; import Body from '../Body/Body'; import Footer from '../Footer/Footer'; -function getAppState() { - return { - items: ItemsStore.getAll() - }; -} - export default class App extends React.Component { - - state = getAppState() - - componentDidMount() { - ItemsStore.addChangeListener(this.onChange); - AppActions.getItems(); - } - - componentWillUnmount() { - ItemsStore.removeChangeListener(this.onChange); - } - - onChange = () => { - this.setState(getAppState()); + constructor() { + super() + this.state = {items: []} + AppActions.getItems() + ItemsStore.listen((data) => { + this.setState({items: data.items}) + }) } render() { diff --git a/app/components/Menu/Menu.jsx b/app/components/Menu/Menu.jsx index 09eaa84..9606fbb 100644 --- a/app/components/Menu/Menu.jsx +++ b/app/components/Menu/Menu.jsx @@ -17,8 +17,8 @@ export default class Menu extends Component { render() { return ( ); diff --git a/app/constants/AppConstants.js b/app/constants/AppConstants.js deleted file mode 100644 index 1c27e06..0000000 --- a/app/constants/AppConstants.js +++ /dev/null @@ -1,7 +0,0 @@ -import pkg from '../../package'; - -export const DEBUG = (process.env.NODE_ENV !== 'production'); -export const APP_TITLE = pkg.name; -export const ITEMS_GET_SUCCESS = 'ITEMS_GET_SUCCESS'; -export const ITEMS_GET_ERROR = 'ITEMS_GET_ERROR'; -export const ITEMS_UPDATED = 'ITEMS_UPDATED'; diff --git a/app/dispatcher/AppDispatcher.js b/app/dispatcher/AppDispatcher.js index 9178b97..bd2bdab 100644 --- a/app/dispatcher/AppDispatcher.js +++ b/app/dispatcher/AppDispatcher.js @@ -1,3 +1,2 @@ -import Flux from 'flux'; - -export default new Flux.Dispatcher(); +import Alt from 'alt'; +export default new Alt(); diff --git a/app/stores/BaseStore.js b/app/stores/BaseStore.js deleted file mode 100644 index 6102ae2..0000000 --- a/app/stores/BaseStore.js +++ /dev/null @@ -1,30 +0,0 @@ -import { EventEmitter } from 'events'; - -export default class BaseStore extends EventEmitter { - - constructor(...args) { - super(...args); - this.data = new Set([]); - } - - setAll(items) { - this.data = new Set(items); - this.emitChange(); - } - - getAll() { - return Array.from(this.data); - } - - set(item) { - if (!this.data.has(item)) { - this.data.add(item); - this.emitChange(); - } - } - - remove(item) { - this.data.delete(item); - this.emitChange(); - } -} diff --git a/app/stores/ItemsStore.js b/app/stores/ItemsStore.js index 7a601c5..899a68a 100644 --- a/app/stores/ItemsStore.js +++ b/app/stores/ItemsStore.js @@ -1,35 +1,22 @@ -import BaseStore from './BaseStore'; -import AppDispatcher from '../dispatcher/AppDispatcher'; - -import { - ITEMS_UPDATED, - ITEMS_GET_SUCCESS -} from '../constants/AppConstants'; - -class ItemsStore extends BaseStore { - - emitChange() { - this.emit(ITEMS_UPDATED); - } - - addChangeListener(callback) { - this.on(ITEMS_UPDATED, callback); +import alt from '../alt'; +import WebAPI from '../util/WebAPI'; +import AppActions from '../actions/AppActions' + +class ItemsStore { + constructor() { + this.bindAction(AppActions.getItems, this.getItems) + this.state = {items: [], error: null} } - removeChangeListener(callback) { - this.removeListener(ITEMS_UPDATED, callback); + getItems() { + WebAPI.getItems() + .then((items) => { + this.setState({items: items}) + }) + .catch(() => { + this.state.error = "error" + }); } } -let store = new ItemsStore(); - -AppDispatcher.register((action) => { - switch(action.actionType) { - case ITEMS_GET_SUCCESS: - store.setAll(action.items); - break; - default: - } -}); - -export default store; +export default alt.createStore(ItemsStore, 'ItemsStore') diff --git a/app/stores/__tests__/BaseStore-test.js b/app/stores/__tests__/BaseStore-test.js deleted file mode 100644 index 2fe2ac2..0000000 --- a/app/stores/__tests__/BaseStore-test.js +++ /dev/null @@ -1,78 +0,0 @@ -import 'babel-core/polyfill'; -import BaseStore from '../BaseStore.js'; -import { expect } from 'chai'; - -const ITEMS_UPDATED = 'ITEMS_UPDATED'; - -class TestStore extends BaseStore { - emitChange() { - this.emit(ITEMS_UPDATED); - } - addChangeListener(callback) { - this.on(ITEMS_UPDATED, callback); - } - removeChangeListener(callback) { - this.removeListener(ITEMS_UPDATED, callback); - } -} - -describe('BaseStore', () => { - - it('Should set, get and remove data', function() { - - let store = new TestStore(); - - expect(store.getAll()).to.eql([]); - - let item = { - foo: 'bar' - }; - - store.setAll([item]); - expect(store.getAll()).to.eql([item]); - - let item2 = { - foobaz: 'bar' - }; - - store.set(item2); - store.set(item2); // intentional check for unique items - expect(store.getAll()).to.eql([item, item2]); - - store.remove(item); - expect(store.getAll()).to.eql([item2]); - }); - - it('Should call the change listener when data changes', () => { - - let store = new TestStore(); - let onChange = sinon.spy(); - store.addChangeListener(onChange); - - store.setAll([{ - foo: 'bar' - }]); - store.set([{ - foobaz: 'bar' - }]); - store.remove({ - foo: 'bar' - }); - expect(onChange.callCount).to.equal(3); - }); - - it('Should remove the change listener', () => { - - let store = new TestStore(); - let onChange = sinon.spy(); - store.addChangeListener(onChange); - store.setAll([{ - foo: 'bar' - }]); - store.removeChangeListener(onChange); - store.setAll([{ - foo: 'bar' - }]); - expect(onChange.callCount).to.equal(1); - }); -}); diff --git a/package.json b/package.json index 74e8332..d579a9f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "webpack": "webpack --colors --progress --config ./webpack/config" }, "dependencies": { + "alt": "^0.17.3", "classnames": "^2.1.3", "flux": "^2.0.3", "normalize.css": "^3.0.3", From 4f7bd8484283dc5e019533b7255dd02fbbeb18b5 Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Wed, 23 Sep 2015 00:19:12 +0100 Subject: [PATCH 02/11] fix lint issues --- app/components/App/App.jsx | 10 +++++----- app/components/Menu/Menu.jsx | 2 +- app/stores/ItemsStore.js | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/components/App/App.jsx b/app/components/App/App.jsx index b5a146f..0c740d2 100644 --- a/app/components/App/App.jsx +++ b/app/components/App/App.jsx @@ -8,12 +8,12 @@ import Footer from '../Footer/Footer'; export default class App extends React.Component { constructor() { - super() - this.state = {items: []} - AppActions.getItems() + super(); + this.state = {items: []}; + AppActions.getItems(); ItemsStore.listen((data) => { - this.setState({items: data.items}) - }) + this.setState({items: data.items}); + }); } render() { diff --git a/app/components/Menu/Menu.jsx b/app/components/Menu/Menu.jsx index 9606fbb..4ce4dda 100644 --- a/app/components/Menu/Menu.jsx +++ b/app/components/Menu/Menu.jsx @@ -18,7 +18,7 @@ export default class Menu extends Component { return ( ); diff --git a/app/stores/ItemsStore.js b/app/stores/ItemsStore.js index 899a68a..4b85b9f 100644 --- a/app/stores/ItemsStore.js +++ b/app/stores/ItemsStore.js @@ -1,22 +1,22 @@ import alt from '../alt'; import WebAPI from '../util/WebAPI'; -import AppActions from '../actions/AppActions' +import AppActions from '../actions/AppActions'; class ItemsStore { constructor() { - this.bindAction(AppActions.getItems, this.getItems) - this.state = {items: [], error: null} + this.bindAction(AppActions.getItems, this.getItems); + this.state = {items: [], error: null}; } getItems() { WebAPI.getItems() .then((items) => { - this.setState({items: items}) + this.setState({items: items}); }) .catch(() => { - this.state.error = "error" + this.state.error = 'error'; }); } } -export default alt.createStore(ItemsStore, 'ItemsStore') +export default alt.createStore(ItemsStore, 'ItemsStore'); From 125a6dbc658a7c331077c541d1dcb51639702b20 Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Mon, 30 Nov 2015 17:30:19 +0000 Subject: [PATCH 03/11] add react-router --- app/app.jsx | 11 ++++++----- app/components/App/App.jsx | 4 ++-- app/components/Body/Body.jsx | 3 ++- app/routes.jsx | 10 ++++++++++ package.json | 3 ++- 5 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 app/routes.jsx diff --git a/app/app.jsx b/app/app.jsx index b637681..60a23a7 100644 --- a/app/app.jsx +++ b/app/app.jsx @@ -5,9 +5,10 @@ import 'normalize.css/normalize.css'; import './scss/app.scss'; import React from 'react'; -import App from './components/App/App'; +import Router from 'react-router'; -React.render( - , - document.getElementById('app') -); +import routes from './routes'; + +Router.run(routes, function (Handler) { + React.render(React.createElement(Handler), document.body); +}); diff --git a/app/components/App/App.jsx b/app/components/App/App.jsx index 0c740d2..1829696 100644 --- a/app/components/App/App.jsx +++ b/app/components/App/App.jsx @@ -1,9 +1,9 @@ import styles from './_App.scss'; import React from 'react'; +import { RouteHandler } from 'react-router'; import AppActions from '../../actions/AppActions'; import ItemsStore from '../../stores/ItemsStore'; -import Body from '../Body/Body'; import Footer from '../Footer/Footer'; export default class App extends React.Component { @@ -19,7 +19,7 @@ export default class App extends React.Component { render() { return (
- +
); diff --git a/app/components/Body/Body.jsx b/app/components/Body/Body.jsx index c2a3781..d541b90 100644 --- a/app/components/Body/Body.jsx +++ b/app/components/Body/Body.jsx @@ -1,4 +1,5 @@ import styles from './_Body.scss'; + import React from 'react'; import Menu from '../Menu/Menu'; @@ -18,7 +19,7 @@ export default class Body extends React.Component { return (

React Seed

-

This is an example seed app, powered by React, ES6 & webpack.

+

This is an example seed app, powered by React, ES6, Al & webpack.

Here is some example data:

Getting started

diff --git a/app/routes.jsx b/app/routes.jsx new file mode 100644 index 0000000..0009420 --- /dev/null +++ b/app/routes.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Route, DefaultRoute } from 'react-router'; +import App from './components/App/App'; +import Body from './components/Body/Body'; + +export default ( + + + +); diff --git a/package.json b/package.json index d579a9f..4dccdae 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "classnames": "^2.1.3", "flux": "^2.0.3", "normalize.css": "^3.0.3", - "react": "^0.13.3" + "react": "^0.13.3", + "react-router": "^0.13.3" }, "devDependencies": { "autoprefixer-core": "^5.2.1", From 723e61772435b082e5e826d622c76019dd27a7aa Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Mon, 30 Nov 2015 17:41:20 +0000 Subject: [PATCH 04/11] fix __Rewire__ dependency --- webpack/loaders.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack/loaders.js b/webpack/loaders.js index 840974a..73d5515 100644 --- a/webpack/loaders.js +++ b/webpack/loaders.js @@ -32,7 +32,7 @@ if (DEBUG || TEST) { if (!TEST) { jsxLoader.push('react-hot'); } - jsxLoader.push('babel-loader?optional[]=runtime&stage=0&plugins=rewire'); + jsxLoader.push('babel-loader?optional[]=runtime&stage=0'); sassParams.push('sourceMap', 'sourceMapContents=true'); sassLoader = [ 'style-loader', @@ -46,7 +46,7 @@ if (DEBUG || TEST) { 'postcss-loader' ].join('!'); } else { - jsxLoader = ['babel-loader?optional[]=runtime&stage=0&plugins=rewire']; + jsxLoader = ['babel-loader?optional[]=runtime&stage=0']; sassLoader = ExtractTextPlugin.extract('style-loader', [ 'css-loader?modules&localIdentName=[hash:base64:5]', 'postcss-loader', From 70ddcf3c2c3ac361c8893fa8ec094aa11e89a3d3 Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Mon, 30 Nov 2015 18:04:36 +0000 Subject: [PATCH 05/11] add StandardJS and remove errors --- app/actions/AppActions.js | 8 +- app/alt.js | 4 +- app/app.jsx | 20 ++--- app/app.tests.js | 6 +- app/components/App/App.jsx | 28 +++---- app/components/Body/Body.jsx | 18 ++--- app/components/Footer/Footer.jsx | 10 +-- .../Footer/__tests__/Footer-test.jsx | 20 ++--- app/components/Menu/Menu.jsx | 18 ++--- app/components/Menu/MenuItem.jsx | 16 ++-- app/components/Menu/__tests__/Menu-test.jsx | 74 +++++++++---------- app/dispatcher/AppDispatcher.js | 4 +- app/routes.jsx | 16 ++-- app/stores/ItemsStore.js | 22 +++--- app/util/WebAPI.js | 12 +-- dev-server.js | 30 ++++---- karma.conf.js | 10 +-- package.json | 50 +++++-------- webpack/config.js | 28 +++---- webpack/config.test.js | 12 +-- webpack/loaders.js | 46 ++++++------ webpack/plugins.js | 24 +++--- 22 files changed, 231 insertions(+), 245 deletions(-) diff --git a/app/actions/AppActions.js b/app/actions/AppActions.js index a518ae9..b3b2ad4 100644 --- a/app/actions/AppActions.js +++ b/app/actions/AppActions.js @@ -1,9 +1,9 @@ -import alt from '../alt'; +import alt from '../alt' class AppActions { - constructor() { - this.generateActions('getItems'); + constructor () { + this.generateActions('getItems') } } -export default alt.createActions(AppActions); +export default alt.createActions(AppActions) diff --git a/app/alt.js b/app/alt.js index bd2bdab..e54dc88 100644 --- a/app/alt.js +++ b/app/alt.js @@ -1,2 +1,2 @@ -import Alt from 'alt'; -export default new Alt(); +import Alt from 'alt' +export default new Alt() diff --git a/app/app.jsx b/app/app.jsx index 60a23a7..65958f3 100644 --- a/app/app.jsx +++ b/app/app.jsx @@ -1,14 +1,14 @@ -import './favicon.ico'; -import './index.html'; -import 'babel-core/polyfill'; -import 'normalize.css/normalize.css'; -import './scss/app.scss'; +import './favicon.ico' +import './index.html' +import 'babel-core/polyfill' +import 'normalize.css/normalize.css' +import './scss/app.scss' -import React from 'react'; -import Router from 'react-router'; +import React from 'react' +import Router from 'react-router' -import routes from './routes'; +import routes from './routes' Router.run(routes, function (Handler) { - React.render(React.createElement(Handler), document.body); -}); + React.render(React.createElement(Handler), document.body) +}) diff --git a/app/app.tests.js b/app/app.tests.js index 4eb1763..63395c9 100644 --- a/app/app.tests.js +++ b/app/app.tests.js @@ -1,4 +1,4 @@ -import 'babel-core/polyfill'; +import 'babel-core/polyfill' -let context = require.context('.', true, /-test\.jsx?$/); -context.keys().forEach(context); +let context = require.context('.', true, /-test\.jsx?$/) +context.keys().forEach(context) diff --git a/app/components/App/App.jsx b/app/components/App/App.jsx index 1829696..e5df830 100644 --- a/app/components/App/App.jsx +++ b/app/components/App/App.jsx @@ -1,27 +1,27 @@ -import styles from './_App.scss'; +import styles from './_App.scss' -import React from 'react'; -import { RouteHandler } from 'react-router'; -import AppActions from '../../actions/AppActions'; -import ItemsStore from '../../stores/ItemsStore'; -import Footer from '../Footer/Footer'; +import React from 'react' +import { RouteHandler } from 'react-router' +import AppActions from '../../actions/AppActions' +import ItemsStore from '../../stores/ItemsStore' +import Footer from '../Footer/Footer' export default class App extends React.Component { - constructor() { - super(); - this.state = {items: []}; - AppActions.getItems(); + constructor () { + super() + this.state = {items: []} + AppActions.getItems() ItemsStore.listen((data) => { - this.setState({items: data.items}); - }); + this.setState({items: data.items}) + }) } - render() { + render () { return (
- ); + ) } } diff --git a/app/components/Body/Body.jsx b/app/components/Body/Body.jsx index d541b90..53f2833 100644 --- a/app/components/Body/Body.jsx +++ b/app/components/Body/Body.jsx @@ -1,25 +1,25 @@ -import styles from './_Body.scss'; +import styles from './_Body.scss' -import React from 'react'; -import Menu from '../Menu/Menu'; +import React from 'react' +import Menu from '../Menu/Menu' -let { PropTypes } = React; +let { PropTypes } = React export default class Body extends React.Component { static defaultProps = { items: [] - }; + } static propTypes = { items: PropTypes.array.isRequired - }; + } - render() { + render () { return (

React Seed

-

This is an example seed app, powered by React, ES6, Al & webpack.

+

This is an example seed app, powered by React, ES6, Al & webpack.

Here is some example data:

Getting started

@@ -30,6 +30,6 @@ export default class Body extends React.Component {
  • Change the data rendered above. Look in:
    ./app/components/App/App.jsx
    Understand how data flows from the actions into the stores and then into the Body component.
  • - ); + ) } } diff --git a/app/components/Footer/Footer.jsx b/app/components/Footer/Footer.jsx index b470486..84f92fe 100644 --- a/app/components/Footer/Footer.jsx +++ b/app/components/Footer/Footer.jsx @@ -1,13 +1,13 @@ -import styles from './_Footer.scss'; -import React from 'react'; +import styles from './_Footer.scss' +import React from 'react' export default class Footer extends React.Component { - render() { - var year = (new Date()).getFullYear(); + render () { + var year = (new Date()).getFullYear() return (
    © Your Company {year}
    - ); + ) } } diff --git a/app/components/Footer/__tests__/Footer-test.jsx b/app/components/Footer/__tests__/Footer-test.jsx index 8ab4f1b..75fb41c 100644 --- a/app/components/Footer/__tests__/Footer-test.jsx +++ b/app/components/Footer/__tests__/Footer-test.jsx @@ -1,15 +1,17 @@ -import React from 'react/addons'; -import Footer from '../Footer.jsx'; -import { expect } from 'chai'; +/* global describe, it */ -let { TestUtils } = React.addons; +import React from 'react/addons' +import Footer from '../Footer.jsx' +import { expect } from 'chai' + +let { TestUtils } = React.addons describe('Footer', () => { it('Should have the correct footer element', () => { let footer = TestUtils.renderIntoDocument(