From a5e051a7ee9acc5e01f77cc3c7ee6a9ee7450c3a Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Wed, 18 Jan 2017 17:55:36 +0200 Subject: [PATCH 1/9] Add redux. Shared actions can be found from _src/ducks/_ folder and route specific are inside container folder. --- package.json | 2 + src/app.js | 35 +++++++++---- src/app.test.js | 7 ++- src/containers/SearchPage/SearchPage.js | 27 ++++++++-- src/containers/SearchPage/SearchPage.test.js | 10 ++-- src/containers/SearchPage/SearchPageDucks.js | 26 ++++++++++ src/containers/reducers.js | 9 ++++ src/ducks/FlashNotification.js | 52 ++++++++++++++++++++ src/ducks/index.js | 9 ++++ src/reducers.js | 14 ++++++ src/store.js | 10 ++++ yarn.lock | 37 +++++++++++++- 12 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 src/containers/SearchPage/SearchPageDucks.js create mode 100644 src/containers/reducers.js create mode 100644 src/ducks/FlashNotification.js create mode 100644 src/ducks/index.js create mode 100644 src/reducers.js create mode 100644 src/store.js diff --git a/package.json b/package.json index 8334977d..bb6b7612 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "react": "^15.4.2", "react-dom": "^15.4.2", "react-helmet": "^4.0.0", + "react-redux": "^5.0.2", "react-router": "4.0.0-alpha.6", "react-test-renderer": "^15.4.2", + "redux": "^3.6.0", "sharetribe-scripts": "0.8.6", "source-map-support": "^0.4.10" }, diff --git a/src/app.js b/src/app.js index 33bb6b69..7e24a06f 100644 --- a/src/app.js +++ b/src/app.js @@ -2,30 +2,45 @@ import React, { PropTypes } from 'react'; import ReactDOMServer from 'react-dom/server'; import Helmet from 'react-helmet'; import { BrowserRouter, ServerRouter } from 'react-router'; +import { Provider } from 'react-redux'; +import configureStore from './store'; import Routes from './Routes'; -const RoutesWithRouterProp = ({ router }) => ; +export const ClientApp = props => { + const { store } = props; + return ( + + { + ({router}) => ( + + + + ) + } + + ); +}; const { any, string } = PropTypes; -RoutesWithRouterProp.propTypes = { router: any.isRequired }; - -export const ClientApp = () => ( - - {RoutesWithRouterProp} - -); +ClientApp.propTypes = { store: any.isRequired }; export const ServerApp = props => { const { url, context } = props; return ( - {RoutesWithRouterProp} + { + ({router}) => ( + + + + ) + } ); }; -ServerApp.propTypes = { url: string.isRequired, context: any.isRequired }; +ServerApp.propTypes = { url: string.isRequired, context: any.isRequired, store: any.isRequired }; /** * Render the given route. diff --git a/src/app.test.js b/src/app.test.js index ad415995..b5707d7a 100644 --- a/src/app.test.js +++ b/src/app.test.js @@ -4,14 +4,17 @@ import ReactDOMServer from 'react-dom/server'; import { forEach } from 'lodash'; import { createServerRenderContext } from 'react-router'; import { ClientApp, ServerApp } from './app'; +import configureStore from './store'; + +const store = configureStore({}); const render = (url, context) => - ReactDOMServer.renderToString(); + ReactDOMServer.renderToString(); describe('Application', () => { it('renders in the client without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); + ReactDOM.render(, div); }); it('renders in the server without crashing', () => { diff --git a/src/containers/SearchPage/SearchPage.js b/src/containers/SearchPage/SearchPage.js index 8b0ba9dd..51d8daf6 100644 --- a/src/containers/SearchPage/SearchPage.js +++ b/src/containers/SearchPage/SearchPage.js @@ -1,9 +1,28 @@ import React from 'react'; import { Link } from 'react-router'; +import { connect } from 'react-redux'; import { Page } from '../../components'; +import { addFlashNotification } from '../../ducks/FlashNotification'; +import { addFilter } from './SearchPageDucks'; -export default () => ( - +export const SearchPage = props => ( + Nice studio in Helsinki - -) + +); + +/** + * Container functions. + */ +const mapStateToProps = function mapStateToProps(state) { + return state; +}; + +const mapDispatchToProps = function mapDispatchToProps(dispatch) { + return { + addNotice: msg => dispatch(addFlashNotification('notice', msg)), + addFilter: (k, v) => dispatch(addFilter(k, v)), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(SearchPage); diff --git a/src/containers/SearchPage/SearchPage.test.js b/src/containers/SearchPage/SearchPage.test.js index 893ea3c5..81419d85 100644 --- a/src/containers/SearchPage/SearchPage.test.js +++ b/src/containers/SearchPage/SearchPage.test.js @@ -1,16 +1,14 @@ import React from 'react'; import { BrowserRouter } from 'react-router'; import renderer from 'react-test-renderer'; -import SearchPage from './SearchPage'; +import { SearchPage } from './SearchPage'; describe('SearchPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/SearchPage/SearchPageDucks.js b/src/containers/SearchPage/SearchPageDucks.js new file mode 100644 index 00000000..8f2a3ecd --- /dev/null +++ b/src/containers/SearchPage/SearchPageDucks.js @@ -0,0 +1,26 @@ +/** + * This file contains Action constants, Action creators, and reducer of a page + * container. We are following Ducks module proposition: + * https://github.com/erikras/ducks-modular-redux + */ +import unionBy from 'lodash/unionBy'; + +// Actions +const ADD_FILTER = 'ADD_FILTER'; + +// Reducer +export default function reducer(state = {}, action = {}) { + const { type, payload } = action; + switch (type) { + case ADD_FILTER: + const stateFilters = state.filters || []; + return Object.assign({}, state, { filters: unionBy(stateFilters, [ payload ]) }); + default: + return state; + } +} + +// Action Creators +export const addFilter = (key, value) => { + return { type: ADD_FILTER, payload: { [key]: value } }; +}; diff --git a/src/containers/reducers.js b/src/containers/reducers.js new file mode 100644 index 00000000..577a391b --- /dev/null +++ b/src/containers/reducers.js @@ -0,0 +1,9 @@ +/** + * Export reducers from ducks modules of different containers (i.e. default export) + * We are following Ducks module proposition: + * https://github.com/erikras/ducks-modular-redux + */ + +import SearchPage from './SearchPage/SearchPageDucks'; + +export { SearchPage }; diff --git a/src/ducks/FlashNotification.js b/src/ducks/FlashNotification.js new file mode 100644 index 00000000..c5aea686 --- /dev/null +++ b/src/ducks/FlashNotification.js @@ -0,0 +1,52 @@ +/** + * This file contains Action constants, Action creators, and reducer of global + * FlashMessages. Global actions can be used in multiple pages. + * We are following Ducks module proposition: + * https://github.com/erikras/ducks-modular-redux + */ + +import find from 'lodash/find'; +import findIndex from 'lodash/findIndex'; + +// Actions: system notifications +const ADD_FLASH_NOTIFICATION = 'FLASH::ADD_NOTIFICATION'; +const REMOVE_FLASH_NOTIFICATION = 'FLASH::REMOVE_NOTIFICATION'; + +const initialState = []; + +// Reducer +export default (state = initialState, action) => { + const { type, payload } = action; + switch (type) { + case ADD_FLASH_NOTIFICATION: + if (!find(state, n => n.content === payload.content && !n.isRead)) { + return state.concat([ + { id: payload.id, type: payload.type, content: payload.content, isRead: false }, + ]); + } + return state; + + case REMOVE_FLASH_NOTIFICATION: + return state.map( + findIndex(state, msg => msg.id === payload.id), + msg => msg.set('isRead', true), + ); + + default: + return state; + } +}; + +// Action Creators +let nextMessageId = 1; + +export const addFlashNotification = (type, content) => { + const id = nextMessageId; + nextMessageId += 1; + return { + type: ADD_FLASH_NOTIFICATION, + payload: { id: `note_${id}`, type, content, isRead: false }, + }; +}; + +export const removeFlashNotification = id => ({ type: REMOVE_FLASH_NOTIFICATION, payload: { id } }); diff --git a/src/ducks/index.js b/src/ducks/index.js new file mode 100644 index 00000000..3f31556a --- /dev/null +++ b/src/ducks/index.js @@ -0,0 +1,9 @@ +/** + * Import reducers from shared ducks modules (default export) + * We are following Ducks module proposition: + * https://github.com/erikras/ducks-modular-redux + */ + +import FlashNotification from './FlashNotification'; + +export { FlashNotification }; diff --git a/src/reducers.js b/src/reducers.js new file mode 100644 index 00000000..a54211b2 --- /dev/null +++ b/src/reducers.js @@ -0,0 +1,14 @@ +import { combineReducers } from 'redux'; +import * as globalReducers from './ducks'; +import * as pageReducers from './containers/reducers'; + +/** + * Function _createReducer_ combines global reducers (reducers that are used in + * multiple pages) and reducers that are handling actions happening inside one page container. + * Future: this structure could take in asyncReducers, which are changed when you navigate pages. + */ +const createReducer = function createReducer() { + return combineReducers({ ...globalReducers, ...pageReducers }); +}; + +export default createReducer; diff --git a/src/store.js b/src/store.js new file mode 100644 index 00000000..2d0f79e3 --- /dev/null +++ b/src/store.js @@ -0,0 +1,10 @@ +import { createStore } from 'redux'; +import createReducer from './reducers'; + +/** + * configureStore creates a new store with initial state and possible enhancers + * (like redux-saga or redux-thunk middleware) + */ +export default function configureStore(initialState) { + return createStore(createReducer(), initialState); +} diff --git a/yarn.lock b/yarn.lock index 52556dc9..722e55f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2608,6 +2608,10 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hoist-non-react-statics@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -2801,7 +2805,7 @@ interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" -invariant@^2.2.0, invariant@^2.2.1: +invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -3455,6 +3459,14 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0. json5 "^0.5.0" object-assign "^4.0.1" +lodash, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash-es@^4.2.0, lodash-es@^4.2.1: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" + lodash._arraycopy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" @@ -4869,6 +4881,16 @@ react-helmet@^4.0.0: object-assign "^4.0.1" react-side-effect "^1.1.0" +react-redux@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.2.tgz#3d9878f5f71c6fafcd45de1fbb162ea31f389814" + dependencies: + hoist-non-react-statics "^1.0.3" + invariant "^2.0.0" + lodash "^4.2.0" + lodash-es "^4.2.0" + loose-envify "^1.1.0" + react-router@4.0.0-alpha.6: version "4.0.0-alpha.6" resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.0.0-alpha.6.tgz#239fcf9a6ba7997021022c9b51d72d370f7b6bf4" @@ -5010,6 +5032,15 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" +redux@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.2" + referrer-policy@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.1.0.tgz#35774eb735bf50fb6c078e83334b472350207d79" @@ -5598,6 +5629,10 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" +symbol-observable@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" + "symbol-tree@>= 3.1.0 < 4.0.0": version "3.2.1" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb" From 731db1991d268361060723a6dc358428d7a3ff2b Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Wed, 18 Jan 2017 20:21:14 +0200 Subject: [PATCH 2/9] Add tests to ducks --- src/containers/SearchPage/SearchPage.js | 4 +- src/containers/SearchPage/SearchPage.test.js | 50 +++++++++++-- src/containers/SearchPage/SearchPageDucks.js | 10 +-- .../__snapshots__/SearchPage.test.js.snap | 2 +- src/ducks/FlashNotification.test.js | 74 +++++++++++++++++++ src/ducks/index.js | 2 +- 6 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 src/ducks/FlashNotification.test.js diff --git a/src/containers/SearchPage/SearchPage.js b/src/containers/SearchPage/SearchPage.js index 51d8daf6..358f6aef 100644 --- a/src/containers/SearchPage/SearchPage.js +++ b/src/containers/SearchPage/SearchPage.js @@ -5,7 +5,7 @@ import { Page } from '../../components'; import { addFlashNotification } from '../../ducks/FlashNotification'; import { addFilter } from './SearchPageDucks'; -export const SearchPage = props => ( +export const SearchPageComponent = () => ( Nice studio in Helsinki @@ -25,4 +25,4 @@ const mapDispatchToProps = function mapDispatchToProps(dispatch) { }; }; -export default connect(mapStateToProps, mapDispatchToProps)(SearchPage); +export default connect(mapStateToProps, mapDispatchToProps)(SearchPageComponent) diff --git a/src/containers/SearchPage/SearchPage.test.js b/src/containers/SearchPage/SearchPage.test.js index 81419d85..ab212ad8 100644 --- a/src/containers/SearchPage/SearchPage.test.js +++ b/src/containers/SearchPage/SearchPage.test.js @@ -1,16 +1,56 @@ import React from 'react'; import { BrowserRouter } from 'react-router'; import renderer from 'react-test-renderer'; -import { SearchPage } from './SearchPage'; +import { SearchPageComponent } from './SearchPage'; +import reducer, { addFilter } from './SearchPageDucks'; -describe('SearchPage', () => { +describe('SearchPageComponent', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); }); }); + +describe('SearchPageDucs', () => { + const ADD_FILTER = 'ADD_FILTER'; + + describe('actions', () => { + it('should create an action to add a filter', () => { + const expectedAction = { type: ADD_FILTER, payload: { location: 'helsinki' } }; + const serializedExpectations = JSON.stringify(expectedAction); + expect(JSON.stringify(addFilter('location', 'helsinki'))).toEqual(serializedExpectations); + }); + }); + + describe('reducer', () => { + it('should return the initial state', () => { + const initial = reducer(undefined, {}); + expect(initial).toEqual({}); + }); + + it('should handle ADD_FILTER', () => { + const filter1 = { location: 'helsinki' }; + const filter2 = { gears: 3 }; + const addFilter1 = { type: ADD_FILTER, payload: filter1 }; + const addFilter2 = { type: ADD_FILTER, payload: filter2 }; + const reduced = reducer([], addFilter1); + const reducedWithInitialContent = reducer({ filters: [filter1] }, addFilter2); + expect(reduced).toEqual({ filters: [filter1] }); + expect(reducedWithInitialContent).toEqual({ filters: [filter1, filter2] }); + }); + + it('should handle duplicates ADD_FILTER', () => { + const filter = { location: 'helsinki' }; + const addFilter = { type: ADD_FILTER, payload: filter }; + const reducedWithInitialContent = reducer({ filters: [filter] }, addFilter); + expect(reducedWithInitialContent).toEqual({ filters: [filter] }); + }); + }); +}); diff --git a/src/containers/SearchPage/SearchPageDucks.js b/src/containers/SearchPage/SearchPageDucks.js index 8f2a3ecd..2316e6c8 100644 --- a/src/containers/SearchPage/SearchPageDucks.js +++ b/src/containers/SearchPage/SearchPageDucks.js @@ -13,14 +13,14 @@ export default function reducer(state = {}, action = {}) { const { type, payload } = action; switch (type) { case ADD_FILTER: - const stateFilters = state.filters || []; - return Object.assign({}, state, { filters: unionBy(stateFilters, [ payload ]) }); + { + const stateFilters = state.filters || []; + return Object.assign({}, state, { filters: unionBy(stateFilters, [ payload ]) }); + } default: return state; } } // Action Creators -export const addFilter = (key, value) => { - return { type: ADD_FILTER, payload: { [key]: value } }; -}; +export const addFilter = (key, value) => ({ type: ADD_FILTER, payload: { [key]: value } }); diff --git a/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap b/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap index cf17ef7e..3009aeb6 100644 --- a/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap +++ b/src/containers/SearchPage/__snapshots__/SearchPage.test.js.snap @@ -1,4 +1,4 @@ -exports[`SearchPage matches snapshot 1`] = ` +exports[`SearchPageComponent matches snapshot 1`] = `
{ + const ADD_FLASH_NOTIFICATION = 'FLASH::ADD_NOTIFICATION'; + const REMOVE_FLASH_NOTIFICATION = 'FLASH::REMOVE_NOTIFICATION'; + + describe('actions', () => { + it('should create an action to add a filter', () => { + const content = 'Error message'; + const type = 'error'; + const expectedAction = { + type: ADD_FLASH_NOTIFICATION, + payload: { + id: 'note_1', + type, + content, + isRead: false, + } + }; + const serializedExpectations = JSON.stringify(expectedAction); + const received = JSON.stringify(addFlashNotification(type, content)); + expect(received).toEqual(serializedExpectations); + }); + + it('should create an action to remove a notification', () => { + const expectedAction = { + type: REMOVE_FLASH_NOTIFICATION, + payload: { id: 1 }, + }; + + expect(removeFlashNotification(1)).toEqual(expectedAction); + }); + }); + + describe('reducer', () => { + it('should return the initial state', () => { + const initial = reducer(undefined, {}); + expect(initial).toEqual([]); + }); + + it('should handle ADD_FLASH_NOTIFICATION', () => { + const flashNote1 = { + id: 0, + type: 'error', + content: 'Run the tests', + isRead: false, + }; + const flashNote2 = { + id: 0, + type: 'error', + content: 'Run the tests again', + isRead: false, + }; + const addFlashNote1 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote1 }; + const addFlashNote2 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote2 }; + const reduced = reducer([], addFlashNote1); + const reducedWithInitialContent = reducer([flashNote1], addFlashNote2); + expect(reduced).toEqual([flashNote1]); + expect(reducedWithInitialContent).toEqual([flashNote1, flashNote2]); + }); + + it('should handle duplicates ADD_FILTER', () => { + const flashNote = { + id: 0, + type: 'error', + content: 'Run the tests', + isRead: false, + }; + const addFlashNote = { type: ADD_FLASH_NOTIFICATION, payload: flashNote }; + const reducedWithInitialContent = reducer([flashNote], addFlashNote); + expect(reducedWithInitialContent).toEqual([flashNote]); + }); + }); +}); diff --git a/src/ducks/index.js b/src/ducks/index.js index 3f31556a..85fe9511 100644 --- a/src/ducks/index.js +++ b/src/ducks/index.js @@ -6,4 +6,4 @@ import FlashNotification from './FlashNotification'; -export { FlashNotification }; +export { FlashNotification }; // eslint-disable-line import/prefer-default-export From 1fa70eadf29a012dcca9c258a07adf887d1999a6 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Wed, 18 Jan 2017 17:58:00 +0200 Subject: [PATCH 3/9] Pass preloaded state to client side. --- public/index.html | 1 + server/index.js | 28 +++++++++++++++++++++------- src/app.js | 9 ++++++--- src/index.js | 6 +++++- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/public/index.html b/public/index.html index f7b8f2dc..e6fd9716 100644 --- a/public/index.html +++ b/public/index.html @@ -5,6 +5,7 @@ +
diff --git a/server/index.js b/server/index.js index 5b486f2a..b8e3aac8 100644 --- a/server/index.js +++ b/server/index.js @@ -21,12 +21,13 @@ const helmet = require('helmet'); const compression = require('compression'); const path = require('path'); const fs = require('fs'); -const auth = require('./auth'); +const qs = require('qs'); const _ = require('lodash'); const React = require('react'); const { createServerRenderContext } = require('react-router'); +const auth = require('./auth'); -// Construct the bundle path where the server side renering function +// Construct the bundle path where the server side rendering function // can be imported. const buildPath = path.resolve(__dirname, '..', 'build'); const manifestPath = path.join(buildPath, 'asset-manifest.json'); @@ -65,9 +66,17 @@ const template = _.template(indexHtml, { escape: reNoMatch, }); -function render(url, context) { - const { head, body } = renderApp(url, context); - return template({ title: head.title.toString(), body }); +function render(url, context, preloadedState) { + const { head, body } = renderApp(url, context, preloadedState); + + // Preloaded state needs to be passed for client side too. + const preloadedStateScript = ` + + `; + + return template({ title: head.title.toString(), preloadedStateScript, body }); } const env = process.env.NODE_ENV; @@ -91,7 +100,12 @@ app.use('/static', express.static(path.join(buildPath, 'static'))); app.get('*', (req, res) => { const context = createServerRenderContext(); - const html = render(req.url, context); + const filters = qs.parse(req.query); + + // TODO fetch this asynchronously + const preloadedState = { search: { filters } }; + + const html = render(req.url, context, preloadedState); const result = context.getResult(); if (result.redirect) { @@ -100,7 +114,7 @@ app.get('*', (req, res) => { // Do a second render pass with the context to clue // components into rendering this time. // See: https://react-router.now.sh/ServerRouter - res.status(404).send(render(req.url, context)); + res.status(404).send(render(req.url, context, preloadedState)); } else { res.send(html); } diff --git a/src/app.js b/src/app.js index 7e24a06f..6fe871dc 100644 --- a/src/app.js +++ b/src/app.js @@ -26,7 +26,7 @@ const { any, string } = PropTypes; ClientApp.propTypes = { store: any.isRequired }; export const ServerApp = props => { - const { url, context } = props; + const { url, context, store } = props; return ( { @@ -52,8 +52,11 @@ ServerApp.propTypes = { url: string.isRequired, context: any.isRequired, store: * - {String} body: Rendered application body of the given route * - {Object} head: Application head metadata from react-helmet */ -export const renderApp = (url, serverContext) => { - const body = ReactDOMServer.renderToString(); +export const renderApp = (url, serverContext, preloadedState) => { + const store = configureStore(preloadedState); + const body = ReactDOMServer.renderToString( + , + ); const head = Helmet.rewind(); return { head, body }; }; diff --git a/src/index.js b/src/index.js index d5c92c40..90e229be 100644 --- a/src/index.js +++ b/src/index.js @@ -14,12 +14,16 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { ClientApp, renderApp } from './app'; +import configureStore from './store'; import './index.css'; // If we're in a browser already, render the client application. if (typeof window !== 'undefined') { - ReactDOM.render(, document.getElementById('root')); + const preloadedState = window.__PRELOADED_STATE__ || {}; // eslint-disable-line no-underscore-dangle + const store = configureStore(preloadedState); + + ReactDOM.render(, document.getElementById('root')); } // Export the function for server side rendering. From c2aeac204e8c59036ecac2d66aa6d67aa46eca2b Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Wed, 18 Jan 2017 17:31:51 +0200 Subject: [PATCH 4/9] Rename Page.js to PageLayout.js --- src/components/Page/Page.js | 23 ---------- src/components/PageLayout/PageLayout.js | 42 +++++++++++++++++++ src/components/index.js | 4 +- .../AuthenticationPage/AuthenticationPage.js | 20 ++++----- .../AuthenticationPage.test.js | 8 ++-- src/containers/CheckoutPage/CheckoutPage.js | 6 +-- .../CheckoutPage/CheckoutPage.test.js | 8 ++-- .../ContactDetailsPage/ContactDetailsPage.js | 4 +- .../ContactDetailsPage.test.js | 8 ++-- .../ConversationPage/ConversationPage.js | 6 +-- .../ConversationPage/ConversationPage.test.js | 8 ++-- .../EditProfilePage/EditProfilePage.js | 4 +- .../EditProfilePage/EditProfilePage.test.js | 8 ++-- src/containers/InboxPage/InboxPage.js | 8 ++-- src/containers/InboxPage/InboxPage.test.js | 2 +- .../__snapshots__/InboxPage.test.js.snap | 2 +- src/containers/LandingPage/LandingPage.js | 8 ++-- .../LandingPage/LandingPage.test.js | 8 ++-- src/containers/ListingPage/ListingPage.js | 6 +-- .../ListingPage/ListingPage.test.js | 8 ++-- .../ManageListingsPage/ManageListingsPage.js | 8 ++-- .../ManageListingsPage.test.js | 8 ++-- src/containers/NotFoundPage/NotFoundPage.js | 8 ++-- .../NotFoundPage/NotFoundPage.test.js | 8 ++-- .../NotificationSettingsPage.js | 4 +- .../NotificationSettingsPage.test.js | 8 ++-- src/containers/OrderPage/OrderPage.js | 14 +++---- src/containers/OrderPage/OrderPage.test.js | 8 ++-- .../PasswordChangePage/PasswordChangePage.js | 4 +- .../PasswordChangePage.test.js | 8 ++-- .../PasswordForgottenPage.js | 4 +- .../PasswordForgottenPage.test.js | 8 ++-- .../PaymentMethodsPage/PaymentMethodsPage.js | 4 +- .../PaymentMethodsPage.test.js | 8 ++-- .../PayoutPreferencesPage.js | 4 +- .../PayoutPreferencesPage.test.js | 8 ++-- src/containers/ProfilePage/ProfilePage.js | 4 +- .../ProfilePage/ProfilePage.test.js | 8 ++-- .../SalesConversationPage.js | 10 ++--- .../SalesConversationPage.test.js | 8 ++-- src/containers/SearchPage/SearchPage.js | 2 +- src/containers/SecurityPage/SecurityPage.js | 8 ++-- .../SecurityPage/SecurityPage.test.js | 8 ++-- 43 files changed, 165 insertions(+), 188 deletions(-) delete mode 100644 src/components/Page/Page.js create mode 100644 src/components/PageLayout/PageLayout.js diff --git a/src/components/Page/Page.js b/src/components/Page/Page.js deleted file mode 100644 index 6be3286f..00000000 --- a/src/components/Page/Page.js +++ /dev/null @@ -1,23 +0,0 @@ -import React, { PropTypes } from 'react'; -import Helmet from 'react-helmet'; -import { Topbar } from '../../containers'; - -const Page = props => { - const { className, title, children } = props; - return ( -
- - -

{title}

- {children} -
- ); -}; - -const { string, any } = PropTypes; - -Page.defaultProps = { className: '', children: null }; - -Page.propTypes = { className: string, title: string.isRequired, children: any }; - -export default Page; diff --git a/src/components/PageLayout/PageLayout.js b/src/components/PageLayout/PageLayout.js new file mode 100644 index 00000000..d3ba63a6 --- /dev/null +++ b/src/components/PageLayout/PageLayout.js @@ -0,0 +1,42 @@ +import React, { Component, PropTypes } from 'react'; +import Helmet from 'react-helmet'; +import { Topbar } from '../../containers'; + +const scrollToTop = () => { + // Todo: this might need fine tuning later + window.scrollTo(0, 0); +} + +class PageLayout extends Component { + + componentDidMount() { + this.historyUnlisten = this.context.history.listen(() => scrollToTop()); + } + + componentWillUnmount() { + this.historyUnlisten(); + } + + + render() { + const { className, title, children } = this.props; + return ( +
+ + +

{title}

+ {children} +
+ ); + } +} + +PageLayout.contextTypes = { history: React.PropTypes.object }; + +PageLayout.defaultProps = { className: '', children: null }; + +const { string, any } = PropTypes; + +Page.propTypes = { className: string, title: string.isRequired, children: any }; + +export default Page; diff --git a/src/components/index.js b/src/components/index.js index 151ee0f4..3c747810 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,4 +1,4 @@ /* eslint-disable import/prefer-default-export */ -import Page from './Page/Page'; +import PageLayout from './PageLayout/PageLayout'; -export { Page }; +export { PageLayout }; diff --git a/src/containers/AuthenticationPage/AuthenticationPage.js b/src/containers/AuthenticationPage/AuthenticationPage.js index 648a4b42..76eac332 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.js +++ b/src/containers/AuthenticationPage/AuthenticationPage.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react'; import { Link, Redirect } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; import { fakeAuth } from '../../Routes'; class AuthenticationPage extends Component { @@ -30,30 +30,28 @@ class AuthenticationPage extends Component { const alternativeMethod = this.props.tab === 'login' ? toSignup : toLogin; const currentMethod = this.props.tab === 'login' ? 'Log in' : 'Sign up'; - const fromLoginMsg = from ? ( -

- You must log in to view the page at - {from.pathname} -

- ) : null; + const fromLoginMsg = from ?

+ You must log in to view the page at + {from.pathname} +

: null; return ( - + {redirectToReferrer ? : null} {fromLoginMsg}

or {alternativeMethod}

-
+ ); } } AuthenticationPage.defaultProps = { location: {}, tab: 'signup' }; -const { shape, string, oneOf } = PropTypes; +const { shape, string, object, oneOf, oneOfType } = PropTypes; AuthenticationPage.propTypes = { - location: shape({ state: shape({ from: string }) }), + location: shape({ state: shape({ from: oneOfType([object, string]) }) }), tab: oneOf([ 'login', 'signup' ]), }; diff --git a/src/containers/AuthenticationPage/AuthenticationPage.test.js b/src/containers/AuthenticationPage/AuthenticationPage.test.js index c6fc46de..c6885d77 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.test.js +++ b/src/containers/AuthenticationPage/AuthenticationPage.test.js @@ -6,11 +6,9 @@ import AuthenticationPage from './AuthenticationPage'; describe('AuthenticationPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index c4aac069..9f2f1a3d 100644 --- a/src/containers/CheckoutPage/CheckoutPage.js +++ b/src/containers/CheckoutPage/CheckoutPage.js @@ -1,11 +1,11 @@ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const CheckoutPage = ({ params }) => ( - + - + ); const { shape, string } = PropTypes; diff --git a/src/containers/CheckoutPage/CheckoutPage.test.js b/src/containers/CheckoutPage/CheckoutPage.test.js index 14834f2f..0887a31a 100644 --- a/src/containers/CheckoutPage/CheckoutPage.test.js +++ b/src/containers/CheckoutPage/CheckoutPage.test.js @@ -6,11 +6,9 @@ import CheckoutPage from './CheckoutPage'; describe('CheckoutPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.js b/src/containers/ContactDetailsPage/ContactDetailsPage.js index c66b62af..c96ad26b 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js index ae0d096c..31207dc4 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js @@ -6,11 +6,9 @@ import ContactDetailsPage from './ContactDetailsPage'; describe('ContactDetailsPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ConversationPage/ConversationPage.js b/src/containers/ConversationPage/ConversationPage.js index 8803ef02..5365123a 100644 --- a/src/containers/ConversationPage/ConversationPage.js +++ b/src/containers/ConversationPage/ConversationPage.js @@ -1,12 +1,12 @@ import React, { PropTypes } from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const ConversationPage = props => { const { params } = props; return ( - +

Conversation id: {params.id}

-
+
); }; diff --git a/src/containers/ConversationPage/ConversationPage.test.js b/src/containers/ConversationPage/ConversationPage.test.js index 3d9af4af..eef04a4c 100644 --- a/src/containers/ConversationPage/ConversationPage.test.js +++ b/src/containers/ConversationPage/ConversationPage.test.js @@ -6,11 +6,9 @@ import ConversationPage from './ConversationPage'; describe('ConversationPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/EditProfilePage/EditProfilePage.js b/src/containers/EditProfilePage/EditProfilePage.js index d92cbe7c..99a1da1c 100644 --- a/src/containers/EditProfilePage/EditProfilePage.js +++ b/src/containers/EditProfilePage/EditProfilePage.js @@ -1,9 +1,9 @@ import React, { PropTypes } from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const EditProfilePage = props => { const { params } = props; - return ; + return ; }; const { shape, string } = PropTypes; diff --git a/src/containers/EditProfilePage/EditProfilePage.test.js b/src/containers/EditProfilePage/EditProfilePage.test.js index ef5c30ba..98a22b7e 100644 --- a/src/containers/EditProfilePage/EditProfilePage.test.js +++ b/src/containers/EditProfilePage/EditProfilePage.test.js @@ -6,11 +6,9 @@ import EditProfilePage from './EditProfilePage'; describe('EditProfilePage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/InboxPage/InboxPage.js b/src/containers/InboxPage/InboxPage.js index f94605a9..01035ee9 100644 --- a/src/containers/InboxPage/InboxPage.js +++ b/src/containers/InboxPage/InboxPage.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const toPath = (filter, id) => { switch (filter) { @@ -16,11 +16,11 @@ const toPath = (filter, id) => { const InboxPage = props => { const { filter } = props; return ( - +
  • Single thread
-
+
); }; @@ -28,6 +28,6 @@ InboxPage.defaultProps = { filter: 'conversation' }; const { oneOf } = PropTypes; -InboxPage.propTypes = { filter: oneOf([ 'orders', 'sales', 'conversation' ]) }; +InboxPage.propTypes = { filter: oneOf([ 'orders', 'sales', 'inbox' ]) }; export default InboxPage; diff --git a/src/containers/InboxPage/InboxPage.test.js b/src/containers/InboxPage/InboxPage.test.js index 7076f86e..cd8eb048 100644 --- a/src/containers/InboxPage/InboxPage.test.js +++ b/src/containers/InboxPage/InboxPage.test.js @@ -8,7 +8,7 @@ describe('InboxPage', () => { const component = renderer.create( ( - + ), ); diff --git a/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap b/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap index 3c315f29..41351df5 100644 --- a/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap +++ b/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap @@ -135,7 +135,7 @@ exports[`InboxPage matches snapshot 1`] = `

- conversation page + inbox page

  • diff --git a/src/containers/LandingPage/LandingPage.js b/src/containers/LandingPage/LandingPage.js index 2c0b297a..db448beb 100644 --- a/src/containers/LandingPage/LandingPage.js +++ b/src/containers/LandingPage/LandingPage.js @@ -1,9 +1,9 @@ import React from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; export default () => ( - + Find studios - -) + +); diff --git a/src/containers/LandingPage/LandingPage.test.js b/src/containers/LandingPage/LandingPage.test.js index bce166df..87a4dd78 100644 --- a/src/containers/LandingPage/LandingPage.test.js +++ b/src/containers/LandingPage/LandingPage.test.js @@ -6,11 +6,9 @@ import LandingPage from './LandingPage'; describe('LandingPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 31e10d6f..c0ab09e8 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const ListingPage = ({ params }) => { // Listing id should be located either in the end of slug @@ -11,10 +11,10 @@ const ListingPage = ({ params }) => { // TODO: Fetch data from SDK if no data is passed through props return ( - +

    Slug: {params.slug}

    -
    + ); }; diff --git a/src/containers/ListingPage/ListingPage.test.js b/src/containers/ListingPage/ListingPage.test.js index 7b427688..2eebda01 100644 --- a/src/containers/ListingPage/ListingPage.test.js +++ b/src/containers/ListingPage/ListingPage.test.js @@ -6,11 +6,9 @@ import ListingPage from './ListingPage'; describe('ListingPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ManageListingsPage/ManageListingsPage.js b/src/containers/ManageListingsPage/ManageListingsPage.js index 5c1d1c11..f9919b58 100644 --- a/src/containers/ManageListingsPage/ManageListingsPage.js +++ b/src/containers/ManageListingsPage/ManageListingsPage.js @@ -1,11 +1,11 @@ import React from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; export default () => ( - +
    • Listing 1234
    -
    -) + +); diff --git a/src/containers/ManageListingsPage/ManageListingsPage.test.js b/src/containers/ManageListingsPage/ManageListingsPage.test.js index 574dd53b..98c305f2 100644 --- a/src/containers/ManageListingsPage/ManageListingsPage.test.js +++ b/src/containers/ManageListingsPage/ManageListingsPage.test.js @@ -6,11 +6,9 @@ import ManageListingsPage from './ManageListingsPage'; describe('ManageListingsPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/NotFoundPage/NotFoundPage.js b/src/containers/NotFoundPage/NotFoundPage.js index 1b6cc3f9..2aa3dc62 100644 --- a/src/containers/NotFoundPage/NotFoundPage.js +++ b/src/containers/NotFoundPage/NotFoundPage.js @@ -1,9 +1,9 @@ import React from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; export default () => ( - + Index page - -) + +); diff --git a/src/containers/NotFoundPage/NotFoundPage.test.js b/src/containers/NotFoundPage/NotFoundPage.test.js index 254c66c6..027ab5dc 100644 --- a/src/containers/NotFoundPage/NotFoundPage.test.js +++ b/src/containers/NotFoundPage/NotFoundPage.test.js @@ -6,11 +6,9 @@ import NotFoundPage from './NotFoundPage'; describe('NotFoundPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/NotificationSettingsPage/NotificationSettingsPage.js b/src/containers/NotificationSettingsPage/NotificationSettingsPage.js index a7116bf9..160a466f 100644 --- a/src/containers/NotificationSettingsPage/NotificationSettingsPage.js +++ b/src/containers/NotificationSettingsPage/NotificationSettingsPage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js b/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js index 2266fec6..6c9df32b 100644 --- a/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js +++ b/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js @@ -6,11 +6,9 @@ import NotificationSettingsPage from './NotificationSettingsPage'; describe('NotificationSettingsPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/OrderPage/OrderPage.js b/src/containers/OrderPage/OrderPage.js index 0bf79296..21b6ac16 100644 --- a/src/containers/OrderPage/OrderPage.js +++ b/src/containers/OrderPage/OrderPage.js @@ -1,28 +1,26 @@ /* eslint-disable react/no-unescaped-entities */ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const OrderPage = props => { const { params } = props; return ( - +

    Order id: {params.id}

    Discussion tab
    Details tab

    Mobile layout needs different views for discussion and details.

    - Discussion view is the default if route doesn't specify mobile tab (e.g. - /order/1234 - ) + Discussion view is the default if route doesn't specify mobile tab (e.g. /order/1234)

    -
    +
    ); }; -const { shape, number } = PropTypes; +const { number, oneOfType, shape, string } = PropTypes; -OrderPage.propTypes = { params: shape({ id: number.isRequired }).isRequired }; +OrderPage.propTypes = { params: shape({ id: oneOfType([number, string]).isRequired }).isRequired }; export default OrderPage; diff --git a/src/containers/OrderPage/OrderPage.test.js b/src/containers/OrderPage/OrderPage.test.js index ebb298d4..f45def33 100644 --- a/src/containers/OrderPage/OrderPage.test.js +++ b/src/containers/OrderPage/OrderPage.test.js @@ -6,11 +6,9 @@ import OrderPage from './OrderPage'; describe('OrderPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PasswordChangePage/PasswordChangePage.js b/src/containers/PasswordChangePage/PasswordChangePage.js index 89b637d6..acbe8501 100644 --- a/src/containers/PasswordChangePage/PasswordChangePage.js +++ b/src/containers/PasswordChangePage/PasswordChangePage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/PasswordChangePage/PasswordChangePage.test.js b/src/containers/PasswordChangePage/PasswordChangePage.test.js index 2680449a..51ba7a55 100644 --- a/src/containers/PasswordChangePage/PasswordChangePage.test.js +++ b/src/containers/PasswordChangePage/PasswordChangePage.test.js @@ -6,11 +6,9 @@ import PasswordChangePage from './PasswordChangePage'; describe('PasswordChangePage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PasswordForgottenPage/PasswordForgottenPage.js b/src/containers/PasswordForgottenPage/PasswordForgottenPage.js index f779933f..1a51882a 100644 --- a/src/containers/PasswordForgottenPage/PasswordForgottenPage.js +++ b/src/containers/PasswordForgottenPage/PasswordForgottenPage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js b/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js index ac9caba1..4f402450 100644 --- a/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js +++ b/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js @@ -6,11 +6,9 @@ import PasswordForgottenPage from './PasswordForgottenPage'; describe('PasswordForgottenPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js index 40a09b48..78c59097 100644 --- a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js +++ b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js b/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js index 49a21213..b7cc0b6a 100644 --- a/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js +++ b/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js @@ -6,11 +6,9 @@ import PaymentMethodsPage from './PaymentMethodsPage'; describe('PaymentMethodsPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js index 65c4fd17..f2b361c2 100644 --- a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js +++ b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js @@ -1,4 +1,4 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; -export default () => +export default () => ; diff --git a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js index 2a249f99..cc60a1cb 100644 --- a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js +++ b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js @@ -6,11 +6,9 @@ import PayoutPreferencesPage from './PayoutPreferencesPage'; describe('PayoutPreferencesPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ProfilePage/ProfilePage.js b/src/containers/ProfilePage/ProfilePage.js index 6696d72a..d20de21e 100644 --- a/src/containers/ProfilePage/ProfilePage.js +++ b/src/containers/ProfilePage/ProfilePage.js @@ -1,8 +1,8 @@ import React, { PropTypes } from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const ProfilePage = ({ params }) => ( - + ); const { shape, string } = PropTypes; diff --git a/src/containers/ProfilePage/ProfilePage.test.js b/src/containers/ProfilePage/ProfilePage.test.js index 2c2e6586..291fe4b7 100644 --- a/src/containers/ProfilePage/ProfilePage.test.js +++ b/src/containers/ProfilePage/ProfilePage.test.js @@ -6,11 +6,9 @@ import ProfilePage from './ProfilePage'; describe('ProfilePage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/SalesConversationPage/SalesConversationPage.js b/src/containers/SalesConversationPage/SalesConversationPage.js index 313dfeb4..f24f6725 100644 --- a/src/containers/SalesConversationPage/SalesConversationPage.js +++ b/src/containers/SalesConversationPage/SalesConversationPage.js @@ -1,23 +1,21 @@ /* eslint-disable react/no-unescaped-entities */ import React, { PropTypes } from 'react'; import { Link } from 'react-router'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; const SalesConversationPage = props => { const { params } = props; return ( - +

    Sale id: {params.id}

    Discussion tab
    Details tab

    Mobile layout needs different views for discussion and details.

    - Discussion view is the default if route doesn't specify mobile tab (e.g. - /order/1234 - ) + Discussion view is the default if route doesn't specify mobile tab (e.g. /order/1234)

    -
    +
    ); }; diff --git a/src/containers/SalesConversationPage/SalesConversationPage.test.js b/src/containers/SalesConversationPage/SalesConversationPage.test.js index 6d45e10f..c32b43d8 100644 --- a/src/containers/SalesConversationPage/SalesConversationPage.test.js +++ b/src/containers/SalesConversationPage/SalesConversationPage.test.js @@ -6,11 +6,9 @@ import SalesConversationPage from './SalesConversationPage'; describe('SalesConversationPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/SearchPage/SearchPage.js b/src/containers/SearchPage/SearchPage.js index 358f6aef..bb8e7d4a 100644 --- a/src/containers/SearchPage/SearchPage.js +++ b/src/containers/SearchPage/SearchPage.js @@ -1,7 +1,7 @@ import React from 'react'; import { Link } from 'react-router'; import { connect } from 'react-redux'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; import { addFlashNotification } from '../../ducks/FlashNotification'; import { addFilter } from './SearchPageDucks'; diff --git a/src/containers/SecurityPage/SecurityPage.js b/src/containers/SecurityPage/SecurityPage.js index 4f343345..c9a834fa 100644 --- a/src/containers/SecurityPage/SecurityPage.js +++ b/src/containers/SecurityPage/SecurityPage.js @@ -1,8 +1,8 @@ import React from 'react'; -import { Page } from '../../components'; +import { PageLayout } from '../../components'; export default () => ( - +

    Change password, delete account

    -
    -) +
    +); diff --git a/src/containers/SecurityPage/SecurityPage.test.js b/src/containers/SecurityPage/SecurityPage.test.js index 6f371307..1da5ecfc 100644 --- a/src/containers/SecurityPage/SecurityPage.test.js +++ b/src/containers/SecurityPage/SecurityPage.test.js @@ -6,11 +6,9 @@ import SecurityPage from './SecurityPage'; describe('SecurityPage', () => { it('matches snapshot', () => { const component = renderer.create( - ( - - - - ), + + + , ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); From 30c3d5abfd1f0ed45293007a6c2829ba1620c80e Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Wed, 18 Jan 2017 17:42:48 +0200 Subject: [PATCH 5/9] Fix scroll position after route changes --- src/components/PageLayout/PageLayout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PageLayout/PageLayout.js b/src/components/PageLayout/PageLayout.js index d3ba63a6..68cbe61b 100644 --- a/src/components/PageLayout/PageLayout.js +++ b/src/components/PageLayout/PageLayout.js @@ -37,6 +37,6 @@ PageLayout.defaultProps = { className: '', children: null }; const { string, any } = PropTypes; -Page.propTypes = { className: string, title: string.isRequired, children: any }; +PageLayout.propTypes = { className: string, title: string.isRequired, children: any }; -export default Page; +export default PageLayout; From 4feb5e8087a90182acc1e224da442bef059481da Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Thu, 19 Jan 2017 13:44:50 +0200 Subject: [PATCH 6/9] A run with correct Prettier version --- src/app.js | 12 ++---- src/components/PageLayout/PageLayout.js | 4 +- .../AuthenticationPage/AuthenticationPage.js | 12 +++--- .../AuthenticationPage.test.js | 8 ++-- .../CheckoutPage/CheckoutPage.test.js | 8 ++-- .../ContactDetailsPage/ContactDetailsPage.js | 2 +- .../ContactDetailsPage.test.js | 8 ++-- .../ConversationPage/ConversationPage.test.js | 8 ++-- .../EditProfilePage/EditProfilePage.test.js | 8 ++-- src/containers/InboxPage/InboxPage.test.js | 2 +- src/containers/LandingPage/LandingPage.js | 2 +- .../LandingPage/LandingPage.test.js | 8 ++-- .../ListingPage/ListingPage.test.js | 8 ++-- .../ManageListingsPage/ManageListingsPage.js | 2 +- .../ManageListingsPage.test.js | 8 ++-- src/containers/NotFoundPage/NotFoundPage.js | 2 +- .../NotFoundPage/NotFoundPage.test.js | 8 ++-- .../NotificationSettingsPage.js | 2 +- .../NotificationSettingsPage.test.js | 8 ++-- src/containers/OrderPage/OrderPage.js | 8 +++- src/containers/OrderPage/OrderPage.test.js | 8 ++-- .../PasswordChangePage/PasswordChangePage.js | 2 +- .../PasswordChangePage.test.js | 8 ++-- .../PasswordForgottenPage.js | 2 +- .../PasswordForgottenPage.test.js | 8 ++-- .../PaymentMethodsPage/PaymentMethodsPage.js | 2 +- .../PaymentMethodsPage.test.js | 8 ++-- .../PayoutPreferencesPage.js | 2 +- .../PayoutPreferencesPage.test.js | 8 ++-- .../ProfilePage/ProfilePage.test.js | 8 ++-- .../SalesConversationPage.js | 4 +- .../SalesConversationPage.test.js | 8 ++-- src/containers/SearchPage/SearchPage.test.js | 10 ++--- src/containers/SecurityPage/SecurityPage.js | 2 +- .../SecurityPage/SecurityPage.test.js | 8 ++-- src/ducks/FlashNotification.js | 2 +- src/ducks/FlashNotification.test.js | 43 +++++-------------- 37 files changed, 138 insertions(+), 123 deletions(-) diff --git a/src/app.js b/src/app.js index 6fe871dc..4c82629b 100644 --- a/src/app.js +++ b/src/app.js @@ -10,13 +10,11 @@ export const ClientApp = props => { const { store } = props; return ( - { - ({router}) => ( + {({ router }) => ( - ) - } + )} ); }; @@ -29,13 +27,11 @@ export const ServerApp = props => { const { url, context, store } = props; return ( - { - ({router}) => ( + {({ router }) => ( - ) - } + )} ); }; diff --git a/src/components/PageLayout/PageLayout.js b/src/components/PageLayout/PageLayout.js index 68cbe61b..046d6627 100644 --- a/src/components/PageLayout/PageLayout.js +++ b/src/components/PageLayout/PageLayout.js @@ -5,10 +5,9 @@ import { Topbar } from '../../containers'; const scrollToTop = () => { // Todo: this might need fine tuning later window.scrollTo(0, 0); -} +}; class PageLayout extends Component { - componentDidMount() { this.historyUnlisten = this.context.history.listen(() => scrollToTop()); } @@ -17,7 +16,6 @@ class PageLayout extends Component { this.historyUnlisten(); } - render() { const { className, title, children } = this.props; return ( diff --git a/src/containers/AuthenticationPage/AuthenticationPage.js b/src/containers/AuthenticationPage/AuthenticationPage.js index 76eac332..3fd82cef 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.js +++ b/src/containers/AuthenticationPage/AuthenticationPage.js @@ -30,10 +30,12 @@ class AuthenticationPage extends Component { const alternativeMethod = this.props.tab === 'login' ? toSignup : toLogin; const currentMethod = this.props.tab === 'login' ? 'Log in' : 'Sign up'; - const fromLoginMsg = from ?

    - You must log in to view the page at - {from.pathname} -

    : null; + const fromLoginMsg = from ? ( +

    + You must log in to view the page at + {from.pathname} +

    + ) : null; return ( @@ -51,7 +53,7 @@ AuthenticationPage.defaultProps = { location: {}, tab: 'signup' }; const { shape, string, object, oneOf, oneOfType } = PropTypes; AuthenticationPage.propTypes = { - location: shape({ state: shape({ from: oneOfType([object, string]) }) }), + location: shape({ state: shape({ from: oneOfType([ object, string ]) }) }), tab: oneOf([ 'login', 'signup' ]), }; diff --git a/src/containers/AuthenticationPage/AuthenticationPage.test.js b/src/containers/AuthenticationPage/AuthenticationPage.test.js index c6885d77..c6fc46de 100644 --- a/src/containers/AuthenticationPage/AuthenticationPage.test.js +++ b/src/containers/AuthenticationPage/AuthenticationPage.test.js @@ -6,9 +6,11 @@ import AuthenticationPage from './AuthenticationPage'; describe('AuthenticationPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/CheckoutPage/CheckoutPage.test.js b/src/containers/CheckoutPage/CheckoutPage.test.js index 0887a31a..14834f2f 100644 --- a/src/containers/CheckoutPage/CheckoutPage.test.js +++ b/src/containers/CheckoutPage/CheckoutPage.test.js @@ -6,9 +6,11 @@ import CheckoutPage from './CheckoutPage'; describe('CheckoutPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.js b/src/containers/ContactDetailsPage/ContactDetailsPage.js index c96ad26b..7ab15bea 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js index 31207dc4..ae0d096c 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js @@ -6,9 +6,11 @@ import ContactDetailsPage from './ContactDetailsPage'; describe('ContactDetailsPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ConversationPage/ConversationPage.test.js b/src/containers/ConversationPage/ConversationPage.test.js index eef04a4c..3d9af4af 100644 --- a/src/containers/ConversationPage/ConversationPage.test.js +++ b/src/containers/ConversationPage/ConversationPage.test.js @@ -6,9 +6,11 @@ import ConversationPage from './ConversationPage'; describe('ConversationPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/EditProfilePage/EditProfilePage.test.js b/src/containers/EditProfilePage/EditProfilePage.test.js index 98a22b7e..ef5c30ba 100644 --- a/src/containers/EditProfilePage/EditProfilePage.test.js +++ b/src/containers/EditProfilePage/EditProfilePage.test.js @@ -6,9 +6,11 @@ import EditProfilePage from './EditProfilePage'; describe('EditProfilePage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/InboxPage/InboxPage.test.js b/src/containers/InboxPage/InboxPage.test.js index cd8eb048..81f4669e 100644 --- a/src/containers/InboxPage/InboxPage.test.js +++ b/src/containers/InboxPage/InboxPage.test.js @@ -8,7 +8,7 @@ describe('InboxPage', () => { const component = renderer.create( ( - + ), ); diff --git a/src/containers/LandingPage/LandingPage.js b/src/containers/LandingPage/LandingPage.js index db448beb..3d583503 100644 --- a/src/containers/LandingPage/LandingPage.js +++ b/src/containers/LandingPage/LandingPage.js @@ -6,4 +6,4 @@ export default () => ( Find studios -); +) diff --git a/src/containers/LandingPage/LandingPage.test.js b/src/containers/LandingPage/LandingPage.test.js index 87a4dd78..bce166df 100644 --- a/src/containers/LandingPage/LandingPage.test.js +++ b/src/containers/LandingPage/LandingPage.test.js @@ -6,9 +6,11 @@ import LandingPage from './LandingPage'; describe('LandingPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ListingPage/ListingPage.test.js b/src/containers/ListingPage/ListingPage.test.js index 2eebda01..7b427688 100644 --- a/src/containers/ListingPage/ListingPage.test.js +++ b/src/containers/ListingPage/ListingPage.test.js @@ -6,9 +6,11 @@ import ListingPage from './ListingPage'; describe('ListingPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ManageListingsPage/ManageListingsPage.js b/src/containers/ManageListingsPage/ManageListingsPage.js index f9919b58..6a76103e 100644 --- a/src/containers/ManageListingsPage/ManageListingsPage.js +++ b/src/containers/ManageListingsPage/ManageListingsPage.js @@ -8,4 +8,4 @@ export default () => (
  • Listing 1234
-); +) diff --git a/src/containers/ManageListingsPage/ManageListingsPage.test.js b/src/containers/ManageListingsPage/ManageListingsPage.test.js index 98c305f2..574dd53b 100644 --- a/src/containers/ManageListingsPage/ManageListingsPage.test.js +++ b/src/containers/ManageListingsPage/ManageListingsPage.test.js @@ -6,9 +6,11 @@ import ManageListingsPage from './ManageListingsPage'; describe('ManageListingsPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/NotFoundPage/NotFoundPage.js b/src/containers/NotFoundPage/NotFoundPage.js index 2aa3dc62..50aa9350 100644 --- a/src/containers/NotFoundPage/NotFoundPage.js +++ b/src/containers/NotFoundPage/NotFoundPage.js @@ -6,4 +6,4 @@ export default () => ( Index page -); +) diff --git a/src/containers/NotFoundPage/NotFoundPage.test.js b/src/containers/NotFoundPage/NotFoundPage.test.js index 027ab5dc..254c66c6 100644 --- a/src/containers/NotFoundPage/NotFoundPage.test.js +++ b/src/containers/NotFoundPage/NotFoundPage.test.js @@ -6,9 +6,11 @@ import NotFoundPage from './NotFoundPage'; describe('NotFoundPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/NotificationSettingsPage/NotificationSettingsPage.js b/src/containers/NotificationSettingsPage/NotificationSettingsPage.js index 160a466f..b1cf74e1 100644 --- a/src/containers/NotificationSettingsPage/NotificationSettingsPage.js +++ b/src/containers/NotificationSettingsPage/NotificationSettingsPage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js b/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js index 6c9df32b..2266fec6 100644 --- a/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js +++ b/src/containers/NotificationSettingsPage/NotificationSettingsPage.test.js @@ -6,9 +6,11 @@ import NotificationSettingsPage from './NotificationSettingsPage'; describe('NotificationSettingsPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/OrderPage/OrderPage.js b/src/containers/OrderPage/OrderPage.js index 21b6ac16..fe6c2762 100644 --- a/src/containers/OrderPage/OrderPage.js +++ b/src/containers/OrderPage/OrderPage.js @@ -13,7 +13,9 @@ const OrderPage = props => { Details tab

Mobile layout needs different views for discussion and details.

- Discussion view is the default if route doesn't specify mobile tab (e.g. /order/1234) + Discussion view is the default if route doesn't specify mobile tab (e.g. + /order/1234 + )

); @@ -21,6 +23,8 @@ const OrderPage = props => { const { number, oneOfType, shape, string } = PropTypes; -OrderPage.propTypes = { params: shape({ id: oneOfType([number, string]).isRequired }).isRequired }; +OrderPage.propTypes = { + params: shape({ id: oneOfType([ number, string ]).isRequired }).isRequired, +}; export default OrderPage; diff --git a/src/containers/OrderPage/OrderPage.test.js b/src/containers/OrderPage/OrderPage.test.js index f45def33..ebb298d4 100644 --- a/src/containers/OrderPage/OrderPage.test.js +++ b/src/containers/OrderPage/OrderPage.test.js @@ -6,9 +6,11 @@ import OrderPage from './OrderPage'; describe('OrderPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PasswordChangePage/PasswordChangePage.js b/src/containers/PasswordChangePage/PasswordChangePage.js index acbe8501..a65c64cf 100644 --- a/src/containers/PasswordChangePage/PasswordChangePage.js +++ b/src/containers/PasswordChangePage/PasswordChangePage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/PasswordChangePage/PasswordChangePage.test.js b/src/containers/PasswordChangePage/PasswordChangePage.test.js index 51ba7a55..2680449a 100644 --- a/src/containers/PasswordChangePage/PasswordChangePage.test.js +++ b/src/containers/PasswordChangePage/PasswordChangePage.test.js @@ -6,9 +6,11 @@ import PasswordChangePage from './PasswordChangePage'; describe('PasswordChangePage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PasswordForgottenPage/PasswordForgottenPage.js b/src/containers/PasswordForgottenPage/PasswordForgottenPage.js index 1a51882a..446a8c64 100644 --- a/src/containers/PasswordForgottenPage/PasswordForgottenPage.js +++ b/src/containers/PasswordForgottenPage/PasswordForgottenPage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js b/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js index 4f402450..ac9caba1 100644 --- a/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js +++ b/src/containers/PasswordForgottenPage/PasswordForgottenPage.test.js @@ -6,9 +6,11 @@ import PasswordForgottenPage from './PasswordForgottenPage'; describe('PasswordForgottenPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js index 78c59097..de4e76f6 100644 --- a/src/containers/PaymentMethodsPage/PaymentMethodsPage.js +++ b/src/containers/PaymentMethodsPage/PaymentMethodsPage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js b/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js index b7cc0b6a..49a21213 100644 --- a/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js +++ b/src/containers/PaymentMethodsPage/PaymentMethodsPage.test.js @@ -6,9 +6,11 @@ import PaymentMethodsPage from './PaymentMethodsPage'; describe('PaymentMethodsPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js index f2b361c2..735d8c25 100644 --- a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js +++ b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.js @@ -1,4 +1,4 @@ import React from 'react'; import { PageLayout } from '../../components'; -export default () => ; +export default () => diff --git a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js index cc60a1cb..2a249f99 100644 --- a/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js +++ b/src/containers/PayoutPreferencesPage/PayoutPreferencesPage.test.js @@ -6,9 +6,11 @@ import PayoutPreferencesPage from './PayoutPreferencesPage'; describe('PayoutPreferencesPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/ProfilePage/ProfilePage.test.js b/src/containers/ProfilePage/ProfilePage.test.js index 291fe4b7..2c2e6586 100644 --- a/src/containers/ProfilePage/ProfilePage.test.js +++ b/src/containers/ProfilePage/ProfilePage.test.js @@ -6,9 +6,11 @@ import ProfilePage from './ProfilePage'; describe('ProfilePage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/SalesConversationPage/SalesConversationPage.js b/src/containers/SalesConversationPage/SalesConversationPage.js index f24f6725..6a80e93e 100644 --- a/src/containers/SalesConversationPage/SalesConversationPage.js +++ b/src/containers/SalesConversationPage/SalesConversationPage.js @@ -13,7 +13,9 @@ const SalesConversationPage = props => { Details tab

Mobile layout needs different views for discussion and details.

- Discussion view is the default if route doesn't specify mobile tab (e.g. /order/1234) + Discussion view is the default if route doesn't specify mobile tab (e.g. + /order/1234 + )

); diff --git a/src/containers/SalesConversationPage/SalesConversationPage.test.js b/src/containers/SalesConversationPage/SalesConversationPage.test.js index c32b43d8..6d45e10f 100644 --- a/src/containers/SalesConversationPage/SalesConversationPage.test.js +++ b/src/containers/SalesConversationPage/SalesConversationPage.test.js @@ -6,9 +6,11 @@ import SalesConversationPage from './SalesConversationPage'; describe('SalesConversationPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/containers/SearchPage/SearchPage.test.js b/src/containers/SearchPage/SearchPage.test.js index ab212ad8..b2ef2b1e 100644 --- a/src/containers/SearchPage/SearchPage.test.js +++ b/src/containers/SearchPage/SearchPage.test.js @@ -41,16 +41,16 @@ describe('SearchPageDucs', () => { const addFilter1 = { type: ADD_FILTER, payload: filter1 }; const addFilter2 = { type: ADD_FILTER, payload: filter2 }; const reduced = reducer([], addFilter1); - const reducedWithInitialContent = reducer({ filters: [filter1] }, addFilter2); - expect(reduced).toEqual({ filters: [filter1] }); - expect(reducedWithInitialContent).toEqual({ filters: [filter1, filter2] }); + const reducedWithInitialContent = reducer({ filters: [ filter1 ] }, addFilter2); + expect(reduced).toEqual({ filters: [ filter1 ] }); + expect(reducedWithInitialContent).toEqual({ filters: [ filter1, filter2 ] }); }); it('should handle duplicates ADD_FILTER', () => { const filter = { location: 'helsinki' }; const addFilter = { type: ADD_FILTER, payload: filter }; - const reducedWithInitialContent = reducer({ filters: [filter] }, addFilter); - expect(reducedWithInitialContent).toEqual({ filters: [filter] }); + const reducedWithInitialContent = reducer({ filters: [ filter ] }, addFilter); + expect(reducedWithInitialContent).toEqual({ filters: [ filter ] }); }); }); }); diff --git a/src/containers/SecurityPage/SecurityPage.js b/src/containers/SecurityPage/SecurityPage.js index c9a834fa..5b039f4c 100644 --- a/src/containers/SecurityPage/SecurityPage.js +++ b/src/containers/SecurityPage/SecurityPage.js @@ -5,4 +5,4 @@ export default () => (

Change password, delete account

-); +) diff --git a/src/containers/SecurityPage/SecurityPage.test.js b/src/containers/SecurityPage/SecurityPage.test.js index 1da5ecfc..6f371307 100644 --- a/src/containers/SecurityPage/SecurityPage.test.js +++ b/src/containers/SecurityPage/SecurityPage.test.js @@ -6,9 +6,11 @@ import SecurityPage from './SecurityPage'; describe('SecurityPage', () => { it('matches snapshot', () => { const component = renderer.create( - - - , + ( + + + + ), ); const tree = component.toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/ducks/FlashNotification.js b/src/ducks/FlashNotification.js index c5aea686..13d56594 100644 --- a/src/ducks/FlashNotification.js +++ b/src/ducks/FlashNotification.js @@ -35,7 +35,7 @@ export default (state = initialState, action) => { default: return state; } -}; +} // Action Creators let nextMessageId = 1; diff --git a/src/ducks/FlashNotification.test.js b/src/ducks/FlashNotification.test.js index 29881adf..21c5e3a7 100644 --- a/src/ducks/FlashNotification.test.js +++ b/src/ducks/FlashNotification.test.js @@ -10,12 +10,7 @@ describe('FlashNotification', () => { const type = 'error'; const expectedAction = { type: ADD_FLASH_NOTIFICATION, - payload: { - id: 'note_1', - type, - content, - isRead: false, - } + payload: { id: 'note_1', type, content, isRead: false }, }; const serializedExpectations = JSON.stringify(expectedAction); const received = JSON.stringify(addFlashNotification(type, content)); @@ -23,10 +18,7 @@ describe('FlashNotification', () => { }); it('should create an action to remove a notification', () => { - const expectedAction = { - type: REMOVE_FLASH_NOTIFICATION, - payload: { id: 1 }, - }; + const expectedAction = { type: REMOVE_FLASH_NOTIFICATION, payload: { id: 1 } }; expect(removeFlashNotification(1)).toEqual(expectedAction); }); @@ -39,36 +31,21 @@ describe('FlashNotification', () => { }); it('should handle ADD_FLASH_NOTIFICATION', () => { - const flashNote1 = { - id: 0, - type: 'error', - content: 'Run the tests', - isRead: false, - }; - const flashNote2 = { - id: 0, - type: 'error', - content: 'Run the tests again', - isRead: false, - }; + const flashNote1 = { id: 0, type: 'error', content: 'Run the tests', isRead: false }; + const flashNote2 = { id: 0, type: 'error', content: 'Run the tests again', isRead: false }; const addFlashNote1 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote1 }; const addFlashNote2 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote2 }; const reduced = reducer([], addFlashNote1); - const reducedWithInitialContent = reducer([flashNote1], addFlashNote2); - expect(reduced).toEqual([flashNote1]); - expect(reducedWithInitialContent).toEqual([flashNote1, flashNote2]); + const reducedWithInitialContent = reducer([ flashNote1 ], addFlashNote2); + expect(reduced).toEqual([ flashNote1 ]); + expect(reducedWithInitialContent).toEqual([ flashNote1, flashNote2 ]); }); it('should handle duplicates ADD_FILTER', () => { - const flashNote = { - id: 0, - type: 'error', - content: 'Run the tests', - isRead: false, - }; + const flashNote = { id: 0, type: 'error', content: 'Run the tests', isRead: false }; const addFlashNote = { type: ADD_FLASH_NOTIFICATION, payload: flashNote }; - const reducedWithInitialContent = reducer([flashNote], addFlashNote); - expect(reducedWithInitialContent).toEqual([flashNote]); + const reducedWithInitialContent = reducer([ flashNote ], addFlashNote); + expect(reducedWithInitialContent).toEqual([ flashNote ]); }); }); }); From 50da195ed3f838a0b511c6fbfabdf3a83c395905 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Thu, 19 Jan 2017 17:11:09 +0200 Subject: [PATCH 7/9] Disable prefer-default-export on folder library files --- src/containers/index.js | 1 + src/containers/reducers.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/containers/index.js b/src/containers/index.js index 9a9b7201..30b85bd4 100644 --- a/src/containers/index.js +++ b/src/containers/index.js @@ -1,3 +1,4 @@ +/* eslint-disable import/prefer-default-export */ import AuthenticationPage from './AuthenticationPage/AuthenticationPage'; import CheckoutPage from './CheckoutPage/CheckoutPage'; import ConversationPage from './ConversationPage/ConversationPage'; diff --git a/src/containers/reducers.js b/src/containers/reducers.js index 577a391b..7ad9e166 100644 --- a/src/containers/reducers.js +++ b/src/containers/reducers.js @@ -4,6 +4,7 @@ * https://github.com/erikras/ducks-modular-redux */ +/* eslint-disable import/prefer-default-export */ import SearchPage from './SearchPage/SearchPageDucks'; export { SearchPage }; From 13e03a3c92ae495406225a37b7c1d7d87b1f3f9d Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Fri, 20 Jan 2017 14:58:44 +0200 Subject: [PATCH 8/9] Refactoring code and improving documentation --- package.json | 1 + public/index.html | 2 +- server/index.js | 7 ++++--- src/components/PageLayout/PageLayout.js | 6 +++--- src/containers/AuthenticationPage/AuthenticationPage.js | 4 ++-- src/containers/SearchPage/SearchPage.js | 2 ++ src/containers/index.js | 1 - src/reducers.js | 2 ++ yarn.lock | 8 ++------ 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index bb6b7612..60ebefcc 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "express": "^4.14.0", "helmet": "^3.3.0", "lodash": "^4.17.4", + "qs": "^6.3.0", "react": "^15.4.2", "react-dom": "^15.4.2", "react-helmet": "^4.0.0", diff --git a/public/index.html b/public/index.html index e6fd9716..d095f837 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ - +
diff --git a/server/index.js b/server/index.js index b8e3aac8..a2cc2d6c 100644 --- a/server/index.js +++ b/server/index.js @@ -70,10 +70,11 @@ function render(url, context, preloadedState) { const { head, body } = renderApp(url, context, preloadedState); // Preloaded state needs to be passed for client side too. + // For security reasons we ensure that preloaded state is considered as a string + // by replacing '<' character with its unicode equivalent. + // http://redux.js.org/docs/recipes/ServerRendering.html#security-considerations const preloadedStateScript = ` - + window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/ ( /** * Container functions. + * Since we add this to global store state with combineReducers, this will only get partial state + * which is page specific. */ const mapStateToProps = function mapStateToProps(state) { return state; diff --git a/src/containers/index.js b/src/containers/index.js index 30b85bd4..9a9b7201 100644 --- a/src/containers/index.js +++ b/src/containers/index.js @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ import AuthenticationPage from './AuthenticationPage/AuthenticationPage'; import CheckoutPage from './CheckoutPage/CheckoutPage'; import ConversationPage from './ConversationPage/ConversationPage'; diff --git a/src/reducers.js b/src/reducers.js index a54211b2..6fa17e41 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -5,6 +5,8 @@ import * as pageReducers from './containers/reducers'; /** * Function _createReducer_ combines global reducers (reducers that are used in * multiple pages) and reducers that are handling actions happening inside one page container. + * Since we combineReducers, pageReducers will get page specific key (e.g. SearchPage) + * which is page specific. * Future: this structure could take in asyncReducers, which are changed when you navigate pages. */ const createReducer = function createReducer() { diff --git a/yarn.lock b/yarn.lock index 722e55f8..02c6ac0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3459,7 +3459,7 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0. json5 "^0.5.0" object-assign "^4.0.1" -lodash, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +lodash, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -3612,10 +3612,6 @@ lodash.uniq@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.16.2, lodash@^4.16.4, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -4804,7 +4800,7 @@ qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" -qs@~6.3.0: +qs@^6.3.0, qs@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" From ed41f49809ffe4ee9e1f813d8de86dd9a3d81911 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Fri, 20 Jan 2017 15:00:50 +0200 Subject: [PATCH 9/9] Renaming and refactoring initial ducks and tests files --- ...SearchPageDucks.js => SearchPage.ducks.js} | 13 ++++--- src/containers/SearchPage/SearchPage.js | 4 +-- src/containers/SearchPage/SearchPage.test.js | 19 ++++------- src/containers/reducers.js | 2 +- ...fication.js => FlashNotification.ducks.js} | 7 ++-- src/ducks/FlashNotification.test.js | 34 +++++++++---------- src/ducks/index.js | 2 +- 7 files changed, 36 insertions(+), 45 deletions(-) rename src/containers/SearchPage/{SearchPageDucks.js => SearchPage.ducks.js} (64%) rename src/ducks/{FlashNotification.js => FlashNotification.ducks.js} (85%) diff --git a/src/containers/SearchPage/SearchPageDucks.js b/src/containers/SearchPage/SearchPage.ducks.js similarity index 64% rename from src/containers/SearchPage/SearchPageDucks.js rename to src/containers/SearchPage/SearchPage.ducks.js index 2316e6c8..7dc216eb 100644 --- a/src/containers/SearchPage/SearchPageDucks.js +++ b/src/containers/SearchPage/SearchPage.ducks.js @@ -3,20 +3,19 @@ * container. We are following Ducks module proposition: * https://github.com/erikras/ducks-modular-redux */ -import unionBy from 'lodash/unionBy'; +import { unionWith, isEqual } from 'lodash'; // Actions -const ADD_FILTER = 'ADD_FILTER'; +export const ADD_FILTER = 'app/SearchPage/ADD_FILTER'; // Reducer export default function reducer(state = {}, action = {}) { const { type, payload } = action; switch (type) { - case ADD_FILTER: - { - const stateFilters = state.filters || []; - return Object.assign({}, state, { filters: unionBy(stateFilters, [ payload ]) }); - } + case ADD_FILTER: { + const stateFilters = state.filters || []; + return { ...state, ...{ filters: unionWith(stateFilters, [ payload ], isEqual) } }; + } default: return state; } diff --git a/src/containers/SearchPage/SearchPage.js b/src/containers/SearchPage/SearchPage.js index 9114ddcc..6863e17c 100644 --- a/src/containers/SearchPage/SearchPage.js +++ b/src/containers/SearchPage/SearchPage.js @@ -2,8 +2,8 @@ import React from 'react'; import { Link } from 'react-router'; import { connect } from 'react-redux'; import { PageLayout } from '../../components'; -import { addFlashNotification } from '../../ducks/FlashNotification'; -import { addFilter } from './SearchPageDucks'; +import { addFlashNotification } from '../../ducks/FlashNotification.ducks'; +import { addFilter } from './SearchPage.ducks'; export const SearchPageComponent = () => ( diff --git a/src/containers/SearchPage/SearchPage.test.js b/src/containers/SearchPage/SearchPage.test.js index b2ef2b1e..9926ee98 100644 --- a/src/containers/SearchPage/SearchPage.test.js +++ b/src/containers/SearchPage/SearchPage.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { BrowserRouter } from 'react-router'; import renderer from 'react-test-renderer'; import { SearchPageComponent } from './SearchPage'; -import reducer, { addFilter } from './SearchPageDucks'; +import reducer, { ADD_FILTER, addFilter } from './SearchPage.ducks'; describe('SearchPageComponent', () => { it('matches snapshot', () => { @@ -19,13 +19,10 @@ describe('SearchPageComponent', () => { }); describe('SearchPageDucs', () => { - const ADD_FILTER = 'ADD_FILTER'; - describe('actions', () => { it('should create an action to add a filter', () => { const expectedAction = { type: ADD_FILTER, payload: { location: 'helsinki' } }; - const serializedExpectations = JSON.stringify(expectedAction); - expect(JSON.stringify(addFilter('location', 'helsinki'))).toEqual(serializedExpectations); + expect(addFilter('location', 'helsinki')).toEqual(expectedAction); }); }); @@ -36,14 +33,12 @@ describe('SearchPageDucs', () => { }); it('should handle ADD_FILTER', () => { - const filter1 = { location: 'helsinki' }; - const filter2 = { gears: 3 }; - const addFilter1 = { type: ADD_FILTER, payload: filter1 }; - const addFilter2 = { type: ADD_FILTER, payload: filter2 }; + const addFilter1 = addFilter('location', 'helsinki'); + const addFilter2 = addFilter('gears', 3); const reduced = reducer([], addFilter1); - const reducedWithInitialContent = reducer({ filters: [ filter1 ] }, addFilter2); - expect(reduced).toEqual({ filters: [ filter1 ] }); - expect(reducedWithInitialContent).toEqual({ filters: [ filter1, filter2 ] }); + const reducedWithInitialContent = reducer({ filters: [ addFilter1.payload ] }, addFilter2); + expect(reduced).toEqual({ filters: [ addFilter1.payload ] }); + expect(reducedWithInitialContent).toEqual({ filters: [ addFilter1.payload, addFilter2.payload ] }); }); it('should handle duplicates ADD_FILTER', () => { diff --git a/src/containers/reducers.js b/src/containers/reducers.js index 7ad9e166..9c6fcc69 100644 --- a/src/containers/reducers.js +++ b/src/containers/reducers.js @@ -5,6 +5,6 @@ */ /* eslint-disable import/prefer-default-export */ -import SearchPage from './SearchPage/SearchPageDucks'; +import SearchPage from './SearchPage/SearchPage.ducks'; export { SearchPage }; diff --git a/src/ducks/FlashNotification.js b/src/ducks/FlashNotification.ducks.js similarity index 85% rename from src/ducks/FlashNotification.js rename to src/ducks/FlashNotification.ducks.js index 13d56594..27eb27fe 100644 --- a/src/ducks/FlashNotification.js +++ b/src/ducks/FlashNotification.ducks.js @@ -5,12 +5,11 @@ * https://github.com/erikras/ducks-modular-redux */ -import find from 'lodash/find'; -import findIndex from 'lodash/findIndex'; +import { find, findIndex } from 'lodash'; // Actions: system notifications -const ADD_FLASH_NOTIFICATION = 'FLASH::ADD_NOTIFICATION'; -const REMOVE_FLASH_NOTIFICATION = 'FLASH::REMOVE_NOTIFICATION'; +export const ADD_FLASH_NOTIFICATION = 'app/FlashNotification/ADD_NOTIFICATION'; +export const REMOVE_FLASH_NOTIFICATION = 'app/FlashNotification/REMOVE_NOTIFICATION'; const initialState = []; diff --git a/src/ducks/FlashNotification.test.js b/src/ducks/FlashNotification.test.js index 21c5e3a7..3a2064c8 100644 --- a/src/ducks/FlashNotification.test.js +++ b/src/ducks/FlashNotification.test.js @@ -1,9 +1,11 @@ -import reducer, { addFlashNotification, removeFlashNotification } from './FlashNotification'; +import reducer, { + ADD_FLASH_NOTIFICATION, + REMOVE_FLASH_NOTIFICATION, + addFlashNotification, + removeFlashNotification, +} from './FlashNotification.ducks'; describe('FlashNotification', () => { - const ADD_FLASH_NOTIFICATION = 'FLASH::ADD_NOTIFICATION'; - const REMOVE_FLASH_NOTIFICATION = 'FLASH::REMOVE_NOTIFICATION'; - describe('actions', () => { it('should create an action to add a filter', () => { const content = 'Error message'; @@ -12,9 +14,8 @@ describe('FlashNotification', () => { type: ADD_FLASH_NOTIFICATION, payload: { id: 'note_1', type, content, isRead: false }, }; - const serializedExpectations = JSON.stringify(expectedAction); - const received = JSON.stringify(addFlashNotification(type, content)); - expect(received).toEqual(serializedExpectations); + const received = addFlashNotification(type, content); + expect(received).toEqual(expectedAction); }); it('should create an action to remove a notification', () => { @@ -31,21 +32,18 @@ describe('FlashNotification', () => { }); it('should handle ADD_FLASH_NOTIFICATION', () => { - const flashNote1 = { id: 0, type: 'error', content: 'Run the tests', isRead: false }; - const flashNote2 = { id: 0, type: 'error', content: 'Run the tests again', isRead: false }; - const addFlashNote1 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote1 }; - const addFlashNote2 = { type: ADD_FLASH_NOTIFICATION, payload: flashNote2 }; + const addFlashNote1 = addFlashNotification('error', 'Run the tests'); + const addFlashNote2 = addFlashNotification('error', 'Run the tests again'); const reduced = reducer([], addFlashNote1); - const reducedWithInitialContent = reducer([ flashNote1 ], addFlashNote2); - expect(reduced).toEqual([ flashNote1 ]); - expect(reducedWithInitialContent).toEqual([ flashNote1, flashNote2 ]); + const reducedWithInitialContent = reducer([ addFlashNote1.payload ], addFlashNote2); + expect(reduced).toEqual([ addFlashNote1.payload ]); + expect(reducedWithInitialContent).toEqual([ addFlashNote1.payload, addFlashNote2.payload ]); }); it('should handle duplicates ADD_FILTER', () => { - const flashNote = { id: 0, type: 'error', content: 'Run the tests', isRead: false }; - const addFlashNote = { type: ADD_FLASH_NOTIFICATION, payload: flashNote }; - const reducedWithInitialContent = reducer([ flashNote ], addFlashNote); - expect(reducedWithInitialContent).toEqual([ flashNote ]); + const addFlashNote = addFlashNotification('error', 'Run the tests'); + const reducedWithInitialContent = reducer([ addFlashNote.payload ], addFlashNote); + expect(reducedWithInitialContent).toEqual([ addFlashNote.payload ]); }); }); }); diff --git a/src/ducks/index.js b/src/ducks/index.js index 85fe9511..739c6c30 100644 --- a/src/ducks/index.js +++ b/src/ducks/index.js @@ -4,6 +4,6 @@ * https://github.com/erikras/ducks-modular-redux */ -import FlashNotification from './FlashNotification'; +import FlashNotification from './FlashNotification.ducks'; export { FlashNotification }; // eslint-disable-line import/prefer-default-export