Toggle a Todo Between Completed and Not Completed
Story: As a user, I want to be able to toggle a todo between completed and not completed statuses, so that I can keep track of it.
TodoMVC actions
It should create an action to toggle a todo between completed and not completed
Write a test.
diff --git a/tests/todomvc/actions.spec.js b/tests/todomvc/actions.spec.js
index fd31f8f..367d6ad 100644
--- a/tests/todomvc/actions.spec.js
+++ b/tests/todomvc/actions.spec.js
@@ -29,5 +29,13 @@ describe('TodoMVC actions', () => {
};
expect(todomvc.actions.deleteTodo('my_id')).to.deep.equal(expectedAction)
+ });
+
+ it('Should create an action to toggle a todo between completed and not completed', () => {
+ const expectedAction = {
+ type: todomvc.types.TOGGLE_COMPLETE_ONE, id: 'my_id'
+ };
+
+ expect(todomvc.actions.toggleCompleteOneTodo('my_id')).to.deep.equal(expectedAction)
})
});
(END)
Run the test and watch it fail.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
1) Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
30 passing (85ms)
1 failing
1) TodoMVC actions Should create an action to toggle a todo between completed and not completed:
TypeError: _todomvc2.default.actions.toggleCompleteOneTodo is not a function
at Context.<anonymous> (tests/todomvc/actions.spec.js:39:28)
Write the code to make the test pass.
diff --git a/src/todomvc/ActionTypes.js b/src/todomvc/ActionTypes.js
index 040095a..6259983 100644
--- a/src/todomvc/ActionTypes.js
+++ b/src/todomvc/ActionTypes.js
@@ -3,3 +3,4 @@
export const ADD = 'todomvc/ADD';
export const EDIT = 'todomvc/EDIT';
export const DELETE = 'todomvc/DELETE';
+export const TOGGLE_COMPLETE_ONE = 'todomvc/TOGGLE_COMPLETE_ONE';
(END)
diff --git a/src/todomvc/actions.js b/src/todomvc/actions.js
index 0f1e307..7778446 100644
--- a/src/todomvc/actions.js
+++ b/src/todomvc/actions.js
@@ -13,3 +13,7 @@ export function editTodo(id, text) {
export function deleteTodo(id) {
return {type: types.DELETE, id: id}
}
+
+export function toggleCompleteOneTodo(id) {
+ return {type: types.TOGGLE_COMPLETE_ONE, id: id}
+}
(END)
Run the test and watch it pass.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
31 passing (98ms)
Commit changes.
$ git add .
$ git commit -m 'created an action to toggle a todo'
TodoMVC reducer
It should handle TOGGLE_COMPLETE_ONE todo
Write the test.
diff --git a/tests/todomvc/reducer.spec.js b/tests/todomvc/reducer.spec.js
index dd79807..c7cd472 100644
--- a/tests/todomvc/reducer.spec.js
+++ b/tests/todomvc/reducer.spec.js
@@ -54,5 +54,16 @@ describe('TodoMVC reducer', () => {
expect(new_state.size).to.equal(1);
expect(new_state.get(0)).to.equal(state.get(0))
+ });
+
+ it('Should handle TOGGLE_COMPLETE_ONE todo', () => {
+ const state = List([
+ Map({id: uuid.v4(), description: 'My todo', completed: false})
+ ]);
+ const new_state = todomvc.reducer(state, {
+ type: todomvc.types.TOGGLE_COMPLETE_ONE, id: state.get(0).get('id')
+ });
+
+ expect(new_state.get(0).get('completed')).to.equal(!state.get(0).get('completed'))
})
});
(END)
Run the test and watch it fail.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
1) Should handle TOGGLE_COMPLETE_ONE todo
31 passing (110ms)
1 failing
1) TodoMVC reducer Should handle TOGGLE_COMPLETE_ONE todo:
AssertionError: expected false to equal true
+ expected - actual
-false
+true
at Context.<anonymous> (tests/todomvc/reducer.spec.js:67:50)
Write the code to make the test pass.
diff --git a/src/todomvc/reducer.js b/src/todomvc/reducer.js
index 5f9a53c..58f9a29 100644
--- a/src/todomvc/reducer.js
+++ b/src/todomvc/reducer.js
@@ -13,6 +13,8 @@ export default function reducer(state = List([]), action) {
return (state.map(todo => todo.get('id') === action.id ? todo.set('description', action.description) : todo));
case types.DELETE:
return state.filter(todo => todo.get('id') !== action.id);
+ case types.TOGGLE_COMPLETE_ONE:
+ return (state.map(todo => todo.get('id') === action.id ? todo.set('completed', !todo.get('completed')) : todo));
default:
// just return the same state
return (state)
(END)
Run the test and watch it pass.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
✓ Should handle TOGGLE_COMPLETE_ONE todo
32 passing (104ms)
Commit changes.
$ git add .
$ git commit -m 'handle TOGGLE_COMPLETE_ONE todo'
TodoItem component
It Should render correctly
It should have a toggle complete status checkbox
Write the test.
diff --git a/tests/todomvc/components/TodoItem.spec.js b/tests/todomvc/components/TodoItem.spec.js
index 9792ed2..67241e8 100644
--- a/tests/todomvc/components/TodoItem.spec.js
+++ b/tests/todomvc/components/TodoItem.spec.js
@@ -47,6 +47,15 @@ describe('TodoItem component', () => {
expect(button).to.have.length(1);
expect(button.children().text()).to.equal('delete todo')
+ });
+
+ it('Should have a toggle complete status checkbox', () => {
+ const {component} = setup();
+ const checkbox = component.children('input');
+
+ expect(checkbox).to.have.length(1);
+ expect(checkbox.props().type).to.equal('checkbox');
+ expect(checkbox.props().name).to.equal('completed')
})
});
(END)
Run the test and watch it fail.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
1) Should have a toggle complete status checkbox
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
✓ Should handle TOGGLE_COMPLETE_ONE todo
32 passing (103ms)
1 failing
1) TodoItem component Should render correctly Should have a toggle complete status checkbox:
AssertionError: expected { Object (root, unrendered, ...) } to have a length of 1 but got 0
+ expected - actual
-0
+1
at Context.<anonymous> (tests/todomvc/components/TodoItem.spec.js:56:32)
Write the code to make the test pass.
diff --git a/src/todomvc/components/TodoItem.js b/src/todomvc/components/TodoItem.js
index 384570d..3eea02e 100644
--- a/src/todomvc/components/TodoItem.js
+++ b/src/todomvc/components/TodoItem.js
@@ -35,7 +35,8 @@ export default class TodoItem extends Component {
<label onDoubleClick={this.handleDoubleClick.bind(this)}>
{todo.get('description')}
</label>
<input type="checkbox" name="completed"/>completed
+ <button onClick={() => deleteTodo(todo.get('id'))}>delete todo</button>
</li>
)
}
@@ -46,7 +47,8 @@ export default class TodoItem extends Component {
return (
<li>
<TodoTextInput text={todo.get('description')} onSave={(text) => this.handleSave(todo.get('id'), text)}/>
<input type="checkbox" name="completed"/>completed
+ <button onClick={() => deleteTodo(todo.get('id'))}>delete todo</button>
</li>
)
}
(END)
Run the test and watch it pass.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
✓ Should have a toggle complete status checkbox
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
✓ Should handle TOGGLE_COMPLETE_ONE todo
33 passing (101ms)
Commit changes.
$ git add .
$ git commit -m 'have a toggle complete status checkbox'
It should behave correctly
It should call toggleCompleteOneTodo() when the complete status checkbox is changed
Write the test.
diff --git a/tests/todomvc/components/TodoItem.spec.js b/tests/todomvc/components/TodoItem.spec.js
index 67241e8..fcfc6e0 100644
--- a/tests/todomvc/components/TodoItem.spec.js
+++ b/tests/todomvc/components/TodoItem.spec.js
@@ -13,7 +13,8 @@ function setup() {
const props = {
todo: Map({id: uuid.v4(), description: 'Use Redux', completed: false}),
editTodo: sinon.spy(),
- deleteTodo: sinon.spy()
+ deleteTodo: sinon.spy(),
+ toggleCompleteOneTodo: sinon.spy()
};
const component = shallow(
<TodoItem {...props} />
@@ -102,6 +103,14 @@ describe('TodoItem component', () => {
component.children('label').simulate('doubleclick'); // switch to edit mode
component.find('TodoTextInput').props().onSave('');
expect(props.deleteTodo.called).to.be.true
+ });
+
+ it('Should call toggleCompleteOneTodo() when the complete status checkbox is changed', () => {
+ const {component, props} = setup();
+ const input = component.find('input');
+
+ input.simulate('change');
+ expect(props.toggleCompleteOneTodo.called).to.be.true
})
})
});
(END)
Run the test and watch it fail.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
✓ Should have a toggle complete status checkbox
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
1) Should call toggleCompleteOneTodo() when the complete status checkbox is changed
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
✓ Should handle TOGGLE_COMPLETE_ONE todo
33 passing (94ms)
1 failing
1) TodoItem component Should behave correctly Should call toggleCompleteOneTodo() when the complete status checkbox is changed:
AssertionError: expected false to be true
+ expected - actual
-false
+true
at Context.<anonymous> (tests/todomvc/components/TodoItem.spec.js:113:7)
Write the code to make the test pass, but also let's immediately work on the required prop warning regression.
diff --git a/src/todomvc/components/TodoItem.js b/src/todomvc/components/TodoItem.js
index 9532d03..bb30c72 100644
--- a/src/todomvc/components/TodoItem.js
+++ b/src/todomvc/components/TodoItem.js
@@ -8,7 +8,8 @@ export default class TodoItem extends Component {
static propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
- deleteTodo: PropTypes.func.isRequired
+ deleteTodo: PropTypes.func.isRequired,
+ toggleCompleteOneTodo: PropTypes.func.isRequired
};
constructor(props, context) {
@@ -28,26 +29,28 @@ export default class TodoItem extends Component {
}
renderTodoListItem() {
- const {todo, deleteTodo} = this.props;
+ const {todo, deleteTodo, toggleCompleteOneTodo} = this.props;
return (
<li>
<label onDoubleClick={ this.handleDoubleClick.bind(this) }>
{ todo.get('description') }
</label>
- <input type="checkbox" name="completed" />completed
+ <input type="checkbox" name="completed" checked={todo.get('completed')}
+ onChange={ () => toggleCompleteOneTodo(todo.get('id')) }/>completed
<button onClick={ () => deleteTodo(todo.get('id')) }>delete todo</button>
</li>
)
}
renderTodoTextInput() {
- const {todo, deleteTodo} = this.props;
+ const {todo, deleteTodo, toggleCompleteOneTodo} = this.props;
return (
<li>
<TodoTextInput text={ todo.get('description') } onSave={ (text) => this.handleSave(todo.get('id'), text) }/>
- <input type="checkbox" name="completed" />completed
+ <input type="checkbox" name="completed" checked={todo.get('completed')}
+ onChange={ () => toggleCompleteOneTodo(todo.get('id')) }/>completed
<button onClick={ () => deleteTodo(todo.get('id')) }>delete todo</button>
</li>
)
diff --git a/tests/todomvc/components/MainSection.spec.js b/tests/todomvc/components/MainSection.spec.js
index badf84c..dd90ed1 100644
--- a/tests/todomvc/components/MainSection.spec.js
+++ b/tests/todomvc/components/MainSection.spec.js
@@ -18,7 +18,8 @@ function setup() {
]),
actions: {
editTodo: sinon.spy(),
- deleteTodo: sinon.spy()
+ deleteTodo: sinon.spy(),
+ toggleCompleteOneTodo: sinon.spy()
}
};
(END)
Run the test and watch it pass.
TodoMVC actions
✓ Should create an action to add a todo
✓ Should create an action to edit a todo
✓ Should create an action to delete a todo
✓ Should create an action to toggle a todo between completed and not completed
Header component
Should render correctly
✓ Should be a Header
✓ Should have a title
✓ Should have a TodoTextInput field
Should behave correctly
✓ Should call addTodo() if length of text is greater than 0
MainSection component
Should render correctly
✓ Should be a MainSection component
✓ Should include a list of todos
TodoApp component
Should render correctly
✓ Should be a TodoApp
✓ Should have a header
✓ Should have a main section
TodoItem component
Should render correctly
✓ Should be an li
✓ Should have a label
✓ Should have a delete button
✓ Should have a toggle complete status checkbox
Should behave correctly
✓ Should switch to edit mode when label onDoubleClick is fired
✓ Should call editTodo() when TodoTextInput onSave is called
✓ Should leave edit mode after TodoTextInput onSave
✓ Should call deleteTodo() when the delete button is clicked
✓ Should call deleteTodo() when TodoTextInput onSave is called with no text
✓ Should call toggleCompleteOneTodo() when the complete status checkbox is changed
TodoTextInput component
✓ Should not call onSave() on blur if isNew
Should render correctly
✓ Should be a TodoTextInput component
Should behave correctly
✓ Should update value on change
✓ Should call onSave() on return key press
✓ Should reset state on return key press if isNew
✓ Should call onSave() on blur if not isNew
TodoMVC reducer
✓ Should handle initial state
✓ Should handle ADD todo
✓ Should handle EDIT todo
✓ Should handle DELETE todo
✓ Should handle TOGGLE_COMPLETE_ONE todo
34 passing (103ms)
Commit changes.
$ git add .
$ git commit -m 'call toggleCompleteOneTodo() when the complete status checkbox is changed'
Run It!
Now let's see what this looks like.
Open http://localhost:4000 in your web browser.
Type "Hello" into the text input box and see a new complete status checkbox appear.
Last updated
Was this helpful?