Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.6.0
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Minimal React/Redux Boilerplate</title>
<style>
.complete {
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="app"></div>
Expand Down
14 changes: 14 additions & 0 deletions src/components/filter-link/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

export default ({
handler = e => console.log( 'no handler method defined' ),
filter,
children
}) => {
return (
<a href="#" onClick={e => {
e.preventDefault();
handler()
}}>{ children }</a>
);
}
16 changes: 16 additions & 0 deletions src/components/filter-link/spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import test from 'tape';
import { shallow } from 'enzyme';

import FilterLink from './';

test( 'FilterLink', t => {
t.plan( 1 );

const wrapper = shallow( <FilterLink /> );
const expected = true;
const actual = wrapper.is( 'a' );

t.ok( actual === expected, 'renders a link' );
});

45 changes: 45 additions & 0 deletions src/components/filter-list/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { connect } from 'react-redux';

import showAll from '../../store/actions/show-all';
import showActive from '../../store/actions/show-active';
import showCompleted from '../../store/actions/show-completed';

import FilterLink from '../filter-link';

export class FilterList extends React.Component {
constructor ( props ) {
super( props )
}

render () {
const {
onShowAll = e => e,
onShowActive = e => e,
onShowCompleted = e => e,
visibilityFilter = ''
} = this.props;

return (
<ul>
<li><FilterLink handler={onShowAll}>All</FilterLink></li>
<li><FilterLink handler={onShowActive}>Active</FilterLink></li>
<li><FilterLink handler={onShowCompleted}>Completed</FilterLink></li>
</ul>
);
}
}

export const mapStateToProps = ({ todos, visibilityFilter }) => ({
todos,
visibilityFilter,
});

export const mapDispatchToProps = dispatch => ({
onShowAll: () => dispatch( showAll() ),
onShowActive: () => dispatch( showActive() ),
onShowCompleted: () => dispatch( showCompleted() ),
});

export default connect( mapStateToProps, mapDispatchToProps )( FilterList );

Empty file.
4 changes: 3 additions & 1 deletion src/components/todo-item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';

export default ({
remove = e => console.log( 'no remove method defined' ),
toggle = e => console.log( 'no toggle method defined' ),
name,
completed
}) => {
return (
<span>
<span>
<span className={ completed ? 'complete' : 'incomplete'} onClick={toggle}>
{name}
</span>
<a href="#" onClick={remove}>X</a>
Expand Down
22 changes: 20 additions & 2 deletions src/components/todo-items/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,29 @@ import TodoItem from '../todo-item';

export default ({
items = [],
filter = 'SHOW_ALL',
onRemove = e => console.log( 'no onRemove method defined' ),
onToggle = e => console.log( 'no onToggle method defined' ),
}) => {
const nodes = items.map( ( todo, i ) => (
const getVisibleTodos = (todos, filter) => {
switch ( filter ) {
case 'SHOW_ALL':
return todos;
case 'SHOW_ACTIVE':
return todos.filter( t => !t.completed );
case 'SHOW_COMPLETED':
return todos.filter( t => t.completed );
default:
return todos;
}
}

const nodes = getVisibleTodos(items, filter).map( ( todo, i ) => (
<li key={i}>
<TodoItem {...todo} remove={() => onRemove( todo )} />
<TodoItem {...todo}
remove={() => onRemove( todo )}
toggle={() => onToggle( todo )}
/>
</li>
));

Expand Down
15 changes: 13 additions & 2 deletions src/components/todo-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { connect } from 'react-redux';

import addItem from '../../store/actions/add-item';
import removeItem from '../../store/actions/remove-item';
import toggleItem from '../../store/actions/toggle-item';

import TodoItems from '../todo-items';
import FilterList from '../filter-list';

export class TodoList extends React.Component {
constructor ( props ) {
Expand All @@ -22,7 +25,9 @@ export class TodoList extends React.Component {
const {
onAdd = e => e,
onRemove = e => e,
onToggle = e => e,
todos = [],
visibilityFilter = 'SHOW_ALL'
} = this.props;

const {
Expand All @@ -47,11 +52,15 @@ export class TodoList extends React.Component {
?
<TodoItems
items={todos}
filter={visibilityFilter}
onRemove={onRemove}
onToggle={onToggle}
/>
:
<p>You have no todos.</p>
}

<FilterList />
</div>
);
}
Expand All @@ -73,13 +82,15 @@ export class TodoList extends React.Component {
}
}

export const mapStateToProps = ({ todos }) => ({
export const mapStateToProps = ({ todos, visibilityFilter }) => ({
todos,
visibilityFilter,
});

export const mapDispatchToProps = dispatch => ({
onAdd: v => dispatch( addItem( v ) ),
onRemove: todo => dispatch( removeItem( todo ) )
onRemove: todo => dispatch( removeItem( todo ) ),
onToggle: todo => dispatch( toggleItem( todo ) ),
});

export default connect( mapStateToProps, mapDispatchToProps )( TodoList );
Expand Down
1 change: 1 addition & 0 deletions src/store/actions/add-item/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default name => ({
type: 'ADD_TODO_ITEM',
name,
completed: false
});

4 changes: 4 additions & 0 deletions src/store/actions/show-active/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default () => ({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_ACTIVE'
});
4 changes: 4 additions & 0 deletions src/store/actions/show-all/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default () => ({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_ALL'
});
4 changes: 4 additions & 0 deletions src/store/actions/show-completed/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default () => ({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
});
5 changes: 5 additions & 0 deletions src/store/actions/toggle-item/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default todo => ({
type: 'TOGGLE_TODO_ITEM',
todo,
});

3 changes: 2 additions & 1 deletion src/store/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { combineReducers } from 'redux';
import todos from './todos';
import visibilityFilter from './visibility-filter'

export default combineReducers({ todos });
export default combineReducers({ todos, visibilityFilter });

16 changes: 12 additions & 4 deletions src/store/reducers/todos.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ export default ( todos = [], { type, ...payload } ) => {
switch ( type ) {
case 'ADD_TODO_ITEM':
return [ ...todos, payload ];
break;
case 'REMOVE_TODO_ITEM':
return todos.filter( t => t !== payload.todo );
break;
case 'TOGGLE_TODO_ITEM':
return todos.map( t => {
if (t.name !== payload.todo.name) {
return t;
}
return {
...t,
completed: !t.completed
}
})
default:
return todos;
}

return todos;
};

8 changes: 8 additions & 0 deletions src/store/reducers/visibility-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default ( visibilityFilter = 'SHOW_ALL', action ) => {
switch ( action.type ) {
case 'SET_VISIBILITY_FILTER':
return action.filter;
default:
return visibilityFilter;
}
};