diff --git a/examples/counter/package.json b/examples/counter/package.json index 25f7eaf9..70a9e101 100644 --- a/examples/counter/package.json +++ b/examples/counter/package.json @@ -26,10 +26,10 @@ "babel-core": "^5.6.18", "babel-loader": "^5.1.4", "node-libs-browser": "^0.5.2", - "react-dock": "0.0.3", + "react-dock": "^0.1.0", "react-hot-loader": "^1.3.0", "redux-devtools": "^3.0.0-alpha-1", - "redux-devtools-log-monitor": "^1.0.0-alpha", + "redux-devtools-log-monitor": "^1.0.0-alpha-3", "webpack": "^1.9.11", "webpack-dev-server": "^1.9.0" } diff --git a/examples/counter/src/store/configureStore.dev.js b/examples/counter/src/store/configureStore.dev.js index 4239b740..85c39ebf 100644 --- a/examples/counter/src/store/configureStore.dev.js +++ b/examples/counter/src/store/configureStore.dev.js @@ -2,10 +2,13 @@ import { createStore, applyMiddleware, compose } from 'redux'; import { devTools, persistState } from 'redux-devtools'; import thunk from 'redux-thunk'; import rootReducer from '../reducers'; +import { createMonitorReducer } from 'redux-devtools-log-monitor'; const finalCreateStore = compose( applyMiddleware(thunk), - devTools(), + devTools(createMonitorReducer({ + isVisibleOnLoad: true + })), persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)) )(createStore); diff --git a/package.json b/package.json index dcdc983c..a28f5fc3 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,10 @@ "webpack": "^1.11.0" }, "peerDependencies": { - "redux": "^2.0.0 || ^3.0.0" + "redux": "^3.0.0" }, "dependencies": { "react-redux": "^3.0.0", - "redux": "^2.0.0 || ^3.0.0" + "redux": "^3.0.0" } } diff --git a/src/connectMonitor.js b/src/connectMonitor.js index c0ae0362..f372e6b4 100644 --- a/src/connectMonitor.js +++ b/src/connectMonitor.js @@ -1,42 +1,54 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { ActionCreators } from './devTools'; -export default function connectMonitor(Monitor) { - const ConnectedMonitor = connect(state => state, ActionCreators)(Monitor); - - class DevTools extends Component { - static Monitor = Monitor; - - static propTypes = { - store(props, propName, componentName) { - if (!props.store) { - return new Error('Required prop `store` was not specified in `' + componentName + '`.'); - } - if (!props.store.devToolsStore) { - return new Error( - 'Could not find the DevTools store inside the `store` prop passed to `' + - componentName + - '`. Have you applied the devTools() store enhancer?' - ); - } - } - }; - - render() { - const { store } = this.props; - if (!store) { - return null; - } - - const { devToolsStore } = store; - if (!devToolsStore) { - return null; - } - - return ; +export default function connectMonitor(monitorActionCreators = {}) { + return Monitor => { + function mapStateToProps(state) { + return state; } - } + function mapDispatchToProps(dispatch) { + return { + ...bindActionCreators(ActionCreators, dispatch), + monitorActions: bindActionCreators(monitorActionCreators, dispatch) + }; + } + const ConnectedMonitor = connect(mapStateToProps, mapDispatchToProps)(Monitor); - return DevTools; + class DevTools extends Component { + static Monitor = Monitor; + + static propTypes = { + store(props, propName, componentName) { + if (!props.store) { + return new Error('Required prop `store` was not specified in `' + componentName + '`.'); + } + if (!props.store.devToolsStore) { + return new Error( + 'Could not find the DevTools store inside the `store` prop passed to `' + + componentName + + '`. Have you applied the devTools() store enhancer?' + ); + } + } + }; + + render() { + const { store } = this.props; + if (!store) { + return null; + } + + const { devToolsStore } = store; + if (!devToolsStore) { + return null; + } + + return ; + } + } + + return DevTools; + }; } diff --git a/src/devTools.js b/src/devTools.js index 4704438a..8658d567 100644 --- a/src/devTools.js +++ b/src/devTools.js @@ -6,7 +6,6 @@ const ActionTypes = { SWEEP: 'SWEEP', TOGGLE_ACTION: 'TOGGLE_ACTION', JUMP_TO_STATE: 'JUMP_TO_STATE', - SET_MONITOR_STATE: 'SET_MONITOR_STATE', RECOMPUTE_STATES: 'RECOMPUTE_STATES' }; @@ -79,15 +78,13 @@ function recomputeStates(reducer, committedState, stagedActions, skippedActions) /** * Lifts the app state reducer into a DevTools state reducer. */ -function liftReducer(reducer, initialState) { +function liftReducer(reducer, monitorStateReducer, initialState) { const initialLiftedState = { committedState: initialState, stagedActions: [INIT_ACTION], skippedActions: {}, currentStateIndex: 0, - monitorState: { - isVisible: true - }, + monitorState: monitorStateReducer(undefined, INIT_ACTION), timestamps: [Date.now()] }; @@ -145,9 +142,6 @@ function liftReducer(reducer, initialState) { stagedActions = [...stagedActions, liftedAction.action]; timestamps = [...timestamps, liftedAction.timestamp]; break; - case ActionTypes.SET_MONITOR_STATE: - monitorState = liftedAction.monitorState; - break; case ActionTypes.RECOMPUTE_STATES: stagedActions = liftedAction.stagedActions; timestamps = liftedAction.timestamps; @@ -166,6 +160,8 @@ function liftReducer(reducer, initialState) { skippedActions ); + monitorState = monitorStateReducer(monitorState, liftedAction); + return { committedState, stagedActions, @@ -202,7 +198,7 @@ function unliftState(liftedState) { /** * Unlifts the DevTools store to act like the app's store. */ -function unliftStore(liftedStore, reducer) { +function unliftStore(liftedStore, monitorStateReducer) { let lastDefinedState; return { ...liftedStore, @@ -218,11 +214,8 @@ function unliftStore(liftedStore, reducer) { } return lastDefinedState; }, - getReducer() { - return reducer; - }, replaceReducer(nextReducer) { - liftedStore.replaceReducer(liftReducer(nextReducer)); + liftedStore.replaceReducer(liftReducer(nextReducer, monitorStateReducer)); } }; } @@ -249,9 +242,6 @@ export const ActionCreators = { jumpToState(index) { return { type: ActionTypes.JUMP_TO_STATE, index }; }, - setMonitorState(monitorState) { - return { type: ActionTypes.SET_MONITOR_STATE, monitorState }; - }, recomputeStates(committedState, stagedActions) { return { type: ActionTypes.RECOMPUTE_STATES, @@ -264,11 +254,11 @@ export const ActionCreators = { /** * Redux DevTools middleware. */ -export default function devTools() { +export default function devTools(monitorStateReducer = () => undefined) { return next => (reducer, initialState) => { - const liftedReducer = liftReducer(reducer, initialState); + const liftedReducer = liftReducer(reducer, monitorStateReducer, initialState); const liftedStore = next(liftedReducer); - const store = unliftStore(liftedStore, reducer); + const store = unliftStore(liftedStore, monitorStateReducer); return store; }; } diff --git a/test/connectMonitor.spec.js b/test/connectMonitor.spec.js index bce19426..40c36d0b 100644 --- a/test/connectMonitor.spec.js +++ b/test/connectMonitor.spec.js @@ -11,12 +11,25 @@ class MockMonitor extends Component { } } +function increment() { + return { type: 'INCREMENT' }; +} + +function mockMonitorReducer(state = 0, action) { + switch (action.type) { + case 'INCREMENT': + return state + 1; + default: + return state; + } +} + describe('connectMonitor', () => { jsdom(); it('should pass devToolsStore to monitor', () => { const store = devTools()(createStore)(() => {}); - const ConnectedMonitor = connectMonitor(MockMonitor); + const ConnectedMonitor = connectMonitor()(MockMonitor); const tree = TestUtils.renderIntoDocument( ); @@ -26,7 +39,7 @@ describe('connectMonitor', () => { it('should pass props to monitor', () => { const store = devTools()(createStore)(() => {}); - const ConnectedMonitor = connectMonitor(MockMonitor); + const ConnectedMonitor = connectMonitor()(MockMonitor); const tree = TestUtils.renderIntoDocument( ); @@ -36,7 +49,7 @@ describe('connectMonitor', () => { }); it('should subscribe monitor to store updates', () => { - const ConnectedMonitor = connectMonitor(MockMonitor); + const ConnectedMonitor = connectMonitor()(MockMonitor); const store = devTools()(createStore)( (state, action) => { switch (action.type) { @@ -63,7 +76,7 @@ describe('connectMonitor', () => { it('should warn if devTools() not in middleware', () => { const store = createStore(() => {}); - const ConnectedMonitor = connectMonitor(MockMonitor); + const ConnectedMonitor = connectMonitor()(MockMonitor); // Force to re-evaluate propType checks on every run ConnectedMonitor.displayName = Math.random().toString(); @@ -77,4 +90,27 @@ describe('connectMonitor', () => { (call, errMsg) => call.arguments[0].match(errMsg) ); }); + + it('should pass monitor state and actions to the monitor', () => { + const store = devTools(mockMonitorReducer)(createStore)(() => {}); + const ConnectedMonitor = connectMonitor()(MockMonitor); + const tree = TestUtils.renderIntoDocument( + + ); + const mockMonitor = TestUtils.findRenderedComponentWithType(tree, MockMonitor); + expect(mockMonitor.props.monitorState).toBe(0); + }); + + it('should pass bound monitor actions to monitor', () => { + const ConnectedMonitor = connectMonitor({ increment })(MockMonitor); + const store = devTools(mockMonitorReducer)(createStore)(() => {}); + const tree = TestUtils.renderIntoDocument( + + ); + + const mockMonitor = TestUtils.findRenderedComponentWithType(tree, MockMonitor); + expect(mockMonitor.props.monitorState).toBe(0); + mockMonitor.props.monitorActions.increment(); + expect(mockMonitor.props.monitorState).toBe(1); + }); }); diff --git a/test/devTools.spec.js b/test/devTools.spec.js index 672fb8b9..0e5014ea 100644 --- a/test/devTools.spec.js +++ b/test/devTools.spec.js @@ -143,11 +143,6 @@ describe('devTools', () => { expect(store.getState()).toBe(2); }); - it('should set monitor state', () => { - devToolsStore.dispatch(ActionCreators.setMonitorState({test: 'test'})); - expect(devToolsStore.getState().monitorState.test).toBe('test'); - }); - it('should recompute', () => { store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'DECREMENT' }); @@ -167,10 +162,6 @@ describe('devTools', () => { expect(store.getState()).toBe(13); }); - it('should get the reducer', () => { - expect(store.getReducer()).toBe(counter); - }); - it('should replace the reducer', () => { store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'DECREMENT' });