├── .gitignore ├── 00_Boilerplate ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── index.html │ └── main.ts ├── tsconfig.json └── webpack.config.js ├── 01_HelloReact ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── hello.tsx │ ├── index.html │ ├── main.ts │ └── main.tsx ├── tsconfig.json └── webpack.config.js ├── 02_Properties ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── hello.tsx │ ├── index.html │ ├── main.ts │ └── main.tsx ├── tsconfig.json └── webpack.config.js ├── 03_State ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 04_Callback ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 05_Refactor ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 06_MoveBackToStateless ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 07_Enable ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 08_Colorpicker ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── color.ts │ ├── colordisplayer.tsx │ ├── colorpicker.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 09_ColorpRefactor ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── color.ts │ ├── colordisplayer.tsx │ ├── colorpicker.tsx │ ├── colorslider.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 10_Sidebar ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── nameEdit.tsx │ ├── sidebar.css │ └── sidebar.tsx ├── tsconfig.json └── webpack.config.js ├── 11_TableMock ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── api │ │ └── memberAPI.ts │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── memberRow.tsx │ ├── membersTable.tsx │ ├── model │ │ ├── member.ts │ │ └── memberMockData.ts │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 12_TableHttp ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── api │ │ └── memberAPI.ts │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── memberHead.tsx │ ├── memberRow.tsx │ ├── membersTable.tsx │ ├── model │ │ ├── member.ts │ │ └── memberMockData.ts │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 12_TableHttp_Axios ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── api │ │ └── memberAPI.ts │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── memberHead.tsx │ ├── memberRow.tsx │ ├── membersTable.tsx │ ├── model │ │ ├── member.ts │ │ └── memberMockData.ts │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 13_ShouldUpdate ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── content │ │ ├── five.png │ │ ├── four.png │ │ ├── one.png │ │ ├── site.css │ │ ├── three.png │ │ └── two.png │ ├── face.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 14_ReactRouter ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── nameEdit.tsx │ ├── pageA.tsx │ └── pageB.tsx ├── tsconfig.json └── webpack.config.js ├── 15_LoginForm ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── index.tsx │ │ └── notification.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ └── loginPage.tsx ├── tsconfig.json └── webpack.config.js ├── 16_Validation ├── .babelrc ├── package.json ├── readme.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── forms │ │ │ └── textFieldForm.tsx │ │ ├── index.tsx │ │ └── notification.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ ├── loginPage.tsx │ │ ├── loginValidations.ts │ │ └── viewmodel.ts ├── tsconfig.json └── webpack.config.js ├── 17_Context ├── .babelrc ├── Readme.md ├── package.json ├── readme_es.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── forms │ │ │ └── textFieldForm.tsx │ │ ├── index.tsx │ │ ├── notification.tsx │ │ └── sessionContext.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ ├── loginPage.tsx │ │ ├── loginValidations.ts │ │ └── viewmodel.ts ├── tsconfig.json └── webpack.config.js ├── 18_Hoc ├── .babelrc ├── Readme.md ├── package.json ├── readme_es.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── forms │ │ │ └── textFieldForm.tsx │ │ ├── index.tsx │ │ ├── notification.tsx │ │ └── sessionContext.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ ├── loginPage.tsx │ │ ├── loginValidations.ts │ │ └── viewmodel.ts ├── tsconfig.json └── webpack.config.js ├── 19_RenderProps ├── .babelrc ├── Readme.md ├── package.json ├── readme_es.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── forms │ │ │ └── textFieldForm.tsx │ │ ├── index.tsx │ │ ├── notification.tsx │ │ └── sessionContext.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ ├── loginPage.tsx │ │ ├── loginValidations.ts │ │ └── viewmodel.ts ├── tsconfig.json └── webpack.config.js ├── 20_ErrorBoundaries ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── app.tsx │ ├── errorBoundary.tsx │ ├── faultyComponent.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 21_Hooks ├── .babelrc ├── package.json ├── readme.md ├── readme_es.md ├── src │ ├── api │ │ └── memberAPI.ts │ ├── app.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── memberHead.tsx │ ├── memberRow.tsx │ ├── membersTable.tsx │ ├── model │ │ ├── member.ts │ │ └── memberMockData.ts │ └── nameEdit.tsx ├── tsconfig.json └── webpack.config.js ├── 22_Hooks_UseContext ├── .babelrc ├── Readme.md ├── package.json ├── readme_es.md ├── src │ ├── api │ │ └── login.ts │ ├── common │ │ ├── forms │ │ │ └── textFieldForm.tsx │ │ ├── index.tsx │ │ ├── notification.tsx │ │ └── sessionContext.tsx │ ├── hello.tsx │ ├── index.html │ ├── main.tsx │ ├── model │ │ └── login.ts │ ├── nameEdit.tsx │ └── pages │ │ ├── b │ │ ├── index.ts │ │ └── pageB.tsx │ │ └── login │ │ ├── index.ts │ │ ├── loginForm.tsx │ │ ├── loginPage.tsx │ │ ├── loginValidations.ts │ │ └── viewmodel.ts ├── tsconfig.json └── webpack.config.js ├── 99_readme_resources ├── 01 HelloReact │ └── browser_output.png └── 04 Callback │ └── browser_output.png ├── LICENSE └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist/ 4 | typings/ 5 | *.orig 6 | .idea/ 7 | */src/**/*.js 8 | */src/**/*.js.map 9 | **/.vscode/ 10 | 10 Sidebar/npm-debug.log 11 | package-lock.json 12 | 00 Boilerplate/package-lock.json 13 | -------------------------------------------------------------------------------- /00_Boilerplate/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /00_Boilerplate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "awesome-typescript-loader": "^5.2.1", 19 | "babel-loader": "^8.0.4", 20 | "css-loader": "^1.0.0", 21 | "file-loader": "^2.0.0", 22 | "html-webpack-plugin": "^3.2.0", 23 | "mini-css-extract-plugin": "^0.4.3", 24 | "style-loader": "^0.23.1", 25 | "typescript": "^3.1.1", 26 | "url-loader": "^1.1.1", 27 | "webpack": "^4.20.2", 28 | "webpack-cli": "^3.1.2", 29 | "webpack-dev-server": "^3.1.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /00_Boilerplate/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /00_Boilerplate/src/main.ts: -------------------------------------------------------------------------------- 1 | document.write("Hello from main.ts !"); -------------------------------------------------------------------------------- /00_Boilerplate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /00_Boilerplate/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.ts' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /01_HelloReact/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /01_HelloReact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /01_HelloReact/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = () => { 4 | return ( 5 |

Hello component !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /01_HelloReact/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /01_HelloReact/src/main.ts: -------------------------------------------------------------------------------- 1 | document.write("Hello from main.ts !"); -------------------------------------------------------------------------------- /01_HelloReact/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { HelloComponent } from './hello'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /01_HelloReact/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /01_HelloReact/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /02_Properties/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /02_Properties/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /02_Properties/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /02_Properties/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /02_Properties/src/main.ts: -------------------------------------------------------------------------------- 1 | document.write("Hello from main.ts !"); -------------------------------------------------------------------------------- /02_Properties/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { HelloComponent } from './hello'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /02_Properties/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /02_Properties/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /03_State/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /03_State/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /03_State/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | } 11 | 12 | export class App extends React.Component { 13 | constructor(props: Props) { 14 | super(props); 15 | 16 | this.state = { userName: 'defaultUserName' }; 17 | } 18 | 19 | setUsernameState = (event) => { 20 | this.setState({ userName: event.target.value }); 21 | } 22 | 23 | 24 | public render() { 25 | return ( 26 | <> 27 | 28 | 29 | 30 | ); 31 | } 32 | } -------------------------------------------------------------------------------- /03_State/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /03_State/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /03_State/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /03_State/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /03_State/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /03_State/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /04_Callback/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /04_Callback/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /04_Callback/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | } 11 | 12 | export class App extends React.Component { 13 | constructor(props: Props) { 14 | super(props); 15 | 16 | this.state = { userName: 'defaultUserName' }; 17 | } 18 | 19 | setUsernameState = (newName: string) => { 20 | this.setState({ userName: newName }); 21 | } 22 | 23 | 24 | public render() { 25 | return ( 26 | <> 27 | 28 | 29 | 30 | ); 31 | } 32 | } -------------------------------------------------------------------------------- /04_Callback/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /04_Callback/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /04_Callback/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /04_Callback/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | initialUserName: string; 5 | onNameUpdated: (newName: string) => any; 6 | } 7 | 8 | interface State { 9 | editingName: string; 10 | } 11 | 12 | 13 | export class NameEditComponent extends React.Component { 14 | 15 | constructor(props: Props) { 16 | super(props); 17 | // Watch out what would happen if we get this user name via an AJAX callback 18 | // you will find a different implementatin on 05 sample 19 | this.state = { editingName: this.props.initialUserName }; 20 | } 21 | 22 | onChange = (event) => { 23 | this.setState({ editingName: event.target.value }); 24 | } 25 | 26 | onNameSubmit = (event: any): any => { 27 | this.props.onNameUpdated(this.state.editingName); 28 | } 29 | 30 | public render() { 31 | return ( 32 | <> 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /04_Callback/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /04_Callback/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /05_Refactor/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /05_Refactor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /05_Refactor/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | editingUserName: string; 11 | } 12 | 13 | export class App extends React.Component { 14 | constructor(props: Props) { 15 | super(props); 16 | 17 | const defaultUserName = 'defaultUserName'; 18 | this.state = { userName: defaultUserName, editingUserName: defaultUserName }; 19 | } 20 | 21 | setUsernameState = () => { 22 | this.setState({ userName: this.state.editingUserName }); 23 | } 24 | 25 | updateEditingName = (editingName: string): void => { 26 | this.setState({ editingUserName: editingName }); 27 | } 28 | 29 | 30 | public render() { 31 | return ( 32 | <> 33 | 34 | 38 | 39 | 40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /05_Refactor/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /05_Refactor/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /05_Refactor/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /05_Refactor/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | editingUserName: string; 5 | onEditingNameUpdated: (newEditingName: string) => void; 6 | onNameUpdateRequest: () => void; 7 | } 8 | 9 | 10 | export class NameEditComponent extends React.Component { 11 | 12 | constructor(props: Props) { 13 | super(props); 14 | } 15 | 16 | onChange = (e: React.ChangeEvent) => { 17 | this.props.onEditingNameUpdated((e.target as HTMLInputElement).value); 18 | } 19 | 20 | 21 | public render() { 22 | return ( 23 | <> 24 | 25 | 27 | 28 | 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /05_Refactor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /05_Refactor/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | editingUserName: string; 11 | } 12 | 13 | export class App extends React.Component { 14 | constructor(props: Props) { 15 | super(props); 16 | 17 | const defaultUserName = 'defaultUserName'; 18 | this.state = { userName: defaultUserName, editingUserName: defaultUserName }; 19 | } 20 | 21 | setUsernameState = () => { 22 | this.setState({ userName: this.state.editingUserName }); 23 | } 24 | 25 | updateEditingName = (editingName: string): void => { 26 | this.setState({ editingUserName: editingName }); 27 | } 28 | 29 | 30 | public render() { 31 | return ( 32 | <> 33 | 34 | 38 | 39 | 40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /06_MoveBackToStateless/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Fragment} from 'react'; 3 | 4 | 5 | interface Props { 6 | editingUserName : string; 7 | onEditingNameUpdated : (newEditingName : string) => void; 8 | onNameUpdateRequest : () => void; 9 | } 10 | 11 | export const NameEditComponent = (props : Props) => 12 |
13 | 14 | props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> 16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /06_MoveBackToStateless/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /07_Enable/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /07_Enable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /07_Enable/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | editingUserName: string; 11 | } 12 | 13 | export class App extends React.Component { 14 | constructor(props: Props) { 15 | super(props); 16 | 17 | const defaultUserName = 'defaultUserName'; 18 | this.state = { userName: defaultUserName, editingUserName: defaultUserName }; 19 | } 20 | 21 | setUsernameState = () => { 22 | this.setState({ userName: this.state.editingUserName }); 23 | } 24 | 25 | updateEditingName = (editingName: string): void => { 26 | this.setState({ editingUserName: editingName }); 27 | } 28 | 29 | 30 | public render() { 31 | return ( 32 | <> 33 | 34 | 39 | 40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /07_Enable/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /07_Enable/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /07_Enable/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /07_Enable/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Fragment} from 'react'; 3 | 4 | 5 | interface Props { 6 | userName : string; 7 | editingUserName : string; 8 | onEditingNameUpdated : (newEditingName : string) => void; 9 | onNameUpdateRequest : () => void; 10 | } 11 | 12 | export const NameEditComponent = (props : Props) => 13 |
14 | 15 | props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> 17 | 18 | 22 |
23 | -------------------------------------------------------------------------------- /07_Enable/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /07_Enable/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /08_Colorpicker/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /08_Colorpicker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /08_Colorpicker/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Color} from './color'; 3 | import {ColorPicker} from './colorpicker'; 4 | import {ColorDisplayer} from './colordisplayer'; 5 | 6 | interface State { 7 | color : Color; 8 | } 9 | 10 | export class App extends React.Component<{}, State> { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = {color: {red: 90, green: 50, blue: 70}}; 15 | } 16 | 17 | setColorState = (newColor : Color) => { 18 | this.setState({color: newColor}); 19 | } 20 | 21 | public render() { 22 | return ( 23 |
24 | 25 | Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] 26 | 27 |
28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /08_Colorpicker/src/color.ts: -------------------------------------------------------------------------------- 1 | export interface Color { 2 | red : number; 3 | green : number; 4 | blue : number; 5 | } -------------------------------------------------------------------------------- /08_Colorpicker/src/colordisplayer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Color} from './color' 3 | 4 | interface Props { 5 | color : Color; 6 | } 7 | 8 | export const ColorDisplayer = (props : Props) => { 9 | 10 | const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. 11 | width: '11rem', 12 | height: '7rem', 13 | backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` 14 | }; 15 | 16 | 17 | return ( 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /08_Colorpicker/src/colorpicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Color } from './color' 3 | 4 | interface Props { 5 | color: Color; 6 | onColorUpdated: (color: Color) => void; 7 | } 8 | 9 | export const ColorPicker = (props: Props) => { 10 | return ( 11 |
12 | props.onColorUpdated( 17 | { red: +event.target.value, green: props.color.green, blue: props.color.blue } 18 | )} 19 | /> 20 | {props.color.red} 21 |
22 | props.onColorUpdated( 27 | { 28 | red: props.color.red, 29 | green: event.target.value, 30 | blue: props.color.blue 31 | } 32 | )} 33 | /> 34 | {props.color.green} 35 |
36 | props.onColorUpdated( 41 | { 42 | red: props.color.red, 43 | green: props.color.green, 44 | blue: event.target.value 45 | } 46 | )} 47 | /> 48 | {props.color.blue} 49 |
50 |
51 | ); 52 | } -------------------------------------------------------------------------------- /08_Colorpicker/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /08_Colorpicker/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /08_Colorpicker/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Fragment} from 'react'; 3 | 4 | 5 | interface Props { 6 | userName : string; 7 | editingUserName : string; 8 | onEditingNameUpdated : (newEditingName : string) => void; 9 | onNameUpdateRequest : () => void; 10 | } 11 | 12 | export const NameEditComponent = (props : Props) => 13 |
14 | 15 | props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> 17 | 18 | 22 |
23 | -------------------------------------------------------------------------------- /08_Colorpicker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /08_Colorpicker/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /09_ColorpRefactor/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /09_ColorpRefactor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /09_ColorpRefactor/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Color} from './color'; 3 | import {ColorPicker} from './colorpicker'; 4 | import {ColorDisplayer} from './colordisplayer'; 5 | 6 | interface State { 7 | color : Color; 8 | } 9 | 10 | export class App extends React.Component<{}, State> { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = {color: {red: 90, green: 50, blue: 70}}; 15 | } 16 | 17 | setColorState = (newColor : Color) => { 18 | this.setState({color: newColor}); 19 | } 20 | 21 | public render() { 22 | return ( 23 |
24 | 25 | Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] 26 | 27 |
28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /09_ColorpRefactor/src/color.ts: -------------------------------------------------------------------------------- 1 | export interface Color { 2 | red : number; 3 | green : number; 4 | blue : number; 5 | } -------------------------------------------------------------------------------- /09_ColorpRefactor/src/colordisplayer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Color} from './color' 3 | 4 | interface Props { 5 | color : Color; 6 | } 7 | 8 | export const ColorDisplayer = (props : Props) => { 9 | 10 | const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. 11 | width: '11rem', 12 | height: '7rem', 13 | backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` 14 | }; 15 | 16 | 17 | return ( 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /09_ColorpRefactor/src/colorpicker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Color } from './color' 3 | import { ColorSliderComponent } from './colorslider'; 4 | 5 | interface Props { 6 | color: Color; 7 | onColorUpdated: (color: Color) => void; 8 | } 9 | 10 | const updateColor = (props: Props, colorId: keyof Color) => (value) => { // keyof Color ensures only 'red', 'blue' or 'green' can be passed in. 11 | props.onColorUpdated({ 12 | ...props.color, // this creates a clone of the current props.color object... 13 | [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value. 14 | }); 15 | }; 16 | 17 | 18 | export const ColorPicker = (props: Props) => { 19 | return ( 20 |
21 | 27 |
28 | 34 |
35 | 41 |
42 |
43 | ); 44 | } -------------------------------------------------------------------------------- /09_ColorpRefactor/src/colorslider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Color} from './color'; 3 | 4 | interface Props { 5 | value : number; 6 | onValueUpdated : (newValue : number) => void; 7 | } 8 | 9 | export const ColorSliderComponent = (props : Props) => { 10 | 11 | return ( 12 |
13 | props.onValueUpdated(event.target.value)} 18 | /> 19 | {props.value} 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /09_ColorpRefactor/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /09_ColorpRefactor/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /09_ColorpRefactor/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Fragment} from 'react'; 3 | 4 | 5 | interface Props { 6 | userName : string; 7 | editingUserName : string; 8 | onEditingNameUpdated : (newEditingName : string) => void; 9 | onNameUpdateRequest : () => void; 10 | } 11 | 12 | export const NameEditComponent = (props : Props) => 13 |
14 | 15 | props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> 17 | 18 | 22 |
23 | -------------------------------------------------------------------------------- /09_ColorpRefactor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /10_Sidebar/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /10_Sidebar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /10_Sidebar/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | import { SidebarComponent } from './sidebar'; 5 | 6 | interface Props { 7 | } 8 | 9 | interface State { 10 | userName: string; 11 | isSidebarVisible: boolean; 12 | } 13 | 14 | export class App extends React.Component { 15 | constructor(props: Props) { 16 | super(props); 17 | 18 | this.state = { userName: "defaultUserName", isSidebarVisible: false }; 19 | } 20 | 21 | setUsernameState = (event) => { 22 | this.setState({ userName: event.target.value }); 23 | } 24 | 25 | toggleSidebarVisibility = () => { 26 | const newVisibleState = !this.state.isSidebarVisible; 27 | 28 | this.setState({ isSidebarVisible: newVisibleState } as State); 29 | } 30 | 31 | public render() { 32 | return ( 33 | <> 34 | 35 |

Cool Scfi movies

36 | 41 |
42 | 43 | 44 | 45 |
46 | 51 |
52 | 53 | ); 54 | } 55 | } -------------------------------------------------------------------------------- /10_Sidebar/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /10_Sidebar/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /10_Sidebar/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /10_Sidebar/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /10_Sidebar/src/sidebar.css: -------------------------------------------------------------------------------- 1 | /* The side navigation menu */ 2 | .sidenav { 3 | height: 100%; /* 100% Full-height */ 4 | width: 0; /* 0 width - change this with JavaScript */ 5 | position: fixed; /* Stay in place */ 6 | z-index: 1; /* Stay on top */ 7 | top: 0; 8 | left: 0; 9 | background-color: #808080; /* Gray*/ 10 | overflow-x: hidden; /* Disable horizontal scroll */ 11 | padding-top: 60px; /* Place content 60px from the top */ 12 | transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */ 13 | } 14 | 15 | 16 | /* Position and style the close button (top right corner) */ 17 | .sidenav .closebtn { 18 | position: absolute; 19 | top: 0; 20 | right: 25px; 21 | font-size: 36px; 22 | margin-left: 50px; 23 | } 24 | 25 | /* Style page content - use this if you want to push the page content to the right when you open the side navigation */ 26 | #main { 27 | transition: margin-left .5s; 28 | padding: 20px; 29 | } 30 | 31 | /* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */ 32 | @media screen and (max-height: 450px) { 33 | .sidenav {padding-top: 15px;} 34 | .sidenav a {font-size: 18px;} 35 | } 36 | -------------------------------------------------------------------------------- /10_Sidebar/src/sidebar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const classNames = require('./sidebar.css'); 4 | 5 | interface Props { 6 | isVisible: boolean; 7 | } 8 | 9 | const divStyle = (props: Props): React.CSSProperties => ({ 10 | width: (props.isVisible) ? '23rem' : '0rem' 11 | }); 12 | 13 | 14 | export const SidebarComponent: React.StatelessComponent = (props) => 15 |
16 | {props.children} 17 |
18 | -------------------------------------------------------------------------------- /10_Sidebar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /11_TableMock/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /11_TableMock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /11_TableMock/src/api/memberAPI.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from '../model/member'; 2 | import MembersMockData from '../model/memberMockData'; 3 | 4 | // Sync mock data API, inspired from: 5 | // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 6 | class MemberAPI { 7 | //This would be performed on the server in a real app. Just stubbing in. 8 | private _clone (item) { 9 | return JSON.parse(JSON.stringify(item)); //return cloned copy so that the item is passed by value instead of by reference 10 | }; 11 | 12 | // Just return a copy of the mock data 13 | getAllMembers() : Array { 14 | return this._clone(MembersMockData); 15 | } 16 | } 17 | 18 | export const memberAPI = new MemberAPI(); 19 | -------------------------------------------------------------------------------- /11_TableMock/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MembersTableComponent} from './membersTable'; 3 | 4 | interface Props { 5 | } 6 | 7 | interface State { 8 | userName: string; 9 | } 10 | 11 | export class App extends React.Component { 12 | constructor(props: Props) { 13 | super(props); 14 | 15 | this.state = { userName: 'defaultUserName' }; 16 | } 17 | 18 | setUsernameState = (event) => { 19 | this.setState({ userName: event.target.value }); 20 | } 21 | 22 | 23 | public render() { 24 | return ( 25 | <> 26 | 27 | 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /11_TableMock/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /11_TableMock/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /11_TableMock/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /11_TableMock/src/memberRow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MemberEntity} from './model/member'; 3 | 4 | export const MemberRow = (props: {member : MemberEntity}) => 5 | 6 | 7 | 8 | 9 | 10 | {props.member.id} 11 | 12 | 13 | {props.member.login} 14 | 15 | 16 | -------------------------------------------------------------------------------- /11_TableMock/src/membersTable.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MemberEntity} from './model/member'; 3 | import {memberAPI} from './api/memberAPI'; 4 | import {MemberRow} from './memberRow'; 5 | 6 | interface Props { 7 | } 8 | 9 | // We define members as a state (the compoment holding this will be a container 10 | // component) 11 | interface State { 12 | members : Array 13 | } 14 | 15 | // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX 16 | export class MembersTableComponent extends React.Component { 17 | 18 | constructor(props : Props){ 19 | super(props); 20 | // set initial state 21 | this.state = {members: []}; 22 | } 23 | 24 | // Standard react lifecycle function: 25 | // https://facebook.github.io/react/docs/component-specs.html 26 | public componentDidMount() { 27 | this.setState({members: memberAPI.getAllMembers()}) 28 | } 29 | 30 | public render() { 31 | 32 | return ( 33 |
34 |

Members Page

35 | 36 | 37 | 38 | 41 | 44 | 47 | 48 | 49 | 50 | { 51 | this.state.members.map((member : MemberEntity) => 52 | 53 | ) 54 | } 55 | 56 |
39 | Avatar 40 | 42 | Id 43 | 45 | Name 46 |
57 |
58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /11_TableMock/src/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface MemberEntity { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | 7 | export const createEmptyMember = () : MemberEntity => ({ 8 | id: -1, 9 | login: "", 10 | avatar_url: "" 11 | }); 12 | -------------------------------------------------------------------------------- /11_TableMock/src/model/memberMockData.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from './member'; 2 | 3 | var MembersMockData : MemberEntity[] = 4 | [ 5 | { 6 | id: 1457912, 7 | login: "brauliodiez", 8 | avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" 9 | }, 10 | { 11 | id: 4374977, 12 | login: "Nasdan", 13 | avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" 14 | } 15 | ]; 16 | 17 | export default MembersMockData; 18 | -------------------------------------------------------------------------------- /11_TableMock/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /11_TableMock/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /11_TableMock/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /12_TableHttp/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /12_TableHttp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "core-js": "^2.5.7", 23 | "css-loader": "^1.0.0", 24 | "file-loader": "^2.0.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "mini-css-extract-plugin": "^0.4.3", 27 | "style-loader": "^0.23.1", 28 | "typescript": "^3.1.1", 29 | "url-loader": "^1.1.1", 30 | "webpack": "^4.20.2", 31 | "webpack-cli": "^3.1.2", 32 | "webpack-dev-server": "^3.1.9", 33 | "whatwg-fetch": "^3.0.0" 34 | }, 35 | "dependencies": { 36 | "react": "^16.5.2", 37 | "react-dom": "^16.5.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /12_TableHttp/src/api/memberAPI.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from '../model/member'; 2 | 3 | // Sync mock data API, inspired from: 4 | // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 5 | class MemberAPI { 6 | 7 | // Just return a copy of the mock data 8 | getAllMembers() : Promise { 9 | const gitHubMembersUrl : string = 'https://api.github.com/orgs/lemoncode/members'; 10 | 11 | return fetch(gitHubMembersUrl) 12 | .then((response) => this.checkStatus(response)) 13 | .then((response) => this.parseJSON(response)) 14 | .then((data) => this.resolveMembers(data)); 15 | } 16 | 17 | private checkStatus(response : Response) : Promise { 18 | if (response.status >= 200 && response.status < 300) { 19 | return Promise.resolve(response); 20 | } else { 21 | let error = new Error(response.statusText); 22 | throw error; 23 | } 24 | } 25 | 26 | private parseJSON(response : Response) : any { 27 | return response.json(); 28 | } 29 | 30 | private resolveMembers (data : any) : Promise { 31 | const members = data.map((gitHubMember) => { 32 | var member : MemberEntity = { 33 | id: gitHubMember.id, 34 | login: gitHubMember.login, 35 | avatar_url: gitHubMember.avatar_url, 36 | }; 37 | 38 | return member; 39 | }); 40 | 41 | return Promise.resolve(members); 42 | } 43 | } 44 | 45 | export const memberAPI = new MemberAPI(); 46 | -------------------------------------------------------------------------------- /12_TableHttp/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MembersTableComponent} from './membersTable'; 3 | 4 | interface Props { 5 | } 6 | 7 | interface State { 8 | userName: string; 9 | } 10 | 11 | export class App extends React.Component { 12 | constructor(props: Props) { 13 | super(props); 14 | 15 | this.state = { userName: 'defaultUserName' }; 16 | } 17 | 18 | setUsernameState = (event) => { 19 | this.setState({ userName: event.target.value }); 20 | } 21 | 22 | 23 | public render() { 24 | return ( 25 | <> 26 | 27 | 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /12_TableHttp/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /12_TableHttp/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /12_TableHttp/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /12_TableHttp/src/memberHead.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | 4 | export const MemberHead = () => 5 | 6 | 7 | Avatar 8 | 9 | 10 | Id 11 | 12 | 13 | Name 14 | 15 | 16 | -------------------------------------------------------------------------------- /12_TableHttp/src/memberRow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MemberEntity} from './model/member'; 3 | 4 | export const MemberRow = (props: {member : MemberEntity}) => 5 | 6 | 7 | 8 | 9 | 10 | {props.member.id} 11 | 12 | 13 | {props.member.login} 14 | 15 | 16 | -------------------------------------------------------------------------------- /12_TableHttp/src/membersTable.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | import { memberAPI } from './api/memberAPI'; 4 | import { MemberRow } from './memberRow'; 5 | import { MemberHead } from './memberHead'; 6 | import {} from 'core-js'; 7 | 8 | interface Props { 9 | } 10 | 11 | // We define members as a state (the compoment holding this will be a container 12 | // component) 13 | interface State { 14 | members: Array 15 | } 16 | 17 | // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX 18 | export class MembersTableComponent extends React.Component { 19 | 20 | constructor(props: Props) { 21 | super(props); 22 | // set initial state 23 | this.state = { members: [] }; 24 | } 25 | 26 | // Standard react lifecycle function: 27 | // https://facebook.github.io/react/docs/component-specs.html 28 | public componentDidMount() { 29 | memberAPI.getAllMembers().then((members) => 30 | this.setState({ members: members }) 31 | ); 32 | } 33 | 34 | public render() { 35 | 36 | return ( 37 |
38 |

Members Page

39 | 40 | 41 | 42 | 43 | 44 | { 45 | this.state.members.map((member: MemberEntity) => 46 | 47 | ) 48 | } 49 | 50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /12_TableHttp/src/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface MemberEntity { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | 7 | export const createEmptyMember = () : MemberEntity => ({ 8 | id: -1, 9 | login: "", 10 | avatar_url: "" 11 | }); 12 | -------------------------------------------------------------------------------- /12_TableHttp/src/model/memberMockData.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from './member'; 2 | 3 | var MembersMockData : MemberEntity[] = 4 | [ 5 | { 6 | id: 1457912, 7 | login: "brauliodiez", 8 | avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" 9 | }, 10 | { 11 | id: 4374977, 12 | login: "Nasdan", 13 | avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" 14 | } 15 | ]; 16 | 17 | export default MembersMockData; 18 | -------------------------------------------------------------------------------- /12_TableHttp/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /12_TableHttp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "core-js": "^2.5.7", 23 | "css-loader": "^1.0.0", 24 | "file-loader": "^2.0.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "mini-css-extract-plugin": "^0.4.3", 27 | "style-loader": "^0.23.1", 28 | "typescript": "^3.1.1", 29 | "url-loader": "^1.1.1", 30 | "webpack": "^4.20.2", 31 | "webpack-cli": "^3.1.2", 32 | "webpack-dev-server": "^3.1.9" 33 | }, 34 | "dependencies": { 35 | "axios": "^0.18.0", 36 | "react": "^16.5.2", 37 | "react-dom": "^16.5.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/api/memberAPI.ts: -------------------------------------------------------------------------------- 1 | import Axios, { AxiosResponse } from 'axios'; 2 | import { MemberEntity } from '../model/member'; 3 | 4 | const gitHubURL = 'https://api.github.com'; 5 | const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; 6 | 7 | const getAllMembers = (): Promise => { 8 | const promise: Promise = new Promise((resolve, reject) => { 9 | try { 10 | Axios.get(gitHubMembersUrl) 11 | .then(response => resolve(mapMemberListApiToModel(response))); 12 | } catch (ex) { 13 | reject(ex); 14 | } 15 | }); 16 | 17 | return promise; 18 | }; 19 | 20 | const mapMemberListApiToModel = ({ data }: AxiosResponse) => 21 | data.map(gitHubMember => gitHubMember); 22 | 23 | export const memberAPI = { 24 | getAllMembers, 25 | }; 26 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MembersTableComponent} from './membersTable'; 3 | 4 | interface Props { 5 | } 6 | 7 | interface State { 8 | userName: string; 9 | } 10 | 11 | export class App extends React.Component { 12 | constructor(props: Props) { 13 | super(props); 14 | 15 | this.state = { userName: 'defaultUserName' }; 16 | } 17 | 18 | setUsernameState = (event) => { 19 | this.setState({ userName: event.target.value }); 20 | } 21 | 22 | 23 | public render() { 24 | return ( 25 | <> 26 | 27 | 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/memberHead.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | 4 | export const MemberHead = () => 5 | 6 | 7 | Avatar 8 | 9 | 10 | Id 11 | 12 | 13 | Name 14 | 15 | 16 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/memberRow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MemberEntity} from './model/member'; 3 | 4 | export const MemberRow = (props: {member : MemberEntity}) => 5 | 6 | 7 | 8 | 9 | 10 | {props.member.id} 11 | 12 | 13 | {props.member.login} 14 | 15 | 16 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/membersTable.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | import { memberAPI } from './api/memberAPI'; 4 | import { MemberRow } from './memberRow'; 5 | import { MemberHead } from './memberHead'; 6 | import {} from 'core-js'; 7 | 8 | interface Props { 9 | } 10 | 11 | // We define members as a state (the compoment holding this will be a container 12 | // component) 13 | interface State { 14 | members: Array 15 | } 16 | 17 | // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX 18 | export class MembersTableComponent extends React.Component { 19 | 20 | constructor(props: Props) { 21 | super(props); 22 | // set initial state 23 | this.state = { members: [] }; 24 | } 25 | 26 | // Standard react lifecycle function: 27 | // https://facebook.github.io/react/docs/component-specs.html 28 | public componentDidMount() { 29 | memberAPI.getAllMembers().then((members) => 30 | this.setState({ members }) 31 | ); 32 | } 33 | 34 | public render() { 35 | 36 | return ( 37 |
38 |

Members Page

39 | 40 | 41 | 42 | 43 | 44 | { 45 | this.state.members.map((member: MemberEntity) => 46 | 47 | ) 48 | } 49 | 50 |
51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface MemberEntity { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | 7 | export const createEmptyMember = () : MemberEntity => ({ 8 | id: -1, 9 | login: "", 10 | avatar_url: "" 11 | }); 12 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/model/memberMockData.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from './member'; 2 | 3 | var MembersMockData : MemberEntity[] = 4 | [ 5 | { 6 | id: 1457912, 7 | login: "brauliodiez", 8 | avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" 9 | }, 10 | { 11 | id: 4374977, 12 | login: "Nasdan", 13 | avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" 14 | } 15 | ]; 16 | 17 | export default MembersMockData; 18 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /12_TableHttp_Axios/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, 'src'), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'], 12 | }, 13 | entry: ['@babel/polyfill', './main.tsx'], 14 | output: { 15 | path: path.join(basePath, 'dist'), 16 | filename: 'bundle.js', 17 | }, 18 | devtool: 'source-map', 19 | devServer: { 20 | contentBase: './dist', // Content base 21 | inline: true, // Enable watch and live reload 22 | host: 'localhost', 23 | port: 8080, 24 | stats: 'errors-only', 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.(ts|tsx)$/, 30 | exclude: /node_modules/, 31 | loader: 'awesome-typescript-loader', 32 | options: { 33 | useBabel: true, 34 | babelCore: '@babel/core', // needed for Babel v7 35 | }, 36 | }, 37 | { 38 | test: /\.css$/, 39 | use: [MiniCssExtractPlugin.loader, 'css-loader'], 40 | }, 41 | { 42 | test: /\.(png|jpg|gif|svg)$/, 43 | loader: 'file-loader', 44 | options: { 45 | name: 'assets/img/[name].[ext]?[hash]', 46 | }, 47 | }, 48 | ], 49 | }, 50 | plugins: [ 51 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 52 | new HtmlWebpackPlugin({ 53 | filename: 'index.html', //Name of file in ./dist/ 54 | template: 'index.html', //Name of template in ./src 55 | hash: true, 56 | }), 57 | new MiniCssExtractPlugin({ 58 | filename: '[name].css', 59 | chunkFilename: '[id].css', 60 | }), 61 | ], 62 | }; 63 | -------------------------------------------------------------------------------- /13_ShouldUpdate/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /13_ShouldUpdate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {FaceComponent} from './face'; 3 | 4 | interface Props { 5 | } 6 | 7 | interface State { 8 | satisfactionLevel : number; 9 | } 10 | 11 | export class App extends React.Component { 12 | constructor(props: Props) { 13 | super(props); 14 | 15 | this.state = {satisfactionLevel: 300}; 16 | } 17 | 18 | public render() { 19 | return ( 20 |
21 | this.setState({satisfactionLevel :event.target.value} as State)} 26 | /> 27 |
28 | {this.state.satisfactionLevel} 29 |
30 | 31 |
32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/five.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/13_ShouldUpdate/src/content/five.png -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/four.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/13_ShouldUpdate/src/content/four.png -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/13_ShouldUpdate/src/content/one.png -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/site.css: -------------------------------------------------------------------------------- 1 | .very-dissatisfied { 2 | width:100%; 3 | height:80px; 4 | background:url('./one.png') no-repeat;; 5 | } 6 | 7 | .somewhat-dissatisfied { 8 | width:100%; 9 | height:80px; 10 | background:url('./two.png') no-repeat; 11 | } 12 | 13 | .neither { 14 | width:100%; 15 | height:80px; 16 | background:url('./three.png') no-repeat; 17 | } 18 | 19 | .somewhat-satisfied { 20 | width:100%; 21 | height:80px; 22 | background:url('./four.png') no-repeat; 23 | } 24 | 25 | .very-satisfied { 26 | width:100%; 27 | height:80px; 28 | background:url('./five.png') no-repeat; 29 | } 30 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/13_ShouldUpdate/src/content/three.png -------------------------------------------------------------------------------- /13_ShouldUpdate/src/content/two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/13_ShouldUpdate/src/content/two.png -------------------------------------------------------------------------------- /13_ShouldUpdate/src/face.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const setSatisfactionClass = (level: number) => { 4 | if (level < 100) { 5 | return "very-dissatisfied" 6 | } 7 | 8 | if (level < 200) { 9 | return "somewhat-dissatisfied" 10 | } 11 | 12 | if (level < 300) { 13 | return "neither" 14 | } 15 | 16 | if (level < 400) { 17 | return "somewhat-satisfied" 18 | } 19 | 20 | return "very-satisfied" 21 | } 22 | 23 | const isRangeChange = (oldValue: number, newValue: number) => { 24 | const oldValueClass = setSatisfactionClass(oldValue); 25 | const newValueClass = setSatisfactionClass(newValue); 26 | 27 | return oldValueClass !== newValueClass; 28 | } 29 | 30 | interface Props { 31 | level: number; 32 | } 33 | 34 | export class FaceComponent extends React.Component { 35 | shouldComponentUpdate(nextProps: Props, nextState) { 36 | return isRangeChange(this.props.level, nextProps.level); 37 | } 38 | 39 | render() { 40 | return ( 41 |
42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /13_ShouldUpdate/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /13_ShouldUpdate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /14_ReactRouter/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /14_ReactRouter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "@types/react-router-dom": "^4.3.1", 21 | "awesome-typescript-loader": "^5.2.1", 22 | "babel-loader": "^8.0.4", 23 | "css-loader": "^1.0.0", 24 | "file-loader": "^2.0.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "mini-css-extract-plugin": "^0.4.3", 27 | "style-loader": "^0.23.1", 28 | "typescript": "^3.1.1", 29 | "url-loader": "^1.1.1", 30 | "webpack": "^4.20.2", 31 | "webpack-cli": "^3.1.2", 32 | "webpack-dev-server": "^3.1.9" 33 | }, 34 | "dependencies": { 35 | "react": "^16.5.2", 36 | "react-dom": "^16.5.2", 37 | "react-router-dom": "^4.3.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /14_ReactRouter/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | 5 | interface Props { 6 | } 7 | 8 | interface State { 9 | userName: string; 10 | } 11 | 12 | export class App extends React.Component { 13 | constructor(props: Props) { 14 | super(props); 15 | 16 | this.state = { userName: 'defaultUserName' }; 17 | } 18 | 19 | setUsernameState = (event) => { 20 | this.setState({ userName: event.target.value }); 21 | } 22 | 23 | 24 | public render() { 25 | return ( 26 | <> 27 | 28 | 29 | 30 | ); 31 | } 32 | } -------------------------------------------------------------------------------- /14_ReactRouter/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /14_ReactRouter/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /14_ReactRouter/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import {PageA} from './pageA'; 5 | import {PageB} from './pageB'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | 12 | 13 | 14 | ,document.getElementById('root') 15 | ); 16 | -------------------------------------------------------------------------------- /14_ReactRouter/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /14_ReactRouter/src/pageA.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | 4 | export const PageA = () => 5 |
6 |

Hello from page A

7 |
8 | Navigate to Page B 9 |
10 | -------------------------------------------------------------------------------- /14_ReactRouter/src/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | 4 | export const PageB = () => 5 |
6 |

Hello from page B

7 |
8 | Navigate to Page A 9 |
10 | -------------------------------------------------------------------------------- /14_ReactRouter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /14_ReactRouter/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /15_LoginForm/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /15_LoginForm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "mini-css-extract-plugin": "^0.4.3", 29 | "style-loader": "^0.23.1", 30 | "typescript": "^3.1.1", 31 | "url-loader": "^1.1.1", 32 | "webpack": "^4.20.2", 33 | "webpack-cli": "^3.1.2", 34 | "webpack-dev-server": "^3.1.9" 35 | }, 36 | "dependencies": { 37 | "react": "^16.5.2", 38 | "react-dom": "^16.5.2", 39 | "react-router-dom": "^4.3.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /15_LoginForm/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /15_LoginForm/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; -------------------------------------------------------------------------------- /15_LoginForm/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /15_LoginForm/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /15_LoginForm/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /15_LoginForm/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | 8 | const theme = createMuiTheme({ 9 | typography: { 10 | useNextVariants: true, 11 | }, 12 | }); 13 | 14 | ReactDOM.render( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | , document.getElementById('root') 24 | ); 25 | -------------------------------------------------------------------------------- /15_LoginForm/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /15_LoginForm/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /15_LoginForm/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /15_LoginForm/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | 4 | export const PageB = () => 5 |
6 |

Hello from page B

7 |
8 | Navigate to Login 9 |
10 | -------------------------------------------------------------------------------- /15_LoginForm/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /15_LoginForm/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | 6 | interface Props { 7 | onLogin: () => void; 8 | onUpdateField: (string, any) => void; 9 | loginInfo : LoginEntity; 10 | } 11 | 12 | export const LoginForm = (props: Props) => { 13 | const { onLogin, onUpdateField, loginInfo } = props; 14 | 15 | const onTexFieldChange = (fieldId) => (e) => { 16 | onUpdateField(fieldId, e.target.value); 17 | } 18 | 19 | return ( 20 |
21 | 27 | 34 | 37 |
38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /15_LoginForm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /16_Validation/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /16_Validation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lc-form-validation": "^2.0.0", 29 | "mini-css-extract-plugin": "^0.4.3", 30 | "style-loader": "^0.23.1", 31 | "typescript": "^3.1.1", 32 | "url-loader": "^1.1.1", 33 | "webpack": "^4.20.2", 34 | "webpack-cli": "^3.1.2", 35 | "webpack-dev-server": "^3.1.9" 36 | }, 37 | "dependencies": { 38 | "react": "^16.5.2", 39 | "react-dom": "^16.5.2", 40 | "react-router-dom": "^4.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /16_Validation/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /16_Validation/src/common/forms/textFieldForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Typography from "@material-ui/core/Typography/Typography"; 4 | 5 | interface Props { 6 | name: string; 7 | label: string; 8 | onChange: any; 9 | value: string; 10 | error?: string; 11 | type? : string; 12 | } 13 | 14 | const defaultProps : Partial = { 15 | type: 'text', 16 | } 17 | 18 | const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { 19 | onChange(fieldId, e.target.value); 20 | } 21 | 22 | export const TextFieldForm : React.StatelessComponent = (props) => { 23 | const {name, label, onChange, value, error, type} = props; 24 | 25 | return ( 26 | <> 27 | 34 | 38 | {props.error} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /16_Validation/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; -------------------------------------------------------------------------------- /16_Validation/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /16_Validation/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /16_Validation/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /16_Validation/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | 8 | const theme = createMuiTheme({ 9 | typography: { 10 | useNextVariants: true, 11 | }, 12 | }); 13 | 14 | ReactDOM.render( 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | , document.getElementById('root') 24 | ); 25 | -------------------------------------------------------------------------------- /16_Validation/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /16_Validation/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /16_Validation/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /16_Validation/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | 4 | export const PageB = () => 5 |
6 |

Hello from page B

7 |
8 | Navigate to Login 9 |
10 | -------------------------------------------------------------------------------- /16_Validation/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /16_Validation/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | import {LoginFormErrors} from './viewmodel'; 6 | import { TextFieldForm } from '../../common/forms/textFieldForm'; 7 | 8 | interface Props { 9 | onLogin: () => void; 10 | onUpdateField: (string, any) => void; 11 | loginInfo : LoginEntity; 12 | loginFormErrors : LoginFormErrors; 13 | } 14 | 15 | export const LoginForm = (props: Props) => { 16 | const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; 17 | 18 | const onTexFieldChange = (fieldId) => (e) => { 19 | onUpdateField(fieldId, e.target.value); 20 | } 21 | 22 | return ( 23 |
24 | 31 | 38 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /16_Validation/src/pages/login/loginValidations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createFormValidation, ValidationConstraints, Validators, 3 | } from 'lc-form-validation'; 4 | 5 | const loginFormValidationConstraints: ValidationConstraints = { 6 | fields: { 7 | login: [ 8 | { validator: Validators.required }, 9 | ], 10 | password: [ 11 | { validator: Validators.required }, 12 | ], 13 | }, 14 | }; 15 | 16 | export const loginFormValidation = createFormValidation(loginFormValidationConstraints); 17 | -------------------------------------------------------------------------------- /16_Validation/src/pages/login/viewmodel.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationResult } from 'lc-form-validation'; 2 | 3 | export interface LoginFormErrors { 4 | login: FieldValidationResult; 5 | password: FieldValidationResult; 6 | } 7 | 8 | export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ 9 | login: new FieldValidationResult(), 10 | password: new FieldValidationResult(), 11 | }); 12 | -------------------------------------------------------------------------------- /16_Validation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /17_Context/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /17_Context/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lc-form-validation": "^2.0.0", 29 | "mini-css-extract-plugin": "^0.4.3", 30 | "style-loader": "^0.23.1", 31 | "typescript": "^3.1.1", 32 | "url-loader": "^1.1.1", 33 | "webpack": "^4.20.2", 34 | "webpack-cli": "^3.1.2", 35 | "webpack-dev-server": "^3.1.9" 36 | }, 37 | "dependencies": { 38 | "react": "^16.5.2", 39 | "react-dom": "^16.5.2", 40 | "react-router-dom": "^4.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /17_Context/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /17_Context/src/common/forms/textFieldForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Typography from "@material-ui/core/Typography/Typography"; 4 | 5 | interface Props { 6 | name: string; 7 | label: string; 8 | onChange: any; 9 | value: string; 10 | error?: string; 11 | type? : string; 12 | } 13 | 14 | const defaultProps : Partial = { 15 | type: 'text', 16 | } 17 | 18 | const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { 19 | onChange(fieldId, e.target.value); 20 | } 21 | 22 | export const TextFieldForm : React.StatelessComponent = (props) => { 23 | const {name, label, onChange, value, error, type} = props; 24 | 25 | return ( 26 | <> 27 | 34 | 38 | {props.error} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /17_Context/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; 2 | export * from './sessionContext'; -------------------------------------------------------------------------------- /17_Context/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /17_Context/src/common/sessionContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export interface SessionContextProps { 4 | login: string; 5 | updateLogin: (value) => void; 6 | } 7 | 8 | export const createDefaultUser = (): SessionContextProps => ({ 9 | login: 'no user', 10 | updateLogin: (value) => { }, 11 | }); 12 | 13 | export const SessionContext = React.createContext(createDefaultUser()); 14 | 15 | interface State extends SessionContextProps { 16 | } 17 | 18 | export class SessionProvider extends React.Component<{}, State> { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | login: createDefaultUser().login, 24 | updateLogin: this.setLoginInfo 25 | } 26 | } 27 | 28 | setLoginInfo = (newLogin) => { 29 | this.setState({ login: newLogin }) 30 | } 31 | 32 | render() { 33 | return ( 34 | 35 | {this.props.children} 36 | 37 | ) 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /17_Context/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /17_Context/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /17_Context/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | import { SessionProvider } from './common'; 8 | 9 | const theme = createMuiTheme({ 10 | typography: { 11 | useNextVariants: true, 12 | }, 13 | }); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | , document.getElementById('root') 27 | ); 28 | -------------------------------------------------------------------------------- /17_Context/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /17_Context/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /17_Context/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /17_Context/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | import { SessionContext } from '../../common/' 4 | 5 | export const PageB = () => 6 |
7 | 8 | { 9 | ({ login }) => ( 10 | <> 11 |

Hello from page B

12 |
13 |
14 |

Login: {login}

15 | 16 | Navigate to Login 17 | 18 | ) 19 | } 20 |
21 |
22 | -------------------------------------------------------------------------------- /17_Context/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /17_Context/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | import {LoginFormErrors} from './viewmodel'; 6 | import { TextFieldForm } from '../../common/forms/textFieldForm'; 7 | 8 | interface Props { 9 | onLogin: () => void; 10 | onUpdateField: (string, any) => void; 11 | loginInfo : LoginEntity; 12 | loginFormErrors : LoginFormErrors; 13 | } 14 | 15 | export const LoginForm = (props: Props) => { 16 | const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; 17 | 18 | const onTexFieldChange = (fieldId) => (e) => { 19 | onUpdateField(fieldId, e.target.value); 20 | } 21 | 22 | return ( 23 |
24 | 31 | 38 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /17_Context/src/pages/login/loginValidations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createFormValidation, ValidationConstraints, Validators, 3 | } from 'lc-form-validation'; 4 | 5 | const loginFormValidationConstraints: ValidationConstraints = { 6 | fields: { 7 | login: [ 8 | { validator: Validators.required }, 9 | ], 10 | password: [ 11 | { validator: Validators.required }, 12 | ], 13 | }, 14 | }; 15 | 16 | export const loginFormValidation = createFormValidation(loginFormValidationConstraints); 17 | -------------------------------------------------------------------------------- /17_Context/src/pages/login/viewmodel.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationResult } from 'lc-form-validation'; 2 | 3 | export interface LoginFormErrors { 4 | login: FieldValidationResult; 5 | password: FieldValidationResult; 6 | } 7 | 8 | export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ 9 | login: new FieldValidationResult(), 10 | password: new FieldValidationResult(), 11 | }); 12 | -------------------------------------------------------------------------------- /17_Context/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /18_Hoc/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /18_Hoc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lc-form-validation": "^2.0.0", 29 | "mini-css-extract-plugin": "^0.4.3", 30 | "style-loader": "^0.23.1", 31 | "typescript": "^3.1.1", 32 | "url-loader": "^1.1.1", 33 | "webpack": "^4.20.2", 34 | "webpack-cli": "^3.1.2", 35 | "webpack-dev-server": "^3.1.9" 36 | }, 37 | "dependencies": { 38 | "react": "^16.5.2", 39 | "react-dom": "^16.5.2", 40 | "react-router-dom": "^4.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /18_Hoc/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /18_Hoc/src/common/forms/textFieldForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Typography from "@material-ui/core/Typography/Typography"; 4 | 5 | interface Props { 6 | name: string; 7 | label: string; 8 | onChange: any; 9 | value: string; 10 | error?: string; 11 | type? : string; 12 | } 13 | 14 | const defaultProps : Partial = { 15 | type: 'text', 16 | } 17 | 18 | const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { 19 | onChange(fieldId, e.target.value); 20 | } 21 | 22 | export const TextFieldForm : React.StatelessComponent = (props) => { 23 | const {name, label, onChange, value, error, type} = props; 24 | 25 | return ( 26 | <> 27 | 34 | 38 | {props.error} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /18_Hoc/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; 2 | export * from './sessionContext'; -------------------------------------------------------------------------------- /18_Hoc/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /18_Hoc/src/common/sessionContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export interface SessionContextProps { 4 | login: string; 5 | updateLogin: (value) => void; 6 | } 7 | 8 | export const createDefaultUser = (): SessionContextProps => ({ 9 | login: 'no user', 10 | updateLogin: (value) => { }, 11 | }); 12 | 13 | export const SessionContext = React.createContext(createDefaultUser()); 14 | 15 | interface State extends SessionContextProps { 16 | } 17 | 18 | export class SessionProvider extends React.Component<{}, State> { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | login: createDefaultUser().login, 24 | updateLogin: this.setLoginInfo 25 | } 26 | } 27 | 28 | setLoginInfo = (newLogin) => { 29 | this.setState({ login: newLogin }) 30 | } 31 | 32 | render() { 33 | return ( 34 | 35 | {this.props.children} 36 | 37 | ) 38 | }; 39 | }; 40 | 41 | export const withSessionContext = (Component) => (props) => ( 42 | 43 | { 44 | ({ login, updateLogin }) => ( 45 | 50 | ) 51 | } 52 | 53 | ); 54 | -------------------------------------------------------------------------------- /18_Hoc/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /18_Hoc/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /18_Hoc/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | import { SessionProvider } from './common'; 8 | 9 | const theme = createMuiTheme({ 10 | typography: { 11 | useNextVariants: true, 12 | }, 13 | }); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | , document.getElementById('root') 27 | ); 28 | -------------------------------------------------------------------------------- /18_Hoc/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /18_Hoc/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /18_Hoc/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /18_Hoc/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | import { SessionContext, withSessionContext } from '../../common/' 4 | 5 | interface Props { 6 | login : string; 7 | } 8 | 9 | const PageBInner = (props : Props) => 10 | <> 11 |

Hello from page B

12 |
13 |
14 |

Login: {props.login}

15 | 16 | Navigate to Login 17 | 18 | 19 | export const PageB = withSessionContext(PageBInner); -------------------------------------------------------------------------------- /18_Hoc/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /18_Hoc/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | import {LoginFormErrors} from './viewmodel'; 6 | import { TextFieldForm } from '../../common/forms/textFieldForm'; 7 | 8 | interface Props { 9 | onLogin: () => void; 10 | onUpdateField: (string, any) => void; 11 | loginInfo : LoginEntity; 12 | loginFormErrors : LoginFormErrors; 13 | } 14 | 15 | export const LoginForm = (props: Props) => { 16 | const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; 17 | 18 | const onTexFieldChange = (fieldId) => (e) => { 19 | onUpdateField(fieldId, e.target.value); 20 | } 21 | 22 | return ( 23 |
24 | 31 | 38 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /18_Hoc/src/pages/login/loginValidations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createFormValidation, ValidationConstraints, Validators, 3 | } from 'lc-form-validation'; 4 | 5 | const loginFormValidationConstraints: ValidationConstraints = { 6 | fields: { 7 | login: [ 8 | { validator: Validators.required }, 9 | ], 10 | password: [ 11 | { validator: Validators.required }, 12 | ], 13 | }, 14 | }; 15 | 16 | export const loginFormValidation = createFormValidation(loginFormValidationConstraints); 17 | -------------------------------------------------------------------------------- /18_Hoc/src/pages/login/viewmodel.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationResult } from 'lc-form-validation'; 2 | 3 | export interface LoginFormErrors { 4 | login: FieldValidationResult; 5 | password: FieldValidationResult; 6 | } 7 | 8 | export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ 9 | login: new FieldValidationResult(), 10 | password: new FieldValidationResult(), 11 | }); 12 | -------------------------------------------------------------------------------- /18_Hoc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /18_Hoc/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /19_RenderProps/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /19_RenderProps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lc-form-validation": "^2.0.0", 29 | "mini-css-extract-plugin": "^0.4.3", 30 | "style-loader": "^0.23.1", 31 | "typescript": "^3.1.1", 32 | "url-loader": "^1.1.1", 33 | "webpack": "^4.20.2", 34 | "webpack-cli": "^3.1.2", 35 | "webpack-dev-server": "^3.1.9" 36 | }, 37 | "dependencies": { 38 | "react": "^16.5.2", 39 | "react-dom": "^16.5.2", 40 | "react-router-dom": "^4.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /19_RenderProps/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /19_RenderProps/src/common/forms/textFieldForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Typography from "@material-ui/core/Typography/Typography"; 4 | 5 | interface Props { 6 | name: string; 7 | label: string; 8 | onChange: any; 9 | value: string; 10 | error?: string; 11 | type? : string; 12 | } 13 | 14 | const defaultProps : Partial = { 15 | type: 'text', 16 | } 17 | 18 | const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { 19 | onChange(fieldId, e.target.value); 20 | } 21 | 22 | export const TextFieldForm : React.StatelessComponent = (props) => { 23 | const {name, label, onChange, value, error, type} = props; 24 | 25 | return ( 26 | <> 27 | 34 | 38 | {props.error} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /19_RenderProps/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; 2 | export * from './sessionContext'; -------------------------------------------------------------------------------- /19_RenderProps/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /19_RenderProps/src/common/sessionContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export interface SessionContextProps { 4 | login: string; 5 | updateLogin: (value) => void; 6 | } 7 | 8 | export const createDefaultUser = (): SessionContextProps => ({ 9 | login: 'no user', 10 | updateLogin: (value) => { }, 11 | }); 12 | 13 | export const SessionContext = React.createContext(createDefaultUser()); 14 | 15 | interface State extends SessionContextProps { 16 | } 17 | 18 | export class SessionProvider extends React.Component<{}, State> { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | login: createDefaultUser().login, 24 | updateLogin: this.setLoginInfo 25 | } 26 | } 27 | 28 | setLoginInfo = (newLogin) => { 29 | this.setState({ login: newLogin }) 30 | } 31 | 32 | render() { 33 | return ( 34 | 35 | {this.props.children} 36 | 37 | ) 38 | }; 39 | }; 40 | 41 | interface Props { 42 | render : (login : string) => React.ReactNode; 43 | } 44 | 45 | export class Session extends React.Component { 46 | constructor(props : Props) { 47 | super(props); 48 | } 49 | 50 | render() { 51 | return ( 52 | 53 | { 54 | ({ login, updateLogin }) => 55 | <> 56 | {this.props.render(login)} 57 | 58 | } 59 | 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /19_RenderProps/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /19_RenderProps/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /19_RenderProps/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | import { SessionProvider } from './common'; 8 | 9 | const theme = createMuiTheme({ 10 | typography: { 11 | useNextVariants: true, 12 | }, 13 | }); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | , document.getElementById('root') 27 | ); 28 | -------------------------------------------------------------------------------- /19_RenderProps/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /19_RenderProps/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /19_RenderProps/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /19_RenderProps/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | import { Session } from '../../common/' 4 | import { checkPropTypes } from "prop-types"; 5 | 6 | 7 | interface Props { 8 | login : string; 9 | } 10 | 11 | const LoginComponent = (props: Props) => 12 | <> 13 |

Hello from page B

14 |
15 |
16 |

Login: {props.login}

17 | 18 | Navigate to Login 19 | 20 | 21 | 22 | export const PageB = () => 23 |
24 | ( 27 | 28 | )} 29 | > 30 | 31 |
32 | -------------------------------------------------------------------------------- /19_RenderProps/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /19_RenderProps/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | import {LoginFormErrors} from './viewmodel'; 6 | import { TextFieldForm } from '../../common/forms/textFieldForm'; 7 | 8 | interface Props { 9 | onLogin: () => void; 10 | onUpdateField: (string, any) => void; 11 | loginInfo : LoginEntity; 12 | loginFormErrors : LoginFormErrors; 13 | } 14 | 15 | export const LoginForm = (props: Props) => { 16 | const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; 17 | 18 | const onTexFieldChange = (fieldId) => (e) => { 19 | onUpdateField(fieldId, e.target.value); 20 | } 21 | 22 | return ( 23 |
24 | 31 | 38 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /19_RenderProps/src/pages/login/loginValidations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createFormValidation, ValidationConstraints, Validators, 3 | } from 'lc-form-validation'; 4 | 5 | const loginFormValidationConstraints: ValidationConstraints = { 6 | fields: { 7 | login: [ 8 | { validator: Validators.required }, 9 | ], 10 | password: [ 11 | { validator: Validators.required }, 12 | ], 13 | }, 14 | }; 15 | 16 | export const loginFormValidation = createFormValidation(loginFormValidationConstraints); 17 | -------------------------------------------------------------------------------- /19_RenderProps/src/pages/login/viewmodel.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationResult } from 'lc-form-validation'; 2 | 3 | export interface LoginFormErrors { 4 | login: FieldValidationResult; 5 | password: FieldValidationResult; 6 | } 7 | 8 | export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ 9 | login: new FieldValidationResult(), 10 | password: new FieldValidationResult(), 11 | }); 12 | -------------------------------------------------------------------------------- /19_RenderProps/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "css-loader": "^1.0.0", 23 | "file-loader": "^2.0.0", 24 | "html-webpack-plugin": "^3.2.0", 25 | "mini-css-extract-plugin": "^0.4.3", 26 | "style-loader": "^0.23.1", 27 | "typescript": "^3.1.1", 28 | "url-loader": "^1.1.1", 29 | "webpack": "^4.20.2", 30 | "webpack-cli": "^3.1.2", 31 | "webpack-dev-server": "^3.1.9" 32 | }, 33 | "dependencies": { 34 | "react": "^16.5.2", 35 | "react-dom": "^16.5.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HelloComponent } from './hello'; 3 | import { NameEditComponent } from './nameEdit'; 4 | import { FaultyComponent } from './faultyComponent'; 5 | import { ErrorBoundary } from './errorBoundary'; 6 | 7 | interface Props { 8 | } 9 | 10 | interface State { 11 | userName: string; 12 | } 13 | 14 | export class App extends React.Component { 15 | constructor(props: Props) { 16 | super(props); 17 | 18 | this.state = { userName: 'defaultUserName' }; 19 | } 20 | 21 | setUsernameState = (event) => { 22 | this.setState({ userName: event.target.value }); 23 | } 24 | 25 | 26 | public render() { 27 | return ( 28 | <> 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | } 37 | } -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/errorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'React'; 2 | 3 | export class ErrorBoundary extends React.Component { 4 | state = { error: null, errorInfo: null }; 5 | 6 | componentDidCatch(error, errorInfo) { 7 | this.setState({ 8 | error: error, 9 | errorInfo: errorInfo 10 | }); 11 | } 12 | 13 | render() { 14 | if (this.state.errorInfo) { 15 | return ( 16 |
17 |

Plugin Failed to load, optional error info:

18 |
19 | {this.state.error && this.state.error.toString()} 20 |
21 | {this.state.errorInfo.componentStack} 22 |
23 |
24 | ); 25 | } 26 | 27 | return this.props.children; 28 | } 29 | } -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/faultyComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export class FaultyComponent extends React.Component { 4 | componentDidMount() { 5 | throw "I'm the faulty component, generating a bad crash." 6 | } 7 | 8 | render() { 9 | return ( 10 |

Hello from Faulty Component

11 | ) 12 | } 13 | } -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /20_ErrorBoundaries/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /21_Hooks/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /21_Hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@types/react": "^16.4.16", 19 | "@types/react-dom": "^16.0.9", 20 | "awesome-typescript-loader": "^5.2.1", 21 | "babel-loader": "^8.0.4", 22 | "core-js": "^2.5.7", 23 | "css-loader": "^1.0.0", 24 | "file-loader": "^2.0.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "mini-css-extract-plugin": "^0.4.3", 27 | "style-loader": "^0.23.1", 28 | "typescript": "^3.1.1", 29 | "url-loader": "^1.1.1", 30 | "webpack": "^4.20.2", 31 | "webpack-cli": "^3.1.2", 32 | "webpack-dev-server": "^3.1.9", 33 | "whatwg-fetch": "^3.0.0" 34 | }, 35 | "dependencies": { 36 | "react": "^16.7.0-alpha.0", 37 | "react-dom": "^16.7.0-alpha.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /21_Hooks/src/api/memberAPI.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from '../model/member'; 2 | import {fetch} from 'whatwg-fetch'; 3 | 4 | // Sync mock data API, inspired from: 5 | // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 6 | class MemberAPI { 7 | 8 | // Just return a copy of the mock data 9 | getAllMembers() : Promise { 10 | const gitHubMembersUrl : string = 'https://api.github.com/orgs/lemoncode/members'; 11 | 12 | return fetch(gitHubMembersUrl) 13 | .then((response) => this.checkStatus(response)) 14 | .then((response) => this.parseJSON(response)) 15 | .then((data) => this.resolveMembers(data)); 16 | } 17 | 18 | private checkStatus(response : Response) : Promise { 19 | if (response.status >= 200 && response.status < 300) { 20 | return Promise.resolve(response); 21 | } else { 22 | let error = new Error(response.statusText); 23 | throw error; 24 | } 25 | } 26 | 27 | private parseJSON(response : Response) : any { 28 | return response.json(); 29 | } 30 | 31 | private resolveMembers (data : any) : Promise { 32 | const members = data.map((gitHubMember) => { 33 | var member : MemberEntity = { 34 | id: gitHubMember.id, 35 | login: gitHubMember.login, 36 | avatar_url: gitHubMember.avatar_url, 37 | }; 38 | 39 | return member; 40 | }); 41 | 42 | return Promise.resolve(members); 43 | } 44 | } 45 | 46 | export const memberAPI = new MemberAPI(); 47 | -------------------------------------------------------------------------------- /21_Hooks/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MembersTableComponent} from './membersTable'; 3 | 4 | interface Props { 5 | } 6 | 7 | interface State { 8 | userName: string; 9 | } 10 | 11 | export class App extends React.Component { 12 | constructor(props: Props) { 13 | super(props); 14 | 15 | this.state = { userName: 'defaultUserName' }; 16 | } 17 | 18 | setUsernameState = (event) => { 19 | this.setState({ userName: event.target.value }); 20 | } 21 | 22 | 23 | public render() { 24 | return ( 25 | <> 26 | 27 | 28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /21_Hooks/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /21_Hooks/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Sample app

10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /21_Hooks/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import {App} from './app'; 4 | 5 | import { HelloComponent } from './hello'; 6 | 7 | ReactDOM.render( 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /21_Hooks/src/memberHead.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | 4 | export const MemberHead = () => 5 | 6 | 7 | Avatar 8 | 9 | 10 | Id 11 | 12 | 13 | Name 14 | 15 | 16 | -------------------------------------------------------------------------------- /21_Hooks/src/memberRow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {MemberEntity} from './model/member'; 3 | 4 | export const MemberRow = (props: {member : MemberEntity}) => 5 | 6 | 7 | 8 | 9 | 10 | {props.member.id} 11 | 12 | 13 | {props.member.login} 14 | 15 | 16 | -------------------------------------------------------------------------------- /21_Hooks/src/membersTable.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { MemberEntity } from './model/member'; 3 | import { memberAPI } from './api/memberAPI'; 4 | import { MemberRow } from './memberRow'; 5 | import { MemberHead } from './memberHead'; 6 | import { } from 'core-js'; 7 | 8 | function useMembers() { 9 | const [members, setMembers] = React.useState([]); 10 | 11 | const loadMembers = () => { 12 | memberAPI.getAllMembers().then((members) => 13 | setMembers(members) 14 | ); 15 | } 16 | 17 | return { members, loadMembers}; 18 | } 19 | 20 | export const MembersTableComponent = () => { 21 | const { members, loadMembers } = useMembers(); 22 | 23 | React.useEffect(() => { 24 | loadMembers(); 25 | }, []); 26 | 27 | return ( 28 |
29 |

Members Page

30 | 31 | 32 | 33 | 34 | 35 | { 36 | members.map((member: MemberEntity) => 37 | 38 | ) 39 | } 40 | 41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /21_Hooks/src/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface MemberEntity { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | 7 | export const createEmptyMember = () : MemberEntity => ({ 8 | id: -1, 9 | login: "", 10 | avatar_url: "" 11 | }); 12 | -------------------------------------------------------------------------------- /21_Hooks/src/model/memberMockData.ts: -------------------------------------------------------------------------------- 1 | import {MemberEntity} from './member'; 2 | 3 | var MembersMockData : MemberEntity[] = 4 | [ 5 | { 6 | id: 1457912, 7 | login: "brauliodiez", 8 | avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" 9 | }, 10 | { 11 | id: 4374977, 12 | login: "Nasdan", 13 | avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" 14 | } 15 | ]; 16 | 17 | export default MembersMockData; 18 | -------------------------------------------------------------------------------- /21_Hooks/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /21_Hooks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /21_Hooks/webpack.config.js: -------------------------------------------------------------------------------- 1 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | var webpack = require('webpack'); 4 | var path = require('path'); 5 | 6 | var basePath = __dirname; 7 | 8 | module.exports = { 9 | context: path.join(basePath, "src"), 10 | resolve: { 11 | extensions: ['.js', '.ts', '.tsx'] 12 | }, 13 | entry: ['@babel/polyfill', 14 | './main.tsx' 15 | ], 16 | output: { 17 | path: path.join(basePath, 'dist'), 18 | filename: 'bundle.js' 19 | }, 20 | devtool: 'source-map', 21 | devServer: { 22 | contentBase: './dist', // Content base 23 | inline: true, // Enable watch and live reload 24 | host: 'localhost', 25 | port: 8080, 26 | stats: 'errors-only' 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(ts|tsx)$/, 32 | exclude: /node_modules/, 33 | loader: 'awesome-typescript-loader', 34 | options: { 35 | useBabel: true, 36 | "babelCore": "@babel/core", // needed for Babel v7 37 | }, 38 | }, 39 | { 40 | test: /\.css$/, 41 | use: [MiniCssExtractPlugin.loader, "css-loader"] 42 | }, 43 | { 44 | test: /\.(png|jpg|gif|svg)$/, 45 | loader: 'file-loader', 46 | options: { 47 | name: 'assets/img/[name].[ext]?[hash]' 48 | } 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: 'index.html', //Name of file in ./dist/ 56 | template: 'index.html', //Name of template in ./src 57 | hash: true, 58 | }), 59 | new MiniCssExtractPlugin({ 60 | filename: "[name].css", 61 | chunkFilename: "[id].css" 62 | }), 63 | ], 64 | }; 65 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactbysample", 3 | "version": "1.0.0", 4 | "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --inline --hot --open", 8 | "build": "webpack --mode development", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/cli": "^7.1.2", 15 | "@babel/core": "^7.1.2", 16 | "@babel/polyfill": "^7.0.0", 17 | "@babel/preset-env": "^7.1.0", 18 | "@material-ui/core": "^3.2.0", 19 | "@material-ui/icons": "^3.0.1", 20 | "@types/react": "^16.4.16", 21 | "@types/react-dom": "^16.0.9", 22 | "@types/react-router-dom": "^4.3.1", 23 | "awesome-typescript-loader": "^5.2.1", 24 | "babel-loader": "^8.0.4", 25 | "css-loader": "^1.0.0", 26 | "file-loader": "^2.0.0", 27 | "html-webpack-plugin": "^3.2.0", 28 | "lc-form-validation": "^2.0.0", 29 | "mini-css-extract-plugin": "^0.4.3", 30 | "style-loader": "^0.23.1", 31 | "typescript": "^3.1.1", 32 | "url-loader": "^1.1.1", 33 | "webpack": "^4.20.2", 34 | "webpack-cli": "^3.1.2", 35 | "webpack-dev-server": "^3.1.9" 36 | }, 37 | "dependencies": { 38 | "react": "^16.7.0-alpha.0", 39 | "react-dom": "^16.7.0-alpha.0", 40 | "react-router-dom": "^4.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/api/login.ts: -------------------------------------------------------------------------------- 1 | import {LoginEntity} from '../model/login'; 2 | 3 | // Just a fake loginAPI 4 | export const isValidLogin = (loginInfo : LoginEntity) : boolean => 5 | (loginInfo.login === 'admin' && loginInfo.password === 'test'); 6 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/common/forms/textFieldForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TextField from "@material-ui/core/TextField"; 3 | import Typography from "@material-ui/core/Typography/Typography"; 4 | 5 | interface Props { 6 | name: string; 7 | label: string; 8 | onChange: any; 9 | value: string; 10 | error?: string; 11 | type? : string; 12 | } 13 | 14 | const defaultProps : Partial = { 15 | type: 'text', 16 | } 17 | 18 | const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { 19 | onChange(fieldId, e.target.value); 20 | } 21 | 22 | export const TextFieldForm : React.StatelessComponent = (props) => { 23 | const {name, label, onChange, value, error, type} = props; 24 | 25 | return ( 26 | <> 27 | 34 | 38 | {props.error} 39 | 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './notification'; 2 | export * from './sessionContext'; -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/common/notification.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import Button from '@material-ui/core/Button'; 3 | import Snackbar from '@material-ui/core/Snackbar'; 4 | import IconButton from '@material-ui/core/IconButton'; 5 | import CloseIcon from '@material-ui/icons/Close'; 6 | import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; 7 | 8 | interface Props extends WithStyles { 9 | message: string; 10 | show: boolean; 11 | onClose: () => void; 12 | } 13 | 14 | const styles = theme => createStyles({ 15 | close: { 16 | padding: theme.spacing.unit / 2, 17 | }, 18 | }); 19 | 20 | const NotificationComponentInner = (props: Props) => { 21 | const { classes, message, show, onClose } = props; 22 | 23 | return ( 24 | {message}} 37 | action={[ 38 | 45 | 46 | , 47 | ]} 48 | 49 | /> 50 | ) 51 | } 52 | 53 | export const NotificationComponent = withStyles(styles)(NotificationComponentInner); 54 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/common/sessionContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export interface SessionContextProps { 4 | login: string; 5 | updateLogin: (value) => void; 6 | } 7 | 8 | export const createDefaultUser = (): SessionContextProps => ({ 9 | login: 'no user', 10 | updateLogin: (value) => { }, 11 | }); 12 | 13 | export const SessionContext = React.createContext(createDefaultUser()); 14 | 15 | interface State extends SessionContextProps { 16 | } 17 | 18 | export class SessionProvider extends React.Component<{}, State> { 19 | 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | login: createDefaultUser().login, 24 | updateLogin: this.setLoginInfo 25 | } 26 | } 27 | 28 | setLoginInfo = (newLogin) => { 29 | this.setState({ login: newLogin }) 30 | } 31 | 32 | render() { 33 | return ( 34 | 35 | {this.props.children} 36 | 37 | ) 38 | }; 39 | }; 40 | 41 | interface Props { 42 | render : (login : string) => React.ReactNode; 43 | } 44 | 45 | export class Session extends React.Component { 46 | constructor(props : Props) { 47 | super(props); 48 | } 49 | 50 | render() { 51 | return ( 52 | 53 | { 54 | ({ login, updateLogin }) => 55 | <> 56 | {this.props.render(login)} 57 | 58 | } 59 | 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/hello.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const HelloComponent = (props: {userName : string}) => { 4 | return ( 5 |

Hello user: {props.userName} !

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/main.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter, Switch, Route } from 'react-router-dom'; 4 | import { LoginPage } from './pages/login'; 5 | import { PageB } from './pages/b'; 6 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 7 | import { SessionProvider } from './common'; 8 | 9 | const theme = createMuiTheme({ 10 | typography: { 11 | useNextVariants: true, 12 | }, 13 | }); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | , document.getElementById('root') 27 | ); 28 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/model/login.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login : string; 3 | password : string; 4 | } 5 | 6 | export const createEmptyLogin = () : LoginEntity => ({ 7 | login: '', 8 | password: '', 9 | }); 10 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/nameEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | userName : string; 5 | onChange : (event) => void; 6 | } 7 | 8 | export const NameEditComponent = (props : Props) => 9 | <> 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/b/index.ts: -------------------------------------------------------------------------------- 1 | export {PageB} from './pageB'; -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/b/pageB.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Link } from 'react-router-dom'; 3 | import { Session } from '../../common/'; 4 | import { SessionContext } from '../../common'; 5 | 6 | interface Props { 7 | login : string; 8 | } 9 | 10 | const LoginComponent = (props: Props) => 11 | <> 12 |

Hello from page B

13 |
14 |
15 |

Login: {props.login}

16 | 17 | Navigate to Login 18 | 19 | 20 | 21 | export const PageB = () => { 22 | const loginContext = React.useContext(SessionContext); 23 | 24 | return ( 25 |
26 | 27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export {LoginPage} from './loginPage'; -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/login/loginForm.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import TextField from "@material-ui/core/TextField"; 3 | import Button from "@material-ui/core/Button"; 4 | import { LoginEntity } from "../../model/login"; 5 | import {LoginFormErrors} from './viewmodel'; 6 | import { TextFieldForm } from '../../common/forms/textFieldForm'; 7 | 8 | interface Props { 9 | onLogin: () => void; 10 | onUpdateField: (string, any) => void; 11 | loginInfo : LoginEntity; 12 | loginFormErrors : LoginFormErrors; 13 | } 14 | 15 | export const LoginForm = (props: Props) => { 16 | const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; 17 | 18 | const onTexFieldChange = (fieldId) => (e) => { 19 | onUpdateField(fieldId, e.target.value); 20 | } 21 | 22 | return ( 23 |
24 | 31 | 38 | 41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/login/loginValidations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createFormValidation, ValidationConstraints, Validators, 3 | } from 'lc-form-validation'; 4 | 5 | const loginFormValidationConstraints: ValidationConstraints = { 6 | fields: { 7 | login: [ 8 | { validator: Validators.required }, 9 | ], 10 | password: [ 11 | { validator: Validators.required }, 12 | ], 13 | }, 14 | }; 15 | 16 | export const loginFormValidation = createFormValidation(loginFormValidationConstraints); 17 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/src/pages/login/viewmodel.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationResult } from 'lc-form-validation'; 2 | 3 | export interface LoginFormErrors { 4 | login: FieldValidationResult; 5 | password: FieldValidationResult; 6 | } 7 | 8 | export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ 9 | login: new FieldValidationResult(), 10 | password: new FieldValidationResult(), 11 | }); 12 | -------------------------------------------------------------------------------- /22_Hooks_UseContext/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es6", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "noLib": false, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "compileOnSave": false, 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /99_readme_resources/01 HelloReact/browser_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/99_readme_resources/01 HelloReact/browser_output.png -------------------------------------------------------------------------------- /99_readme_resources/04 Callback/browser_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/react-by-sample/d18533c163738940e8dd4e062f63e5bd4800815e/99_readme_resources/04 Callback/browser_output.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Lemoncode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------