diff --git a/index.html b/index.html
index 2c4ace3..d855a4e 100644
--- a/index.html
+++ b/index.html
@@ -11,5 +11,6 @@
Step 9
Day 2
Step 1
+ Step 2
Playground
diff --git a/step2-01/README.md b/step2-01/README.md
index e775930..31b3ed6 100644
--- a/step2-01/README.md
+++ b/step2-01/README.md
@@ -2,7 +2,7 @@
Typescript
-- modules
+- ES6 modules review
- a look at types
- defining interfaces
-
diff --git a/step2-02/README.md b/step2-02/README.md
new file mode 100644
index 0000000..430fbea
--- /dev/null
+++ b/step2-02/README.md
@@ -0,0 +1,9 @@
+# Step 2.2
+
+Integrates Fabric
+
+Learn about Basic Components
+
+- Stack
+- Text
+- Show case some components
diff --git a/step2-02/index.html b/step2-02/index.html
new file mode 100644
index 0000000..454cef5
--- /dev/null
+++ b/step2-02/index.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/step2-02/src/components/TodoApp.tsx b/step2-02/src/components/TodoApp.tsx
new file mode 100644
index 0000000..9966c46
--- /dev/null
+++ b/step2-02/src/components/TodoApp.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import { Stack } from '@uifabric/experiments';
+import { TodoFooter } from './TodoFooter';
+import { TodoHeader } from './TodoHeader';
+import { TodoList } from './TodoList';
+import { Store } from '../store';
+
+let index = 0;
+
+export class TodoApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ todos: {},
+ filter: 'all'
+ };
+ }
+ render() {
+ const { filter, todos } = this.state;
+ return (
+
+
+
+
+
+
+
+ );
+ }
+
+ 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
+ });
+ };
+}
diff --git a/step2-02/src/components/TodoFooter.tsx b/step2-02/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..54129c1
--- /dev/null
+++ b/step2-02/src/components/TodoFooter.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { Text, Stack } from '@uifabric/experiments';
+import { Store } from '../store';
+import { DefaultButton } from 'office-ui-fabric-react';
+
+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;
+
+ return (
+
+
+ {itemCount} item{itemCount > 1 ? 's' : ''} left
+
+ props.clear()}>Clear Completed
+
+ );
+};
diff --git a/step2-02/src/components/TodoHeader.tsx b/step2-02/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..fb6aef3
--- /dev/null
+++ b/step2-02/src/components/TodoHeader.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { Text, Stack } from '@uifabric/experiments';
+import { Pivot, PivotItem, TextField, DefaultButton } from 'office-ui-fabric-react';
+import { FilterTypes } from '../store';
+
+interface TodoHeaderProps {
+ addTodo: (label: string) => void;
+ setFilter: (filter: FilterTypes) => void;
+ filter: string;
+}
+
+interface TodoHeaderState {
+ labelInput: string;
+}
+
+export class TodoHeader extends React.Component {
+ constructor(props: TodoHeaderProps) {
+ super(props);
+ this.state = { labelInput: undefined };
+ }
+
+ render() {
+ return (
+
+
+ todos
+
+
+
+
+
+
+ Add
+
+
+
+
+
+
+
+
+ );
+ }
+
+ private onAdd = () => {
+ this.props.addTodo(this.state.labelInput);
+ this.setState({ labelInput: undefined });
+ };
+
+ private onChange = (evt: React.FormEvent, newValue: string) => {
+ this.setState({ labelInput: newValue });
+ };
+
+ private onFilter = (item: PivotItem) => {
+ this.props.setFilter(item.props.headerText as FilterTypes);
+ };
+}
diff --git a/step2-02/src/components/TodoList.tsx b/step2-02/src/components/TodoList.tsx
new file mode 100644
index 0000000..0e10b82
--- /dev/null
+++ b/step2-02/src/components/TodoList.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { Stack } from '@uifabric/experiments';
+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 class TodoList extends React.Component {
+ render() {
+ const { filter, todos, complete, remove, edit } = this.props;
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+
+ return (
+
+ {filteredTodos.map(id => (
+
+ ))}
+
+ );
+ }
+}
diff --git a/step2-02/src/components/TodoListItem.tsx b/step2-02/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..e2d7909
--- /dev/null
+++ b/step2-02/src/components/TodoListItem.tsx
@@ -0,0 +1,91 @@
+import React from 'react';
+import { Stack } from '@uifabric/experiments';
+import { Checkbox, IconButton, TextField, DefaultButton } from 'office-ui-fabric-react';
+import { mergeStyles } from '@uifabric/styling';
+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;
+}
+
+const className = mergeStyles({
+ selectors: {
+ '.clearButton': {
+ visibility: 'hidden'
+ },
+ '&:hover .clearButton': {
+ visibility: 'visible'
+ }
+ }
+});
+
+export class TodoListItem extends React.Component {
+ /**
+ *
+ */
+ constructor(props: TodoListItemProps) {
+ super(props);
+ this.state = { editing: false, editLabel: undefined };
+ }
+
+ render() {
+ const { todos, id, complete, remove } = this.props;
+ const item = todos[id];
+
+ return (
+
+ {!this.state.editing && (
+ <>
+ complete(id)} />
+
+
+ remove(id)} />
+
+ >
+ )}
+
+ {this.state.editing && (
+
+
+
+
+
+ Save
+
+
+ )}
+
+ );
+ }
+
+ 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, newValue: string) => {
+ this.setState({ editLabel: newValue });
+ };
+}
diff --git a/step2-02/src/index.tsx b/step2-02/src/index.tsx
new file mode 100644
index 0000000..2587243
--- /dev/null
+++ b/step2-02/src/index.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './components/TodoApp';
+import { initializeIcons } from '@uifabric/icons';
+
+// Initializes the UI Fabric icons that we can use
+// Choose one from this list: https://developer.microsoft.com/en-us/fabric#/styles/icons
+initializeIcons();
+
+ReactDOM.render(, document.getElementById('app'));
diff --git a/step2-02/src/store/index.ts b/step2-02/src/store/index.ts
new file mode 100644
index 0000000..221b5f4
--- /dev/null
+++ b/step2-02/src/store/index.ts
@@ -0,0 +1,14 @@
+export type FilterTypes = 'all' | 'active' | 'completed';
+
+export interface TodoItem {
+ label: string;
+ completed: boolean;
+}
+
+export interface Store {
+ todos: {
+ [id: string]: TodoItem;
+ };
+
+ filter: FilterTypes;
+}
diff --git a/webpack.config.js b/webpack.config.js
index e6b0963..8330b18 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,6 +8,7 @@ const entries = {
'step1-06': './step1-06/src/index',
'step1-07': './step1-07/src/index',
'step2-01': './step2-01/src/index',
+ 'step2-02': './step2-02/src/index',
playground: './playground/src/index'
};