mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
redux example init
This commit is contained in:
21
docs/examples/todos/LICENSE.md
Normal file
21
docs/examples/todos/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present Dan Abramov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
2
docs/examples/todos/README.md
Normal file
2
docs/examples/todos/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Code in this folder is borrowed from https://github.com/reduxjs/redux/tree/master/examples/todos
|
||||||
|
See [LICENSE.md](./LICENSE.md) for license
|
||||||
21
docs/examples/todos/public/index.html
Normal file
21
docs/examples/todos/public/index.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Redux Todos Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<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>
|
||||||
|
</html>
|
||||||
22
docs/examples/todos/src/actions/index.js
Normal file
22
docs/examples/todos/src/actions/index.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
let nextTodoId = 0
|
||||||
|
export const addTodo = text => ({
|
||||||
|
type: 'ADD_TODO',
|
||||||
|
id: nextTodoId++,
|
||||||
|
text
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setVisibilityFilter = filter => ({
|
||||||
|
type: 'SET_VISIBILITY_FILTER',
|
||||||
|
filter
|
||||||
|
})
|
||||||
|
|
||||||
|
export const toggleTodo = id => ({
|
||||||
|
type: 'TOGGLE_TODO',
|
||||||
|
id
|
||||||
|
})
|
||||||
|
|
||||||
|
export const VisibilityFilters = {
|
||||||
|
SHOW_ALL: 'SHOW_ALL',
|
||||||
|
SHOW_COMPLETED: 'SHOW_COMPLETED',
|
||||||
|
SHOW_ACTIVE: 'SHOW_ACTIVE'
|
||||||
|
}
|
||||||
25
docs/examples/todos/src/actions/index.spec.js
Normal file
25
docs/examples/todos/src/actions/index.spec.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
14
docs/examples/todos/src/components/App.js
Normal file
14
docs/examples/todos/src/components/App.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Footer from './Footer'
|
||||||
|
import AddTodo from '../containers/AddTodo'
|
||||||
|
import VisibleTodoList from '../containers/VisibleTodoList'
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<div>
|
||||||
|
<AddTodo />
|
||||||
|
<VisibleTodoList />
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default App
|
||||||
20
docs/examples/todos/src/components/Footer.js
Normal file
20
docs/examples/todos/src/components/Footer.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import FilterLink from '../containers/FilterLink'
|
||||||
|
import { VisibilityFilters } from '../actions'
|
||||||
|
|
||||||
|
const Footer = () => (
|
||||||
|
<div>
|
||||||
|
<span>Show: </span>
|
||||||
|
<FilterLink filter={VisibilityFilters.SHOW_ALL}>
|
||||||
|
All
|
||||||
|
</FilterLink>
|
||||||
|
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>
|
||||||
|
Active
|
||||||
|
</FilterLink>
|
||||||
|
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>
|
||||||
|
Completed
|
||||||
|
</FilterLink>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Footer
|
||||||
22
docs/examples/todos/src/components/Link.js
Normal file
22
docs/examples/todos/src/components/Link.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const Link = ({ active, children, onClick }) => (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={active}
|
||||||
|
style={{
|
||||||
|
marginLeft: '4px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
Link.propTypes = {
|
||||||
|
active: PropTypes.bool.isRequired,
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
onClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Link
|
||||||
21
docs/examples/todos/src/components/Todo.js
Normal file
21
docs/examples/todos/src/components/Todo.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const Todo = ({ onClick, completed, text }) => (
|
||||||
|
<li
|
||||||
|
onClick={onClick}
|
||||||
|
style={{
|
||||||
|
textDecoration: completed ? 'line-through' : 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
|
||||||
|
Todo.propTypes = {
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
completed: PropTypes.bool.isRequired,
|
||||||
|
text: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Todo
|
||||||
26
docs/examples/todos/src/components/TodoList.js
Normal file
26
docs/examples/todos/src/components/TodoList.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import Todo from './Todo'
|
||||||
|
|
||||||
|
const TodoList = ({ todos, toggleTodo }) => (
|
||||||
|
<ul>
|
||||||
|
{todos.map(todo =>
|
||||||
|
<Todo
|
||||||
|
key={todo.id}
|
||||||
|
{...todo}
|
||||||
|
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
|
||||||
27
docs/examples/todos/src/containers/AddTodo.js
Normal file
27
docs/examples/todos/src/containers/AddTodo.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { addTodo } from '../actions'
|
||||||
|
|
||||||
|
const AddTodo = ({ dispatch }) => {
|
||||||
|
let input
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<form onSubmit={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!input.value.trim()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dispatch(addTodo(input.value))
|
||||||
|
input.value = ''
|
||||||
|
}}>
|
||||||
|
<input ref={node => input = node} />
|
||||||
|
<button type="submit">
|
||||||
|
Add Todo
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect()(AddTodo)
|
||||||
16
docs/examples/todos/src/containers/FilterLink.js
Normal file
16
docs/examples/todos/src/containers/FilterLink.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { setVisibilityFilter } from '../actions'
|
||||||
|
import Link from '../components/Link'
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
active: ownProps.filter === state.visibilityFilter
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, ownProps) => ({
|
||||||
|
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Link)
|
||||||
30
docs/examples/todos/src/containers/VisibleTodoList.js
Normal file
30
docs/examples/todos/src/containers/VisibleTodoList.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { toggleTodo } from '../actions'
|
||||||
|
import TodoList from '../components/TodoList'
|
||||||
|
import { VisibilityFilters } from '../actions'
|
||||||
|
|
||||||
|
const getVisibleTodos = (todos, filter) => {
|
||||||
|
switch (filter) {
|
||||||
|
case VisibilityFilters.SHOW_ALL:
|
||||||
|
return todos
|
||||||
|
case VisibilityFilters.SHOW_COMPLETED:
|
||||||
|
return todos.filter(t => t.completed)
|
||||||
|
case VisibilityFilters.SHOW_ACTIVE:
|
||||||
|
return todos.filter(t => !t.completed)
|
||||||
|
default:
|
||||||
|
throw new Error('Unknown filter: ' + filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
todos: getVisibleTodos(state.todos, state.visibilityFilter)
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
toggleTodo: id => dispatch(toggleTodo(id))
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TodoList)
|
||||||
15
docs/examples/todos/src/index.js
Normal file
15
docs/examples/todos/src/index.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render } from 'react-dom'
|
||||||
|
import { createStore } from 'redux'
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
import App from './components/App'
|
||||||
|
import rootReducer from './reducers'
|
||||||
|
|
||||||
|
const store = createStore(rootReducer)
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<App />
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('root')
|
||||||
|
)
|
||||||
8
docs/examples/todos/src/reducers/index.js
Normal file
8
docs/examples/todos/src/reducers/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { combineReducers } from 'redux'
|
||||||
|
import todos from './todos'
|
||||||
|
import visibilityFilter from './visibilityFilter'
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
todos,
|
||||||
|
visibilityFilter
|
||||||
|
})
|
||||||
23
docs/examples/todos/src/reducers/todos.js
Normal file
23
docs/examples/todos/src/reducers/todos.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const todos = (state = [], action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'ADD_TODO':
|
||||||
|
return [
|
||||||
|
...state,
|
||||||
|
{
|
||||||
|
id: action.id,
|
||||||
|
text: action.text,
|
||||||
|
completed: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
case 'TOGGLE_TODO':
|
||||||
|
return state.map(todo =>
|
||||||
|
(todo.id === action.id)
|
||||||
|
? {...todo, completed: !todo.completed}
|
||||||
|
: todo
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default todos
|
||||||
111
docs/examples/todos/src/reducers/todos.spec.js
Normal file
111
docs/examples/todos/src/reducers/todos.spec.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
12
docs/examples/todos/src/reducers/visibilityFilter.js
Normal file
12
docs/examples/todos/src/reducers/visibilityFilter.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { VisibilityFilters } from '../actions'
|
||||||
|
|
||||||
|
const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_VISIBILITY_FILTER':
|
||||||
|
return action.filter
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default visibilityFilter
|
||||||
Reference in New Issue
Block a user