Merge branch 'master' into js-demo

This commit is contained in:
Elizabeth Craig
2019-02-26 11:40:53 -08:00
committed by GitHub
54 changed files with 226 additions and 181 deletions

View File

@@ -8,7 +8,7 @@ This repo is the content for a two day workshop focused on introducing you to th
## 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.
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
@@ -17,7 +17,8 @@ This workshop starts as a very high level introduction to the core principles of
- 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)
- Up to date browser with React Developer Tools
- React Developer Tools: [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) or [Firefox](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)
### Getting Set Up
@@ -25,11 +26,11 @@ This workshop starts as a very high level introduction to the core principles of
$ git clone https://github.com/Microsoft/frontend-bootcamp.git
$ cd frontend-bootcamp
$ npm install
// For Day 1, Steps 1-3
$ npm run static
$ npm run static
// For the rest of the steps
$ npm start
$ npm start
```
## Table Of Contents
@@ -48,13 +49,13 @@ This workshop starts as a very high level introduction to the core principles of
- [Exercise](step1-03/exercise)
4. [React Introduction](step1-04)
- [Demo](step1-04/demo)
5. [Thinking in React: Building a Static Page](step1-05)
5. [Building a Static Page](step1-05)
- [Demo](step1-05/demo)
- [Exercise](step1-05/exercise)
6. [Thinking in React: State Driven UI](step1-06)
6. [State Driven UI](step1-06)
- [Demo](step1-06/demo)
- [Exercise](step1-06/exercise)
7. [Thinking in React: UI Driven State](step1-07)
7. [Types & UI Driven State](step1-07)
- [Demo](step1-07/demo)
- [Exercise](step1-07/exercise)
@@ -62,7 +63,7 @@ This workshop starts as a very high level introduction to the core principles of
Demo and Exercises are combined
1. [Introduction to Typescript](step2-01)
1. [Introduction to TypeScript](step2-01)
2. [UI Fabric Component Library](step2-02)
3. [Theming and Styling](step2-03)
4. [Testing with Jest](step2-04)
@@ -78,6 +79,13 @@ Demo and Exercises are combined
- [React Docs](https://reactjs.org/docs/getting-started.html)
- [Thinking in React](https://reactjs.org/docs/thinking-in-react.html)
### Follow the Authors!
If you are interested in JavaScript, TypeScript, React, Redux, Design Systems, follow us on Twitter:
- [@kenneth_chau](https://twitter.com/kenneth_chau)
- [@micahgodbolt](https://twitter.com/micahgodbolt)
# Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a

11
assets/scripts.js Normal file
View File

@@ -0,0 +1,11 @@
// prettier-ignore
var appInsights=window.appInsights||function(a){
function b(a){c[a]=function(){var b=arguments;c.queue.push(function(){c[a].apply(c,b)})}}var c={config:a},d=document,e=window;setTimeout(function(){var b=d.createElement("script");b.src=a.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js",d.getElementsByTagName("script")[0].parentNode.appendChild(b)});try{c.cookie=d.cookie}catch(a){}c.queue=[];for(var f=["Event","Exception","Metric","PageView","Trace","Dependency"];f.length;)b("track"+f.pop());if(b("setAuthenticatedUserContext"),b("clearAuthenticatedUserContext"),b("startTrackEvent"),b("stopTrackEvent"),b("startTrackPage"),b("stopTrackPage"),b("flush"),!a.disableExceptionTracking){f="onerror",b("_"+f);var g=e[f];e[f]=function(a,b,d,e,h){var i=g&&g(a,b,d,e,h);return!0!==i&&c["_"+f](a,b,d,e,h),i}}return c
}({
instrumentationKey: "6ad37ae0-c4ab-4739-925c-1e2773c31f17"
});
// prettier-ignore
if (window.location.hostname !== 'localhost') {
window.appInsights=appInsights,appInsights.queue&&0===appInsights.queue.length&&appInsights.trackPageView(null, null, {urlReferrer: document.referrer});
}

View File

@@ -67,10 +67,10 @@ h1 a {
content: counter(steps);
position: absolute;
left: -20px;
bottom: -20px;
bottom: -3px;
font-size: 220px;
line-height: 188px;
color: #f3f2f1;
color: #eaeaea;
z-index: 1;
}

20
azure-pipelines.pr.yml Normal file
View File

@@ -0,0 +1,20 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger: none
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- script: |
npm install
npm run build
displayName: 'npm install, build'

33
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,33 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
pr: none
trigger:
- master
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- script: |
git config user.email "kchau@microsoft.com"
git config user.name "Ken Chau"
git remote set-url origin https://kenotron:$(git.pat)@github.com/Microsoft/frontend-bootcamp.git
git checkout master
git pull
npm install
git checkout -b build_$(Build.BuildId)
npm run build
git add .
git commit -m "adding docs"
git subtree split --prefix docs -b temp_$(Build.BuildId)
git push origin temp_$(Build.BuildId):refs/heads/gh-pages --force
displayName: 'npm install, build and push docs to gh-pages'

View File

@@ -4,16 +4,7 @@
<meta charset="UTF-8" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
<link rel="stylesheet" href="./assets/shared.css" />
<script type="text/javascript">
// prettier-ignore
var appInsights=window.appInsights||function(a){
function b(a){c[a]=function(){var b=arguments;c.queue.push(function(){c[a].apply(c,b)})}}var c={config:a},d=document,e=window;setTimeout(function(){var b=d.createElement("script");b.src=a.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js",d.getElementsByTagName("script")[0].parentNode.appendChild(b)});try{c.cookie=d.cookie}catch(a){}c.queue=[];for(var f=["Event","Exception","Metric","PageView","Trace","Dependency"];f.length;)b("track"+f.pop());if(b("setAuthenticatedUserContext"),b("clearAuthenticatedUserContext"),b("startTrackEvent"),b("stopTrackEvent"),b("startTrackPage"),b("stopTrackPage"),b("flush"),!a.disableExceptionTracking){f="onerror",b("_"+f);var g=e[f];e[f]=function(a,b,d,e,h){var i=g&&g(a,b,d,e,h);return!0!==i&&c["_"+f](a,b,d,e,h),i}}return c
}({
instrumentationKey: "6ad37ae0-c4ab-4739-925c-1e2773c31f17"
});
// prettier-ignore
window.appInsights=appInsights,appInsights.queue&&0===appInsights.queue.length&&appInsights.trackPageView();
</script>
<title>Microsoft Days in the Web - Welcome</title>
</head>
<body class="ms-Fabric">
<div class="Container">
@@ -76,7 +67,7 @@
</li>
<li class="Tile Tile--numbered">
<div class="Tile-link">
UI-Driven State
Types & <br />UI-Driven State
<div class="Tile-links">
<a target="_blank" href="./step1-07/demo/">demo</a> | <a target="_blank" href="./step1-07/exercise/">exercise</a> |
<a target="_blank" href="./step1-07/final/">final</a>
@@ -93,7 +84,7 @@
</li>
<li class="Tile Tile--numbered">
<div class="Tile-link">
Typescript Basics
TypeScript Basics
<div class="Tile-links">
<a target="_blank" href="./step2-01/demo/">demo</a> | <a target="_blank" href="./step2-01/exercise/">exercise</a>
</div>
@@ -171,8 +162,9 @@
<h2>Playground</h2>
Build your app here!
</li>
<li class="Tile Tile--unnumbered"><a target="_blank" href="/playground/" class="Tile-link">Playground</a></li>
<li class="Tile Tile--unnumbered"><a target="_blank" href="./playground/" class="Tile-link">Playground</a></li>
</ul>
</div>
<script src="./assets/scripts.js"></script>
</body>
</html>

View File

@@ -9,7 +9,8 @@
"build": "rimraf docs && webpack --progress --mode production",
"start:server": "nodemon -w server server/index.js",
"start": "run-p start:server start:client",
"static": "live-server --host=localhost"
"static": "live-server --host=localhost",
"deploy-ghpages": "git push origin :gh-pages && git subtree push --prefix docs origin gh-pages"
},
"keywords": [],
"author": "",

View File

@@ -1,6 +1,6 @@
## HTML Demo
The [HTML demo page](http://localhost:8080/step1-00/html-demo/html-demo.html) is a large collection of HTML elements that you will come across during development. The full list of elements can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
The [HTML demo page](http://localhost:8080/step1-01/html-demo/html-demo.html) is a large collection of HTML elements that you will come across during development. The full list of elements can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
To learn more about each element, click on the elements below.

View File

@@ -13,7 +13,18 @@
<body>
<section>
<h2><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Document_metadata">Document Meta Data</a></h2>
<p>head, title, link, style</p>
<pre>
&lt;head&gt;
&lt;title&gt;Intro to HTML&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;./style.css&quot; /&gt;
&lt;style&gt;
hr {
margin: 40px;
}
&lt;/style&gt;
&lt;/head&gt;
</pre>
</section>
<hr />

View File

@@ -15,5 +15,6 @@
</ul>
<div id="markdownReadme"></div>
</div>
<script src="../assets/scripts.js"></script>
</body>
</html>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<link rel="stylesheet" href="../css-demo/css-demo-finished.css" />
<link rel="stylesheet" href="../css-demo/css-demo-final.css" />
</head>
<body>
<div>

View File

@@ -4,7 +4,7 @@ Every website, application, form or component starts with markup. The HTML will
## Demo
In this exercise we will scaffold out some HTML for out Todo app, and add some basic styling to it.
In this exercise we will scaffold out some HTML for our Todo app, then add some basic styling to it.
### Page scaffold
@@ -18,7 +18,7 @@ In this exercise we will scaffold out some HTML for out Todo app, and add some b
1. The DOCTYPE tells the browser that this file is written in modern HTML.
2. The HTML tag wraps the entire page, and is the page root. Nothing is placed outside of those tags. Attributes can be set on HTML
3. Head will contain all of the page's meta data, in this case a link to our css file
3. Head will contain all of the page's meta data, in this case a link to our CSS file
4. Body is where all of the visible content should be placed.
### Content Sectioning
@@ -41,7 +41,7 @@ As we saw in the previous demo, HTML elements can be used to describe different
### Updating the header
The header of our page is where most of the action is going to happen. First, lets give our page a title, adding 'TODO' to our `h1`. Then we can add an input and button to our `addTodo` div.
The header of our page is where most of the action is going to happen. First, lets give our app a name, adding 'TODO' to our `h1`. Then we can add an input and button to our `addTodo` div.
```html
<input class="textfield" placeholder="add todo" /> <button class="submit">Add</button>
@@ -61,7 +61,7 @@ The navigation for this application is quite simple. We want users to be able to
### Adding styles
Now that we've got the top of our application scaffolded, we can add some our styles in the head.
Now that we've got the top of our application scaffolded, we can add some styles in the head.
```html
<head>
@@ -73,7 +73,7 @@ Now that we've got the top of our application scaffolded, we can add some our st
It looks like the selected button isn't getting any special styles. Let's dig in and see why that is.
Open up the browser inspector and target our 'all' button. You'll notice that the blue style is present on the list, but it is being overriden by the `border: none` above it. This is a situation where specificity is winning out over the cascade.
Open up the browser inspector and target our 'all' button. You'll notice that the blue style is present on the list, but it is being overridden by the `border: none` above it. This is a situation where specificity is winning out over the cascade.
> **Cascade** states that if two selectors are equal, the lowest one on the page wins

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head></head>
<body>
<header>
<h1></h1>
<div class="addTodo"></div>
<nav></nav>
</header>
<main class="filter"></main>
<footer></footer>
</body>
</html>

View File

@@ -1,16 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../assets/shared.css" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
</head>
<body class="ms-Fabric">
<div class="Container">
<ul class="Tiles">
<li class="Tile"><a href="./demo/index.html" class="Tile-link">Demo Start</a></li>
<li class="Tile"><a href="./exercise/index.html" class="Tile-link">Exercise Start</a></li>
<li class="Tile"><a href="./final/index.html" class="Tile-link">Final</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -1,6 +1,6 @@
# Javascript Demo
# JavaScript Demo
Now that we a UI that looks like a todo app, we need to add functionality to make it **function** like a todo app. In this example we are going to use raw Javascript explicitly modify our application as we interact with it. This will be in stark contrast to the implicit approach we will take when we do this with React in the next exercise.
Now that we a UI that looks like a todo app, we need to add functionality to make it **function** like a todo app. In this example we are going to use raw JavaScript explicitly modify our application as we interact with it. This will be in stark contrast to the implicit approach we will take when we do this with React in the next exercise.
> Keep an eye on how often user actions directly modify the HTML on the page. You'll see this number drop to zero when we start using React.
@@ -8,13 +8,14 @@ Now that we a UI that looks like a todo app, we need to add functionality to mak
This demo starts off with a few elements already in place. Let's walk through what's already here.
- **clearInput()** - This is a generic, reusable function that takes in a `selector` paramater, finds the first matching element, and sets the element's value to an empty string. This direct modification is called a side effect.
- **clearInput()** - This is a generic, reusable function that takes in a `selector` parameter, finds the first matching element, and sets the element's value to an empty string. This direct modification is called a side effect.
- **getTodoText()** - This is a quick helper function that returns the value inside of our textfield. Notice how some functions return values and how you can set that return to a variable.
- **addTodo()** - This is the primary logic of our todo app. Here's how the lines break down.
1. `todo` is set to equal the first todo item
2. `newTodo` is a clone of todo. Passing true means it is a deep clone, so we get the todo's children as well. Cloning does not duplicate the DOM node. We'll need to insert it in step 4
> Note that this approach is very fragile, as it requires a todo node to always be present on the page
3. We set the innerText of the `<span class='title'>` to the value returned from getTodoText
> Note that if we left off the `()` we'd actully be assiging innerText to the 'function' instead of the function return
> Note that if we left off the `()` we'd actually be assigning innerText to the 'function' instead of the function return
4. Insert our new todo into the todo's parent (the `ul`), before our reference todo. [insertBefore](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore)
- **filter()** - This function takes in a `filterName` string, and a `button` which is a reference to the clicked button.
1. Remove any `selected` class names

View File

@@ -3,13 +3,13 @@
### Write updateRemaining function
1. Get a reference to the span with the `remaining` class, and store it in a variable
2. Use [querySelectAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) to get all of the todos.
2. Use [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) to get all of the todos.
3. Set the `innerText` of the remaining span to the length of those todos.
4. Add updateRemaining() to addTodo
### Write a clearCompleted function
1. Get a reference to all of the todos with [querySelectAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)
1. Get a reference to all of the todos with [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)
2. Use a `for (let todo of todos)` loop to iterate over each todo
3. Inside the for loop write an `if` statement to test if the `input` inside of the todo has a checked value of true
> Hint: you can use querySelector on any HTML node to find child elements within

View File

@@ -48,7 +48,7 @@
function updateRemaining() {
const remaining = document.querySelector('.remaining');
const todos = document.querySelectorAll('.todo');
const todos = document.querySelectorAll('.todo').length;
remaining.innerText = todos;
}

View File

@@ -1,16 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../assets/shared.css" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
</head>
<body class="ms-Fabric">
<div class="Container">
<ul class="Tiles">
<li class="Tile"><a href="./demo/index.html" class="Tile-link">Demo Start</a></li>
<li class="Tile"><a href="./exercise/index.html" class="Tile-link">Exercise Start</a></li>
<li class="Tile"><a href="./final/index.html" class="Tile-link">Final</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -26,10 +26,10 @@ export class App extends React.Component {
}
```
- **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.
- **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
> 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:
@@ -63,7 +63,7 @@ React solves this by allowing each control to specify its own data store, called
#### 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.
JavaScript 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) {
@@ -138,7 +138,7 @@ return (
);
```
> 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.
> Note the capitalization of Counter. HTML might not be case sensitive, 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

View File

@@ -1,15 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../assets/shared.css" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
</head>
<body class="ms-Fabric">
<div class="Container">
<ul class="Tiles">
<li class="Tile"><a href="./demo/index.html" class="Tile-link">Demo Start</a></li>
<li class="Tile"><a href="./final/index.html" class="Tile-link">Final</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -1,15 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../assets/shared.css" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
</head>
<body class="ms-Fabric">
<div class="Container">
<ul class="Tiles">
<li class="Tile"><a href="./demo/index.html" class="Tile-link">Demo Start</a></li>
<li class="Tile"><a href="./final/index.html" class="Tile-link">Final</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -1,10 +1,10 @@
# Creating a State Driven UI
In React data travels two directions, top down in the form of state propegating throughout controls, and bottom up, as interacting with the UI flows back up to modify the state. When writing an application it's often helpful to think of these two directions as separate parts of the development process.
In React data travels two directions, top down in the form of state propagating throughout controls, and bottom up, as interacting with the UI flows back up to modify the state. When writing an application it's often helpful to think of these two directions as separate parts of the development process.
## demo
[Step #3 of Thinking in React](https://reactjs.org/docs/thinking-in-react.html) suggests finding the "minimal set of mutable state" that your application requires. So in this demo we are going to add that "minimal state" to our application and drive our UI off of that data. With that done the next step will be to create ways to modify that state, which will in turn cascade down through our UI. This [reconcilation](https://reactjs.org/docs/reconciliation.html) process, figuring out what in your UI needs to change based on changing state, is what React excels in.
[Step #3 of Thinking in React](https://reactjs.org/docs/thinking-in-react.html) suggests finding the "minimal set of mutable state" that your application requires. So in this demo we are going to add that "minimal state" to our application and drive our UI off of that data. With that done the next step will be to create ways to modify that state, which will in turn cascade down through our UI. This [reconciliation](https://reactjs.org/docs/reconciliation.html) process, figuring out what in your UI needs to change based on changing state, is what React excels in.
### Adding State to App
@@ -71,15 +71,15 @@ I've already pulled out our props into `filter` and `todos` variables, and writt
}
```
- A JavaScript map takes in an array and transforms it into a new array
- We use the `id` from the `filterTodos` array as the [list key](https://reactjs.org/docs/lists-and-keys.html) to help React track each item as state changes.
- The key is not actually passed into the component, so we pass the key in as `id` as well. This will help us out later.
- Lastly we use the `id` to grab the todo from our `todos` object, then use the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) to pass through the todo's `label` and `completed` values.
> This spread opperator is the same as saying `label={todos[id].label} completed={todos[id].completed}`. Pretty obvious why spread is so handy!
- **map**: A JavaScript map takes in an array (filteredTodos) and transforms it into a new array (our rendered TodoListItems)
- **key**: We use the `id` from the `filterTodos` array as the [list key](https://reactjs.org/docs/lists-and-keys.html) to help React track each item as state changes.
- **id**: The key is not actually passed into the component, so we pass the key in as `id` as well. This will help us out later.
- **todos[id]**: Lastly we use the `id` to grab the todo from our `todos` object, then use the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) to pass through the todo's `label` and `completed` values.
> This spread operator is the same as saying `label={todos[id].label} completed={todos[id].completed}`. Pretty obvious why spread is so handy!
### State Driven and Stateful Header
Within the header we've got a situation where we not only want to pass `filter` state down to it, but we also want to maintain state within the control. Fortunatly, this is no problem at all for React. First off let's deal with the incoming state.
Within the header we've got a situation where we not only want to pass `filter` state down to it, but we also want to maintain state within the control. Fortunately, this is no problem at all for React. First off let's deal with the incoming state.
#### Conditional ClassNames
@@ -87,15 +87,15 @@ In CSS based styling, visual states are applied by adding and removing classes.
```jsx
<nav className="filter">
<button className={filter == 'all' ? 'completed' : ''}>all</button>
<button className={filter == 'active' ? 'completed' : ''}>active</button>
<button className={filter == 'completed' ? 'completed' : ''}>completed</button>
<button className={filter == 'all' ? 'selected' : ''}>all</button>
<button className={filter == 'active' ? 'selected' : ''}>active</button>
<button className={filter == 'completed' ? 'selected' : ''}>completed</button>
</nav>
```
> Terniary operators are very popular in React code as each expression could be a string for a className, or even a JSX element.
> Ternary operators are very popular in React code as each expression could be a string for a className, or even a JSX element.
#### Creating a Controled Input
#### Creating a Controlled Input
In tradition HTML forms users interact with the form, and on submit, those values are captured and transmitted. Those are called **uncontrolled inputs**. A **controlled input** is one whos value is defined by state, and interaction with that input updates state with each keystroke. This round trip process might sound inefficient, but in reality it has little to no impact, and it enables some advanced form functionality.

View File

@@ -1,7 +1,7 @@
import React from 'react';
export const TodoFooter = (props: any) => {
const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
// const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
return (
<footer>
<span>4 items left</span>

View File

@@ -5,9 +5,9 @@ export class TodoList extends React.Component<any, any> {
render() {
const { filter, todos } = this.props;
const filteredTodos = Object.keys(todos).filter(id => {
return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
});
// const filteredTodos = Object.keys(todos).filter(id => {
// return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
// });
return (
<ul className="todos">
<TodoListItem />

View File

@@ -17,9 +17,9 @@ export class TodoHeader extends React.Component<any, any> {
<button className="submit">Add</button>
</div>
<nav className="filter">
<button className={filter == 'all' ? 'completed' : ''}>all</button>
<button className={filter == 'active' ? 'completed' : ''}>active</button>
<button className={filter == 'completed' ? 'completed' : ''}>completed</button>
<button className={filter == 'all' ? 'selected' : ''}>all</button>
<button className={filter == 'active' ? 'selected' : ''}>active</button>
<button className={filter == 'completed' ? 'selected' : ''}>completed</button>
</nav>
</header>
);

View File

@@ -5,7 +5,7 @@ export class TodoHeader extends React.Component<any, any> {
super(props);
this.state = { labelInput: '' };
}
render() {
const { filter } = this.props;
@@ -17,9 +17,9 @@ export class TodoHeader extends React.Component<any, any> {
<button className="submit">Add</button>
</div>
<nav className="filter">
<button className={filter == 'all' ? 'completed' : ''}>all</button>
<button className={filter == 'active' ? 'completed' : ''}>active</button>
<button className={filter == 'completed' ? 'completed' : ''}>completed</button>
<button className={filter == 'all' ? 'selected' : ''}>all</button>
<button className={filter == 'active' ? 'selected' : ''}>active</button>
<button className={filter == 'completed' ? 'selected' : ''}>completed</button>
</nav>
</header>
);

View File

@@ -6,21 +6,21 @@ Now that we have a UI that is purely driven by the state of our app, we need to
This is our core 'business logic' and handles everything our basic 'CRUD' operations of "Create, Read, Update, Delete". We don't have time to walk through writing all of those functions, but you can see that they are already provided in the demo's `TodoApp` and passed into our components.
## Intro to Typescript
## Intro to TypeScript
Taking a look at our components in `TodoApp` you can see that our list of props is not just getting longer, but is getting much more complex! We're passing through functions with various signatures, complex `todos` objects as well as filter strings which are always one of three values.
As applications grow, it becomes increasing difficult to remember what each function does, or what each todo contains. Also, as JavaScript is a loosly type language, if I wanted to change the value of `todos` to an array inside my `TodoList`, javascript wouldn't care. But if `TodoListItems` was expecting an object, our application would break.
As applications grow, it becomes increasing difficult to remember what each function does, or what each todo contains. Also, as JavaScript is a loosely type language, if I wanted to change the value of `todos` to an array inside my `TodoList`, JavaScript wouldn't care. But if `TodoListItems` was expecting an object, our application would break.
It is because of these two reasons that the entire industry is shifting to writing applications that are strongly typed, and are using Typescript to accomplish that.
It is because of these two reasons that the entire industry is shifting to writing applications that are strongly typed, and are using TypeScript to accomplish that.
As [their website](https://www.typescriptlang.org/) state:
> Typescript is a superset of JavaScript that compiles to plain JavaScript
> TypeScript is a superset of JavaScript that compiles to plain JavaScript
If you've ever used [Sass](https://sass-lang.com/) you are familiar with this concept. In the same say that all valid CSS is valid Sass, all valid JavaScript is valid Typescript. That's why most of this project has been writting in `ts` and `tsx` files instead of `js` and `jsx` files.
If you've ever used [Sass](https://sass-lang.com/) you are familiar with this concept. In the same say that all valid CSS is valid Sass, all valid JavaScript is valid TypeScript. That's why most of this project has been writing in `ts` and `tsx` files instead of `js` and `jsx` files.
Let's dive into the demo and see how Typescript can help us better understand our component props, and guard against future regressions.
Let's dive into the demo and see how TypeScript can help us better understand our component props, and guard against future regressions.
## Demo
@@ -80,7 +80,7 @@ Now try going back to `TodoApp` and changing the `filter` attribute in `TodoList
#### Complete Type
The `complete` props isn't data, but rather a function. Fortunatly, Typescript can handle function types just as well as data.
The `complete` props isn't data, but rather a function. Fortunately, TypeScript can handle function types just as well as data.
```tsx
interface TodoListProps {
@@ -115,7 +115,7 @@ Now that our interface is complete, try changing the word 'all' in `filter === a
### Abstracting types
Most of our components are going to need to add types for `todos` and `filter`, so it's a good thing that Typescript allows us to abstract those. I've already written up and exported those shared types in the file `TodoApp.types.ts`, so we just need to import them and pull them into our interface.
Most of our components are going to need to add types for `todos` and `filter`, so it's a good thing that TypeScript allows us to abstract those. I've already written up and exported those shared types in the file `TodoApp.types.ts`, so we just need to import them and pull them into our interface.
```tsx
import { FilterTypes, Todos } from '../TodoApp.types';
@@ -129,7 +129,7 @@ interface TodoListProps {
### Updating TodoApp
Our `TodoApp` doesn't take any props, but it does have state. We can use Typescript to define that as well.
Our `TodoApp` doesn't take any props, but it does have state. We can use TypeScript to define that as well.
I've already imported `Todos`, and `FilterTypes` into the `TodoApp`, so we just need to add them to our class. We can even skip the 'interface', if we want to, and add them directly to the class.
@@ -160,8 +160,12 @@ const { label, completed, complete, id } = this.props;
And then use the input's `onChange` event to fire our `complete` callback. We can see in the signature that we expect and `id` of type string, so we'll pass our `id` prop in.
> A [callback function](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) is a function passed into a component as a prop
```tsx
<input type="checkbox" checked={completed} onChange={() => complete(id)} />
```
> Note that the function param and prop name just happen to be the same. This isn't required.
Now that our todos are firing the `onChange` callback, give them a click and take look at how the app response. Since our footer text is driven off of the number of unchecked todos, the footer will automatically update to reflect the new state.

View File

@@ -10,7 +10,24 @@ export class TodoApp extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
todos: {},
todos: {
'04': {
label: 'Todo 4',
completed: true
},
'03': {
label: 'Todo 3',
completed: false
},
'02': {
label: 'Todo 2',
completed: false
},
'01': {
label: 'Todo 1',
completed: false
}
},
filter: 'all'
};
}

View File

@@ -10,7 +10,24 @@ export class TodoApp extends React.Component<any, { todos: Todos; filter: Filter
constructor(props) {
super(props);
this.state = {
todos: {},
todos: {
'04': {
label: 'Todo 4',
completed: true
},
'03': {
label: 'Todo 3',
completed: false
},
'02': {
label: 'Todo 2',
completed: false
},
'01': {
label: 'Todo 1',
completed: false
}
},
filter: 'all'
};
}

View File

@@ -26,7 +26,7 @@ export class TodoHeader extends React.Component<any, any> {
}
_onFilter = evt => {
this.props.setFilter(evt.target.textContet);
this.props.setFilter(evt.target.innerText);
};
_onChange = evt => {

View File

@@ -44,7 +44,7 @@ export class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState
}
_onFilter = evt => {
this.props.setFilter(evt.target.textContet);
this.props.setFilter(evt.target.innerText);
};
_onChange = evt => {

View File

@@ -1,15 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../assets/shared.css" />
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css" />
</head>
<body class="ms-Fabric">
<div class="Container">
<ul class="Tiles">
<li class="Tile"><a href="./demo/index.html" class="Tile-link">Demo Start</a></li>
<li class="Tile"><a href="./final/index.html" class="Tile-link">Final</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -1,8 +1,8 @@
# Step 2.1: Introduction to Typescript
# 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.
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:
@@ -22,13 +22,13 @@ 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
- ESM (ECMAScript module) - language level support
- statically analyzable and synchronous
- dynamic and asynchronous support via `import()` that returns a promise
## Typescript Types
## TypeScript Types
Refer to the `demo/src` for some examples of some of the types avaible in TS that benefits a React developer.
Refer to the `demo/src` for some examples of some of the types available in TS that benefits a React developer.
## Spread Syntax
@@ -162,7 +162,7 @@ Create inside `index.ts`:
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.
> 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>`

View File

@@ -8,5 +8,6 @@
<div id="app">
Nothing to show here, just look at your console window for output. Hit F12 to open console window.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -1,4 +1,4 @@
// Interesting Typescript Topics
// Interesting TypeScript Topics
// types
import './types';

View File

@@ -8,5 +8,6 @@
<div id="app">
Nothing to show here, just look at your console window for output. Hit F12 to open console window.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -2,7 +2,7 @@
[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.
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/
@@ -43,7 +43,7 @@ describe('Foo Component Tests', () => {
});
```
`mount` does a full mount of the component. You can use the `enzyme` wrapper to simulate clicks, etc:
`mount` does a full mount of the component. You can use the `enzyme` wrapper to simulate clicks, etc.:
```ts
wrapper.find('button').simulate('click');

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -10,5 +10,6 @@
<pre>npm test</pre>
in the command line.
</div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -2,6 +2,8 @@
[Lessons](../) | [Exercise](./exercise/) | [Demo](./demo/)
(First off, this doesn't work with the live site on github.io. Clone this repo to try this step out)
Redux Thunk middleware for actions with service calls. The documentation is here:
https://github.com/reduxjs/redux-thunk

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>

View File

@@ -6,5 +6,6 @@
<body class="ms-Fabric">
<div id="markdownReadme"></div>
<div id="app"></div>
<script src="../../assets/scripts.js"></script>
</body>
</html>