Skip to content
This repository has been archived by the owner on Jan 16, 2023. It is now read-only.

Commit

Permalink
Added counter example
Browse files Browse the repository at this point in the history
  • Loading branch information
eitak committed Mar 24, 2016
1 parent c658b18 commit da431b1
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
node_modules
lib
build
coverage
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
src
test
examples
4 changes: 4 additions & 0 deletions examples/counter/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["es2015", "react"],
"plugins": ["syntax-async-functions", "transform-regenerator", "transform-object-rest-spread"]
}
40 changes: 40 additions & 0 deletions examples/counter/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "redux-live-counter-example",
"version": "0.0.0",
"description": "Redux Live counter example",
"scripts": {
"compile": "npm run compile:server && npm run compile:shared && npm run compile:client",
"compile:server": "babel src/server -d build/server",
"compile:shared": "babel src/shared -d build/shared",
"compile:client": "mkdir -p build/client && browserify src/client/index.js -t babelify -o build/client/bundle.js",
"start": "node build/server/index"
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/eitak/redux-live.git"
},
"author": "eitak",
"license": "ISC",
"bugs": {
"url": "https://github.com/eitak/redux-live/issues"
},
"homepage": "https://github.com/eitak/redux-live#readme",
"dependencies": {
"express": "^4.13.4",
"express-handlebars": "^3.0.0",
"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-redux": "^4.4.1",
"redux": "^3.3.1",
"redux-live": "0.0.2"
},
"devDependencies": {
"babel-cli": "^6.6.5",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-transform-object-rest-spread": "^6.6.5",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babelify": "^7.2.0",
"browserify": "^13.0.0"
}
}
52 changes: 52 additions & 0 deletions examples/counter/src/client/components/Counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { Component, PropTypes } from 'react'

class Counter extends Component {
constructor(props) {
super(props);
this.incrementAsync = this.incrementAsync.bind(this);
this.incrementIfOdd = this.incrementIfOdd.bind(this)
}

incrementIfOdd() {
if (this.props.value % 2 !== 0) {
this.props.onIncrement()
}
}

incrementAsync() {
setTimeout(this.props.onIncrement, 1000)
}

render() {
const { value, onIncrement, onDecrement } = this.props;
return (
<p>
Clicked: {value} times
{' '}
<button onClick={onIncrement}>
+
</button>
{' '}
<button onClick={onDecrement}>
-
</button>
{' '}
<button onClick={this.incrementIfOdd}>
Increment if odd
</button>
{' '}
<button onClick={this.incrementAsync}>
Increment async
</button>
</p>
)
}
}

Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired
};

export default Counter
29 changes: 29 additions & 0 deletions examples/counter/src/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react'
import { render } from 'react-dom'

import initializeClient from 'redux-live/lib/client/index'
import SocketIo from 'redux-live/lib/client/repository/socketio'
import createStore from 'redux-live/lib/client/store/redux'

import Counter from './components/Counter'
import sharedOptions from '../shared/redux-live-options'

const socketio = new SocketIo('count');

initializeClient({...sharedOptions, repository: socketio})
.then((store) => {
console.log(store.getState());
function renderCounter() {
render(
<Counter
value={store.getState().value}
onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
/>,
document.getElementById('root')
);
}

renderCounter();
store.subscribe(renderCounter)
});
65 changes: 65 additions & 0 deletions examples/counter/src/server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import express from 'express'
import exphbs from 'express-handlebars'
import http from 'http'

import LocalDb from 'redux-live/lib/server/db/local-db'
import SocketIoClient from 'redux-live/lib/server/client/socketio'
import initializeServer from 'redux-live/lib/server/index'

import reduxLiveOptions from '../shared/redux-live-options'

require("babel-polyfill");

const app = express();
const server = http.Server(app);

/**
* Static files
*/
app.use(express.static(__dirname + '/../../build/client'));

/**
* Error handling
*/
app.use((err, req, res, next) => {
console.trace(err);
console.error(err);
res.status(err.status || 500);
res.send({
message: err.message,
error: err
});
});

/**
* Views
*/
app.set('views', __dirname + '/../../views');
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');

/**
* Routes
*/
app.get('/', (req, res) => {
res.render('index');
});

/**
* Redux Live initialisation
*/
const client = new SocketIoClient({server: server});
async function startServer() {
const {db} = await initializeServer({...reduxLiveOptions, dbClass: LocalDb, client});
db.createState('count');
}

startServer();

/**
* Start app
*/
const port = process.env.PORT || '3000';
server.listen(port, () => {
console.log(`counter app listening on port ${port}`);
});
10 changes: 10 additions & 0 deletions examples/counter/src/shared/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function counter(state, action) {
switch (action.type) {
case 'INCREMENT':
return {...state, value: state.value + 1};
case 'DECREMENT':
return {...state, value: state.value - 1};
default:
return state
}
}
8 changes: 8 additions & 0 deletions examples/counter/src/shared/redux-live-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import counter from './reducers/index'

export default {
reducer: counter,
initialState: {
value: 0
}
}
5 changes: 5 additions & 0 deletions examples/counter/test/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"env": {
"mocha": true
}
}
67 changes: 67 additions & 0 deletions examples/counter/test/components/Counter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import expect from 'expect'
import React from 'react'
import { shallow } from 'enzyme'
import Counter from '../../src/client/components/Counter'

function setup(value = 0) {
const actions = {
onIncrement: expect.createSpy(),
onDecrement: expect.createSpy()
};
const component = shallow(
<Counter value={value} {...actions} />
);

return {
component: component,
actions: actions,
buttons: component.find('button'),
p: component.find('p')
}
}

describe('Counter component', () => {
it('should display count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^Clicked: 0 times/)
});

it('first button should call onIncrement', () => {
const { buttons, actions } = setup();
buttons.at(0).simulate('click');
expect(actions.onIncrement).toHaveBeenCalled()
});

it('second button should call onDecrement', () => {
const { buttons, actions } = setup();
buttons.at(1).simulate('click');
expect(actions.onDecrement).toHaveBeenCalled()
});

it('third button should not call onIncrement if the counter is even', () => {
const { buttons, actions } = setup(42);
buttons.at(2).simulate('click');
expect(actions.onIncrement).toNotHaveBeenCalled()
});

it('third button should call onIncrement if the counter is odd', () => {
const { buttons, actions } = setup(43);
buttons.at(2).simulate('click');
expect(actions.onIncrement).toHaveBeenCalled()
});

it('third button should call onIncrement if the counter is odd and negative', () => {
const { buttons, actions } = setup(-43);
buttons.at(2).simulate('click');
expect(actions.onIncrement).toHaveBeenCalled()
});

it('fourth button should call onIncrement in a second', (done) => {
const { buttons, actions } = setup();
buttons.at(3).simulate('click');
setTimeout(() => {
expect(actions.onIncrement).toHaveBeenCalled();
done()
}, 1000)
})
});
22 changes: 22 additions & 0 deletions examples/counter/test/reducers/counter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import expect from 'expect'
import counter from '../../src/shared/reducers'

describe('reducers', () => {
describe('counter', () => {
it('should provide the initial state', () => {
expect(counter(undefined, {})).toBe(0)
});

it('should handle INCREMENT action', () => {
expect(counter(1, { type: 'INCREMENT' })).toBe(2)
});

it('should handle DECREMENT action', () => {
expect(counter(1, { type: 'DECREMENT' })).toBe(0)
});

it('should ignore unknown actions', () => {
expect(counter(1, { type: 'unknown' })).toBe(1)
})
})
});
9 changes: 9 additions & 0 deletions examples/counter/views/index.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
<title>React Live counter example</title>
</head>
<body>
<div id="root"></div>
<script src="/bundle.js"></script>
</body>
</html>

0 comments on commit da431b1

Please sign in to comment.