adding documentation

This commit is contained in:
Ken
2019-02-23 07:56:29 -08:00
parent 672099ccdd
commit 221485a940
59 changed files with 3448 additions and 102 deletions

35
docs/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Web Bootcamp/Day in the Web
## Purpose of this workshop
This repo is the content for a two day workshop focused on introducing you to the fundamentals of web development. By the end of these two days you will have better understanding of how we write and deliver features on the web. You will also get some hands on experience using everything from HTML, CSS and JavaScript to React, Redux and UI Fabric as we build a working application together.
## Who is this workshop for
This workshop starts as a very high level introduction to the core principles of the web: HTML, CSS and JavaScript. This section is targeted at new and experienced developers alike. The second day dives into more complex topics such as Typescript and state management within an application. The examples should be accessible to anyone, but you will get more out of the day if you have some foundational experience in programing or web technologies.
## Web Workshop Setup
#### Computer Setup
- Current version of [Node/NPM](https://nodejs.org/en/)
- Install [Git](https://git-scm.com/downloads)
- [VSCode Editor](https://code.visualstudio.com)
- Up to date browser with dev tools (some demos will be shown using Chrome)
### Getting Set Up
```bash
$ git clone tba
$ cd bootcamp
$ npm install
$ npm start
```
After the project loads in your browser, click on `Step 0 - HTML/CSS/JS`. Then go to the `Day 1: Step 0` page for the lesson and exercise instructions.
### Additional Resources
- [MDN Web Docs](https://developer.mozilla.org/en-US/)
- [React Docs](https://reactjs.org/docs/getting-started.html)
- [Thinking in React](https://reactjs.org/docs/thinking-in-react.html)

99
docs/assets/github.css Normal file
View File

@@ -0,0 +1,99 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}
.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}
.hljs-string,
.hljs-doctag {
color: #d14;
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}
.hljs-subst {
font-weight: normal;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}
.hljs-regexp,
.hljs-link {
color: #009926;
}
.hljs-symbol,
.hljs-bullet {
color: #990073;
}
.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}
.hljs-meta {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

28
docs/assets/step.css Normal file
View File

@@ -0,0 +1,28 @@
@import url(https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css);
@import url(./github.css);
body {
display: flex;
margin: 0;
padding: 0;
}
#app {
flex: 1;
padding: 50px;
}
#markdownReadme {
flex: 1;
background: #efefef;
padding: 50px;
margin: 0 auto;
}
pre {
padding: 15px;
border: 1px #999 solid;
border-radius: 2px;
background-color: white;
margin: 15px 0;
}

File diff suppressed because one or more lines are too long

View File

@@ -2,5 +2,5 @@
<html>
<body>
<div id="app"></div>
<script src="../playground/playground.js"></script></body>
<script src="../playground/playground.js"></script><script src="../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

209
docs/step1-04/README.md Normal file
View File

@@ -0,0 +1,209 @@
# Introduction To React
In our last example we saw how we could take a static HTML page and turn it into an interactive page with some buttons and their `onclick` handlers.
In this example we'll see how React turns that paradigm completely around. With React, the entire DOM is generated and maintained by JavaScript, directly inside the browser. This makes it easier to assemble your application out of reusable pieces, maintain state within a component, and pass data between them.
## Demo
In this demo we'll be creating a simple counter that will display a count and increment on click.
### The App
This is the starting point of our React application. It is a component just like the other ones we will be building, but this component will only ever be used once, to render the application. Here's how our app starts out. Let's walk through each line:
```jsx
import React from 'react';
export class App extends React.Component {
render() {
return (
<div>
<h2>My App</h2>
</div>
);
}
}
```
- **import React from 'react';** - This is how we [import modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) in Javascript. This line creates a variable in this file called `React` that is equal to the default `export` of the `react` npm module.
- **export class App** - Just like react exports code, our App component is nothing more than an exported "App" class. This allows us to import the class into other files
- **extends React.Component** - A JavaScript class is similar to other programming languages (it's a collection of methods and properties). Classes can also be extended, so when we create a React component class, we always extend the base React.Component class. Note that this `Component` class is coming from the `React` variable imported up top.
> Note that `<any, any>` is necessary for Typescript which we will touch on later
- **render()** - One of the methods defined by React.Component is the `render()` method. This is a function that defines the HTML the component is going to render.
- **return** - Remember that functions can return values in addition to side effects, and this component is no different.
- **Inside of the return?** It's HTML! Actually, it's JSX, but with very few exceptions you can treat it like HTML. A few key differences:
1. Since 'class' is a reserved word in JavaScript, you will need to use className on your HTML tags `<div className="foo">`
2. We can use custom HTML tags created by these render functions `<div><MyControl>hi</MyControl></div>`
3. Controls can be self closing `<div><MyControl text='hi' /></div>`
4. You can use JavaScript inside of JSX! If you declare `const name = 'Micah';` inside the render function, you can use that variable inside of your JSX `<div>{name}</div>` or `<div><MyControl text={name} /></div>`. Works with functions, loops, conditionals as well.
### index.jsx
This is the file that places your App onto the page.
```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
ReactDOM.render(<App />, document.getElementById('app'));
```
- **import ReactDOM from "react-dom";** - We've seen React imported before, but now we're also grabbing ReactDom from a package called "react-dom".
> Note that this casing is intentional. NPM packages are kabab-case, exported items are usually camelCase or PascalCase. PascalCase is usually used for 'proper noun' exports. ProjectName, ComponentName etc.
- **import { App } from "./App";** - If we had exported our app like this: `export default class extends React.Component`, this line would look like the lines above - `import App from "./App";`. But React convention is to use named exports, which can easily be extracted like this `{ App }`.
> This notation is called [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring), and it's awesome!
- **ReactDOM.render...** This line calls the render function inside of ReactDOM and attaches our `<App />` component to the element with `id=app`. Take a peak in the index.html file. Shouldn't be too hard to find it.
### Counter Component
In this example we'll start with an already scaffolded out control. The goal of our counter is to keep track of how many times the counter button is clicked. In the past JavaScript demo we might grab a reference to `document.querySelector('.counter')` and then manually increment the number we find there. While using the DOM as you data store works, it's REALLY hard to scale past the most basic demo.
React solves this by allowing each control to specify its own data store, called 'state'. We can reference values in state when we render our UI, and we can also update state over the lifetime of our application.
#### Adding State
JavaScipt uses a `constructor` method to instantiate each copy of a class. So for class based controls, this is where we specify state.
```js
constructor(props) {
super(props);
this.state = {
counter: 0
};
}
```
- The constructor takes in the component props (values passed into the control).
- the [super()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super) function is called to gain access to some functionality in React.Component
- Now we can define any state variables we want to use in the control, and give them a default value. This counter value can now be accessed via `this.state.counter`. We can also update state by calling `this.setState({counter: 1})`
#### Using [object destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring) for props and state
Both props are state are JavaScript objects. They have a bunch of key value pairs in them which you can access via `this.props.foo` or `this.state.bar`. Sometimes they have **MANY** values inside of them which you need access to. You could do this:
```js
let cat = this.props.cat;
let dog = this.props.dog;
let bird = this.props.bird;
let pig = this.props.pig;
let cow = this.props.cow;
```
> Note that we access props and state on `this`, which is how you reference all of the class properties and methods
But this is verbose and repetative. Instead you can use destructuring to turn this into a one liner.
```js
let { cat, dog, bird, pig, cow } = this.props;
```
So even though this isn't 100% necessary today, it does future proof our code if we add more props or state later. So let's add this inside of the render method, but above the return:
```js
const { counter } = this.state;
const { text } = this.props;
```
#### Adding JSX
```jsx
return (
<div>
{text}: {counter}
<button>Click</button>
</div>
);
```
Each JSX return needs to be a single element, so start with a wrapping `<div>`. Inside of that we can add the `text` we get from `this.props`, then after a colon, the `counter` we pulled in from `this.state`. This will render as the string `My Text Prop: 0`. After that let's add a button we'll use later. For now, we're going to see how we can use this in our app.
#### Updating the App to use Counters
Before we can use our `Counter`, we need to import it into the App file
```js
import { Counter } from './components/Counter';
```
Now that we have access to `Counter`, we can add it to the App just as if it were an HTML element.
```jsx
return (
<div>
<h2>My App</h2>
<Counter text="Chickens" />
<Counter text="Ducks" />
</div>
);
```
> Note the capitalization of Counter. HTML might not be case sensative, but JSX is! A common practice is to use the capitalized versions of HTML elements to name their JSX counterpart. Button, Select, Label, Form etc.
### Exploring Component Props
Now that we've got two Counters on our page, we can see that the string passed into the `text` attribute got passed into our Counter, and rendered on the page. Being able to pass values into controls make them more flexible and reusable. Props can be strings, numbers, booleans, and even arrays and objects.
```jsx
<MyComponent
open={false}
count={5}
text="Hi there"
items={['cat', 'dog', 'bird']}
config={{
start: 1,
end: 10,
autoStart: true
}}
/>
```
> Note that all non string values are passed through as JavaScript by wrapping it in `{}`
### Writing our Button Click
Our next step is to wire up the button to increment the `counter` in our component state. This will very similar to what we did in step 3, but instead of placing the function in a script tag, we can create it as a class method, and keep it out of the global scope.
> By convention we place methods below render, and private methods (those for internal use only) are prefixed with an underscore.
```jsx
_onButtonCLick = () => {
this.setState({ counter: this.state.counter + 1 });
};
```
This function will update our component state, incrementing our counter value by 1. Note that setState only affects the values of keys we have listed.
> Note that this isn't exactly a method, but a property that is equal to a [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). This works just as well as `onButtonClick() { }`, but doesn't require extra binding up in the constructor.
Now that we have a function to increment out count, all that we have left is to connect it to our button.
```jsx
<button onClick={this._onButtonCLick}>Click</button>
```
> Note the syntax is a bit different than HTML. `onclick="funcName()"` vs `onClick={this.funcName}`
## Bonus: Using a Button component
Buttons are one of the most common components to write. They help abstract common styling, add icons or other decorations, and increase functionality (menu buttons etc). Using an existing Button component is as easy as importing it `import {Button} from './Button';` and replacing `<button></button>` with `<Button></Button>`. Lets take a quick look at button to see how it came together.
```jsx
import React from 'react';
import './Button.css';
export const Button = props => {
return (
<button className="Button" onClick={props.onClick}>
{props.children}
</button>
);
};
```
- All controls need to import React (don't worry, only 1 copy ever gets into your app)
- Importing CSS files into the component means that the CSS is only loaded if the component is used
- React components can be created as a class **or** as a function. In this function, props are passed in as a function parameter.
> Until recently, you could only access state in class based components. But with the advent of [hooks](https://reactjs.org/docs/hooks-intro.html) you can create stateful function components.
- Since this is a function, we don't have any methods, including `render()`. Just return your JSX as you would in a class based component.
- `props.children` is anything passed between the opening and closing tags `<Button>I'm children</Button>`

View File

@@ -2,5 +2,5 @@
<html>
<body>
<div id="app"></div>
<script src="../../step1-04/final/step1-04/final.js"></script></body>
<script src="../../step1-04/final/step1-04/final.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

87
docs/step1-05/README.md Normal file
View File

@@ -0,0 +1,87 @@
# Thinking In React: Hierarchy and Building a Static Version
## Demo
To start off our Todo application we are going to follow the steps outline in [Thinking in React](https://reactjs.org/docs/thinking-in-react.html). The first step of the process is to break our application into a component hierarchy. For this app, we're going to keep it simple and just use four parts.
- TodoHeader
- TodoList
-TodoListItem
- TodoFooter
We could go a lot deeping creating buttons, inputs and checkboxes, but this is a great place start. Often you'll want to start with a single large control, and then start breaking it up into smaller pieces.
### TodoApp
```jsx
import React from 'react';
import { TodoFooter } from './components/TodoFooter';
import { TodoHeader } from './components/TodoHeader';
import { TodoList } from './components/TodoList';
export class TodoApp extends React.Component {
render() {
return (
<div>
<TodoHeader />
<TodoList />
<TodoFooter />
</div>
);
}
}
```
We'll start off with all of the file scaffolded and imported into our App. This will let us dive right into each control and see updates quickly.
### TodoHeader
Our objective is to create a static version of our application, so we'll copy over the entire header tag, minus any function calls we may have added.
> Note that since this is React we had to change `class` to `className`, otherwise nothing changes
```jsx
return (
<header>
<h1>todos</h1>
<div className="addTodo">
<input className="textfield" placeholder="add todo" />
<button className="submit">Add</button>
</div>
<nav className="filter">
<button className="completed">all</button>
<button>active</button>
<button>completed</button>
</nav>
</header>
);
```
### TodoListItem
Anytime you see repeated complex elements, that is usually a sign to create a new component. With a few props you can typically abstract all of those elements into a single component. This is certainly the case with Todos items.
```jsx
return (
<li className="todo">
<label>
<input type="checkbox" /> Todo 1
</label>
</li>
);
```
> Note that I've removed the title span as it was only needed to make targeting that text easier
## Exercise
### TodoFooter
1. Update the TodoFooter component, copying over the `<footer>` tag and all of its children
2. Remove any `onclick` properties, and change `class` to `className`
### TodoList
1. Update the TodoList component like you did with the footer.
2. Import TodoListItem and add 4 of them inside of the `<ul>`
3. Bonus points for using a [`for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration) loop or using [`map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) to create 4 list items based on the array `[1,2,3,4]`

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-05/demo/step1-05/demo.js"></script></body>
<script src="../../step1-05/demo/step1-05/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-05/exercise/step1-05/exercise.js"></script></body>
<script src="../../step1-05/exercise/step1-05/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-05/final/step1-05/final.js"></script></body>
<script src="../../step1-05/final/step1-05/final.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

20
docs/step1-06/README.md Normal file
View File

@@ -0,0 +1,20 @@
## demo
add state to AppTodo
pass to header and list
### header
- pull filter from props
- update buttons with classNames for active
- Add value/onChange to input
### TodoList
- write map to loop through items, pass key and todos
## exercise
update footer to include todos
add item count and item(s)
update ListItem, pull in props, use label/completed already passed in

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-06/demo/step1-06/demo.js"></script></body>
<script src="../../step1-06/demo/step1-06/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-06/exercise/step1-06/exercise.js"></script></body>
<script src="../../step1-06/exercise/step1-06/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-06/final/step1-06/final.js"></script></body>
<script src="../../step1-06/final/step1-06/final.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

43
docs/step1-07/README.md Normal file
View File

@@ -0,0 +1,43 @@
already done
Types in header
TodoApp methods
filteredTodos in List
demo
## app
Add Types to TodoApp
change 'filter' state value to demonstrate
## List (open list next to app)
Demo TodoApp.types
Add Types in List
## App
Back to App, add complete={this.\_complete} to Todolist - show types, change complete to 'false', filter
## List
add complete, pass to item (prop drilling)
## List Item (move List Item into App window)
TodoListItemProps, extend, id, complete (possible abstraction)
add props, add complete to List item
## List
Demo how you can't add random things to TodoListItem or item's this.props now
exercise
Add types to footer
Add onClick to button
Add types to header
Add setFilter to filter buttons
write onAdd function
place onAdd to submit button

View File

@@ -3,5 +3,5 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-07/demo/step1-07/demo.js"></script></body>
<script src="../../step1-07/demo/step1-07/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-07/exercise/step1-07/exercise.js"></script></body>
<script src="../../step1-07/exercise/step1-07/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -3,7 +3,7 @@
<link rel="stylesheet" href="./src/style.css" />
<body>
<div id="app"></div>
<script src="../../step1-07/final/step1-07/final.js"></script></body>
<script src="../../step1-07/final/step1-07/final.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

204
docs/step2-01/README.md Normal file
View File

@@ -0,0 +1,204 @@
# Step 2.1: Introduction to Typescript
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
In this step, we'll cover enough of the Typescript concepts to be productive with the React & Redux frameworks.
Topics in this step will include:
- ES modules
- Basic Types
- Interfaces & Classes
- Basic Generics
- Spread and Destructuring
- Async / Await
## Modules
Historically, JS is only executed in browser. The code all had to be loaded from `<script>` tags. Since the introduction of node.js, the JS community needed a way to scale beyond just single script files. Other language support the notion of modules. There are many JS modularity standards today.
The most important ones to know about are:
- commonjs - Node.js's standard to support modules
- synchronous
- require() function, can be dynamically called in the course of a program
- ESM (Ecmascript module) - language level support
- statically analyzable and synchronous
- dynamic and asynchronous support via `import()` that returns a promise
## Typescript Types
Refer to the `demo/src` for some examples of some of the types avaible in TS that benefits a React developer.
## Spread Syntax
Spread syntax allows for quick way to clone and concatenate objects and arrays. This syntax is seen a lot inside React props and Redux reducers.
To shallow copy something:
```ts
const cloned = { ...obj };
```
To shallow copy and add / overwrite a key:
```ts
const overridden = { ...obj, key: value };
```
You can have an expression to calculate this key if it is dynamic:
```ts
const overridden = { ...object, [key + '-suffix']: value };
```
## Destructuring
Destructuring is a concise way to take properties out of an object or array:
```ts
const obj = { foo: 1, bar: 2 };
const { foo, bar } = obj;
// foo = 1, bar = 2
```
Same thing for array:
```ts
const arr = [1, 2];
const [foo, bar] = arr;
// foo = 1, bar = 2
```
You can separate an item and the rest of the object with destructuring:
```ts
const obj = { a: 1, b: 2, c: 3, d: 4 };
const { a, ...rest } = obj;
// a = 1, rest = {b: 2, c: 3, d: 4}
```
# Promise
A promise is an object that represent work that will be completed later, asynchronously. It is a chainable so writing async code is maintainable. Typically legacy async code uses callback to let the caller have control over what to do after the task has been completed.
```ts
const aPromise = new Promise((resolve, reject) => {
// do something async and call resolve() to let promise know it is done
setTimeout(() => {
// setTimeout will call this method after 1s, simulating async operation like network calls
resolve();
}, 1000);
});
```
The promise object exposes a `then()` function that is chainable. `catch()` is present that catches all exceptions or `reject()` calls:
```ts
const aPromise = Promise.resolve('hello world'); /* ... just an example promise */
aPromise
.then(result => {
return makeAnotherPromise();
})
.then(result => {
return makeYetAnotherPromise();
})
.catch(err => {
console.error(err);
});
```
# Async / Await
This syntax is inspired heavily by C#'s async / await syntax. To write an async function write it like this:
```ts
async function someFunctionAsync() {
// Inside here, we can await on other async functions
const result = await someOtherFunctionAsync();
return result + ' hello';
}
```
All functions that are marked `async` return a `Promise` automatically. This previous example returned a `Promise<string>`, and can be used like this:
```ts
someFunctionAsync().then(result => {
console.log(result);
});
```
# 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
1. Open up file called `index.ts` in VS Code
2. Create another module file called `fibonacci.ts`
3. Inside the file from (step 2), write a function called `fib(n)` that takes in a number and returns a the n-th Fibonacci number - be sure the specify the type of n
> HINT: fib(n) = fib(n - 1) + fib(n - 2); fib(n <= 1) = n;
4. Export `fib(n)` as a **named export**
5. Export another const variable as a **default export**
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
Create inside `index.ts`:
1. a type alias for string union type describing the states of Red-Green-Yellow traffic light: `type TrafficLight = ???`
2. describe a type of car with an interface: `interface Car { ... }`
## Generic
Inside `stack.ts`, create a generic class for a `Stack<T>` complete with a typed `pop()` and `push()` methods
> Hint: the Javascript array already has `push()` and `pop()` implemented for you. That can be your backing store.
Be sure to use the provided `log()` to show the functionality of `Stack<T>`
## Spread and Destructure
1. Note the following code in index.ts:
```ts
const obj1 = {
first: 'who',
second: 'what',
third: 'dunno',
left: 'why'
};
const obj2 = {
center: 'because',
pitcher: 'tomorrow',
catcher: 'today'
};
```
2. Now create a one-liner using the spread syntax `{...x, ...y}` to create a new variable that combines these two objects.
3. Using the destructuring syntax to retrieve the values for `{first, second, catcher}` from this new object created in step (2).
## Async / Await
1. Note the following code in index.ts:
```ts
function makePromise() {
return Promise.resolve(5);
}
```
2. call `makePromise()` with the `await` syntax and log the results using the provided `log()` function
3. create a new function that uses the `async` keyword to create an async function. Make an await call to `makePromise()` and return the results

View File

@@ -1,6 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script src="../../step2-01/demo/step2-01/demo.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
Nothing to show here, just look at your console window for output. Hit F12 to open console window.
</div>
<script src="../../step2-01/demo/step2-01/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -1,20 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
outline: 0;
}
textarea {
width: 100vh;
height: 100vh;
border: none;
}
</style>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body>
<div id="app"></div>
<script src="../../step2-01/exercise/step2-01/exercise.js"></script></body>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
Nothing to show here, just look at your console window for output. Hit F12 to open console window.
</div>
<script src="../../step2-01/exercise/step2-01/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -1,11 +1,3 @@
const app = document.getElementById('app');
const textarea = document.createElement('textarea');
textarea.setAttribute('readonly', 'true');
app.appendChild(textarea);
function log(results: string) {
textarea.innerText += results;
}
// Some setup code for exercises
const obj1 = {
first: 'who',
@@ -24,11 +16,13 @@ function makePromise() {
return Promise.resolve(5);
}
// Do the exercises here, output your results with "log()" function
// Do the exercises here, output your results with "console.log()" function
// ...
log('hello world');
console.log('hello world');
(async () => {
async function run() {
// Place your code for the async / await exercise here
// ...
})();
}
run();

View File

@@ -93,7 +93,7 @@
/*! no static exports found */
/***/ (function(module, exports) {
eval("var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nvar __generator = (this && this.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (_) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nvar _this = this;\nvar app = document.getElementById('app');\nvar textarea = document.createElement('textarea');\ntextarea.setAttribute('readonly', 'true');\napp.appendChild(textarea);\nfunction log(results) {\n textarea.innerText += results;\n}\n// Some setup code for exercises\nvar obj1 = {\n first: 'who',\n second: 'what',\n third: 'dunno',\n left: 'why'\n};\nvar obj2 = {\n center: 'because',\n pitcher: 'tomorrow',\n catcher: 'today'\n};\nfunction makePromise() {\n return Promise.resolve(5);\n}\n// Do the exercises here, output your results with \"log()\" function\n// ...\nlog('hello world');\n(function () { return __awaiter(_this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n return [2 /*return*/];\n });\n}); })();\n\n\n//# sourceURL=webpack:///./step2-01/exercise/src/index.ts?");
eval("var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nvar __generator = (this && this.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (_) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\n// Some setup code for exercises\nvar obj1 = {\n first: 'who',\n second: 'what',\n third: 'dunno',\n left: 'why'\n};\nvar obj2 = {\n center: 'because',\n pitcher: 'tomorrow',\n catcher: 'today'\n};\nfunction makePromise() {\n return Promise.resolve(5);\n}\n// Do the exercises here, output your results with \"console.log()\" function\n// ...\nconsole.log('hello world');\nfunction run() {\n return __awaiter(this, void 0, void 0, function () {\n return __generator(this, function (_a) {\n return [2 /*return*/];\n });\n });\n}\nrun();\n\n\n//# sourceURL=webpack:///./step2-01/exercise/src/index.ts?");
/***/ })

32
docs/step2-02/README.md Normal file
View File

@@ -0,0 +1,32 @@
# Step 2.2: UI Fabric Component Library
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
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:
https://github.com/officedev/office-ui-fabric-react
Documentation can be found here:
https://developer.microsoft.com/en-us/fabric/#/components
## Learn about Components and How to Look up Documentation
- Stack
- Default Button / Primary Button
# Exercise
1. Open up the [Documentation for DefaultButton](https://developer.microsoft.com/en-us/fabric/#/components/button)
2. Open up the TSX files inside `components/`
3. Replace the DOM tags with Fabric components in those TSX files with these components:
- Stack
- DefaultButton
- Checkbox
- TextField
- Pivot (for the filter)
# Bonus Exercise
GO WILD! There are so many components from the Fabric library! Try to put some components in the exercise component files.

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-02/demo/step2-02/demo.js"></script></body>
<script src="../../step2-02/demo/step2-02/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-02/exercise/step2-02/exercise.js"></script></body>
<script src="../../step2-02/exercise/step2-02/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

123
docs/step2-03/README.md Normal file
View File

@@ -0,0 +1,123 @@
# Step 2.3: Theming and Styling
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
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.
A CodePen that illustrates what `mergeStyles` does: https://codepen.io/dzearing/pen/jGdgrE?editors=1011
There are three areas that we should focus on in this step:
1. Theming with Fabric
2. CSS-in-JS with mergeStyles
3. Customizing Fabric Components `styles` prop
## 1. Theming with Fabric
- Fabric applies themes by propagating the theme down the children through the React Context mechanism
- It is applied with the `<Customizer>` component
- There are some predefined themes within Fabric already, like Fluent (which will become the default in the next major), MDL2, Azure, and some other sample themes like Teams.
- Take a look at `demo/src/components/TodoApp.tsx`
## 2. CSS-in-JS with mergeStyles
- `mergeStyles` is a styling library that creates CSS class from styles that are expressed in JS
- These classes can be passed into `className` prop of any component like `<div>`
- This library replaces the need to import CSS stylesheets because they are bundled as normal JS code
- Take a look at `demo/src/components/TodoApp.tsx`
## 3. Customizing Fabric Controls
- calling `mergeStyles` is time consuming and very static
- Fabric components expose a `styles` prop (not to be confused with the React built-in one called `style`)
- You can use intellisense to discover which parts of the component you can to customize
- You can even use a style function to change the style based on some style prop
- Take a look at these customizations in `demo/src/components/TodoHeader.tsx`
# Exercises
## Themes - Using Predefined Theme
Apply some included and predefined themes from the UI Fabric package inside the `/step2-03/exercise/src/components/TodoApp.tsx`. Do this by replacing:
```ts
import { FluentCustomizations } from '@uifabric/fluent-theme';
```
with:
```ts
import { TeamsCustomizations } from '@uifabric/theme-samples';
```
## Themes - Customized Theme
Create your own theme and apply the color here: https://developer.microsoft.com/en-us/fabric#/styles/themegenerator
```ts
import { loadTheme } from 'office-ui-fabric-react';
loadTheme({
palette: {
themePrimary: '#0078d4',
themeLighterAlt: '#eff6fc',
themeLighter: '#deecf9',
themeLight: '#c7e0f4',
themeTertiary: '#71afe5',
themeSecondary: '#2b88d8',
themeDarkAlt: '#106ebe',
themeDark: '#005a9e',
themeDarker: '#004578',
neutralLighterAlt: '#f8f8f8',
neutralLighter: '#f4f4f4',
neutralLight: '#eaeaea',
neutralQuaternaryAlt: '#dadada',
neutralQuaternary: '#d0d0d0',
neutralTertiaryAlt: '#c8c8c8',
neutralTertiary: '#c2c2c2',
neutralSecondary: '#858585',
neutralPrimaryAlt: '#4b4b4b',
neutralPrimary: '#333333',
neutralDark: '#272727',
black: '#1d1d1d',
white: '#ffffff'
}
});
```
1. Delete the `Customizer` component
2. Paste in this code in the `TodoApp.tsx` before the `TodoApp` component definition
3. Play around with the values and use intellisense to discover the `ITheme` type within VS Code
## CSS in JS with MergeStyles
The styling library name is glamorous nor does it bring about emotion, but it is very quick and lightweight. `MergeStyles` turns CSS Rules into CSS class names to be applied to the components.
1. Try applying a merged style `className` as a prop inside `TodoApp`
```tsx
import { mergeStyles } from 'office-ui-fabric-react';
const className = mergeStyles({
backgroundColor: 'red',
selectors: {
':hover': {
backgroundColor: 'blue'
}
}
});
```
2. Try to give a few components extra padding
## Customize the Fabric Components
1. Open `exercise/src/components/TodoFooter.tsx`
2. Find the `<DefaultButton>` and insert a `styles` prop
3. Try to customize this with a styles object (let the Intellisense of VS Code guide you on what you can use to customize)
4. Try to customize this with a styles function

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-03/demo/step2-03/demo.js"></script></body>
<script src="../../step2-03/demo/step2-03/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-03/exercise/step2-03/exercise.js"></script></body>
<script src="../../step2-03/exercise/step2-03/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

33
docs/step2-04/README.md Normal file
View File

@@ -0,0 +1,33 @@
# Step 2.4
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
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.
https://jestjs.io/
- Multi-threaded and isolated test runner
- Provides a "fake" browser environment if needed (window, document, DOM, etc).
- Snapshots: show API or large object changes along side code changes in pull requests
- Code coverage is integrated (--coverage)
- Very clear error messages of where the test failures occur
# Demo
In this repo, we can start an inner loop development of tests with the command: `npm test`
Take a look at code inside `demo/src`:
1. `index.ts` is exports a few functions for a counter as well as a test for squaring numbers but demonstrates out jest uses mocks
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 on save to test file changes as well as source code changes under `src`
# Exercise
1. Run the tests by running `npm test` at the root of the bootcamp project
2. Look at the `stack.ts` for a sample implementation of a stack
3. Follow the instructions inside the `stack.spec.ts` file to complete the two tests

View File

@@ -1,8 +1,14 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
<script src="../../step2-04/demo/step2-04/demo.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</div>
<script src="../../step2-04/demo/step2-04/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

55
docs/step2-05/README.md Normal file
View File

@@ -0,0 +1,55 @@
# Step 2.5: Redux: Reducers
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
Redux is an implementation of the Flux architectural pattern:
![Flux Diagram](../assets/flux.png)
Ideally React gives us a mental model of:
```
f(data) => view
```
And it renders when data changes. However, in the real world, data is shaped like a tree and view is shaped like a tree. They don't always match. There are many approaches to Flux, but Redux promotes the data into a singleton state tree that listens for messages to manipulate the state as appropriate.
## View
These are the React Components that consume the store as its data. There is a special way Redux will map its data from the state tree into the different React Components. The Components will know to re-render when these bits of state are changed.
## Store
This is a singleton state tree. The state tree is immutable and needs to be re-created at every action. This helps connected views to know when to update itself - just doing a simple reference comparison rather than a deep comparison.
## Action
Actions are messages to be dispatched to the store to let reducers to change (replace reference of) the state tree.
## Reducers
These are simple stateless, pure functions that takes state + action message and returns a copy of state some modifications according to the action message type and payload.
## Dispatcher
There is a single dispatcher. It simply informs of the store of all the actions that needs to be performed. Certain middleware can be applied to the store and the dispatcher's job is to dispatch the message through all the middleware layers.
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 fill in the missing case statements for the switch of `action.type`.
4. Open `exercise/src/reducers/pureFunctions.spec.ts` and implement tests for the functions you wrote for `remove`, `complete`, and `clear`.

View File

@@ -1,8 +1,14 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
<script src="../../step2-05/demo/step2-05/demo.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</div>
<script src="../../step2-05/demo/step2-05/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -1,8 +1,14 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
<script src="../../step2-05/exercise/step2-05/exercise.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</div>
<script src="../../step2-05/exercise/step2-05/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

20
docs/step2-06/README.md Normal file
View File

@@ -0,0 +1,20 @@
# Step 2.6
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
Redux: Dispatching Actions and Examining State.
In this step, we learn about `dispatch` and `getState()`. Dispatching action messages to the store is the only means by which to inform the reducers to modify the shared state tree.
We also see how we may compose the reducers according to the shape.
If you want a really neat UI to show what the store looks when actions are dispatched to the store, use this Chrome extension:
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd
# Exercise
1. open the `exercise/src/reducers/reducer.spec.ts`
2. Follow the instructions to fill out the reducer tests
3. Run the tests with `npm test`

View File

@@ -1,8 +1,14 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
<script src="../../step2-06/demo/step2-06/demo.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</div>
<script src="../../step2-06/demo/step2-06/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

View File

@@ -1,8 +1,14 @@
<!DOCTYPE html>
<html>
<body>
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
<script src="../../step2-06/exercise/step2-06/exercise.js"></script></body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app">
For this step, we look at unit testing. Run
<pre>npm test</pre>
in the command line.
</div>
<script src="../../step2-06/exercise/step2-06/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

23
docs/step2-07/README.md Normal file
View File

@@ -0,0 +1,23 @@
# Step 2.7
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
Connect store to view with `react-redux`. `connect()` is used to turn Redux store and dispatch functions into props inside React components. The state and action dispatchers are passed along with a `<Provider>` component.
```ts
const NewComponent = function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(OldComponent);
```
The `connect()` function takes in a few functions that maps some portion of the state tree and dispatcher functions as props. It is a **higher order function** meaning that the return value of `connect()` is a function that decorates OldComponents into a NewComponent with all the mapped props.
This `mapStateToProps` function selects out portions of the state tree. This function informs the connected view when to re-render based on a shallow comparison from previous state.
# Exercise
1. open up `exercise/src/index.tsx` and wrap `<TodoApp>` with `<Provider>` as instructed in the comment
2. open up `exercise/src/components/TodoFooter.tsx` and erase the "nullable" type modifier (i.e. the ?) in the interface definition of `TodoFooterProps`
3. uncomment the bottom bits of code and fill in the implementation for `mapStateToProps()` and `mapDispatchToProps()` - feel free to use `TodoListItem.tsx` as a guide
4. do steps 2 and 3 for the `TodoHeader.tsx` file

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-07/demo/step2-07/demo.js"></script></body>
<script src="../../step2-07/demo/step2-07/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-07/exercise/step2-07/exercise.js"></script></body>
<script src="../../step2-07/exercise/step2-07/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

70
docs/step2-08/README.md Normal file
View File

@@ -0,0 +1,70 @@
# Step 2.8
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
Combine Reducers
This lesson is just a helper to make the process of writing reducers use less boilerplate code. This step briefly introduces to a world of helpers in writing Redux code.
Our Redux store so far has this shape, roughly:
```js
const state = {
todos: {
id0: {
label: 'hello',
completed: false
},
id1: {
label: 'world',
completed: true
}
},
filter: 'all'
};
```
As the application grows in complexity, so will the shape of the store. Currently, the store captures two separate but related ideas: the todo items and the selected filter. The reducers should follow the shape of the store. Think of reducers as part of the store itself and are responsible to update a single part of the store based on actions that they receive as a second argument. As complexity of state grows, we split these reducers:
```ts
function todoReducer(state: Store['todos'] = {}, action: any) {
// reduce on the todos part of the state tree
}
function filterReducer(state: Store['filter'] = 'all', action: any) {
// reduce on the filter flag
}
// Then use the redux-provided combineReducers() to combine them
export const reducer = combineReducers({
todos: todoReducer,
filter: filterReducer
});
```
# Take a peek at useful helpers and middleware created by community are:
- immer: https://github.com/mweststrate/immer - improves ergonomics of working with immutables by introducing the concept of mutating a draft
- redux-starter-kit: https://github.com/reduxjs/redux-starter-kit - help address common concerns of Redux in boilerplate and complexity
# Exercise
1. open up `exercise/src/reducers/index.ts`
2. implement the `filterReducer` function with a switch / case statement - it is contrived to have a switch case for ONE condition, but serves to be a good exercise here
3. replace the export reducer function with the help of the `combineReducer()` function from `redux`
# Bonus Exercise
The Redux team came up with `redux-starter-kit` to address a lot of boilerplate concerns. They also embed the immer library to make it nicer to write reducer functions. So, let's try out `immer`! Look at this example: https://github.com/mweststrate/immer#reducer-example
1. import immer into the `exercise/src/reducers/pureFunction.ts` file
2. replace the implementation of the pure functions with the help of immer's `produce()`
3. run `npm test` in the root folder to see if it still works!
4. look at the web app to make sure it still works!

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-08/demo/step2-08/demo.js"></script></body>
<script src="../../step2-08/demo/step2-08/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-08/exercise/step2-08/exercise.js"></script></body>
<script src="../../step2-08/exercise/step2-08/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

21
docs/step2-09/README.md Normal file
View File

@@ -0,0 +1,21 @@
# Step 2.9
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
Redux Thunk middleware for actions with service calls. The documentation is here:
https://github.com/reduxjs/redux-thunk
Action creators are a natural place to put service calls. Redux thunk middleware passes in the `dispatch()` and `getState()` from the store into the action creators. This allows the action creator itself to dispatch different actions in between async side effects. Combined with the async / await syntax, coding service calls is a cinch!
Most of the time, in a single-page app, we apply **optimistic UI updates**. We can update the UI before the network call completes so the UI feels more responsive.
# Exercise
1. open up `exercise/src/service/index.ts` and study the signature of the functions to call the service such as the `add()` function
2. open `exercise/src/actions/index.ts` and fill in the missing content inside `actionsWithService`
- note that the `complete` and `clear` functions require you to write your own wrapper function
3. open `exercise/src/index.tsx` and follow the instructions in the TODO comment to make the app prepopulate with data from the service.

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-09/demo/step2-09/demo.js"></script></body>
<script src="../../step2-09/demo/step2-09/demo.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="../../assets/step.css" />
</head>
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../step2-09/exercise/step2-09/exercise.js"></script></body>
<script src="../../step2-09/exercise/step2-09/exercise.js"></script><script src="../../markdownReadme/markdownReadme.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -76,6 +76,12 @@ module.exports = function() {
to: outPath
};
}),
...Object.keys(entries).map(entry => {
return {
from: `${entry}/../**/*.md`,
to: outPath
};
}),
{
from: 'assets/**/*',
to: outPath
@@ -84,10 +90,6 @@ module.exports = function() {
from: 'index.html',
to: outPath
},
{
from: '*.md',
to: outPath
},
...nonWebpackedEntries.map(entry => ({ from: `${entry}/**/*`, to: outPath }))
]),
new ForkTsCheckerWebpackPlugin({