mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
redux example WIP
This commit is contained in:
@@ -4,18 +4,15 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Redux Todos Example</title>
|
<title>Redux Todos Example</title>
|
||||||
|
|
||||||
|
<script src='https://unpkg.com/react/umd/react.development.js'></script>
|
||||||
|
<script src='https://unpkg.com/react-dom/umd/react-dom.development.js'></script>
|
||||||
|
<script src='https://unpkg.com/redux'></script>
|
||||||
|
<script src='https://unpkg.com/react-redux'></script>
|
||||||
|
|
||||||
|
<script type='module' src='../src/index.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` in this folder.
|
|
||||||
To create a production bundle, use `npm run build`.
|
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import * as actions from './index'
|
|
||||||
|
|
||||||
describe('todo actions', () => {
|
|
||||||
it('addTodo should create ADD_TODO action', () => {
|
|
||||||
expect(actions.addTodo('Use Redux')).toEqual({
|
|
||||||
type: 'ADD_TODO',
|
|
||||||
id: 0,
|
|
||||||
text: 'Use Redux'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('setVisibilityFilter should create SET_VISIBILITY_FILTER action', () => {
|
|
||||||
expect(actions.setVisibilityFilter('active')).toEqual({
|
|
||||||
type: 'SET_VISIBILITY_FILTER',
|
|
||||||
filter: 'active'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toggleTodo should create TOGGLE_TODO action', () => {
|
|
||||||
expect(actions.toggleTodo(1)).toEqual({
|
|
||||||
type: 'TOGGLE_TODO',
|
|
||||||
id: 1
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import React from 'react'
|
import Footer from './Footer.js'
|
||||||
import Footer from './Footer'
|
import AddTodo from '../containers/AddTodo.js'
|
||||||
import AddTodo from '../containers/AddTodo'
|
import VisibleTodoList from '../containers/VisibleTodoList.js'
|
||||||
import VisibleTodoList from '../containers/VisibleTodoList'
|
|
||||||
|
const h = React.createElement
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<div>
|
h('div', null,
|
||||||
<AddTodo />
|
h(AddTodo),
|
||||||
<VisibleTodoList />
|
h(VisibleTodoList),
|
||||||
<Footer />
|
h(Footer),
|
||||||
</div>
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
import React from 'react'
|
import FilterLink from '../containers/FilterLink.js'
|
||||||
import FilterLink from '../containers/FilterLink'
|
import { VisibilityFilters } from '../actions/index.js'
|
||||||
import { VisibilityFilters } from '../actions'
|
|
||||||
|
const h = React.createElement
|
||||||
|
|
||||||
const Footer = () => (
|
const Footer = () => (
|
||||||
<div>
|
h('div', null,
|
||||||
<span>Show: </span>
|
h('span', null, 'Show: '),
|
||||||
<FilterLink filter={VisibilityFilters.SHOW_ALL}>
|
h(FilterLink, {filter: VisibilityFilters.SHOW_ALL}, 'All'),
|
||||||
All
|
h(FilterLink, {filter: VisibilityFilters.SHOW_ACTIVE}, 'Active'),
|
||||||
</FilterLink>
|
h(FilterLink, {filter: VisibilityFilters.SHOW_COMPLETED}, 'Completed'),
|
||||||
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>
|
)
|
||||||
Active
|
|
||||||
</FilterLink>
|
|
||||||
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>
|
|
||||||
Completed
|
|
||||||
</FilterLink>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export default Footer
|
export default Footer
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
import React from 'react'
|
const h = React.createElement
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const Link = ({ active, children, onClick }) => (
|
const Link = ({ active, children, onClick }) => (
|
||||||
<button
|
h('button', {
|
||||||
onClick={onClick}
|
onClick,
|
||||||
disabled={active}
|
disabled: active,
|
||||||
style={{
|
style:{
|
||||||
marginLeft: '4px',
|
marginLeft: '4px',
|
||||||
}}
|
}
|
||||||
>
|
}, children)
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Link.propTypes = {
|
|
||||||
active: PropTypes.bool.isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
onClick: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Link
|
export default Link
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
import React from 'react'
|
const h = React.createElement
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const Todo = ({ onClick, completed, text }) => (
|
const Todo = ({ onClick, completed, text }) => (
|
||||||
<li
|
h('li', {
|
||||||
onClick={onClick}
|
onClick,
|
||||||
style={{
|
style: {
|
||||||
textDecoration: completed ? 'line-through' : 'none'
|
textDecoration: completed ? 'line-through' : 'none'
|
||||||
}}
|
},
|
||||||
>
|
}, text)
|
||||||
{text}
|
|
||||||
</li>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Todo.propTypes = {
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
completed: PropTypes.bool.isRequired,
|
|
||||||
text: PropTypes.string.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Todo
|
export default Todo
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
import React from 'react'
|
import Todo from './Todo.js'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import Todo from './Todo'
|
const h = React.createElement
|
||||||
|
|
||||||
const TodoList = ({ todos, toggleTodo }) => (
|
const TodoList = ({ todos, toggleTodo }) => (
|
||||||
<ul>
|
h('ul', null,
|
||||||
{todos.map(todo =>
|
todos.map(todo =>
|
||||||
<Todo
|
h(Todo, {
|
||||||
key={todo.id}
|
key: todo.id,
|
||||||
{...todo}
|
...todo,
|
||||||
onClick={() => toggleTodo(todo.id)}
|
onClick: () => toggleTodo(todo.id),
|
||||||
/>
|
})
|
||||||
)}
|
)
|
||||||
</ul>
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
TodoList.propTypes = {
|
|
||||||
todos: PropTypes.arrayOf(PropTypes.shape({
|
|
||||||
id: PropTypes.number.isRequired,
|
|
||||||
completed: PropTypes.bool.isRequired,
|
|
||||||
text: PropTypes.string.isRequired
|
|
||||||
}).isRequired).isRequired,
|
|
||||||
toggleTodo: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TodoList
|
export default TodoList
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
import React from 'react'
|
import { addTodo } from '../actions/index.js'
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import { addTodo } from '../actions'
|
const h = React.createElement
|
||||||
|
|
||||||
const AddTodo = ({ dispatch }) => {
|
const AddTodo = ({ dispatch }) => {
|
||||||
let input
|
let input
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
h('div', null,
|
||||||
<form onSubmit={e => {
|
h('form', { onSubmit: e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (!input.value.trim()) {
|
if (!input.value.trim()) {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
dispatch(addTodo(input.value))
|
||||||
|
input.value = ''
|
||||||
}
|
}
|
||||||
dispatch(addTodo(input.value))
|
},
|
||||||
input.value = ''
|
h('input', {ref: node => {input = node}}),
|
||||||
}}>
|
h('button', {type: 'submit'}, 'Add Todo')
|
||||||
<input ref={node => input = node} />
|
)
|
||||||
<button type="submit">
|
)
|
||||||
Add Todo
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect()(AddTodo)
|
export default ReactRedux.connect()(AddTodo)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { connect } from 'react-redux'
|
import { setVisibilityFilter } from '../actions/index.js'
|
||||||
import { setVisibilityFilter } from '../actions'
|
import Link from '../components/Link.js'
|
||||||
import Link from '../components/Link'
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
active: ownProps.filter === state.visibilityFilter
|
active: ownProps.filter === state.visibilityFilter
|
||||||
@@ -10,7 +9,7 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
|
|||||||
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
|
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(
|
export default ReactRedux.connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(Link)
|
)(Link)
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import { connect } from 'react-redux'
|
import { toggleTodo } from '../actions/index.js'
|
||||||
import { toggleTodo } from '../actions'
|
import TodoList from '../components/TodoList.js'
|
||||||
import TodoList from '../components/TodoList'
|
import { VisibilityFilters } from '../actions/index.js'
|
||||||
import { VisibilityFilters } from '../actions'
|
|
||||||
|
|
||||||
const getVisibleTodos = (todos, filter) => {
|
const getVisibleTodos = (todos, filter) => {
|
||||||
switch (filter) {
|
return {
|
||||||
case VisibilityFilters.SHOW_ALL:
|
[VisibilityFilters.SHOW_ALL]: todos,
|
||||||
return todos
|
[VisibilityFilters.SHOW_COMPLETED]: todos.filter(t => t.completed),
|
||||||
case VisibilityFilters.SHOW_COMPLETED:
|
[VisibilityFilters.SHOW_ACTIVE]: todos.filter(t => !t.completed),
|
||||||
return todos.filter(t => t.completed)
|
}[filter]
|
||||||
case VisibilityFilters.SHOW_ACTIVE:
|
|
||||||
return todos.filter(t => !t.completed)
|
|
||||||
default:
|
|
||||||
throw new Error('Unknown filter: ' + filter)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
@@ -24,7 +18,7 @@ const mapDispatchToProps = dispatch => ({
|
|||||||
toggleTodo: id => dispatch(toggleTodo(id))
|
toggleTodo: id => dispatch(toggleTodo(id))
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(
|
export default ReactRedux.connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(TodoList)
|
)(TodoList)
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import React from 'react'
|
import App from './components/App.js'
|
||||||
import { render } from 'react-dom'
|
import rootReducer from './reducers/index.js'
|
||||||
import { createStore } from 'redux'
|
|
||||||
import { Provider } from 'react-redux'
|
|
||||||
import App from './components/App'
|
|
||||||
import rootReducer from './reducers'
|
|
||||||
|
|
||||||
const store = createStore(rootReducer)
|
const h = React.createElement
|
||||||
|
|
||||||
render(
|
const store = Redux.createStore(rootReducer)
|
||||||
<Provider store={store}>
|
|
||||||
<App />
|
ReactDOM.render(
|
||||||
</Provider>,
|
h(ReactRedux.Provider, {store}, h(App)),
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { combineReducers } from 'redux'
|
import todos from './todos.js'
|
||||||
import todos from './todos'
|
import visibilityFilter from './visibilityFilter.js'
|
||||||
import visibilityFilter from './visibilityFilter'
|
|
||||||
|
|
||||||
export default combineReducers({
|
export default Redux.combineReducers({
|
||||||
todos,
|
todos,
|
||||||
visibilityFilter
|
visibilityFilter
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
import todos from './todos'
|
|
||||||
|
|
||||||
describe('todos reducer', () => {
|
|
||||||
it('should handle initial state', () => {
|
|
||||||
expect(
|
|
||||||
todos(undefined, {})
|
|
||||||
).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle ADD_TODO', () => {
|
|
||||||
expect(
|
|
||||||
todos([], {
|
|
||||||
type: 'ADD_TODO',
|
|
||||||
text: 'Run the tests',
|
|
||||||
id: 0
|
|
||||||
})
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(
|
|
||||||
todos([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}
|
|
||||||
], {
|
|
||||||
type: 'ADD_TODO',
|
|
||||||
text: 'Use Redux',
|
|
||||||
id: 1
|
|
||||||
})
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}, {
|
|
||||||
text: 'Use Redux',
|
|
||||||
completed: false,
|
|
||||||
id: 1
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(
|
|
||||||
todos([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}, {
|
|
||||||
text: 'Use Redux',
|
|
||||||
completed: false,
|
|
||||||
id: 1
|
|
||||||
}
|
|
||||||
], {
|
|
||||||
type: 'ADD_TODO',
|
|
||||||
text: 'Fix the tests',
|
|
||||||
id: 2
|
|
||||||
})
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}, {
|
|
||||||
text: 'Use Redux',
|
|
||||||
completed: false,
|
|
||||||
id: 1
|
|
||||||
}, {
|
|
||||||
text: 'Fix the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 2
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle TOGGLE_TODO', () => {
|
|
||||||
expect(
|
|
||||||
todos([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: false,
|
|
||||||
id: 1
|
|
||||||
}, {
|
|
||||||
text: 'Use Redux',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}
|
|
||||||
], {
|
|
||||||
type: 'TOGGLE_TODO',
|
|
||||||
id: 1
|
|
||||||
})
|
|
||||||
).toEqual([
|
|
||||||
{
|
|
||||||
text: 'Run the tests',
|
|
||||||
completed: true,
|
|
||||||
id: 1
|
|
||||||
}, {
|
|
||||||
text: 'Use Redux',
|
|
||||||
completed: false,
|
|
||||||
id: 0
|
|
||||||
}
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { VisibilityFilters } from '../actions'
|
import { VisibilityFilters } from '../actions/index.js'
|
||||||
|
|
||||||
const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
|
const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|||||||
Reference in New Issue
Block a user