adding exercise for step 5

This commit is contained in:
Ken
2019-02-19 14:14:34 -08:00
parent a64d048706
commit be83489acc
23 changed files with 233 additions and 98 deletions

View File

@@ -101,10 +101,11 @@
</div>
</li>
<li class="Tile">
<a target="_blank" href="/step2-05/" class="Tile-link">
<div class="Tile-link">
Step 5<br />
Redux: Reducers
</a>
<div><a target="_blank" href="/step2-05/demo/">demo</a> | <a target="_blank" href="/step2-05/exercise/">exercise</a></div>
</div>
</li>
<li class="Tile">
<a target="_blank" href="/step2-06/" class="Tile-link">

View File

@@ -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<T>` 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<T>`
### 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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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<T>` code you have developed in Step 2.1
2. import the `Stack<T>` 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

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</body>
</html>

View File

@@ -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`.

8
step2-05/demo/index.html Normal file
View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</body>
</html>

View File

@@ -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;
}

View File

@@ -0,0 +1,17 @@
import { addTodo } from './pureFunctions';
import { Store } from '../store';
describe('TodoApp reducers', () => {
it('can add an item', () => {
const state = <Store['todos']>{};
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();
});
});

View File

@@ -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;
}

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</body>
</html>

View File

@@ -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());

View File

@@ -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;
}

View File

@@ -0,0 +1,19 @@
import { addTodo } from './pureFunctions';
import { Store } from '../store';
describe('TodoApp reducers', () => {
it('can add an item', () => {
const state = <Store['todos']>{};
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
});

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,6 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -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;
}

View File

@@ -1,20 +0,0 @@
import { addTodo } from './pureFunctions';
import { Store } from '../store';
describe('TodoApp reducers', () => {
it('can add an item', () => {
const state = <Store>{
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();
});
});

View File

@@ -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
};
}