diff --git a/index.html b/index.html
index 87fdd11..8d59589 100644
--- a/index.html
+++ b/index.html
@@ -101,10 +101,11 @@
-
+
Step 5
Redux: Reducers
-
+
+
diff --git a/step2-01/README.md b/step2-01/README.md
index 1416688..c18bcf3 100644
--- a/step2-01/README.md
+++ b/step2-01/README.md
@@ -1,6 +1,4 @@
-# Step 2.1
-
-## Introduction to Typescript
+# Step 2.1: Introduction to Typescript
In this exercise, we'll cover enough of the Typescript concepts to be productive with the React & Redux frameworks.
@@ -13,11 +11,11 @@ Topics in this Exercise will include:
- Spread and Destructuring
- Async / Await
-## Exercise
+# Exercise
Please complete all exercises inside the `exercise/src` folder unless otherwise specified in the exercises below. First, open up [Step2-01 exercise page](http://localhost:8080/step2-01/exercise/) to see the results while you're implementing things.
-### Modules
+## Modules
1. Open up file called `index.ts` in VS Code
@@ -33,7 +31,7 @@ Please complete all exercises inside the `exercise/src` folder unless otherwise
6. Import both the modules created in steps (4) and (5) and use the provided `log()` function to log it onto the page.
-### Types, Interfaces, and Classes
+## Types, Interfaces, and Classes
Create inside `index.ts`:
@@ -43,7 +41,7 @@ Create inside `index.ts`:
3. describe an object type with an interface
-### Generic
+## Generic
Inside `index.ts`, create a generic class for a `Stack` complete with a typed `pop()` and `push()` methods
@@ -51,7 +49,7 @@ Hint: the Javascript array already has `push()` and `pop()` implemented for you.
Be sure to use the provided `log()` to show the functionality of `Stack`
-### Spread and Destructure
+## Spread and Destructure
1. Note the following code in index.ts:
@@ -74,7 +72,7 @@ const obj2 = {
3. Using the destructuring syntax to retrieve the values for `{first, second, catcher}` from this new object created in step (2).
-### Async / Await
+## Async / Await
1. Note the following code in index.ts:
diff --git a/step2-02/README.md b/step2-02/README.md
index aa26cae..3e1011e 100644
--- a/step2-02/README.md
+++ b/step2-02/README.md
@@ -1,4 +1,4 @@
-# Step 2.2
+# Step 2.2: UI Fabric Component Library
UI Fabric is a Component Library that is developed to reflect the latest Microsoft design language. It is used in many Microsoft web applications and is developed in the open:
diff --git a/step2-03/README.md b/step2-03/README.md
index fdf3b45..eb3ec4c 100644
--- a/step2-03/README.md
+++ b/step2-03/README.md
@@ -1,4 +1,4 @@
-# Step 2.3
+# Step 2.3: Theming and Styling
Theming and Styling with UI Fabric. In this section, we will illustrate how to utilize some of the built-in theming and styling features right inside UI Fabric component library. UI Fabric exposes its own css-in-js library called `mergeStyles` that is very performant compared with other similar libraries.
@@ -18,7 +18,7 @@ with:
import { TeamsCustomizations } from '@uifabric/theme-samples';
```
-## Themes - Using Predefined Theme
+## Themes - Customized Theme
Create your own theme and apply the color here: https://developer.microsoft.com/en-us/fabric#/styles/themegenerator
diff --git a/step2-04/README.md b/step2-04/README.md
index b7197b4..4bb0b02 100644
--- a/step2-04/README.md
+++ b/step2-04/README.md
@@ -1,11 +1,15 @@
# Step 2.4
-Testing Typescript code with jest.
+Testing Typescript code with jest. jest is a test framework made by Facebook and is very popular in the React and the wider JS ecosystem. We will work on implementing simple unit tests here in this exercise.
+
+We won't go too deeply into mocks in this exercise, but feel free to play around with that yourself by reading up on this:
+
+https://jestjs.io/docs/en/mock-functions
# Exercise
1. copy the generic `Stack` code you have developed in Step 2.1
-2. import the `Stack` class into `stack.spec.ts`
+2. Run the tests by running `npm test` at the root of the bootcamp project
-3. Follow the instructions inside the file to complete the two tests
+3. Follow the instructions inside the `stack.spec.ts` file to complete the two tests
diff --git a/step2-04/exercise/index.html b/step2-04/exercise/index.html
new file mode 100644
index 0000000..be1bd8a
--- /dev/null
+++ b/step2-04/exercise/index.html
@@ -0,0 +1,8 @@
+
+
+
+ For this step, we look at unit testing. Run
+ npm test
+ in the command line.
+
+
diff --git a/step2-05/README.md b/step2-05/README.md
index aa8024f..4534ab4 100644
--- a/step2-05/README.md
+++ b/step2-05/README.md
@@ -1,3 +1,21 @@
-# Step 2.5
+# Step 2.5: Redux: Reducers
-Actions and Reducers
+Redux is used inside many large and complex applications because of its clarity and its predictability. It is really easy to debug and is easily extensible via its middleware architecture. In this exercise, we'll explore the heart of how Redux modifies state.
+
+Redux uses what is called a "reducer" to modify its state. It is called this because a "reducer" is what is used inside an `Array.reduce()`.
+
+A reducer is a **pure function** that receives some state and an action message as inputs and generates a copy of modified state as the output. Redux manages state changes mainly through reducers, and they are directly related to the UI, so for this exercise, we'll have to use jest tests to see the inner workings.
+
+From the official documentation site:
+
+> Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state.
+
+# Exercise
+
+1. First, take a look at the store interface in the `exercise/src/store/index.tsx` - note that the `Store` interface has two keys: `todos` and `filter`. We'll concentrate on the `todos` which is an object where the keys are IDs and the values are of an `TodoItem` type.
+
+2. Open `exercise/src/reducers/pureFunctions.ts` and fill in the missing body of the pure functions.
+
+3. Open `exercise/src/reducers/index.ts` and observe how those pureFunctions are called.
+
+4. Open `exercise/src/reducers/pureFunctions.spec.ts` and implement tests for the functions you wrote for `remove`, `complete`, and `clear`.
diff --git a/step2-05/demo/index.html b/step2-05/demo/index.html
new file mode 100644
index 0000000..be1bd8a
--- /dev/null
+++ b/step2-05/demo/index.html
@@ -0,0 +1,8 @@
+
+
+
+ For this step, we look at unit testing. Run
+ npm test
+ in the command line.
+
+
diff --git a/step2-05/src/index.tsx b/step2-05/demo/src/index.tsx
similarity index 100%
rename from step2-05/src/index.tsx
rename to step2-05/demo/src/index.tsx
diff --git a/step2-05/demo/src/reducers/index.ts b/step2-05/demo/src/reducers/index.ts
new file mode 100644
index 0000000..9dd64b3
--- /dev/null
+++ b/step2-05/demo/src/reducers/index.ts
@@ -0,0 +1,20 @@
+import { Store } from '../store';
+import { addTodo, remove, complete, clear } from './pureFunctions';
+
+export function reducer(state: Store['todos'], payload: any): Store['todos'] {
+ switch (payload.type) {
+ case 'addTodo':
+ return addTodo(state, payload.id, payload.label);
+
+ case 'remove':
+ return remove(state, payload.id);
+
+ case 'complete':
+ return complete(state, payload.id);
+
+ case 'clear':
+ return clear(state);
+ }
+
+ return state;
+}
diff --git a/step2-05/demo/src/reducers/pureFunctions.spec.ts b/step2-05/demo/src/reducers/pureFunctions.spec.ts
new file mode 100644
index 0000000..99adcfd
--- /dev/null
+++ b/step2-05/demo/src/reducers/pureFunctions.spec.ts
@@ -0,0 +1,17 @@
+import { addTodo } 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();
+ });
+});
diff --git a/step2-05/demo/src/reducers/pureFunctions.ts b/step2-05/demo/src/reducers/pureFunctions.ts
new file mode 100644
index 0000000..4e6f599
--- /dev/null
+++ b/step2-05/demo/src/reducers/pureFunctions.ts
@@ -0,0 +1,32 @@
+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) {
+ const newTodos = { ...state };
+ newTodos[id].completed = !newTodos[id].completed;
+
+ return newTodos;
+}
+
+export function clear(state: Store['todos']) {
+ const newTodos = { ...state };
+
+ Object.keys(state.todos).forEach(key => {
+ if (state.todos[key].completed) {
+ delete newTodos[key];
+ }
+ });
+
+ return newTodos;
+}
diff --git a/step2-05/src/store/index.ts b/step2-05/demo/src/store/index.ts
similarity index 100%
rename from step2-05/src/store/index.ts
rename to step2-05/demo/src/store/index.ts
diff --git a/step2-05/exercise/index.html b/step2-05/exercise/index.html
new file mode 100644
index 0000000..be1bd8a
--- /dev/null
+++ b/step2-05/exercise/index.html
@@ -0,0 +1,8 @@
+
+
+
+ For this step, we look at unit testing. Run
+ npm test
+ in the command line.
+
+
diff --git a/step2-05/exercise/src/index.tsx b/step2-05/exercise/src/index.tsx
new file mode 100644
index 0000000..46b98b1
--- /dev/null
+++ b/step2-05/exercise/src/index.tsx
@@ -0,0 +1,9 @@
+import { reducer } from './reducers';
+import { createStore, compose } from 'redux';
+
+declare var window: any;
+const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
+
+const store = createStore(reducer, {}, composeEnhancers());
+
+console.log(store.getState());
diff --git a/step2-05/exercise/src/reducers/index.ts b/step2-05/exercise/src/reducers/index.ts
new file mode 100644
index 0000000..9dd64b3
--- /dev/null
+++ b/step2-05/exercise/src/reducers/index.ts
@@ -0,0 +1,20 @@
+import { Store } from '../store';
+import { addTodo, remove, complete, clear } from './pureFunctions';
+
+export function reducer(state: Store['todos'], payload: any): Store['todos'] {
+ switch (payload.type) {
+ case 'addTodo':
+ return addTodo(state, payload.id, payload.label);
+
+ case 'remove':
+ return remove(state, payload.id);
+
+ case 'complete':
+ return complete(state, payload.id);
+
+ case 'clear':
+ return clear(state);
+ }
+
+ return state;
+}
diff --git a/step2-05/exercise/src/reducers/pureFunctions.spec.ts b/step2-05/exercise/src/reducers/pureFunctions.spec.ts
new file mode 100644
index 0000000..4cf3f4a
--- /dev/null
+++ b/step2-05/exercise/src/reducers/pureFunctions.spec.ts
@@ -0,0 +1,19 @@
+import { addTodo } 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();
+ });
+
+ // test remove, complete and clear
+});
diff --git a/step2-05/exercise/src/reducers/pureFunctions.ts b/step2-05/exercise/src/reducers/pureFunctions.ts
new file mode 100644
index 0000000..b94c02a
--- /dev/null
+++ b/step2-05/exercise/src/reducers/pureFunctions.ts
@@ -0,0 +1,38 @@
+import { Store, FilterTypes } from '../store';
+
+export function addTodo(state: Store['todos'], id: string, label: string): Store['todos'] {
+ // Write code to clone the state object while inserting a new TodoItem inside
+ // - the new object must be of the type TodoItem
+ // - the new state should be cloned using the spread syntax
+ // - return the new state
+
+ return state;
+}
+
+export function remove(state: Store['todos'], id: string) {
+ // Write code:
+ // - to clone the state object into new state object
+ // - remove and item from the new state by using the "delete" keyword
+ // - return the new state
+
+ return state;
+}
+
+export function complete(state: Store['todos'], id: string) {
+ // Write code:
+ // - to clone the state object into new state object
+ // - create a clone of the state[id] into a new item object
+ // - modify new state and set the id key to the value of the new item object
+
+ return state;
+}
+
+export function clear(state: Store['todos']) {
+ // Write code:
+ // - to clone the state object into new state object
+ // - loop through the keys of the new state object
+ // - remove those items inside that new state if the item is completed using the "delete" keyword
+ // - return the new state
+
+ return state;
+}
diff --git a/step2-05/exercise/src/store/index.ts b/step2-05/exercise/src/store/index.ts
new file mode 100644
index 0000000..221b5f4
--- /dev/null
+++ b/step2-05/exercise/src/store/index.ts
@@ -0,0 +1,14 @@
+export type FilterTypes = 'all' | 'active' | 'completed';
+
+export interface TodoItem {
+ label: string;
+ completed: boolean;
+}
+
+export interface Store {
+ todos: {
+ [id: string]: TodoItem;
+ };
+
+ filter: FilterTypes;
+}
diff --git a/step2-05/index.html b/step2-05/index.html
deleted file mode 100644
index 454cef5..0000000
--- a/step2-05/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/step2-05/src/reducers/index.ts b/step2-05/src/reducers/index.ts
deleted file mode 100644
index 7464bfe..0000000
--- a/step2-05/src/reducers/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Store } from '../store';
-import { addTodo, remove, complete } from './pureFunctions';
-
-let index = 0;
-
-export function reducer(state: Store, payload: any): Store {
- switch (payload.type) {
- case 'addTodo':
- return addTodo(state, payload.label);
-
- case 'remove':
- return remove(state, payload.id);
-
- case 'complete':
- return complete(state, payload.id);
- }
-
- return state;
-}
diff --git a/step2-05/src/reducers/pureFunctions.spec.ts b/step2-05/src/reducers/pureFunctions.spec.ts
deleted file mode 100644
index e151959..0000000
--- a/step2-05/src/reducers/pureFunctions.spec.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { addTodo } from './pureFunctions';
-import { Store } from '../store';
-
-describe('TodoApp reducers', () => {
- it('can add an item', () => {
- const state = {
- todos: {},
- filter: 'all'
- };
-
- const newState = addTodo(state, 'item1');
-
- const keys = Object.keys(newState.todos);
-
- expect(newState).not.toBe(state);
- expect(keys.length).toBe(1);
- expect(newState.todos[keys[0]].label).toBe('item1');
- expect(newState.todos[keys[0]].completed).toBeFalsy();
- });
-});
diff --git a/step2-05/src/reducers/pureFunctions.ts b/step2-05/src/reducers/pureFunctions.ts
deleted file mode 100644
index ddc9e23..0000000
--- a/step2-05/src/reducers/pureFunctions.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Store } from '../store';
-
-let index = 0;
-
-export function addTodo(state: Store, label: string): Store {
- const { todos } = state;
- const id = index++;
-
- return {
- ...state,
- todos: { ...todos, [id]: { label, completed: false } }
- };
-}
-
-export function remove(state: Store, id: string) {
- const newTodos = { ...state.todos };
-
- delete newTodos[id];
-
- return {
- ...state,
- todos: newTodos
- };
-}
-
-export function complete(state: Store, id: string) {
- const newTodos = { ...this.state.todos };
- newTodos[id].completed = !newTodos[id].completed;
-
- return {
- ...state,
- todos: newTodos
- };
-}