From 593df3f4e1895b7a149d0667fe7d39a16dd2a64e Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 22 Feb 2019 14:12:21 -0800 Subject: [PATCH 1/3] updating step 3 with more info in exercise and info about styles prop --- step2-02/README.md | 20 +++++---- step2-03/README.md | 42 ++++++++++++++++++- step2-03/demo/src/components/TodoHeader.tsx | 17 +++++++- step2-03/exercise/src/components/TodoApp.tsx | 1 + .../exercise/src/components/TodoFooter.tsx | 3 ++ 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/step2-02/README.md b/step2-02/README.md index 3e1011e..a5cfe10 100644 --- a/step2-02/README.md +++ b/step2-02/README.md @@ -15,10 +15,16 @@ https://developer.microsoft.com/en-us/fabric/#/components # Exercise -- Open up the TSX files inside `components/` -- Replace the DOM tags with Fabric components in those TSX files with these components: - - Stack - - DefaultButton - - Checkbox - - TextField - - Pivot (for the filter) +1. Open up the Documentation for DefaultButton here: +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. diff --git a/step2-03/README.md b/step2-03/README.md index eb3ec4c..f5bf00d 100644 --- a/step2-03/README.md +++ b/step2-03/README.md @@ -2,6 +2,36 @@ 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 `` 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 `
` +- 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 @@ -63,7 +93,7 @@ loadTheme({ 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 any component that you would find. +1. Try applying a merged style `className` as a prop inside `TodoApp` ```tsx import { mergeStyles } from 'office-ui-fabric-react'; @@ -79,3 +109,13 @@ const className = mergeStyles({ ``` 2. Try to give a few components extra padding + +## Customize the Fabric Components + +1. Open `exercise/src/components/TodoFooter.tsx` + +2. Find the `` 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 diff --git a/step2-03/demo/src/components/TodoHeader.tsx b/step2-03/demo/src/components/TodoHeader.tsx index 7a9cae2..9a56b33 100644 --- a/step2-03/demo/src/components/TodoHeader.tsx +++ b/step2-03/demo/src/components/TodoHeader.tsx @@ -29,9 +29,22 @@ export class TodoHeader extends React.Component - + ({ + ...(props.focused && { + field: { + backgroundColor: 'black' + } + }) + })} + /> - Add + + Add + diff --git a/step2-03/exercise/src/components/TodoApp.tsx b/step2-03/exercise/src/components/TodoApp.tsx index c7e0c35..4d52c64 100644 --- a/step2-03/exercise/src/components/TodoApp.tsx +++ b/step2-03/exercise/src/components/TodoApp.tsx @@ -10,6 +10,7 @@ import { TeamsCustomizations } from '@uifabric/theme-samples'; let index = 0; +// TODO: Change this to add other CSS styles like backgroundColor, fontSize, etc const className = mergeStyles({ padding: 25, ...getTheme().effects.elevation4 diff --git a/step2-03/exercise/src/components/TodoFooter.tsx b/step2-03/exercise/src/components/TodoFooter.tsx index 6a44d83..e806563 100644 --- a/step2-03/exercise/src/components/TodoFooter.tsx +++ b/step2-03/exercise/src/components/TodoFooter.tsx @@ -12,6 +12,9 @@ interface TodoFooterProps { export const TodoFooter = (props: TodoFooterProps) => { const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length; + // TODO: play around with the DefaultButton component below with a "styles" prop + // - try it with an object: styles={{ ... }} + // - try it with a function: styles={props => ({ ... })} return ( From 79fdc8cd4798a39bcbf81a33561cdebbc822d647 Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 22 Feb 2019 15:02:53 -0800 Subject: [PATCH 2/3] doing step 4 demo notes --- step2-01/README.md | 10 ++++------ step2-01/exercise/src/stack.ts | 10 ++++++++++ step2-04/README.md | 24 ++++++++++++++++++++---- step2-04/demo/src/index.spec.ts | 13 ++++++++++--- step2-04/demo/src/index.ts | 4 +++- step2-04/demo/src/multiply.ts | 3 +++ step2-04/demo/src/store/index.ts | 14 -------------- step2-04/exercise/src/stack.ts | 14 +++++++++++++- 8 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 step2-01/exercise/src/stack.ts create mode 100644 step2-04/demo/src/multiply.ts delete mode 100644 step2-04/demo/src/store/index.ts diff --git a/step2-01/README.md b/step2-01/README.md index 753bd64..15733d2 100644 --- a/step2-01/README.md +++ b/step2-01/README.md @@ -152,17 +152,15 @@ Please complete all exercises inside the `exercise/src` folder unless otherwise Create inside `index.ts`: -1. a type alias for string union type describing the states of Red-Green-Yellow traffic light +1. a type alias for string union type describing the states of Red-Green-Yellow traffic light: `type TrafficLight = ???` -2. a class hierarchy of your favorite metaphor (e.g. family, autombiles, animals) - -3. describe an object type with an interface +2. describe a type of car with an interface: `interface Car { ... }` ## Generic -Inside `index.ts`, create a generic class for a `Stack` complete with a typed `pop()` and `push()` methods +Inside `stack.ts`, create a generic class for a `Stack` 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` diff --git a/step2-01/exercise/src/stack.ts b/step2-01/exercise/src/stack.ts new file mode 100644 index 0000000..99d1ad3 --- /dev/null +++ b/step2-01/exercise/src/stack.ts @@ -0,0 +1,10 @@ +// TODO: create a Stack generic class here: + +/** + * + * export class Stack { + * push(...) { ... } + * pop(...) { ... } + * } + * + */ diff --git a/step2-04/README.md b/step2-04/README.md index 4bb0b02..0633334 100644 --- a/step2-04/README.md +++ b/step2-04/README.md @@ -2,14 +2,30 @@ Testing Typescript code with jest. jest is a test framework made by Facebook and is very popular in the React and the wider JS ecosystem. We will work on implementing simple unit tests here in this exercise. -We won't go too deeply into mocks in this exercise, but feel free to play around with that yourself by reading up on this: +https://jestjs.io/ -https://jestjs.io/docs/en/mock-functions +- 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. copy the generic `Stack` code you have developed in Step 2.1 +1. Run the tests by running `npm test` at the root of the bootcamp project -2. 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 diff --git a/step2-04/demo/src/index.spec.ts b/step2-04/demo/src/index.spec.ts index 87620da..81d4418 100644 --- a/step2-04/demo/src/index.spec.ts +++ b/step2-04/demo/src/index.spec.ts @@ -1,13 +1,20 @@ import { square } from '.'; +import { multiply } from './multiply'; + +// Mocked here by jest for the entire test module file +jest.mock('./multiply'); describe('jest example', () => { beforeEach(() => { jest.resetModules(); }); - it('should be able to give the square of two numbers', () => { - console.log('test'); - expect(square(5)).toBe(25); + it('should be passing in the multiple two of the same number', () => { + square(5); + + // .toBeCalledTimes() and .toBeCalledWith() only work on mocks - we mocked the multiply function from the + expect(multiply).toBeCalledTimes(1); + expect(multiply).toBeCalledWith(5, 5); }); it('should increment counter', () => { diff --git a/step2-04/demo/src/index.ts b/step2-04/demo/src/index.ts index 0ac7fff..ecdd946 100644 --- a/step2-04/demo/src/index.ts +++ b/step2-04/demo/src/index.ts @@ -1,3 +1,5 @@ +import { multiply } from './multiply'; + let counter = 0; export function getCount() { @@ -13,5 +15,5 @@ export function decrement() { } export function square(x: number) { - return x * x; + return multiply(x, x); } diff --git a/step2-04/demo/src/multiply.ts b/step2-04/demo/src/multiply.ts new file mode 100644 index 0000000..aa528c0 --- /dev/null +++ b/step2-04/demo/src/multiply.ts @@ -0,0 +1,3 @@ +export function multiply(x: number, y: number) { + return x * y; +} diff --git a/step2-04/demo/src/store/index.ts b/step2-04/demo/src/store/index.ts deleted file mode 100644 index 221b5f4..0000000 --- a/step2-04/demo/src/store/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -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/step2-04/exercise/src/stack.ts b/step2-04/exercise/src/stack.ts index 52c8311..267a771 100644 --- a/step2-04/exercise/src/stack.ts +++ b/step2-04/exercise/src/stack.ts @@ -1 +1,13 @@ -// Place your implementation from Step 2-01 exercise here +export class Stack { + private _items: T[] = []; + + push(item: T) { + this._items.push(item); + } + + pop(): T { + if (this._items.length > 0) { + return this._items.pop(); + } + } +} From 5a6a5aa3590e9151f735d92d7ed1b2c326fa1b6b Mon Sep 17 00:00:00 2001 From: Ken Date: Fri, 22 Feb 2019 15:25:12 -0800 Subject: [PATCH 3/3] complete demo notes for step 2-5 --- assets/flux.png | Bin 0 -> 26132 bytes step2-05/README.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 assets/flux.png diff --git a/assets/flux.png b/assets/flux.png new file mode 100644 index 0000000000000000000000000000000000000000..9286f70b05040406167af15587d08c282ad97c6c GIT binary patch literal 26132 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 literal 0 HcmV?d00001 diff --git a/step2-05/README.md b/step2-05/README.md index 66d2f10..9a98566 100644 --- a/step2-05/README.md +++ b/step2-05/README.md @@ -1,5 +1,37 @@ # Step 2.5: Redux: Reducers +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()`.