View a List of Todos

Story: As a user, I want to see all of todos in my todos list

To complete this story, we will need to create the TodoItem and MainSection components. Since this story does not involve making any changes to state, we will not have to write any more actions or reducers at this time.

TodoItem component

Should render correctly

It should be an li

Write the test.

// tests/todomvc/components/TodoItem.spec.js

'use strict';

import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'

import TodoItem from '../../../src/todomvc/components/TodoItem'

function setup() {
  const component = shallow(
    <TodoItem/>
  );

  return {
    component: component
  }
}

describe('TodoItem component', () => {
  describe('Should render correctly', () => {
    it('Should be an li', () => {
      const {component} = setup();

      expect(component.type()).to.equal('li')
    })
  })
});

Run the test and watch it fail.

module.js:487
    throw err;
    ^

Error: Cannot find module '../../../src/todomvc/components/TodoItem'

Write the code to make the test pass.

// src/todomvc/components/TodoItem.js

'use strict';

import React, {Component} from 'react'

export default class TodoItem extends Component {
  render() {
    return (
      <li/>
    )
  }
}

Run the test and watch it pass.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  TodoApp component
    Should render correctly
      ✓ Should be a TodoApp

  TodoItem component
    Should render correctly
      ✓ Should be an li

  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


  15 passing (61ms)

Commit changes.

$ git add .
$ git commit -m 'be an li'

It should have a label

Write the test.

diff --git a/tests/todomvc/components/TodoItem.spec.js b/tests/todomvc/components/TodoItem.spec.js
index e285de1..772bb61 100644
--- a/tests/todomvc/components/TodoItem.spec.js
+++ b/tests/todomvc/components/TodoItem.spec.js
@@ -1,18 +1,24 @@
 'use strict';

 import React from 'react'
+import {Map} from 'immutable'
+import uuid from 'uuid'
 import {expect} from 'chai'
 import {shallow} from 'enzyme'

 import TodoItem from '../../../src/todomvc/components/TodoItem'

 function setup() {
+  const props = {
+    todo: Map({id: uuid.v4(), description: 'Use Redux', completed: false})
+  };
   const component = shallow(
-    <TodoItem/>
+    <TodoItem {...props} />
   );

   return {
-    component: component
+    component: component,
+    props: props
   }
 }

@@ -22,6 +28,14 @@ describe('TodoItem component', () => {
       const {component} = setup();

       expect(component.type()).to.equal('li')
+    });
+
+    it('Should have a label', () => {
+      const {component, props} = setup();
+      const label = component.children('label');
+
+      expect(label).to.have.length(1);
+      expect(label.children().text()).to.equal(props.todo.get('description'))
     })
   })
 });
(END)

Run the test and watch it fail.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  TodoApp component
    Should render correctly
      ✓ Should be a TodoApp

  TodoItem component
    Should render correctly
      ✓ Should be an li
      1) Should have a label

  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


  15 passing (67ms)
  1 failing

  1) TodoItem component Should render correctly Should have a label:

      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:37:29)

Write the code to make the test pass.

diff --git a/src/todomvc/components/TodoItem.js b/src/todomvc/components/TodoItem.js
index 8c64c9d..6afa1eb 100644
--- a/src/todomvc/components/TodoItem.js
+++ b/src/todomvc/components/TodoItem.js
@@ -1,11 +1,22 @@
 'use strict';

 import React, {Component} from 'react'
+import PropTypes from 'prop-types'

 export default class TodoItem extends Component {
+  static propTypes = {
+    todo: PropTypes.object.isRequired
+  };
+
   render() {
+    const {todo} = this.props;
+
     return (
-      <li/>
+      <li>
+        <label>
+          { todo.get('description') }
+        </label>
+      </li>
     )
   }
 }
(END)

Run the test and watch it pass.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  TodoApp component
    Should render correctly
      ✓ Should be a TodoApp

  TodoItem component
    Should render correctly
      ✓ Should be an li
      ✓ Should have a label

  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


  16 passing (68ms)

Commit changes.

$ git add .
$ git commit -m 'have a label'

MainSection component

Should render correctly

It should be a MainSection component

Write the test.

// tests/todomvc/components/MainSection.spec.js

'use strict';

import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'

import MainSection from '../../../src/todomvc/components/MainSection'

function setup() {
  const component = shallow(
    <MainSection/>
  );

  return {
    component: component
  }
}

describe('MainSection component', () => {
  describe('Should render correctly', () => {
    it('Should be a MainSection component', () => {
      const {component} = setup();

      expect(component.type()).to.equal('section')
    })
  })
});

Run the test and watch it fail.

module.js:487
    throw err;
 ^

Error: Cannot find module '../../../src/todomvc/components/MainSection'

Write the code to make the test pass.

// src/todomvc/components/MainSection.js

'use strict';

import React, {Component} from 'react'

export default class MainSection extends Component {
  render() {
    return (
      <section/>
    )
  }
}

Run the test and watch it pass.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  TodoApp component
    Should render correctly
      ✓ Should be a TodoApp

  TodoItem component
    Should render correctly
      ✓ Should be an li
      ✓ Should have a label

  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


  17 passing (70ms)

Commit changes.

$ git add .
$ git commit -m 'be a MainSection'

It should include a list of todos

Write the test.

diff --git a/tests/todomvc/components/MainSection.spec.js b/tests/todomvc/components/MainSection.spec.js
index fc31f15..9e0dedd 100644
--- a/tests/todomvc/components/MainSection.spec.js
+++ b/tests/todomvc/components/MainSection.spec.js
@@ -1,18 +1,29 @@
 'use strict';

 import React from 'react'
+import {List, Map} from 'immutable'
+import uuid from 'uuid'
 import {expect} from 'chai'
 import {shallow} from 'enzyme'

 import MainSection from '../../../src/todomvc/components/MainSection'
+import TodoItem from '../../../src/todomvc/components/TodoItem'

 function setup() {
+  const props = {
+    todos: List([
+      Map({id: uuid.v4(), description: 'Use Redux', completed: false}),
+      Map({id: uuid.v4(), description: 'Run the tests', completed: false})
+    ])
+  };
+
   const component = shallow(
-    <MainSection/>
+    <MainSection {...props} />
   );

   return {
-    component: component
+    component: component,
+    props: props
   }
 }

@@ -22,6 +33,19 @@ describe('MainSection component', () => {
       const {component} = setup();

       expect(component.type()).to.equal('section')
+    });
+
+    it('Should include a list of todos', () => {
+      const {component, props} = setup();
+      const ul = component.children('ul');
+      const items = ul.children();
+
+      expect(ul).to.have.length(1);
+      expect(items).to.have.length(props.todos.size);
+      items.forEach((item, i) => {
+        expect(item.type()).to.equal(TodoItem);
+        expect(item.props().todo).to.equal(props.todos.get(i))
+      })
     })
   })
 });
(END)

Run the test and watch it fail.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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
      1) Should include a list of todos

  TodoApp component
    Should render correctly
      ✓ Should be a TodoApp

  TodoItem component
    Should render correctly
      ✓ Should be an li
      ✓ Should have a label

  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


  17 passing (76ms)
  1 failing

  1) MainSection component Should render correctly Should include a list of todos:

      AssertionError: expected { Object (root, unrendered, ...) } to have a length of 1 but got 0
      + expected - actual

      -0
      +1

      at Context.<anonymous> (tests/todomvc/components/MainSection.spec.js:43:26)

Write the code to make the test pass.

diff --git a/src/todomvc/components/MainSection.js b/src/todomvc/components/MainSection.js
index 997a7fa..72bd1e9 100644
--- a/src/todomvc/components/MainSection.js
+++ b/src/todomvc/components/MainSection.js
@@ -1,11 +1,24 @@
 'use strict';

 import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+
+import TodoItem from './TodoItem'

 export default class MainSection extends Component {
+  static propTypes = {
+    todos: PropTypes.object.isRequired
+  };
+
   render() {
+    const props = this.props;
+
     return (
-      <section/>
+      <section>
+        <ul>
+          {props.todos.map(todo => <TodoItem key={todo.get('id')} todo={todo}/>)}
+        </ul>
+      </section>
     )
   }
 }
(END)

Run the test and watch it pass.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  TodoItem component
    Should render correctly
      ✓ Should be an li
      ✓ Should have a label

  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


  18 passing (73ms)

Commit changes.

$ git add .
$ git commit -m 'include a list of todos'

TodoApp Component

It should render correctly

It should have a MainSection

Write the test.

diff --git a/tests/todomvc/components/TodoApp.spec.js b/tests/todomvc/components/TodoApp.spec.js
index 67f18ac..4632b7f 100644
--- a/tests/todomvc/components/TodoApp.spec.js
+++ b/tests/todomvc/components/TodoApp.spec.js
@@ -32,7 +32,19 @@ describe('TodoApp component', () => {
     it('Should be a TodoApp', () => {
       const {component} = setup();

-      expect(component.name()).to.equal('Header')
+      expect(component.name()).to.equal('div')
+    });
+
+    it('Should have a header', () => {
+      const {component} = setup();
+
+      expect(component.children('Header')).to.have.length(1)
+    });
+
+    it('Should have a main section', () => {
+      const {component} = setup();
+
+      expect(component.children('MainSection')).to.have.length(1)
     })
   })
 });
(END)

Run the test and watch it fail.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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
      1) Should be a TodoApp
      2) Should have a header
      3) Should have a main section

  TodoItem component
    Should render correctly
      ✓ Should be an li
      ✓ Should have a label

  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


  17 passing (77ms)
  3 failing

  1) TodoApp component Should render correctly Should be a TodoApp:

      AssertionError: expected 'Header' to equal 'div'
      + expected - actual

      -Header
      +div

      at Context.<anonymous> (tests/todomvc/components/TodoApp.spec.js:35:35)

  2) TodoApp component Should render correctly Should have a header:

      AssertionError: expected { Object (root, unrendered, ...) } to have a length of 1 but got 0
      + expected - actual

      -0
      +1

      at Context.<anonymous> (tests/todomvc/components/TodoApp.spec.js:41:52)

  3) TodoApp component Should render correctly Should have a main section:

      AssertionError: expected { Object (root, unrendered, ...) } to have a length of 1 but got 0
      + expected - actual

      -0
      +1

      at Context.<anonymous> (tests/todomvc/components/TodoApp.spec.js:47:57)

Write the code to make the test pass.

diff --git a/src/todomvc/components/TodoApp.js b/src/todomvc/components/TodoApp.js
index faf089a..1507dbb 100644
--- a/src/todomvc/components/TodoApp.js
+++ b/src/todomvc/components/TodoApp.js
@@ -1,18 +1,28 @@
 'use strict';

 import React, {Component} from 'react'
+import PropTypes from 'prop-types'
 import {bindActionCreators} from 'redux'
 import {connect} from 'react-redux'

 import Header from '../components/Header'
+import MainSection from '../components/MainSection'
 import * as actions from '../actions'

 class TodoApp extends Component {
+  static propTypes = {
+    todos: PropTypes.object.isRequired,
+    actions: PropTypes.object.isRequired
+  };
+
   render() {
-    const props = this.props;
+    const {todos, actions} = this.props;

     return (
-      <Header actions={props.actions}/>
+      <div>
+        <Header actions={actions}/>
+        <MainSection todos={todos} actions={actions}/>
+      </div>
     )
   }
 }
(END)

Run the test and watch it pass.

  TodoMVC actions
    ✓ Should create an action to add a todo

  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

  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


  20 passing (78ms)

Commit changes.

$ git add .
$ git commit -m 'have a MainSection'

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 what happens. Then enter "World" You can see the todos appear in your list, as well as watch state update with each action in the Redux Dev Tools window on the right, like this:

Last updated

Was this helpful?