diff --git a/archived/jest/demo/README.md b/archived/jest/demo/README.md
deleted file mode 100644
index 60467a9..0000000
--- a/archived/jest/demo/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Step 2.4: Testing TypeScript code with Jest (Demo)
-
-[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
-
-[Jest](https://jestjs.io/) is a test framework made by Facebook and is very popular in the React and wider JS ecosystems.
-
-In this exercise, we will work on implementing simple unit tests using Jest.
-
-## Jest Features
-
-- Multi-threaded and isolated test runner
-- Provides a fake browser-like environment if needed (window, document, DOM, etc) using jsdom
-- Snapshots: Jest can create text-based snapshots of rendered components. These snapshots can be checked in and show API or large object changes alongside code changes in pull requests.
-- Code coverage is integrated (`--coverage`)
-- Very clear error messages showing where a test failure occurred
-
-## How to use Jest
-
-- Using `create-react-app` or other project generators, Jest should already be pre-configured. Running `npm test` usually will trigger it!
-- A `jest.config.js` file is used for configuration
-- `jsdom` might not have enough API from real browsers, for those cases, polyfills are required. Place these inside `jest.setup.js` and hook up the setup file in `jest.config.js`
-- in order to use `enzyme` library to test React Components, more config bits are needed inside `jest.setup.js`
-
-## What does a test look like?
-
-```ts
-// describe(), it() and expect() are globally exported, so they don't need to be imported when jest runs these tests
-describe('Something to be tested', () => {
- it('should describe the behavior', () => {
- expect(true).toBe(true);
- });
-});
-```
-
-## Testing React components using Enzyme
-
-[Enzyme](https://airbnb.io/enzyme/) is made by Airbnb and provides utilities to help test React components.
-
-In a real app using ReactDOM, the top-level component will be rendered on the page using `ReactDOM.render()`. Enzyme provides a lighter-weight `mount()` function which is usually adequate for testing purposes.
-
-`mount()` returns a wrapper that can be inspected and provides functionality like `find()`, simulating clicks, etc.
-
-The following code demonstrates how Enzyme can be used to help test React components.
-
-```jsx
-import React from 'react';
-import { mount } from 'enzyme';
-import { TestMe } from './TestMe';
-
-describe('TestMe Component', () => {
- it('should have a non-clickable component when the original InnerMe is clicked', () => {
- const wrapper = mount( );
- wrapper.find('#innerMe').simulate('click');
- expect(wrapper.find('#innerMe').text()).toBe('Clicked');
- });
-});
-
-describe('Foo Component Tests', () => {
- it('allows us to set props', () => {
- const wrapper = mount( );
- expect(wrapper.props().bar).toBe('baz');
- wrapper.setProps({ bar: 'foo' });
- expect(wrapper.props().bar).toBe('foo');
-
- wrapper.find('button').simulate('click');
- });
-});
-```
-
-## Advanced topics
-
-### Mocking
-
-Mocking functions is a large part of what makes Jest a powerful testing library. Jest actually intercepts the module loading process in Node.js, allowing it to mock entire modules if needed.
-
-There are many ways to mock, as you'd imagine in a language as flexible as JS. We only look at the simplest case, but there's a lot of depth here.
-
-To mock a function:
-
-```ts
-it('some test function', () => {
- const mockCallback = jest.fn(x => 42 + x);
- mockCallback(1);
- mockCallback(2);
- expect(mockCallback).toHaveBeenCalledTimes(2);
-});
-```
-
-Read more about jest mocking [here](https://jestjs.io/docs/en/mock-functions.html).
-
-### Async Testing
-
-For testing async scenarios, the test runner needs some way to know when the scenario is finished. Jest tests can handle async scenarios using callbacks, promises, or async/await.
-
-```ts
-// Callback
-it('tests callback functions', (done) => {
- setTimeout(() => {
- done();
- }, 1000);
-});
-
-// Returning a promise
-it('tests promise functions', () => {
- return someFunctionThatReturnsPromise());
-});
-
-// Async/await (recommended)
-it('tests async functions', async () => {
- expect(await someFunction()).toBe(5);
-});
-```
-
-# Demo
-
-## Jest basics
-
-In this repo, we can start an inner loop development of tests by running `npm test` from the root of the `frontend-bootcamp` folder.
-
-Take a look at code inside `demo/src`:
-
-1. `index.ts` exports a few functions for a counter as well as a function for squaring numbers. We'll use this last function to demonstrate how mocks work.
-
-2. `multiply.ts` is a contrived example of a function that is exported
-
-3. `index.spec.ts` is the test file
-
-Note how tests are re-run when either test files or source files under `src` are saved.
diff --git a/archived/jest/demo/index.html b/archived/jest/demo/index.html
deleted file mode 100644
index 92a9499..0000000
--- a/archived/jest/demo/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
- For this step, we look at unit testing. Run
-
npm test
- in the command line.
-
-
-
-
diff --git a/archived/jest/demo/src/TestMe.spec.tsx b/archived/jest/demo/src/TestMe.spec.tsx
deleted file mode 100644
index 70452b6..0000000
--- a/archived/jest/demo/src/TestMe.spec.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { TestMe } from './TestMe';
-
-describe('TestMe Component', () => {
- it('should have a non-clickable component when the original InnerMe is clicked', () => {
- const wrapper = mount( );
- wrapper.find('#innerMe').simulate('click');
- expect(wrapper.find('#innerMe').text()).toBe('Clicked');
- });
-});
diff --git a/archived/jest/demo/src/TestMe.tsx b/archived/jest/demo/src/TestMe.tsx
deleted file mode 100644
index 3d68f34..0000000
--- a/archived/jest/demo/src/TestMe.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-
-export interface TestMeProps {
- name: string;
-}
-
-export interface TestMeState {
- clicked: boolean;
-}
-
-export const TestMe = (props: TestMeProps) => {
- return (
-
-
-
- );
-};
-
-export class InnerMe extends React.Component {
- state = {
- clicked: false
- };
-
- onClick = () => {
- this.setState({ clicked: true });
- };
-
- render() {
- return !this.state.clicked ? (
-
- Hello {this.props.name}, Click Me
-
- ) : (
- Clicked
- );
- }
-}
diff --git a/archived/jest/demo/src/index.spec.tsx b/archived/jest/demo/src/index.spec.tsx
deleted file mode 100644
index 6fa5c3e..0000000
--- a/archived/jest/demo/src/index.spec.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-
-describe('index', () => {
- it('placeholder', () => {
- });
-});
diff --git a/archived/jest/demo/src/index.ts b/archived/jest/demo/src/index.ts
deleted file mode 100644
index ecdd946..0000000
--- a/archived/jest/demo/src/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { multiply } from './multiply';
-
-let counter = 0;
-
-export function getCount() {
- return counter;
-}
-
-export function increment() {
- return ++counter;
-}
-
-export function decrement() {
- return --counter;
-}
-
-export function square(x: number) {
- return multiply(x, x);
-}
diff --git a/archived/jest/demo/src/multiply.ts b/archived/jest/demo/src/multiply.ts
deleted file mode 100644
index aa528c0..0000000
--- a/archived/jest/demo/src/multiply.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function multiply(x: number, y: number) {
- return x * y;
-}
diff --git a/archived/jest/exercise/README.md b/archived/jest/exercise/README.md
deleted file mode 100644
index aed944e..0000000
--- a/archived/jest/exercise/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Step 2.4: Testing TypeScript code with Jest (Exercise)
-
-[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
-
-Start the test runner by running `npm test` in the root of the `frontend-bootcamp` folder.
-
-## Basic testing
-
-1. Look at `exercise/src/stack.ts` for a sample implementation of a stack
-
-2. Follow the instructions inside `stack.spec.ts` file to complete the two tests
-
-## Enzyme Testing
-
-1. Open up `exercise/src/TestMe.spec.tsx`
-
-2. Fill in the test using Enzyme concepts introduced in the demo
diff --git a/archived/jest/exercise/index.html b/archived/jest/exercise/index.html
deleted file mode 100644
index e908cc3..0000000
--- a/archived/jest/exercise/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
- For this step, we look at unit testing. Run
-
npm test
- in the command line.
-
-
-
-
diff --git a/archived/jest/exercise/src/TestMe.spec.tsx b/archived/jest/exercise/src/TestMe.spec.tsx
deleted file mode 100644
index afd5164..0000000
--- a/archived/jest/exercise/src/TestMe.spec.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { TestMe } from './TestMe';
-
-describe('TestMe Component', () => {
- it('should render correctly when hovered', () => {
- // TODO:
- // 1. mount a Component here
- // 2. use enzyme wrapper's find() method to retrieve the #innerMe element
- // 3. simulate a hover with "mouseover" event via the simulate() API
- // 4. make assertions with expect on the text() of the #innerMe element
- });
-});
diff --git a/archived/jest/exercise/src/TestMe.tsx b/archived/jest/exercise/src/TestMe.tsx
deleted file mode 100644
index 8f39d8d..0000000
--- a/archived/jest/exercise/src/TestMe.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-
-export interface TestMeProps {
- name: string;
-}
-
-export interface TestMeState {
- enabled: boolean;
-}
-
-export const TestMe = (props: TestMeProps) => {
- return (
-
-
-
- );
-};
-
-export class InnerMe extends React.Component {
- state = {
- enabled: false
- };
-
- onMouseOver = () => {
- this.setState({ enabled: true });
- };
-
- render() {
- return !this.state.enabled ? (
-
- Hello {this.props.name}, Hover Over Me
-
- ) : (
- Enabled
- );
- }
-}
diff --git a/archived/jest/exercise/src/index.ts b/archived/jest/exercise/src/index.ts
deleted file mode 100644
index 8b6ab17..0000000
--- a/archived/jest/exercise/src/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { Stack } from './stack';
-export { TestMe } from './TestMe';
diff --git a/archived/jest/exercise/src/stack.spec.ts b/archived/jest/exercise/src/stack.spec.ts
deleted file mode 100644
index 03b7d9a..0000000
--- a/archived/jest/exercise/src/stack.spec.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// TODO: Import the stack here
-
-describe('Stack', () => {
- it('should push item to the top of the stack', () => {
- // TODO: implement test here:
- // 1. Instantiate a new Stack - i.e. const stack = new Stack();
- // 2. Use stack push calls to add some items to the stack
- // 3. Write assertions via the expect() API
- });
-
- it('should pop the item from the top of stack', () => {
- // TODO: implement test here:
- // 1. Instantiate a new Stack - i.e. const stack = new Stack();
- // 2. Use stack push calls to add some items to the stack
- // 3. pop a few items off the stack
- // 4. write assertions via the expect() API
- });
-});
diff --git a/archived/jest/exercise/src/stack.ts b/archived/jest/exercise/src/stack.ts
deleted file mode 100644
index 7f62d2d..0000000
--- a/archived/jest/exercise/src/stack.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-export class Stack {
- private _items: T[] = [];
-
- /** Add an item to the top of the stack. */
- push(item: T) {
- this._items.push(item);
- }
-
- /** Remove the top item from the stack and return it. */
- pop(): T {
- if (this._items.length > 0) {
- return this._items.pop();
- }
- }
-
- /** Return the top item from the stack without removing it. */
- peek(): T {
- if (this._items.length > 0) {
- return this._items[this._items.length - 1];
- }
- }
-
- /** Get the number of items in the stack/ */
- get count(): number {
- return this._items.length;
- }
-}
diff --git a/step2-04/exercise/README.md b/step2-04/exercise/README.md
index 507edbf..b7118c6 100644
--- a/step2-04/exercise/README.md
+++ b/step2-04/exercise/README.md
@@ -20,4 +20,8 @@ If you don't already have the app running, start it by running `npm start` from
1. Open `exercise/src/components/TodoHeader.tsx`
-2. Replace the couple of TODO
+2. Replace the `onAdd` with a real implementation using the `this.context` object
+
+3. Replace the `onFilter` with a real implementation using the `this.context` object
+
+4. Be sure to set the `contextType` of the TodoHeader component
diff --git a/step2-06/exercise/README.md b/step2-06/exercise/README.md
index b8d8031..8638161 100644
--- a/step2-06/exercise/README.md
+++ b/step2-06/exercise/README.md
@@ -1,25 +1,17 @@
-# Step 2.6 - Redux: Dispatching actions and examining state (Exercise)
+# Step 2.6: Redux: React Binding (Exercise)
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
-## Visualize state changes with Chrome extension
+## Bind Redux Store with Class Component
-If you still have `npm test` running from the previous step, stop it with `ctrl+C`. Start the app by running `npm start` from the root of the `frontend-bootcamp` folder. Click the "exercise" link under day 2 step 6.
+1. Open `exercise/src/components/TodoHeader.tsx`.
-1. Install the [Redux DevTools extension](https://github.com/zalmoxisus/redux-devtools-extension)
- - [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd)
- - [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)
+2. Just like the 2.4 exercise, implement `onAdd` and `onFilter` using `this.context.dispatch()` calls to dispatch actions.
-2. Hit F12 (`cmd+option+I` on Mac) and open the inspector panel entitled **Redux**
+## Bind Redux Store with Functional Component
-3. Modify `exercise/src/index.tsx` to dispatch actions (you're not limited to adding todos; you can also remove and clear)
+1. Open `exercise/src/components/TodoFooter.tsx`.
-4. Explore the actions' effects using the extension
+2. Follow the instructions in the file to replace the `todos` const using the `useMappedState()` hook.
-## Playing with dispatching actions inside tests
-
-Stop the app using `ctrl+C` and start the tests by running `npm test`.
-
-1. Open `exercise/src/reducers/reducer.spec.ts`
-
-2. Follow the instructions to fill out the reducer tests
+3. Retrieve the dispatch function with `useDispatch()` hook.
diff --git a/step2-06/exercise/src/actions/index.ts b/step2-06/exercise/src/actions/index.ts
index d75c2d9..a483a82 100644
--- a/step2-06/exercise/src/actions/index.ts
+++ b/step2-06/exercise/src/actions/index.ts
@@ -1,5 +1,10 @@
import uuid from 'uuid/v4';
export const actions = {
- addTodo: (label: string) => ({ type: 'addTodo', id: uuid(), label })
+ addTodo: (label: string) => ({ type: 'addTodo', id: uuid(), label }),
+ remove: (id: string) => ({ type: 'remove', id }),
+ complete: (id: string) => ({ type: 'complete', id }),
+ clear: () => ({ type: 'clear' }),
+ setFilter: (filter: string) => ({ type: 'setFilter', filter }),
+ edit: (id: string, label: string) => ({ type: 'edit', id, label })
};
diff --git a/step2-06/exercise/src/components/TodoApp.tsx b/step2-06/exercise/src/components/TodoApp.tsx
new file mode 100644
index 0000000..4425e1d
--- /dev/null
+++ b/step2-06/exercise/src/components/TodoApp.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Stack } from 'office-ui-fabric-react';
+import { TodoFooter } from './TodoFooter';
+import { TodoHeader } from './TodoHeader';
+import { TodoList } from './TodoList';
+
+export const TodoApp = () => {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/step2-06/exercise/src/components/TodoFooter.tsx b/step2-06/exercise/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..fc5307e
--- /dev/null
+++ b/step2-06/exercise/src/components/TodoFooter.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { DefaultButton, Stack, Text } from 'office-ui-fabric-react';
+import { actions } from '../actions';
+import { useMappedState, useDispatch } from 'redux-react-hook';
+
+export const TodoFooter = () => {
+ // TODO: make use of useMappedState(state => state) and the useDispatch functions to get
+ // the Redux store and dispatching actions
+ // HINT: const { todos } = useMappedState(...);
+ // HINT: useDispatch() here too.
+ const todos = {};
+ const dispatch = (...args: any[]) => {};
+
+ const itemCount = Object.keys(todos).filter(id => !todos[id].completed).length;
+
+ return (
+
+
+ {itemCount} item{itemCount === 1 ? '' : 's'} left
+
+ dispatch(actions.clear())}>Clear Completed
+
+ );
+};
diff --git a/step2-06/exercise/src/components/TodoHeader.tsx b/step2-06/exercise/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..e447dad
--- /dev/null
+++ b/step2-06/exercise/src/components/TodoHeader.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import { Stack, Text, Pivot, PivotItem, TextField, PrimaryButton } from 'office-ui-fabric-react';
+import { FilterTypes } from '../store';
+import { actions } from '../actions';
+import { StoreContext } from 'redux-react-hook';
+
+interface TodoHeaderState {
+ labelInput: string;
+}
+
+export class TodoHeader extends React.Component<{}, TodoHeaderState> {
+ constructor(props: {}) {
+ super(props);
+ this.state = { labelInput: undefined };
+ }
+
+ render() {
+ return (
+
+
+ todos
+
+
+
+
+ ({
+ ...(props.focused && {
+ field: {
+ backgroundColor: '#c7e0f4'
+ }
+ })
+ })}
+ />
+
+ Add
+
+
+
+
+
+
+
+
+ );
+ }
+
+ private onAdd = () => {
+ // TODO: Fill in a dispatch call to add the todo item
+ // HINT: this.context.dispatch(...);
+ this.setState({ labelInput: undefined });
+ };
+
+ private onChange = (evt: React.FormEvent, newValue: string) => {
+ this.setState({ labelInput: newValue });
+ };
+
+ private onFilter = (item: PivotItem) => {
+ // TODO: Fill in the dispatch call to set the filter
+ // HINT: this.context.dispatch(...);
+ };
+}
+
+// TODO: set the context type of this Class to StoreContext
diff --git a/step2-06/exercise/src/components/TodoList.tsx b/step2-06/exercise/src/components/TodoList.tsx
new file mode 100644
index 0000000..b577baa
--- /dev/null
+++ b/step2-06/exercise/src/components/TodoList.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Stack } from 'office-ui-fabric-react';
+import { TodoListItem } from './TodoListItem';
+import { useMappedState } from 'redux-react-hook';
+
+export const TodoList = () => {
+ const { filter, todos } = useMappedState(state => state);
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+
+ return (
+
+ {filteredTodos.map(id => (
+
+ ))}
+
+ );
+};
diff --git a/step2-06/exercise/src/components/TodoListItem.tsx b/step2-06/exercise/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..e105851
--- /dev/null
+++ b/step2-06/exercise/src/components/TodoListItem.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import { Stack, Checkbox, IconButton, TextField, DefaultButton } from 'office-ui-fabric-react';
+import { actions } from '../actions';
+import { StoreContext } from 'redux-react-hook';
+
+interface TodoListItemProps {
+ id: string;
+}
+
+interface TodoListItemState {
+ editing: boolean;
+ editLabel: string;
+}
+
+export class TodoListItem extends React.Component {
+ constructor(props: TodoListItemProps) {
+ super(props);
+ this.state = { editing: false, editLabel: undefined };
+ }
+
+ render() {
+ const { id } = this.props;
+ const { todos } = this.context.getState();
+ const dispatch = this.context.dispatch;
+
+ const item = todos[id];
+
+ return (
+
+ {!this.state.editing && (
+ <>
+ dispatch(actions.complete(id))} />
+
+
+ dispatch(actions.remove(id))} />
+
+ >
+ )}
+
+ {this.state.editing && (
+
+
+
+
+
+ Save
+
+
+ )}
+
+ );
+ }
+
+ private onEdit = () => {
+ const { id } = this.props;
+ const { todos } = this.context.getState();
+ const { label } = todos[id];
+
+ this.setState({
+ editing: true,
+ editLabel: this.state.editLabel || label
+ });
+ };
+
+ private onDoneEdit = () => {
+ this.context.dispatch(actions.edit(this.props.id, this.state.editLabel));
+ this.setState({
+ editing: false,
+ editLabel: undefined
+ });
+ };
+
+ private onChange = (evt: React.FormEvent, newValue: string) => {
+ this.setState({ editLabel: newValue });
+ };
+}
+
+TodoListItem.contextType = StoreContext;
diff --git a/step2-06/exercise/src/index.tsx b/step2-06/exercise/src/index.tsx
index 566917b..d994e52 100644
--- a/step2-06/exercise/src/index.tsx
+++ b/step2-06/exercise/src/index.tsx
@@ -1,16 +1,19 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
import { reducer } from './reducers';
import { createStore } from 'redux';
+import { TodoApp } from './components/TodoApp';
+import { initializeIcons } from '@uifabric/icons';
import { composeWithDevTools } from 'redux-devtools-extension';
-import { actions } from './actions';
+import { StoreContext } from 'redux-react-hook';
const store = createStore(reducer, {}, composeWithDevTools());
-console.log(store.getState());
+initializeIcons();
-// TODO: dispatch several actions and see the effects on state inside the Redux devtools
-
-// store.dispatch(actions.???);
-// store.dispatch(actions.???);
-// store.dispatch(actions.???);
-
-console.log(store.getState());
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('app')
+);
diff --git a/step2-06/exercise/src/reducers/index.ts b/step2-06/exercise/src/reducers/index.ts
index 6eed3c5..757ac54 100644
--- a/step2-06/exercise/src/reducers/index.ts
+++ b/step2-06/exercise/src/reducers/index.ts
@@ -1,27 +1,43 @@
import { Store } from '../store';
-import { addTodo, remove, complete, clear } from './pureFunctions';
+import { combineReducers } from 'redux';
+import { createReducer } from 'redux-starter-kit';
-function todoReducer(state: Store['todos'] = {}, action: any): Store['todos'] {
- switch (action.type) {
- case 'addTodo':
- return addTodo(state, action.id, action.label);
+export const todosReducer = createReducer(
+ {},
+ {
+ addTodo(state, action) {
+ state[action.id] = { label: action.label, completed: false };
+ },
- case 'remove':
- return remove(state, action.id);
+ remove(state, action) {
+ delete state[action.id];
+ },
- case 'clear':
- return clear(state);
+ clear(state, action) {
+ Object.keys(state).forEach(key => {
+ if (state[key].completed) {
+ delete state[key];
+ }
+ });
+ },
- case 'complete':
- return complete(state, action.id);
+ complete(state, action) {
+ state[action.id].completed = !state[action.id].completed;
+ },
+
+ edit(state, action) {
+ state[action.id].label = action.label;
+ }
}
+);
- return state;
-}
+export const filterReducer = createReducer('all', {
+ setFilter(state, action) {
+ return action.filter;
+ }
+});
-export function reducer(state: Store, action: any): Store {
- return {
- todos: todoReducer(state.todos, action),
- filter: 'all'
- };
-}
+export const reducer = combineReducers({
+ todos: todosReducer,
+ filter: filterReducer
+});
diff --git a/step2-06/exercise/src/reducers/pureFunctions.spec.ts b/step2-06/exercise/src/reducers/pureFunctions.spec.ts
deleted file mode 100644
index b3815cf..0000000
--- a/step2-06/exercise/src/reducers/pureFunctions.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { addTodo, complete } from './pureFunctions';
-import { Store } from '../store';
-
-describe('TodoApp reducers', () => {
- it('can add an item', () => {
- const state = {};
-
- const newState = addTodo(state, '0', 'item1');
-
- const keys = Object.keys(newState);
-
- expect(newState).not.toBe(state);
- expect(keys.length).toBe(1);
- expect(newState[keys[0]].label).toBe('item1');
- expect(newState[keys[0]].completed).toBeFalsy();
- });
-
- it('can complete an item', () => {
- const state = {};
-
- let newState = addTodo(state, '0', 'item1');
-
- const key = Object.keys(newState)[0];
-
- newState = complete(newState, key);
-
- expect(newState[key].completed).toBeTruthy();
- });
-});
diff --git a/step2-06/exercise/src/reducers/pureFunctions.ts b/step2-06/exercise/src/reducers/pureFunctions.ts
deleted file mode 100644
index e1954e5..0000000
--- a/step2-06/exercise/src/reducers/pureFunctions.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Store, FilterTypes } from '../store';
-
-export function addTodo(state: Store['todos'], id: string, label: string): Store['todos'] {
- return { ...state, [id]: { label, completed: false } };
-}
-
-export function remove(state: Store['todos'], id: string) {
- const newTodos = { ...state };
-
- delete newTodos[id];
-
- return newTodos;
-}
-
-export function complete(state: Store['todos'], id: string) {
- // Clone the todo, overriding
- const newTodo = { ...state[id], completed: !state[id].completed };
- return { ...state, [id]: newTodo };
-}
-
-export function clear(state: Store['todos']) {
- const newTodos = { ...state };
-
- Object.keys(state).forEach(key => {
- if (state[key].completed) {
- delete newTodos[key];
- }
- });
-
- return newTodos;
-}
-
-export function setFilter(state: Store['filter'], filter: FilterTypes) {
- return filter;
-}
diff --git a/step2-06/exercise/src/reducers/reducer.spec.ts b/step2-06/exercise/src/reducers/reducer.spec.ts
deleted file mode 100644
index 86b9272..0000000
--- a/step2-06/exercise/src/reducers/reducer.spec.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { createStore } from 'redux';
-import { reducer } from '.';
-import { actions } from '../actions';
-
-describe('reducers', () => {
- it('should add items', () => {
- // 1. Use Redux's createStore() to create a store. Pass in a reducer along with the initial state.
- //
- // 2. Call store.dispatch() with some action messages to indicate the kind of
- // action to perform (in this case, addTodo)
- //
- // 3. Assert with expect() on the resultant store.getState().todos
- });
-
- // Tests left for you to write:
- // - remove
- // - clear
- // - complete
-});