Skip to content

Commit 22a17b5

Browse files
author
Zachery Moneypenny
committed
Adds completion of todo items
1 parent e431d6d commit 22a17b5

File tree

7 files changed

+92
-19
lines changed

7 files changed

+92
-19
lines changed

.eslintrc.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"object-curly-spacing": 0,
99
"space-before-function-paren": ["error", "always"],
1010
"arrow-body-style": 0,
11-
"react/no-array-index-key": 0
11+
"react/no-array-index-key": 0,
12+
"no-console": 0,
13+
"class-methods-use-this": 0,
14+
"no-param-reassign": 0
1215
}
1316
}

app/javascript/components/App.jsx

+26-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {PureComponent} from 'react';
2-
import {remove} from 'lodash';
2+
import {remove, each} from 'lodash';
3+
import {v4 as uuid} from 'uuid';
34

45
import TodoForm from './TodoForm';
56
import TodoList from './TodoList';
@@ -16,15 +17,31 @@ class App extends PureComponent {
1617

1718
this.handleTodoAdd = this.handleTodoAdd.bind(this);
1819
this.handleTodoRemove = this.handleTodoRemove.bind(this);
20+
this.handleTodoToggle = this.handleTodoToggle.bind(this);
1921
}
2022

21-
handleTodoAdd (todo) {
23+
handleTodoAdd (todoDescription) {
24+
const todo = {
25+
id: uuid.v4(),
26+
label: todoDescription,
27+
completed: false,
28+
};
2229
this.setState({todos: [todo, ...this.state.todos]});
2330
}
2431

25-
handleTodoRemove (todo) {
32+
handleTodoRemove (todoId) {
33+
const todos = [...this.state.todos];
34+
remove(todos, t => t.id === todoId);
35+
this.setState({todos});
36+
}
37+
38+
handleTodoToggle (todoId) {
2639
const todos = [...this.state.todos];
27-
remove(todos, t => t === todo);
40+
each(todos, (todo) => {
41+
if (todo.id === todoId) {
42+
todo.completed = !todo.completed;
43+
}
44+
});
2845
this.setState({todos});
2946
}
3047

@@ -35,7 +52,11 @@ class App extends PureComponent {
3552
<div className={styles.app}>
3653
<h2>My Todo List</h2>
3754
<TodoForm handleTodoAdd={this.handleTodoAdd} />
38-
<TodoList todos={todos} handleTodoRemove={this.handleTodoRemove} />
55+
<TodoList
56+
todos={todos}
57+
handleTodoRemove={this.handleTodoRemove}
58+
handleTodoToggle={this.handleTodoToggle}
59+
/>
3960
</div>
4061
);
4162
}

app/javascript/components/Todo.jsx

+33-6
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,60 @@
1-
import React, {PureComponent} from 'react';
2-
import PropTypes from 'prop-types';
1+
import React, {Component} from 'react';
2+
import PropTypes from 'prop-types';
33

44
import styles from './Todo.scss';
55

6-
class Todo extends PureComponent {
6+
class Todo extends Component {
77
constructor (props) {
88
super(props);
99

1010
this.onRemoveClick = this.onRemoveClick.bind(this);
11+
this.onCompletedToggle = this.onCompletedToggle.bind(this);
12+
}
13+
14+
shouldComponentUpdate () {
15+
return true;
1116
}
1217

1318
onRemoveClick (event) {
1419
event.preventDefault();
15-
this.props.handleTodoRemove(this.props.todo);
20+
const {todo} = this.props;
21+
this.props.handleTodoRemove(todo.id);
22+
}
23+
24+
onCompletedToggle () {
25+
const {todo} = this.props;
26+
this.props.handleTodoToggle(todo.id);
1627
}
1728

1829
render () {
30+
const {todo} = this.props;
31+
let classes = '';
32+
if (todo.completed) {
33+
classes = styles.completed;
34+
}
35+
1936
return (
2037
<li className={styles.todo}>
38+
<input
39+
type="checkbox"
40+
checked={todo.completed}
41+
onChange={this.onCompletedToggle}
42+
/>
2143
<a role="button" tabIndex="-1" onClick={this.onRemoveClick}>&times;</a>
22-
{this.props.todo}
44+
<span className={classes}>{todo.label}</span>
2345
</li>
2446
);
2547
}
2648
}
2749

2850
Todo.propTypes = {
29-
todo: PropTypes.string.isRequired,
51+
todo: PropTypes.shape({
52+
id: PropTypes.string,
53+
label: PropTypes.string,
54+
completed: PropTypes.bool,
55+
}).isRequired,
3056
handleTodoRemove: PropTypes.func.isRequired,
57+
handleTodoToggle: PropTypes.func.isRequired,
3158
};
3259

3360
export default Todo;

app/javascript/components/Todo.scss

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
padding: 10px 10px;
33
border-bottom: 1px dashed #666;
44

5+
input[type=checkbox] {
6+
font-size: 120%;
7+
margin-right: 10px;
8+
}
9+
510
&:last-child { border-bottom: none; }
611
&:hover { background-color: lightyellow; }
712

@@ -10,3 +15,9 @@
1015
cursor: pointer;
1116
}
1217
}
18+
19+
.completed {
20+
text-decoration: line-through;
21+
font-style: italic;
22+
color: #ddd;
23+
}
+16-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1-
import React, {PureComponent} from 'react';
1+
import React, {Component} from 'react';
22
import PropTypes from 'prop-types';
33

44
import Todo from './Todo';
55

66
import styles from './TodoList.scss';
77

8-
class TodoList extends PureComponent {
8+
class TodoList extends Component {
9+
shouldComponentUpdate () {
10+
return true;
11+
}
12+
913
render () {
10-
let todos = this.props.todos.map((todo, key) => {
14+
let todos = this.props.todos.map((todo) => {
1115
return (
1216
<Todo
13-
key={key}
17+
key={todo.id}
1418
todo={todo}
1519
handleTodoRemove={this.props.handleTodoRemove}
20+
handleTodoToggle={this.props.handleTodoToggle}
1621
/>
1722
);
1823
});
1924

2025
if (todos.length === 0) {
21-
todos = [<li className={styles.noTodos}>No todos yet</li>];
26+
todos = [<li key="noTodos" className={styles.noTodos}>No todos yet</li>];
2227
}
2328

2429
return (
@@ -30,8 +35,13 @@ class TodoList extends PureComponent {
3035
}
3136

3237
TodoList.propTypes = {
33-
todos: PropTypes.arrayOf(PropTypes.string).isRequired,
38+
todos: PropTypes.arrayOf(PropTypes.shape({
39+
id: PropTypes.string,
40+
label: PropTypes.string,
41+
completed: PropTypes.bool,
42+
})).isRequired,
3443
handleTodoRemove: PropTypes.func.isRequired,
44+
handleTodoToggle: PropTypes.func.isRequired,
3545
};
3646

3747
export default TodoList;

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"react-hot-loader": "next",
3030
"sass-loader": "^6.0.5",
3131
"style-loader": "^0.18.1",
32+
"uuid": "^3.0.1",
3233
"webpack": "^2.5.1",
3334
"webpack-manifest-plugin": "^1.1.0",
3435
"webpack-merge": "^4.1.0"

yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -5112,7 +5112,7 @@ uuid@^2.0.2:
51125112
version "2.0.3"
51135113
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
51145114

5115-
uuid@^3.0.0:
5115+
uuid@^3.0.0, uuid@^3.0.1:
51165116
version "3.0.1"
51175117
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
51185118

0 commit comments

Comments
 (0)