Merge branch 'master' into day2-5

This commit is contained in:
Elizabeth Craig
2019-02-28 15:32:27 -08:00
committed by GitHub
14 changed files with 321 additions and 114 deletions

View File

@@ -9,7 +9,7 @@ hljs.registerLanguage('typescript', typescript);
async function run() {
const div = document.getElementById('markdownReadme');
// // Create your custom renderer.
// Create your custom renderer.
const renderer = new Renderer();
renderer.code = (code, language) => {
// Check whether the given language is valid for highlight.js.
@@ -21,32 +21,20 @@ async function run() {
};
marked.setOptions({ renderer });
// if (typeof hljs != 'undefined') {
// marked.setOptions({
// highlight: function(code, lang) {
// if (lang && hljs.getLanguage(lang)) {
// return hljs.highlight(lang, code).value;
// } else {
// return code;
// }
// }
// });
// }
if (div) {
const response = await fetch('../README.md');
const markdownText = await response.text();
div.innerHTML = marked(markdownText, { baseUrl: '../' });
restoreScroll(div);
div.addEventListener('scroll', evt => {
saveScroll(div);
});
window.addEventListener('resize', evt => {
saveScroll(div);
});
}
div.addEventListener('scroll', evt => {
saveScroll(div);
});
window.addEventListener('resize', evt => {
saveScroll(div);
});
}
const scrollKey = `${window.location.pathname}_scrolltop`;

View File

@@ -1,4 +1,4 @@
# Types and Creating a UI Driven State
# Types and Creating a UI-Driven State
Now that we have a UI that is purely driven by the state of our app, we need to add functionality to allow the UI to drive the state. This is often done by creating functions that call `setState` like we saw in the `TodoHeader`. Values from the state are then passed down to the UI as props.

View File

@@ -167,35 +167,35 @@ Exercises will be completed under this step's `exercise/src` folder unless other
## Modules
1. Open the file `exercise/src/index.ts` in VS Code
1. Open the file `exercise/src/fibonacci.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 the `n`-th Fibonacci number (be sure the specify the type of `n`).
2. Inside this file, write a function called `fib(n)` that takes in a number and returns the `n`-th Fibonacci number (be sure the specify the type of `n`).
> HINT: `function fib(n: number) { return n <= 1 ? n : fib(n - 1) + fib(n - 2); }`
4. Export `fib(n)` as a **named export**
3. Export `fib(n)` as a **named export**
5. Export another const variable as a **default export**
4. Export a const variable `FibConst` as a **default export**
6. Inside `index.ts`, import both of the modules created in steps (4) and (5) and use the built-in `console.log()` function to log the result of `fib(FibConst)`.
5. Inside `index.ts` in the same folder, import both `fib` and `FibConst`, and use the built-in `console.log()` function to log the result of `fib(FibConst)`.
## Types and Interfaces
Inside `index.ts`:
Inside `exercise/src/index.ts`:
1. Add 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 { ... }` complete with `wheels`, `color`, `make`, `model`
3. Create a valid car instance and log it using `console.log()`: `const myCar: Car = { ??? }`;
## Generics
Inside `stack.ts`, create a generic class for a `Stack<T>` complete with a typed `pop()` and `push()` methods.
Inside `exercise/src/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 built-in `console.log()` to show the functionality of `Stack<T>`.
In `exercise/src/index.ts`, create a `Stack<number>` and use `console.log()` to demonstrate its functionality.
## Spread and Destructuring
@@ -216,9 +216,9 @@ const obj2 = {
};
```
2. Now create a one-liner using the spread syntax `{...x, ...y}` to create a new variable that combines these two objects.
2. Now create a one-liner using the spread syntax `{...x, ...y}` to create a new variable `megaObj` that combines these two objects.
3. Use the destructuring syntax to retrieve the values for `{first, second, catcher}` from the new object created in step (2).
3. Use the destructuring syntax to retrieve the values for `{first, second, catcher}` from `megaObj`.
## Async / Await
@@ -230,6 +230,6 @@ function makePromise() {
}
```
1. Call `makePromise()` with the `await` syntax and log the results using the provided `log()` function.
1. Call `makePromise()` with the `await` syntax and log the results.
2. Create a new function that uses the `async` keyword. Make an `await` call to `makePromise()` and return the results.
2. Create a new function that uses the `async` keyword. Inside the function, make an `await` call to `makePromise()` and return the results.

View File

@@ -1,5 +1,5 @@
// TODO: create a named export of a function called fib(n)
// export function fib(n: number) ...
// TODO: create a default export of a constant of a number
// TODO: create a default export of a constant of a number FibConst
// export default ...

View File

@@ -1,7 +1,32 @@
// TODO: import the fib(n) function and the constant from './fibonacci.ts'
// import {fib}, FibConst from ...
// Some setup code for exercises
// TODO: import Stack from './stack.ts'
// Do the exercises here, outputting results using console.log()
console.log('hello world');
// ---- Modules ----
// TODO: log the result of fib(FibConst)
// ---- Types and Interfaces ----
// TODO: define TrafficLight type
// type TrafficLight = ???
// TODO: define Car interface
// interface Car { ??? }
// TODO: create Car instance
// const myCar: Car = { ??? }
// ---- Generics ----
// TODO: Demonstrate the Stack
// const myStack: Stack<number> = ???
// ---- Spread and Destructuring ----
const obj1 = {
first: 'who',
second: 'what',
@@ -15,17 +40,16 @@ const obj2 = {
catcher: 'today'
};
// TODO: combine obj1 and obj2 into a single object megaObj using spread syntax
// const megaObj = ???
// TODO: use destructuring syntax to extract { first, second, catcher }
// ---- Async / Await ----
function makePromise() {
return Promise.resolve(5);
}
// Do the exercises here, outputting results using console.log()
// ...
console.log('hello world');
// TODO: call makePromise() using await syntax and log the results
async function run() {
// Call the function you added for the async / await exercise here
// ...
}
run();
// TODO: create a new async function

View File

@@ -107,19 +107,19 @@ Stack abstracts these CSS styles and provides typings to make them more discover
Check out a cookbook of sorts in our [documentation](https://developer.microsoft.com/en-us/fabric#/components/stack).
# Exercise
# Exercise 1: Getting familiar with the Fabric documentation site:
1. Open the [documentation for DefaultButton](https://developer.microsoft.com/en-us/fabric/#/components/button)
2. Open each TSX file inside `exercise/src/components`
3. Replace some of the built-in HTML tags with these Fabric components:
Open the [documentation for DefaultButton](https://developer.microsoft.com/en-us/fabric/#/components/button)
- Stack
- DefaultButton
- Checkbox
- TextField
- Pivot (for the filter)
# Exercise 2: "Fabric"ize the TodoFooter.tsx
> Hint: think about how you can use Stacks to make the layout look nicer.
1. Open TSX file inside `exercise/src/components/TodoFooter.tsx`
2. Follow the top TODO comment to import Stack, Text and DefaultButton components from Fabric
3. Follow the TODO comment to:
- replace `<footer>` with a `<Stack>`
- replace `<span>` with a `<Text>`
- replace `<button>` with a `<DefaultButton>`
## Bonus Exercise
@@ -127,4 +127,4 @@ GO WILD! There are so many components in the Fabric library! Try to put some com
- Importing components from `office-ui-fabric-react`
- Customizing component with props found on the documentation site
- Customize component with render props (these will be called onRender___ or similar)
- Customize component with render props (these will be called onRender\_\_\_ or similar)

View File

@@ -11,7 +11,10 @@ export class TodoApp extends React.Component<any, Store> {
constructor(props) {
super(props);
this.state = {
todos: {},
todos: {
id0: { label: 'hello', completed: false },
id1: { label: 'world', completed: false }
},
filter: 'all'
};
}

View File

@@ -5,16 +5,86 @@ import { TodoHeader } from './TodoHeader';
import { TodoList } from './TodoList';
import { Store } from '../store';
let index = 0;
export class TodoApp extends React.Component<any, Store> {
constructor(props) {
super(props);
this.state = {
todos: {
id0: { label: 'hello', completed: false },
id1: { label: 'world', completed: false }
},
filter: 'all'
};
}
render() {
const { filter, todos } = this.state;
return (
<Stack horizontalAlign="center">
<Stack style={{ width: 400 }} gap={25}>
<TodoHeader />
<TodoList />
<TodoFooter />
<TodoHeader addTodo={this._addTodo} setFilter={this._setFilter} filter={filter} />
<TodoList complete={this._complete} todos={todos} filter={filter} remove={this._remove} edit={this._edit} />
<TodoFooter clear={this._clear} todos={todos} />
</Stack>
</Stack>
);
}
private _addTodo = label => {
const { todos } = this.state;
const id = index++;
this.setState({
todos: { ...todos, [id]: { label } }
});
};
private _remove = id => {
const newTodos = { ...this.state.todos };
delete newTodos[id];
this.setState({
todos: newTodos
});
};
private _complete = id => {
const newTodos = { ...this.state.todos };
newTodos[id].completed = !newTodos[id].completed;
this.setState({
todos: newTodos
});
};
private _edit = (id, label) => {
const newTodos = { ...this.state.todos };
newTodos[id] = { ...newTodos[id], label };
this.setState({
todos: newTodos
});
};
private _clear = () => {
const { todos } = this.state;
const newTodos = {};
Object.keys(this.state.todos).forEach(id => {
if (!todos[id].completed) {
newTodos[id] = todos[id];
}
});
this.setState({
todos: newTodos
});
};
private _setFilter = filter => {
this.setState({
filter: filter
});
};
}

View File

@@ -1,7 +1,20 @@
import React from 'react';
import { Store } from '../store';
export const TodoFooter = (props: any) => {
const itemCount = 3;
// TODO: import DefaultButton, Stack, and Text
interface TodoFooterProps {
clear: () => void;
todos: Store['todos'];
}
export const TodoFooter = (props: TodoFooterProps) => {
const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
// TODO:
// 1. replace the <footer> with the Fabric control <Stack>
// 2. replace the <span> with the Fabric control <Text>
// 3. replace the <button> with Fabric control <DefaultButton>
return (
<footer>

View File

@@ -1,19 +1,56 @@
import React from 'react';
import { Stack, Text, Pivot, PivotItem, TextField, PrimaryButton } from 'office-ui-fabric-react';
import { FilterTypes } from '../store';
export class TodoHeader extends React.Component<{}, {}> {
interface TodoHeaderProps {
addTodo: (label: string) => void;
setFilter: (filter: FilterTypes) => void;
filter: string;
}
interface TodoHeaderState {
labelInput: string;
}
export class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
constructor(props: TodoHeaderProps) {
super(props);
this.state = { labelInput: undefined };
}
render() {
return (
<div>
<h1>todos - step2-02 exercise</h1>
<input className="textfield" placeholder="add todo" />
<button className="button add">Add</button>
<div className="filter">
<button>all</button>
<button>active</button>
<button>completed</button>
</div>
</div>
<Stack>
<Stack horizontal horizontalAlign="center">
<Text variant="xxLarge">todos - step2-02 demo</Text>
</Stack>
<Stack horizontal>
<Stack.Item grow>
<TextField placeholder="What needs to be done?" value={this.state.labelInput} onChange={this.onChange} />
</Stack.Item>
<PrimaryButton onClick={this.onAdd}>Add</PrimaryButton>
</Stack>
<Pivot onLinkClick={this.onFilter}>
<PivotItem headerText="all" />
<PivotItem headerText="active" />
<PivotItem headerText="completed" />
</Pivot>
</Stack>
);
}
private onAdd = () => {
this.props.addTodo(this.state.labelInput);
this.setState({ labelInput: undefined });
};
private onChange = (evt: React.FormEvent<HTMLInputElement>, newValue: string) => {
this.setState({ labelInput: newValue });
};
private onFilter = (item: PivotItem) => {
this.props.setFilter(item.props.headerText as FilterTypes);
};
}

View File

@@ -1,13 +1,27 @@
import React from 'react';
import { Stack } from 'office-ui-fabric-react';
import { TodoListItem } from './TodoListItem';
import { Store, FilterTypes } from '../store';
interface TodoListProps {
complete: (id: string) => void;
remove: (id: string) => void;
todos: Store['todos'];
filter: FilterTypes;
edit: (id: string, label: string) => void;
}
export const TodoList = (props: TodoListProps) => {
const { filter, todos, complete, remove, edit } = props;
const filteredTodos = Object.keys(todos).filter(id => {
return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
});
export const TodoList = (props: any) => {
return (
<ul className="todos">
<TodoListItem />
<TodoListItem />
<TodoListItem />
</ul>
<Stack gap={10}>
{filteredTodos.map(id => (
<TodoListItem key={id} id={id} todos={todos} complete={complete} remove={remove} edit={edit} />
))}
</Stack>
);
};

View File

@@ -1,13 +1,75 @@
import React from 'react';
import { Stack, Checkbox, IconButton, TextField, DefaultButton } from 'office-ui-fabric-react';
import { Store } from '../store';
interface TodoListItemProps {
id: string;
todos: Store['todos'];
remove: (id: string) => void;
complete: (id: string) => void;
edit: (id: string, label: string) => void;
}
interface TodoListItemState {
editing: boolean;
editLabel: string;
}
export class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState> {
constructor(props: TodoListItemProps) {
super(props);
this.state = { editing: false, editLabel: undefined };
}
export class TodoListItem extends React.Component<any, any> {
render() {
const { todos, id, complete, remove } = this.props;
const item = todos[id];
return (
<li className="todo">
<label>
<input type="checkbox" defaultChecked={false} /> test item
</label>
</li>
<Stack horizontal verticalAlign="center" horizontalAlign="space-between">
{!this.state.editing && (
<>
<Checkbox label={item.label} checked={item.completed} onChange={() => complete(id)} />
<div>
<IconButton iconProps={{ iconName: 'Edit' }} className="clearButton" onClick={this.onEdit} />
<IconButton iconProps={{ iconName: 'Cancel' }} className="clearButton" onClick={() => remove(id)} />
</div>
</>
)}
{this.state.editing && (
<Stack.Item grow>
<Stack horizontal>
<Stack.Item grow>
<TextField value={this.state.editLabel} onChange={this.onChange} />
</Stack.Item>
<DefaultButton onClick={this.onDoneEdit}>Save</DefaultButton>
</Stack>
</Stack.Item>
)}
</Stack>
);
}
private onEdit = () => {
const { todos, id } = this.props;
const { label } = todos[id];
this.setState({
editing: true,
editLabel: this.state.editLabel || label
});
};
private onDoneEdit = () => {
this.props.edit(this.props.id, this.state.editLabel);
this.setState({
editing: false,
editLabel: undefined
});
};
private onChange = (evt: React.FormEvent<HTMLInputElement>, newValue: string) => {
this.setState({ editLabel: newValue });
};
}

View File

@@ -87,28 +87,27 @@ The `styles` prop can take either an object, or a function which returns a style
The following code (simplified from `demo/src/components/TodoHeader.tsx`) demonstrates using `styles` to customize individual components. The TextField uses a style function and the PrimaryButton uses a style object.
```tsx
```ts
function render() {
const buttonStyles = {
root: { backgroundColor: 'maroon' },
rootHovered: { background: 'green' }
};
const textFieldStyles = (props: ITextFieldStyleProps): Partial<ITextFieldStyles> => ({
...(props.focused && {
field: {
backgroundColor: '#c7e0f4'
}
})
});
return (
<Stack>
<Stack.Item>
<TextField
placeholder="What needs to be done?"
styles={(props: ITextFieldStyleProps): Partial<ITextFieldStyles> => ({
...(props.focused && {
field: {
backgroundColor: '#c7e0f4'
}
})
})}
/>
<TextField placeholder="What needs to be done?" styles={textFieldStyles} />
</Stack.Item>
<PrimaryButton styles={{
root: { backgroundColor: 'maroon' },
rootHovered: { background: 'green' }
}}>
Add
</PrimaryButton>
<PrimaryButton styles={buttonStyles}>Add</PrimaryButton>
</Stack>
);
}
@@ -121,6 +120,7 @@ function render() {
This is an advanced approach which also works outside of Fabric. Within Fabric-based apps, you would typically only use `mergeStyles` in certain niche scenarios. (Fabric itself uses `mergeStyles` under the hood to power some of its styling.)
Benefits of `mergeStyles` include:
- Works in any app
- Eliminates the need to import or bundle CSS stylesheets (all styles are bundled as normal JS code)
- Provides type checking for styles (like Sass) when used with TypeScript
@@ -144,11 +144,7 @@ const className = mergeStyles(blueBackgroundClassName, {
}
});
const myDiv = (
<div className={className}>
I am a green div that turns red on hover!
</div>
);
const myDiv = <div className={className}>I am a green div that turns red on hover!</div>;
```
# Exercises

View File

@@ -1,16 +1,16 @@
// Import the stack here
// TODO: Import the stack here
describe('Stack', () => {
it('should push item to the top of the stack', () => {
// implement test here:
// 1. Create a stack instance
// TODO: implement test here:
// 1. Instantiate a new Stack - i.e. const stack = new Stack<number>();
// 2. Use stack push calls to add some items to the stack
// 3. Write assertions via the expect() API
});
it('should pop the item from the top of stack', () => {
// implement test here:
// 1. Create a stack instance
// TODO: implement test here:
// 1. Instantiate a new Stack - i.e. const stack = new Stack<number>();
// 2. Use stack push calls to add some items to the stack
// 3. pop a few items off the stack
// 4. write assertions via the expect() API