diff --git a/client/index.js b/client/index.js index e8e3ce0bd1..c2c98abb6f 100644 --- a/client/index.js +++ b/client/index.js @@ -11,7 +11,8 @@ import { import { render } from 'redux-epic'; import { createHistory } from 'history'; import useLangRoutes from './utils/use-lang-routes'; -import sendPageAnalytics from './utils/send-page-analytics.js'; +import sendPageAnalytics from './utils/send-page-analytics'; +import flashToToast from './utils/flash-to-toast'; import createApp from '../common/app'; import provideStore from '../common/app/provide-store'; @@ -34,6 +35,9 @@ const initialState = isColdStored() ? getColdStorage() : window.__fcc__.data; initialState.app.csrfToken = csrfToken; +initialState.toasts = flashToToast(window.__fcc__.flash); + +delete window.__fcc__; const serviceOptions = { xhrPath: '/services', context: { _csrf: csrfToken } }; @@ -72,10 +76,13 @@ createApp({ } }) .doOnNext(() => log('rendering')) - .flatMap(({ props, store }) => render( - provideStore(React.createElement(Router, props), store), - DOMContainer - )) + .flatMap( + ({ props, store }) => render( + provideStore(React.createElement(Router, props), store), + DOMContainer + ), + ({ store }) => store + ) .subscribe( () => debug('react rendered'), err => { throw err; }, diff --git a/client/utils/flash-to-toast.js b/client/utils/flash-to-toast.js new file mode 100644 index 0000000000..b7529f7056 --- /dev/null +++ b/client/utils/flash-to-toast.js @@ -0,0 +1,16 @@ +import { makeToast } from '../../common/app/toasts/redux/actions'; + +export default function flashToToast(flash) { + return Object.keys(flash) + .map(key => { + const messages = flash[key]; + return messages.map(message => ({ + message: message.msg, + type: key, + timeout: 5000 + })); + }) + .reduce((toasts, messages) => toasts.concat(messages), []) + .map(makeToast) + .map(({ payload }) => payload); +} diff --git a/client/utils/flash-to-toast.test.js b/client/utils/flash-to-toast.test.js new file mode 100644 index 0000000000..9361b11767 --- /dev/null +++ b/client/utils/flash-to-toast.test.js @@ -0,0 +1,32 @@ +import test from 'tape'; + +import flashToToast from './flash-to-toast'; + +test('client/utils/flash-to-toast.js', t => { + t.test('should return an array', t => { + t.plan(2); + const toasts = flashToToast({}); + t.assert(Array.isArray(toasts), 'toasts was not an array'); + t.equal(toasts.length, 0, 'toasts should be empty'); + }); + t.test('should convert keyed messages to typed toasts', t => { + t.plan(3); + const expected = [{ message: 'foo', type: 'info' }]; + const actual = flashToToast({ + info: [{ msg: 'foo' }] + }); + t.equal( + expected.length, + actual.length, + 'number of toasts does not match number of messages' + ); + t.equal( + expected[0].type, + actual[0].type + ); + t.equal( + expected[0].message, + actual[0].message + ); + }); +}); diff --git a/package.json b/package.json index 1e291cb095..f3bc3c115a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "snyk-protect": "snyk protect", "prepublish": "npm run snyk-protect", "test-challenges": "babel-node seed/test-challenges.js | tap-spec", - "test-js": "tape -r babel-register 'common/**/*.test.js'", + "test-js": "npm run test-js-client && npm run test-js-common", + "test-js-client": "tape -r babel-register 'client/**/*.test.js' | tap-spec", + "test-js-common": "tape -r babel-register 'common/**/*.test.js' | tap-spec", "test": "npm run test-js && npm run test-challenges", "cover": "babel-node ./node_modules/.bin/babel-istanbul cover tape common/**/*.test.js", "coveralls": "npm run cover && istanbul-coveralls" diff --git a/server/boot/react.js b/server/boot/react.js index 3a431184ba..83a1686373 100644 --- a/server/boot/react.js +++ b/server/boot/react.js @@ -87,6 +87,7 @@ export default function reactSubRouter(app) { const { title } = state.app; epic.dispose(); res.expose(state, 'data'); + res.expose(req.flash(), 'flash'); return res.render$( 'layout-react', { markup, title }