Skip to content

Instantly share code, notes, and snippets.

@MatthewDailey
Created June 30, 2017 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatthewDailey/afc6f6c7c177c0a563cf6e25284b2d92 to your computer and use it in GitHub Desktop.
Save MatthewDailey/afc6f6c7c177c0a563cf6e25284b2d92 to your computer and use it in GitHub Desktop.
VoiceOps Todo App
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { TextField } from 'material-ui';
import { Title, Content, ContentBold } from './components/Text';
import ContainerWithNav from './components/ContainerWithNav';
import { Button } from './components/Buttons';
import { addTodo, removeTodo } from './reducers/TodoReducer';
class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
isEditing: true,
edittingValue: props.todoString,
};
}
render() {
const { todoId, todoString, deleteTodo } = this.props;
if (this.state.isEditing) {
const saveTodoInput = edittingValue => this.setState({ edittingValue });
return (
<div>
<TextField value={this.state.edittingValue} />
<Button
label="Save"
onPress={() => deleteTodo(todoId)}
onChange={(event, newInput) => saveTodoInput(newInput)}
/>
</div>
);
}
return (
<div>
<Content>{todoString}</Content>
<Button label="Delete" onPress={() => deleteTodo(todoId)} />
<Button label="Edit" onPress={() => deleteTodo(todoId)} />
</div>
);
}
}
Todo.propTypes = {
todoId: PropTypes.string.isRequired,
todoString: PropTypes.string.isRequired,
deleteTodo: PropTypes.func.isRequired,
};
const ConnectedTodo = connect(undefined, (dispatch) => ({
deleteTodo: todoId => dispatch(removeTodo(todoId)),
}))(Todo);
class TodoView extends React.Component {
constructor(props) {
super(props);
this.state = {
todoInput: '',
};
}
render() {
const { todos, saveTodo, deleteTodo } = this.props;
const saveTodoInput = todoInput => this.setState({ todoInput });
const addTodoToList = () => {
saveTodo(this.state.todoInput);
this.setState({ todoInput: '' });
};
return (
<ContainerWithNav>
<Title>
Hello TODO
</Title>
{
Object.keys(todos.idToTodo).map(todoId =>
<ConnectedTodo key={todoId} todoId={todoId} todoString={todos.idToTodo[todoId].todoString} />)
}
<TextField value={this.state.todoInput} onChange={(event, newValue) => saveTodoInput(newValue)} />
<Button label="Add Todo" onPress={addTodoToList} />
</ContainerWithNav>
);
}
}
TodoView.propTypes = {
todos: PropTypes.shape({}).isRequired,
saveTodo: PropTypes.func.isRequired,
};
const mapStateToProps = ({ todos }) => ({ todos });
const mapDispatchToProps = dispatch => ({
saveTodo: todoString => dispatch(addTodo(todoString)),
});
export default connect(mapStateToProps, mapDispatchToProps)(TodoView);
import _ from 'lodash-uuid';
import { getActionType } from '@mjd/reducer-utils';
const ADD = 'add-todo';
const REMOVE = 'remove-todo';
const EDIT = 'edit-todo';
const defaultState = {
idToTodo: {},
};
export default function TodoReducer(priorState = defaultState, action) {
switch (getActionType(action)) {
case ADD:
return {
idToTodo: {
[action.id]: {
todoString: action.todoString,
},
...priorState.idToTodo,
},
};
case REMOVE:
const newIdToTodo = {
...priorState.idToTodo,
};
delete newIdToTodo[action.id];
return {
idToTodo: newIdToTodo,
};
default:
return priorState;
}
}
export function addTodo(todoString, id = _.uuid()) {
return {
type: ADD,
todoString,
id,
};
}
export function removeTodo(id) {
return {
type: REMOVE,
id,
};
}
export function editTodo() {
}
import TodoReducer, { addTodo, removeTodo } from '../../src/reducers/TodoReducer';
describe('TodoReducer', () => {
it('add action has id', () => {
expect(addTodo('some string').id).to.exist();
});
it('can add a todo', () => {
const initialState = TodoReducer();
expect(Object.keys(initialState.idToTodo)).to.eql([]);
const todoString = 'some todo';
const secondState = TodoReducer(initialState, addTodo(todoString));
expect(Object.keys(secondState.idToTodo).length).to.equal(1);
const todoId = Object.keys(secondState.idToTodo)[0];
expect(secondState.idToTodo[todoId].todoString).to.equal(todoString);
});
it('can remove a todo', () => {
const initialState = TodoReducer();
const todoString = 'some todo';
const secondState = TodoReducer(initialState, addTodo(todoString));
const todoId = Object.keys(secondState.idToTodo)[0];
expect(todoId).to.exist();
const stateAfterDelete = TodoReducer(secondState, removeTodo(todoId));
expect(Object.keys(stateAfterDelete.idToTodo)).to.eql([]);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment