├── .gitignore ├── 00-props-state ├── README.md └── search-example │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── actions.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reducers.js │ └── serviceWorker.js ├── 01-react-hooks ├── README.md └── hooks-example │ ├── .eslintrc │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── brace_yourselves.jpg │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── serviceWorker.js │ ├── useCustom │ │ ├── class.jsx │ │ ├── hook.jsx │ │ └── index.jsx │ ├── useEffect │ │ ├── class.jsx │ │ └── hook.jsx │ └── useState │ │ ├── class.jsx │ │ └── hook.jsx │ └── yarn.lock ├── 02-component-patterns ├── .prettierrc ├── README.md ├── exercises │ ├── compound-components │ │ ├── exercise-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── Stepper.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── exercise │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── Stepper.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── lecture-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── Logo.js │ │ │ │ ├── Navigation.js │ │ │ │ ├── SearchBar.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ └── lecture │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── Logo.js │ │ │ ├── Navigation.js │ │ │ ├── SearchBar.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ ├── controlled-components │ │ ├── exercise-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── Stepper.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── exercise │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── Stepper.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── lecture-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── RadioGroup.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ └── lecture │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── RadioGroup.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ ├── higher-order-components │ │ ├── exercise-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── App.test.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── exercise │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ ├── lecture-solution │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ ├── index.html │ │ │ │ └── manifest.json │ │ │ └── src │ │ │ │ ├── App.css │ │ │ │ ├── App.js │ │ │ │ ├── App.test.js │ │ │ │ ├── NetworkStatus.js │ │ │ │ ├── index.css │ │ │ │ ├── index.js │ │ │ │ └── serviceWorker.js │ │ └── lecture │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── NetworkStatus.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ └── render-props │ │ ├── exercise-solution │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── Count.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ │ ├── exercise │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── Count.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ │ ├── lecture-solution │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── src │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── Fetch.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── serviceWorker.js │ │ └── lecture │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── Fetch.js │ │ ├── index.css │ │ ├── index.js │ │ └── serviceWorker.js └── package.json ├── 03-css-debugging ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── App.js │ ├── App.scss │ ├── App.test.js │ ├── examples │ ├── 00-intro.js │ ├── 01-vertical-alignment.js │ ├── 02-z-index.js │ ├── 03-selector-specificity.js │ ├── 04-responsive-design.js │ ├── 05-transitions.js │ ├── examples.scss │ └── index.js │ ├── index.css │ ├── index.js │ └── lib │ ├── index.js │ └── lib.scss ├── 04-suspense-and-async-mode ├── .eslintrc ├── README.md ├── exercise-fetch-solution │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── spinner.css │ │ └── spinner.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── pages │ │ ├── content.jsx │ │ ├── home.jsx │ │ ├── kitties.css │ │ └── kitties.jsx ├── exercise-fetch │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── spinner.css │ │ └── spinner.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── pages │ │ ├── content.jsx │ │ ├── home.jsx │ │ ├── kitties.css │ │ └── kitties.jsx ├── exercise-lazy-solution │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── spinner.css │ │ └── spinner.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── pages │ │ ├── content.jsx │ │ ├── home.jsx │ │ └── prague-image.jsx ├── exercise-lazy │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── spinner.css │ │ └── spinner.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── pages │ │ ├── content.jsx │ │ ├── home.jsx │ │ └── prague-image.jsx └── images │ ├── concurrent_lifecycle_methods.jpeg │ └── concurrent_mode_explained.png ├── 05-component-testing ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.tsx │ ├── Fetcher.tsx │ ├── Movie.tsx │ ├── MovieList.tsx │ ├── __tests__ │ │ ├── exercise.001.test.jsx │ │ ├── exercise.002.test.jsx │ │ ├── exercise.003.test.jsx │ │ └── exercise.004.test.jsx │ ├── actions │ │ └── wishlistAction.ts │ ├── index.css │ ├── index.tsx │ ├── logo.svg │ ├── playground.test.jsx │ ├── react-app-env.d.ts │ ├── reducers │ │ ├── index.ts │ │ └── wishlistReducer.ts │ ├── serviceWorker.ts │ └── store.ts └── tsconfig.json ├── 06-graphql ├── README.md ├── backend │ ├── app.js │ ├── getData.js │ ├── graphqlSchema.js │ ├── package-lock.json │ └── package.json └── web │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── components │ │ ├── Character.js │ │ ├── Character.scss │ │ ├── CharacterSheet.js │ │ └── CharacterSheet.scss │ ├── graphqlRequest.js │ ├── helpers │ │ └── getRandomCharacterIds.js │ ├── index.html │ └── index.js │ ├── webpack.common.js │ ├── webpack.dev.js │ └── webpack.prod.js ├── 07-firebase ├── README.md ├── excersise │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── components │ │ │ ├── Account │ │ │ │ └── index.js │ │ │ ├── Admin │ │ │ │ └── index.js │ │ │ ├── App │ │ │ │ └── index.js │ │ │ ├── Firebase │ │ │ │ ├── context.js │ │ │ │ ├── firebase.js │ │ │ │ └── index.js │ │ │ ├── Home │ │ │ │ └── index.js │ │ │ ├── Landing │ │ │ │ └── index.js │ │ │ ├── Navigation │ │ │ │ └── index.js │ │ │ ├── PasswordChange │ │ │ │ └── index.js │ │ │ ├── PasswordForget │ │ │ │ └── index.js │ │ │ ├── Session │ │ │ │ ├── context.js │ │ │ │ ├── index.js │ │ │ │ ├── withAuthentication.js │ │ │ │ └── withAuthorization.js │ │ │ ├── SignIn │ │ │ │ └── index.js │ │ │ ├── SignOut │ │ │ │ └── index.js │ │ │ └── SignUp │ │ │ │ └── index.js │ │ ├── constants │ │ │ └── routes.js │ │ ├── index.css │ │ ├── index.js │ │ └── serviceWorker.js │ └── yarn.lock └── solution │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── components │ │ ├── Account │ │ │ └── index.js │ │ ├── Admin │ │ │ └── index.js │ │ ├── App │ │ │ └── index.js │ │ ├── Firebase │ │ │ ├── context.js │ │ │ ├── firebase.js │ │ │ └── index.js │ │ ├── Home │ │ │ └── index.js │ │ ├── Landing │ │ │ └── index.js │ │ ├── Navigation │ │ │ └── index.js │ │ ├── PasswordChange │ │ │ └── index.js │ │ ├── PasswordForget │ │ │ └── index.js │ │ ├── Session │ │ │ ├── context.js │ │ │ ├── index.js │ │ │ ├── withAuthentication.js │ │ │ └── withAuthorization.js │ │ ├── SignIn │ │ │ └── index.js │ │ ├── SignOut │ │ │ └── index.js │ │ └── SignUp │ │ │ └── index.js │ ├── constants │ │ └── routes.js │ ├── index.css │ ├── index.js │ └── serviceWorker.js │ └── yarn.lock ├── 08-testing-hooks ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── 01 │ │ ├── 01-exercise.js │ │ ├── 01-exercise.test.js │ │ ├── 01-solution.js │ │ └── 01-solution.test.js │ ├── 02 │ │ ├── 02-exercise.js │ │ ├── 02-exercise.test.js │ │ ├── 02-solution.js │ │ └── 02-solution.test.js │ ├── 03 │ │ ├── 03-exercise.js │ │ ├── 03-exercise.test.js │ │ ├── 03-solution.js │ │ └── 03-solution.test.js │ ├── 04 │ │ ├── 04-exercise.js │ │ ├── 04-exercise.test.js │ │ ├── 04-solution.js │ │ └── 04-solution.test.js │ ├── 05 │ │ ├── 05-exercise.js │ │ ├── 05-exercise.test.js │ │ ├── 05-solution.js │ │ └── 05-solution.test.js │ ├── 06 │ │ ├── 06-solution.js │ │ ├── 06-solution.test.js │ │ └── react-redux.js │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── serviceWorker.js └── yarn.lock ├── 09-typescript-in-react ├── .gitignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── IMG_5437.JPG │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.tsx │ ├── exercise-1 │ │ ├── blog.jsx │ │ └── index.tsx │ ├── exercise-2 │ │ ├── fetch-jokes.ts │ │ ├── index.tsx │ │ └── jokes.jsx │ ├── exercise-3 │ │ ├── clock.jsx │ │ └── index.tsx │ ├── homepage │ │ ├── homepage.tsx │ │ └── index.tsx │ ├── index.css │ ├── index.tsx │ ├── react-app-env.d.ts │ ├── sandbox.tsx │ ├── solution-1 │ │ ├── blog.tsx │ │ └── index.tsx │ ├── solution-2 │ │ ├── fetch-jokes.ts │ │ ├── index.tsx │ │ └── jokes.tsx │ ├── solution-3 │ │ ├── clock.tsx │ │ └── index.tsx │ ├── use-context │ │ ├── index.tsx │ │ └── use-context.tsx │ └── use-reducer │ │ ├── index.tsx │ │ └── use-reducer.tsx └── tsconfig.json ├── 10-suspense-for-data-fetching ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html ├── src │ ├── 01-exercise-suspense │ │ └── index.tsx │ ├── 02-exercise-error-boundaries │ │ ├── index.tsx │ │ └── people.tsx │ ├── 03-exercise-loading-states │ │ ├── index.tsx │ │ ├── people.tsx │ │ ├── person.tsx │ │ ├── searchForm.tsx │ │ └── style.css │ ├── 04-exercise-suspense-list │ │ ├── index.tsx │ │ ├── leftNav.tsx │ │ ├── mainContent.tsx │ │ ├── navBar.tsx │ │ ├── rightNav.tsx │ │ ├── spinner.tsx │ │ └── style.css │ ├── app.tsx │ ├── index.tsx │ └── utils │ │ ├── errorBoundary.tsx │ │ ├── fetch.ts │ │ ├── types.ts │ │ └── wrapPromise.ts └── tsconfig.json ├── LICENSE └── README.md /00-props-state/search-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00-props-state", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0-alpha", 7 | "react-dom": "^16.7.0-alpha", 8 | "react-scripts": "2.1.1", 9 | "redux": "^4.0.1", 10 | "redux-thunk": "^2.3.0", 11 | "react-redux": "^5.1.0" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": [ 23 | ">0.2%", 24 | "not dead", 25 | "not ie <= 11", 26 | "not op_mini all" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /00-props-state/search-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/00-props-state/search-example/public/favicon.ico -------------------------------------------------------------------------------- /00-props-state/search-example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: space-between; 6 | height: 90vh; 7 | padding-top: 5vh; 8 | } 9 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/actions.js: -------------------------------------------------------------------------------- 1 | const fakeRes1 = [{title: 'Result 1', body: 'Long Description', more: 'abc'}] 2 | const fakeRes2 = [{title: 'Result 1', body: 'Description D', more: 'def'}, {title: 'Result 2', body: 'Description E'}] 3 | const getHash = () => window.decodeURI(window.location.hash.substr(1)) 4 | 5 | export const setRoute = route => ({type: 'SET_ROUTE', route}) 6 | export const setQuery = query => ({type: 'SET_QUERY', query}) 7 | export const setResults = results => ({type: 'SET_RESULTS', results}) 8 | 9 | export const setRouteAndSubmit = route => dispatch => { 10 | window.location.hash = route 11 | dispatch(setRoute(route)) 12 | dispatch(setResults([])) 13 | setTimeout(() => { 14 | dispatch(setResults(Math.random() < 0.5 ? fakeRes1 : fakeRes2))}, 15 | 500 16 | ) 17 | } 18 | 19 | export const loadRoute = () => dispatch => { 20 | const hash = getHash() 21 | dispatch(setRouteAndSubmit(hash)) 22 | dispatch(setQuery(hash)) 23 | } 24 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import {createStore, applyMiddleware, compose} from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | import rootReducer from './reducers' 7 | import {loadRoute} from './actions'; 8 | import './index.css'; 9 | import App from './App'; 10 | import * as serviceWorker from './serviceWorker'; 11 | 12 | const store = createStore( 13 | rootReducer, 14 | compose( 15 | applyMiddleware(thunk), 16 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 17 | ) 18 | ) 19 | 20 | ReactDOM.render( 21 | 22 | 23 | , 24 | document.getElementById('root') 25 | ); 26 | 27 | const dispatchLoadRoute = () => store.dispatch(loadRoute()) 28 | dispatchLoadRoute() 29 | window.onhashchange = dispatchLoadRoute 30 | 31 | // If you want your app to work offline and load faster, you can change 32 | // unregister() to register() below. Note this comes with some pitfalls. 33 | // Learn more about service workers: http://bit.ly/CRA-PWA 34 | serviceWorker.unregister(); 35 | -------------------------------------------------------------------------------- /00-props-state/search-example/src/reducers.js: -------------------------------------------------------------------------------- 1 | const initialState = {route: '', query: '', results: []} 2 | 3 | const reducer = (state = initialState, action) => { 4 | const {type, route, query, results} = action 5 | switch (type) { 6 | case 'SET_ROUTE': return {...state, route} 7 | case 'SET_QUERY': return {...state, query} 8 | case 'SET_RESULTS': return {...state, results} 9 | default: return state 10 | } 11 | } 12 | 13 | export default reducer 14 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/README.md: -------------------------------------------------------------------------------- 1 | ## To run the application 2 | 3 | If you've just downloaded the code and you are running the application 4 | for the first time, go to the project directory and install all packages 5 | 6 | ```shell 7 | npm ci 8 | ``` 9 | 10 | Then, you can run the app in the development mode: 11 | 12 | ```shell 13 | npm start 14 | ``` 15 | 16 | The page will reload if you make edits.
17 | You will also see any lint errors in the console. 18 | 19 | ```shell 20 | npm run build 21 | ``` 22 | 23 | Builds the app for production to the `build` folder.
24 | It correctly bundles React in production mode and optimizes the build for the best performance. 25 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-hooks", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0-alpha.2", 7 | "react-dom": "^16.7.0-alpha.2", 8 | "react-router-dom": "^4.3.1", 9 | "react-scripts": "2.1.1" 10 | }, 11 | "scripts": { 12 | "build": "react-scripts build", 13 | "eject": "react-scripts eject", 14 | "eslint": "eslint --ext .js --ext .jsx .", 15 | "lint": "npm run eslint", 16 | "start": "react-scripts start", 17 | "test": "react-scripts test" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": [ 23 | ">0.2%", 24 | "not dead", 25 | "not ie <= 11", 26 | "not op_mini all" 27 | ], 28 | "devDependencies": { 29 | "eslint": "^5.9.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/01-react-hooks/hooks-example/public/favicon.ico -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/brace_yourselves.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/01-react-hooks/hooks-example/src/brace_yourselves.jpg -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html { 12 | background-color: #282c34; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } 19 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/useCustom/index.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {JokeList} from './class'; 3 | 4 | export class ChuckJokes extends Component { 5 | state = { 6 | searchInput: '', 7 | query: '' 8 | }; 9 | 10 | _handleQueryChange = e => { 11 | this.setState({ 12 | searchInput: e.target.value 13 | }); 14 | }; 15 | 16 | _triggerSearch = e => { 17 | const {searchInput} = this.state; 18 | e.preventDefault(); 19 | this.setState({ 20 | query: searchInput 21 | }); 22 | }; 23 | 24 | render() { 25 | const {query} = this.state; 26 | return ( 27 |
28 |
29 | 30 | 33 |
34 | 35 |
36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/useEffect/class.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export class MyMouse extends React.Component { 4 | state = { 5 | x: 0, 6 | y: 0 7 | } 8 | 9 | componentDidMount() { 10 | window.addEventListener('mousemove', this.onMouseMove); 11 | } 12 | 13 | componentWillUnmount() { 14 | window.removeEventListener('mousemove', this.onMouseMove); 15 | } 16 | 17 | onMouseMove = event => { 18 | this.setState({ 19 | x: event.clientX, 20 | y: event.clientY 21 | }); 22 | } 23 | 24 | render = () => { 25 | const {x, y} = this.state; 26 | return ( 27 |
My mouse x position is {x} and y position is {y}
28 | ); 29 | } 30 | } 31 | 32 | export const MyMouseHook = () => { 33 | // @TODO useEffect, mount and unmount vs. update, un/bk 34 | }; 35 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/useEffect/hook.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react'; 2 | 3 | export const MyMouse = () => { 4 | const [mousePosition, setMousePosition] = useState({x: 0, y: 0}); 5 | const onMouseMove = event => { 6 | setMousePosition({ 7 | x: event.clientX, 8 | y: event.clientY 9 | }); 10 | }; 11 | useEffect(() => { 12 | window.addEventListener('mousemove', onMouseMove); 13 | return () => { 14 | window.removeEventListener('mousemove', onMouseMove); 15 | }; 16 | }, []); 17 | const {x, y} = mousePosition; 18 | return ( 19 |
My mouse x position is {x} and y position is {y}
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/useState/class.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export class MyStatus extends React.Component { 4 | state = { 5 | myStatus: 'cool' 6 | } 7 | switchMyStatus = () => { 8 | const {myStatus} = this.state; 9 | this.setState({myStatus: (myStatus === 'cool') ? 'awesome' : 'cool'}); 10 | } 11 | render = () => ( 12 |
13 |

I am {this.state.myStatus}

14 | 15 |
16 | ); 17 | } 18 | 19 | export const MyStatusHook = () => { 20 | // @TODO for superman and batman 21 | }; 22 | -------------------------------------------------------------------------------- /01-react-hooks/hooks-example/src/useState/hook.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | 3 | export const MyStatus = () => { 4 | // whatever we pass to useState function will become the initial value 5 | const [myStatus, setMyStatus] = useState('cool'); 6 | return ( 7 |
8 |

I am {myStatus}

9 | 12 |
13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /02-component-patterns/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": true, 10 | "arrowParens": "always" 11 | } -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/compound-components/exercise-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | } 4 | 5 | .stepper { 6 | margin: 50px auto; 7 | padding: 15px; 8 | border: 1px solid #efefef; 9 | border-radius: 4px; 10 | text-align: center; 11 | max-width: 80%; 12 | } 13 | 14 | .stepper-status { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: 12px; 19 | margin: 10px 0px; 20 | } 21 | 22 | .status-item { 23 | cursor: pointer; 24 | } 25 | 26 | .status-divider { 27 | width: 15px; 28 | display: inline-block; 29 | height: 1px; 30 | border-top: 1px solid #afb1b3; 31 | margin: 5px; 32 | margin-top: 7px; 33 | } 34 | 35 | .step-btn { 36 | background-color: #0064ff; 37 | color: white; 38 | border: none; 39 | padding: 5px 10px; 40 | border-radius: 4px; 41 | outline: none; 42 | margin-right: 10px; 43 | cursor: pointer; 44 | } 45 | 46 | .step-btn:disabled { 47 | background-color: #8bb8ff; 48 | cursor: not-allowed; 49 | } 50 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import Stepper from './Stepper' 4 | import './App.css' 5 | 6 | const steps = [ 7 | { 8 | title: 'First Step!', 9 | content: 'Here is the first step, its really good.' 10 | }, 11 | { 12 | title: 'Second Step!', 13 | content: 'First is the worst, second the best' 14 | }, 15 | { 16 | title: 'Third Step!', 17 | content: 'THIRD STEP YOU MADE IT, GOOD JOB!' 18 | } 19 | ] 20 | 21 | const App = () => ( 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | 29 | export default App 30 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excersise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/compound-components/exercise/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | } 4 | 5 | .stepper { 6 | margin: 50px auto; 7 | padding: 15px; 8 | border: 1px solid #efefef; 9 | border-radius: 4px; 10 | text-align: center; 11 | max-width: 80%; 12 | } 13 | 14 | .stepper-status { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: 12px; 19 | margin: 10px 0px; 20 | } 21 | 22 | .status-item { 23 | cursor: pointer; 24 | } 25 | 26 | .status-divider { 27 | width: 15px; 28 | display: inline-block; 29 | height: 1px; 30 | border-top: 1px solid #afb1b3; 31 | margin: 5px; 32 | margin-top: 7px; 33 | } 34 | 35 | .step-btn { 36 | background-color: #0064ff; 37 | color: white; 38 | border: none; 39 | padding: 5px 10px; 40 | border-radius: 4px; 41 | outline: none; 42 | margin-right: 10px; 43 | cursor: pointer; 44 | } 45 | 46 | .step-btn:disabled { 47 | background-color: #8bb8ff; 48 | cursor: not-allowed; 49 | } 50 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/exercise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-free-solid": "^5.0.13", 7 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 8 | "@fortawesome/react-fontawesome": "^0.1.3", 9 | "classnames": "^2.2.6", 10 | "react": "^16.7.0", 11 | "react-dom": "^16.7.0", 12 | "react-router-dom": "^4.3.1", 13 | "react-scripts": "2.1.3" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": [ 25 | ">0.2%", 26 | "not dead", 27 | "not ie <= 11", 28 | "not op_mini all" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/compound-components/lecture-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/App.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | list-style: none; 3 | display: flex; 4 | border-bottom: 1px solid #efefef; 5 | height: 50px; 6 | margin: 0; 7 | background-color: #282c34; 8 | color: #efefef; 9 | align-items: center; 10 | padding: 0px 15px; 11 | } 12 | 13 | .nav li { 14 | padding-right: 10px; 15 | } 16 | 17 | .nav a { 18 | color: white; 19 | text-decoration: none; 20 | } 21 | 22 | .nav-split { 23 | margin-left: auto; 24 | margin-right: auto; 25 | } 26 | 27 | .nav-active a { 28 | color: pink; 29 | } 30 | 31 | .searchbar { 32 | border: none; 33 | padding: 4px; 34 | border-radius: 2px; 35 | min-width: 150px; 36 | margin-right: 10px; 37 | outline: none; 38 | } 39 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import './App.css' 3 | import Navigation from './Navigation' 4 | import SearchBar from './SearchBar' 5 | import Logo from './Logo' 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | ) 23 | } 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' 3 | import {faCrow} from '@fortawesome/fontawesome-free-solid' 4 | 5 | const Logo = () => 6 | 7 | export default Logo 8 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SearchBar = () => 4 | 5 | export default SearchBar 6 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-free-solid": "^5.0.13", 7 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 8 | "@fortawesome/react-fontawesome": "^0.1.3", 9 | "classnames": "^2.2.6", 10 | "react": "^16.7.0", 11 | "react-dom": "^16.7.0", 12 | "react-router-dom": "^4.3.1", 13 | "react-scripts": "2.1.3" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": [ 25 | ">0.2%", 26 | "not dead", 27 | "not ie <= 11", 28 | "not op_mini all" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/compound-components/lecture/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/App.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | list-style: none; 3 | display: flex; 4 | border-bottom: 1px solid #efefef; 5 | height: 50px; 6 | margin: 0; 7 | background-color: #282c34; 8 | color: #efefef; 9 | align-items: center; 10 | padding: 0px 15px; 11 | } 12 | 13 | .nav li { 14 | padding-right: 10px; 15 | } 16 | 17 | .nav a { 18 | color: white; 19 | text-decoration: none; 20 | } 21 | 22 | .nav-split { 23 | margin-left: auto; 24 | margin-right: auto; 25 | } 26 | 27 | .nav-active a { 28 | color: pink; 29 | } 30 | 31 | .searchbar { 32 | border: none; 33 | padding: 4px; 34 | border-radius: 2px; 35 | min-width: 150px; 36 | margin-right: 10px; 37 | outline: none; 38 | } 39 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import Navigation from './Navigation' 3 | import Logo from './Logo' 4 | 5 | // Compound components! 6 | const navItems = [ 7 | {to: '/', label: 'Home'}, 8 | {to: '/messages', label: 'Messages'}, 9 | {to: '/notifications', label: 'Notifications'}, 10 | {to: '/profile', label: 'Profile'} 11 | ] 12 | 13 | class App extends Component { 14 | render() { 15 | return ( 16 |
17 | 18 |
19 | ) 20 | } 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' 3 | import {faCrow} from '@fortawesome/fontawesome-free-solid' 4 | 5 | const Logo = () => 6 | 7 | export default Logo 8 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/Navigation.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import cx from 'classnames' 3 | import {BrowserRouter as Router, Link} from 'react-router-dom' 4 | 5 | import './App.css' 6 | 7 | const NavigationItem = ({label, to, isActive}) => ( 8 |
  • 12 | {label} 13 |
  • 14 | ) 15 | 16 | class Navigation extends React.Component { 17 | static Item = NavigationItem 18 | // Pretend this is dynamic and must be 19 | // read from state! 20 | state = {active: 'Home'} 21 | render() { 22 | const {children} = this.props 23 | return ( 24 |
      25 | {children} 26 |
    27 | ) 28 | } 29 | } 30 | 31 | export default Navigation 32 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const SearchBar = () => 4 | 5 | export default SearchBar 6 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/compound-components/lecture/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/controlled-components/exercise-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | } 4 | 5 | .stepper { 6 | margin: 50px auto; 7 | padding: 15px; 8 | border: 1px solid #efefef; 9 | border-radius: 4px; 10 | text-align: center; 11 | max-width: 80%; 12 | } 13 | 14 | .stepper-status { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: 12px; 19 | margin: 10px 0px; 20 | } 21 | 22 | .status-item { 23 | cursor: pointer; 24 | } 25 | 26 | .status-divider { 27 | width: 15px; 28 | display: inline-block; 29 | height: 1px; 30 | border-top: 1px solid #afb1b3; 31 | margin: 5px; 32 | margin-top: 7px; 33 | } 34 | 35 | .step-btn { 36 | background-color: #0064ff; 37 | color: white; 38 | border: none; 39 | padding: 5px 10px; 40 | border-radius: 4px; 41 | outline: none; 42 | margin-right: 10px; 43 | cursor: pointer; 44 | } 45 | 46 | .step-btn:disabled { 47 | background-color: #8bb8ff; 48 | cursor: not-allowed; 49 | } 50 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/controlled-components/exercise/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | } 4 | 5 | .stepper { 6 | margin: 50px auto; 7 | padding: 15px; 8 | border: 1px solid #efefef; 9 | border-radius: 4px; 10 | text-align: center; 11 | max-width: 80%; 12 | } 13 | 14 | .stepper-status { 15 | display: flex; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: 12px; 19 | margin: 10px 0px; 20 | } 21 | 22 | .status-item { 23 | cursor: pointer; 24 | } 25 | 26 | .status-divider { 27 | width: 15px; 28 | display: inline-block; 29 | height: 1px; 30 | border-top: 1px solid #afb1b3; 31 | margin: 5px; 32 | margin-top: 7px; 33 | } 34 | 35 | .step-btn { 36 | background-color: #0064ff; 37 | color: white; 38 | border: none; 39 | padding: 5px 10px; 40 | border-radius: 4px; 41 | outline: none; 42 | margin-right: 10px; 43 | cursor: pointer; 44 | } 45 | 46 | .step-btn:disabled { 47 | background-color: #8bb8ff; 48 | cursor: not-allowed; 49 | } 50 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/exercise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/controlled-components/lecture-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | height: 100vh; 7 | } 8 | 9 | .radio-group { 10 | display: flex; 11 | width: 85px; 12 | border: none; 13 | border-radius: 3px; 14 | } 15 | 16 | .radio-group-item { 17 | margin-top: 5px; 18 | justify-content: space-between; 19 | display: flex; 20 | flex-direction: row-reverse; 21 | } 22 | 23 | .radio-group-item input[type='radio']:checked + label { 24 | color: #004bde; 25 | } 26 | 27 | .radio-group-item label, 28 | input { 29 | cursor: pointer; 30 | } 31 | 32 | .radio-group-item label:hover { 33 | text-decoration: underline; 34 | } -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import RadioGroup from './RadioGroup' 4 | import './App.css' 5 | 6 | class App extends Component { 7 | state = { 8 | value: 'Second' 9 | } 10 | 11 | onChange = (value) => { 12 | this.setState({ 13 | value 14 | }) 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 |
    Value: {this.state.value}
    21 | 22 | 23 | 24 | 25 | 26 |
    27 | ) 28 | } 29 | } 30 | 31 | export default App 32 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/src/RadioGroup.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | const RadioContext = React.createContext() 4 | 5 | const RadioGroupOption = ({value, label}) => ( 6 |
    7 | 8 | {({contextValue, onChange}) => ( 9 | 10 | )} 11 | 12 | 13 |
    14 | ) 15 | 16 | class RadioGroup extends Component { 17 | static Option = RadioGroupOption 18 | 19 | onChange = (event) => { 20 | this.props.onChange(event.target.value) 21 | } 22 | getContext = () => ({ 23 | contextValue: this.props.value, 24 | onChange: this.onChange 25 | }) 26 | 27 | render() { 28 | return ( 29 | 30 |
    {this.props.children}
    31 |
    32 | ) 33 | } 34 | } 35 | 36 | export default RadioGroup 37 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/controlled-components/lecture/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | height: 100vh; 7 | } 8 | 9 | .radio-group { 10 | display: flex; 11 | width: 85px; 12 | border: none; 13 | border-radius: 3px; 14 | } 15 | 16 | .radio-group-item { 17 | margin-top: 5px; 18 | justify-content: space-between; 19 | display: flex; 20 | flex-direction: row-reverse; 21 | } 22 | 23 | .radio-group-item input[type='radio']:checked + label { 24 | color: #004bde; 25 | } 26 | 27 | .radio-group-item label, 28 | input { 29 | cursor: pointer; 30 | } 31 | 32 | .radio-group-item label:hover { 33 | text-decoration: underline; 34 | } 35 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import RadioGroup from './RadioGroup' 4 | import './App.css' 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | } 16 | } 17 | 18 | export default App 19 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/src/RadioGroup.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | const RadioGroupOption = ({value, label}) => ( 4 |
    5 | 6 | 7 |
    8 | ) 9 | 10 | class RadioGroup extends Component { 11 | static Option = RadioGroupOption 12 | 13 | render() { 14 | return
    {this.props.children}
    15 | } 16 | } 17 | 18 | export default RadioGroup 19 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/controlled-components/lecture/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "hoist-non-react-statics": "^3.2.1", 7 | "react": "^16.7.0", 8 | "react-dom": "^16.7.0", 9 | "react-scripts": "2.1.3" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/higher-order-components/exercise-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | height: 60vh; 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | font-size: 30px; 7 | } 8 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | ReactDOM.unmountComponentAtNode(div) 9 | }) 10 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "hoist-non-react-statics": "^3.2.1", 7 | "react": "^16.7.0", 8 | "react-dom": "^16.7.0", 9 | "react-scripts": "2.1.3" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/higher-order-components/exercise/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | height: 60vh; 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | font-size: 30px; 7 | } 8 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/exercise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "hoist-non-react-statics": "^3.2.1", 7 | "react": "^16.7.0", 8 | "react-dom": "^16.7.0", 9 | "react-scripts": "2.1.3" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/higher-order-components/lecture-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | font-size: 20px; 4 | } 5 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import {Online, Offline} from './NetworkStatus' 4 | import './App.css' 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
    10 |

    11 | 12 | Online with a connection. 13 |
    14 | Running at ~ 15 |
    16 | 17 |

    18 |
    19 | ) 20 | } 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | ReactDOM.unmountComponentAtNode(div) 9 | }) 10 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lecture", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "hoist-non-react-statics": "^3.2.1", 7 | "react": "^16.7.0", 8 | "react-dom": "^16.7.0", 9 | "react-scripts": "2.1.3" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/higher-order-components/lecture/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | font-size: 20px; 4 | } 5 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import {Online, Offline} from './NetworkStatus.js' 3 | import './App.css' 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
    9 |

    10 | 11 | Online with a connection. 12 |
    13 | Running at 14 |
    15 | Where'd you go?! 16 |

    17 |
    18 | ) 19 | } 20 | } 21 | 22 | export default App 23 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/higher-order-components/lecture/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/render-props/exercise-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | display: flex; 4 | } 5 | 6 | .App-counter { 7 | margin: 50px auto; 8 | padding: 15px; 9 | border: 1px solid #efefef; 10 | border-radius: 4px; 11 | text-align: center; 12 | max-width: 80%; 13 | } 14 | 15 | .App-counter-btn { 16 | background-color: #0064ff; 17 | color: white; 18 | border: none; 19 | padding: 5px 10px; 20 | border-radius: 4px; 21 | outline: none; 22 | margin-right: 5px; 23 | margin-left: 5px; 24 | cursor: pointer; 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Count from './Count' 4 | import './App.css' 5 | 6 | const App = () => ( 7 |
    8 | 9 | {({increase, decrease, count}) => ( 10 |
    11 |

    Counter

    12 | 15 | 18 |

    {count}

    19 |
    20 | )} 21 |
    22 |
    23 | ) 24 | 25 | export default App 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/src/Count.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | class Count extends Component { 4 | state = { 5 | count: 0 6 | } 7 | 8 | increase = () => { 9 | this.setState({count: this.state.count + 1}) 10 | } 11 | 12 | decrease = () => { 13 | this.setState({count: this.state.count - 1}) 14 | } 15 | 16 | render() { 17 | return this.props.children({ 18 | increase: this.increase, 19 | decrease: this.decrease, 20 | count: this.state.count 21 | }) 22 | } 23 | } 24 | 25 | export default Count 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/render-props/exercise/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | display: flex; 4 | } 5 | 6 | .App-counter { 7 | margin: 50px auto; 8 | padding: 15px; 9 | border: 1px solid #efefef; 10 | border-radius: 4px; 11 | text-align: center; 12 | max-width: 80%; 13 | } 14 | 15 | .App-counter-btn { 16 | background-color: #0064ff; 17 | color: white; 18 | border: none; 19 | padding: 5px 10px; 20 | border-radius: 4px; 21 | outline: none; 22 | margin-right: 5px; 23 | margin-left: 5px; 24 | cursor: pointer; 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/src/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RENDER PROPS EXERCISE 3 | * 4 | * The App component will be only for presentational purpose. 5 | * The Counter container will hold all the logic and local state. 6 | * 7 | * Using Render props share: 8 | * 1. Event handlers for increasing and decreasing counts 9 | * 2. Total count 10 | * 11 | */ 12 | import React, {Component} from 'react' 13 | 14 | import Count from './Count' 15 | import './App.css' 16 | 17 | class App extends Component { 18 | render() { 19 | return ( 20 |
    21 |
    22 | 23 | 26 | 29 |

    count

    30 |
    31 |
    32 | ) 33 | } 34 | } 35 | 36 | export default App 37 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/src/Count.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | class Count extends Component { 4 | render() { 5 | return

    Counter

    6 | } 7 | } 8 | 9 | export default Count 10 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/exercise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/render-props/lecture-solution/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import Fetch from './Fetch' 4 | import './App.css' 5 | 6 | const URL = 'http://quotes.rest/qod.json?category=inspire' 7 | 8 | class App extends Component { 9 | render() { 10 | return ( 11 |
    12 | 13 | {({loading, error, data}) => { 14 | if (error !== null) { 15 | console.log(error) 16 | return

    {error.toString()}

    17 | } 18 | return loading ?

    'Loading...'

    :

    {data.contents.quotes[0].quote}

    19 | }} 20 |
    21 |
    22 | ) 23 | } 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/src/Fetch.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | class Fetch extends Component { 4 | state = { 5 | loading: true, 6 | error: null, 7 | data: null 8 | } 9 | 10 | componentDidMount() { 11 | fetch(this.props.url) 12 | .then((res) => res.json()) 13 | .then((data) => 14 | this.setState({ 15 | loading: false, 16 | data: data 17 | }) 18 | ) 19 | .catch((error) => 20 | this.setState({ 21 | loading: false, 22 | error: error 23 | }) 24 | ) 25 | } 26 | 27 | render() { 28 | return this.props.children(this.state) 29 | } 30 | } 31 | 32 | export default Fetch 33 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.7.0", 7 | "react-dom": "^16.7.0", 8 | "react-scripts": "2.1.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/02-component-patterns/exercises/render-props/lecture/public/favicon.ico -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | import Fetch from './Fetch' 4 | import './App.css' 5 | 6 | // const URL = 'http://quotes.rest/qod.json?category=inspire' 7 | // data path data.contents.quotes[0].quote 8 | 9 | class App extends Component { 10 | render() { 11 | return ( 12 |
    13 | 14 |
    15 | ) 16 | } 17 | } 18 | 19 | export default App 20 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/src/Fetch.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | 3 | class Fetch extends Component { 4 | render() { 5 | return

    Fetch Component

    6 | } 7 | } 8 | 9 | export default Fetch 10 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /02-component-patterns/exercises/render-props/lecture/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: http://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /02-component-patterns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-workshop-component-patterns", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Blazek Adam ", 6 | "license": "MIT", 7 | "scripts": { 8 | "prettier": "prettier --write \"exercises/**/*.js\"" 9 | }, 10 | "dependencies": { 11 | "prettier": "^1.15.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /03-css-debugging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "03-css-debugging", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "emotion": "10.0.7", 7 | "node-sass": "4.11.0", 8 | "react": "^16.7.0-alpha", 9 | "react-dom": "^16.7.0-alpha", 10 | "react-router": "4.3.1", 11 | "react-router-dom": "4.3.1", 12 | "react-scripts": "2.1.3" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": [ 24 | ">0.2%", 25 | "not dead", 26 | "not ie <= 11", 27 | "not op_mini all" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /03-css-debugging/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/03-css-debugging/public/favicon.ico -------------------------------------------------------------------------------- /03-css-debugging/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /03-css-debugging/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {BrowserRouter as Router, Route, NavLink} from 'react-router-dom' 3 | import './App.scss' 4 | import examples from './examples' 5 | 6 | const App = () => ( 7 | 8 |
    9 |
    10 | {examples.map(ex => {ex.title})} 11 |
    12 | 13 |
    14 | {examples.map(ex => )} 15 |
    16 |
    17 |
    18 | ) 19 | 20 | export default App 21 | -------------------------------------------------------------------------------- /03-css-debugging/src/App.scss: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | height: 100vh; 6 | background: #aaafae; 7 | font-size: 32px; 8 | } 9 | 10 | .App__header { 11 | margin: 10px; 12 | display: flex; 13 | flex-wrap: wrap; 14 | justify-content: center; 15 | 16 | a { 17 | color: #557; 18 | text-decoration: none; 19 | padding: 0 10px; 20 | transition: 0.2s; 21 | } 22 | a.active { 23 | color: black; 24 | font-weight: bold; 25 | } 26 | a:hover { 27 | color: #33b; 28 | } 29 | } 30 | 31 | .App__content { 32 | display: flex; 33 | flex: 1; 34 | 35 | & > *:not(.Vertical) { 36 | margin: auto; 37 | } 38 | } 39 | 40 | // !! Keep following code, to uncomment during parts of live demo. !! 41 | // 42 | //$debugColor: #c30; 43 | //* { 44 | // background: rgba($debugColor, .1) !important; 45 | // outline: 1px dotted $debugColor !important; 46 | // color: $debugColor !important; 47 | //} 48 | // 49 | // !! End keep !! 50 | -------------------------------------------------------------------------------- /03-css-debugging/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import examples from './examples' 5 | import fs from 'fs' 6 | 7 | it('renders without crashing', () => { 8 | const div = document.createElement('div') 9 | ReactDOM.render(, div) 10 | ReactDOM.unmountComponentAtNode(div) 11 | }); 12 | 13 | it('all examples are imported', () => { 14 | const files = fs.readdirSync(`${__dirname}/examples`).filter(f => f.match(/^\d.*\.js$/)).map(f => f.slice(0, 2)) 15 | const imported = examples.map(e => e.id) 16 | expect(imported.sort()).toEqual(files.sort()) 17 | }) 18 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/00-intro.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {css, cx} from 'emotion' 3 | 4 | const clsPrefix = 'Intro' 5 | const coin = 0.5 // Math.random() 6 | 7 | const randomStyle = css`color: orange;` 8 | const randomName = cx({ 9 | first: coin < 0.6, 10 | second: coin > 0.4 11 | }) 12 | const niceName = cx(`${clsPrefix}__niceName`, css`color: green;`) 13 | 14 | const Example = () => ( 15 |
    16 | Example 00: browser inspector + class names 17 |
    Lorem ipsum
    18 |
    Lorem ipsum
    19 |
    Lorem ipsum
    20 |
    21 | ) 22 | 23 | export default { 24 | id: '00', 25 | title: 'Intro', 26 | component: Example, 27 | } 28 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/01-vertical-alignment.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Example = () => ( 4 |
    5 | Example 01: why am I not vertically centered? 6 |
    7 | ) 8 | 9 | export default { 10 | id: '01', 11 | title: 'Vertical alignment', 12 | component: Example, 13 | } 14 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/02-z-index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Example = () => ( 4 |
    5 |
    6 | Example 02: why am I not on top? 7 |
    8 |
    9 | Lorem ipsum 10 |
    11 |
    12 | ) 13 | 14 | export default { 15 | id: '02', 16 | title: 'Z-index', 17 | component: Example, 18 | } 19 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/03-selector-specificity.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Button} from './../lib' 3 | 4 | const Example = () => ( 5 |
    6 | Example 03: "cascade" style sheets 7 | 8 | 9 | 10 |
    11 | ) 12 | 13 | export default { 14 | id: '03', 15 | title: 'Selector specificity', 16 | component: Example, 17 | } 18 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/04-responsive-design.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const boxes = Array.from(Array(50)).map((_, i) =>
    {i}
    ) 4 | const Example = () => ( 5 |
    6 | {boxes} 7 |
    8 | ) 9 | 10 | export default { 11 | id: '04', 12 | title: 'Responsive design', 13 | component: Example, 14 | } 15 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/05-transitions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Example = () => ( 4 |
    5 | Example 05: Transition defined on "after" class 6 |
    7 | ) 8 | 9 | export default { 10 | id: '05', 11 | title: 'Transitions', 12 | component: Example, 13 | } 14 | -------------------------------------------------------------------------------- /03-css-debugging/src/examples/index.js: -------------------------------------------------------------------------------- 1 | import './examples.scss' 2 | 3 | import ex00 from './00-intro' 4 | import ex01 from './01-vertical-alignment' 5 | import ex02 from './02-z-index' 6 | import ex03 from './03-selector-specificity' 7 | import ex04 from './04-responsive-design' 8 | import ex05 from './05-transitions' 9 | 10 | export default [ 11 | ex00, 12 | ex01, 13 | ex02, 14 | ex03, 15 | ex04, 16 | ex05, 17 | ] 18 | -------------------------------------------------------------------------------- /03-css-debugging/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /03-css-debugging/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /03-css-debugging/src/lib/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './lib.scss' 3 | 4 | export const Button = ({className = '', children}) => 5 | -------------------------------------------------------------------------------- /03-css-debugging/src/lib/lib.scss: -------------------------------------------------------------------------------- 1 | .lib { 2 | $bg: #998; 3 | &-button { 4 | border-radius: 5px; 5 | border: 1px solid darken($bg, 20); 6 | background: $bg; 7 | font-size: inherit; 8 | padding: 3px 10px; 9 | margin: 3px; 10 | cursor: pointer; 11 | } 12 | &-button:hover { 13 | background: darken($bg, 10); 14 | border-color: darken($bg, 100); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/README.md: -------------------------------------------------------------------------------- 1 | # To run the application 2 | 3 | If you've just downloaded the code and you are running the application 4 | for the first time, go to the project directory and install all packages 5 | 6 | ```shell 7 | npm ci 8 | ``` 9 | 10 | Then, you can run the app in the development mode: 11 | 12 | ```shell 13 | npm start 14 | ``` 15 | 16 | The page will reload if you make edits. 17 | You will also see any lint errors in the console. 18 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "suspense-and-concurrent-mode", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "lodash": "^4.17.11", 7 | "react": "16.7.0-alpha.2", 8 | "react-cache": "^2.0.0-alpha.1", 9 | "react-dom": "16.7.0-alpha.2", 10 | "react-router-dom": "4.4.0-beta.7", 11 | "react-scripts": "2.1.1", 12 | "scheduler": "0.13.3", 13 | "serve": "^10.1.2" 14 | }, 15 | "scripts": { 16 | "build": "SKIP_PREFLIGHT_CHECK=true react-scripts build", 17 | "eject": "react-scripts eject", 18 | "eslint": "eslint --ext .js --ext .jsx .", 19 | "lint": "npm run eslint", 20 | "serve": "./node_modules/serve/bin/serve.js -s build", 21 | "start": "SKIP_PREFLIGHT_CHECK=true PORT=3006 react-scripts start", 22 | "test": "react-scripts test" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "babel-eslint": "^10.0.1", 35 | "eslint": "^5.9.0", 36 | "prop-types": "^15.7.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/exercise-fetch-solution/public/favicon.ico -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {lazy, Suspense} from 'react'; 2 | import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom'; 3 | import './App.css'; 4 | import Spinner from './components/spinner'; 5 | 6 | const Home = lazy(() => import('./pages/home')); 7 | const Content = lazy(() => import('./pages/content')); 8 | const Kitties = lazy(() => import('./pages/kitties')); 9 | 10 | const App = () => ( 11 | 12 |
    13 | 20 |
    21 | Loading the page...}> 22 | 23 | 24 | 25 | 26 | 27 | 28 |
    29 |
    30 |
    31 | ); 32 | 33 | export default App; 34 | 35 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/components/spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './spinner.css'; 4 | 5 | const Spinner = ({children}) => ( 6 | <> 7 |
    8 | {children} 9 | 10 | ); 11 | 12 | Spinner.propTypes = { 13 | children: PropTypes.node 14 | }; 15 | 16 | Spinner.defaultProps = { 17 | children: null 18 | }; 19 | 20 | export default Spinner; 21 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html { 12 | background-color: #282c34; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } 19 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/pages/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../logo.svg'; 4 | 5 | const Home = () => ( 6 |
    7 | logo 8 |

    Suspense and Concurrent Mode in React

    9 |

    10 | "Code splitting and improving UX made easy." 11 |

    12 |
    13 | ); 14 | 15 | export default Home; 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch-solution/src/pages/kitties.css: -------------------------------------------------------------------------------- 1 | .kitties { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .kitties > * { 8 | margin-top: 20px; 9 | } 10 | 11 | .kitties img { 12 | max-width: 80vw; 13 | max-height: 80vh; 14 | } -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/README.md: -------------------------------------------------------------------------------- 1 | # To run the application 2 | 3 | If you've just downloaded the code and you are running the application 4 | for the first time, go to the project directory and install all packages 5 | 6 | ```shell 7 | npm ci 8 | ``` 9 | 10 | Then, you can run the app in the development mode: 11 | 12 | ```shell 13 | npm start 14 | ``` 15 | 16 | The page will reload if you make edits. 17 | You will also see any lint errors in the console. 18 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "suspense-and-concurrent-mode", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "lodash": "^4.17.11", 7 | "react": "16.7.0-alpha.2", 8 | "react-cache": "^2.0.0-alpha.1", 9 | "react-dom": "16.7.0-alpha.2", 10 | "react-router-dom": "4.4.0-beta.7", 11 | "react-scripts": "2.1.1", 12 | "scheduler": "0.13.3", 13 | "serve": "^10.1.2" 14 | }, 15 | "scripts": { 16 | "build": "SKIP_PREFLIGHT_CHECK=true react-scripts build", 17 | "eject": "react-scripts eject", 18 | "eslint": "eslint --ext .js --ext .jsx .", 19 | "lint": "npm run eslint", 20 | "serve": "./node_modules/serve/bin/serve.js -s build", 21 | "start": "SKIP_PREFLIGHT_CHECK=true PORT=3006 react-scripts start", 22 | "test": "react-scripts test" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "babel-eslint": "^10.0.1", 35 | "eslint": "^5.9.0", 36 | "prop-types": "^15.7.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/exercise-fetch/public/favicon.ico -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/App.js: -------------------------------------------------------------------------------- 1 | import React, {lazy, Suspense} from 'react'; 2 | import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom'; 3 | import './App.css'; 4 | import Spinner from './components/spinner'; 5 | 6 | const Home = lazy(() => import('./pages/home')); 7 | const Content = lazy(() => import('./pages/content')); 8 | const Kitties = lazy(() => import('./pages/kitties')); 9 | 10 | const App = () => ( 11 | 12 |
    13 | 20 |
    21 | Loading the page...}> 22 | 23 | 24 | 25 | 26 | 27 | 28 |
    29 |
    30 |
    31 | ); 32 | 33 | export default App; 34 | 35 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/components/spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './spinner.css'; 4 | 5 | const Spinner = ({children}) => ( 6 | <> 7 |
    8 | {children} 9 | 10 | ); 11 | 12 | Spinner.propTypes = { 13 | children: PropTypes.node 14 | }; 15 | 16 | Spinner.defaultProps = { 17 | children: null 18 | }; 19 | 20 | export default Spinner; 21 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html { 12 | background-color: #282c34; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } 19 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/pages/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../logo.svg'; 4 | 5 | const Home = () => ( 6 |
    7 | logo 8 |

    Suspense and Concurrent Mode in React

    9 |

    10 | "Code splitting and improving UX made easy." 11 |

    12 |
    13 | ); 14 | 15 | export default Home; 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-fetch/src/pages/kitties.css: -------------------------------------------------------------------------------- 1 | .kitties { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .kitties > * { 8 | margin-top: 20px; 9 | } 10 | 11 | .kitties img { 12 | max-width: 80vw; 13 | max-height: 80vh; 14 | } -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/README.md: -------------------------------------------------------------------------------- 1 | # To run the application 2 | 3 | If you've just downloaded the code and you are running the application 4 | for the first time, go to the project directory and install all packages 5 | 6 | ```shell 7 | npm ci 8 | ``` 9 | 10 | Then, you can run the app in the development mode: 11 | 12 | ```shell 13 | npm start 14 | ``` 15 | 16 | The page will reload if you make edits. 17 | You will also see any lint errors in the console. 18 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "suspense-and-concurrent-mode", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "lodash": "^4.17.11", 7 | "react": "16.7.0-alpha.2", 8 | "react-cache": "^2.0.0-alpha.1", 9 | "react-dom": "16.7.0-alpha.2", 10 | "react-router-dom": "4.4.0-beta.7", 11 | "react-scripts": "2.1.1", 12 | "scheduler": "0.13.3", 13 | "serve": "^10.1.2" 14 | }, 15 | "scripts": { 16 | "build": "SKIP_PREFLIGHT_CHECK=true react-scripts build", 17 | "eject": "react-scripts eject", 18 | "eslint": "eslint --ext .js --ext .jsx .", 19 | "lint": "npm run eslint", 20 | "serve": "./node_modules/serve/bin/serve.js -s build", 21 | "start": "SKIP_PREFLIGHT_CHECK=true PORT=3006 react-scripts start", 22 | "test": "react-scripts test" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "babel-eslint": "^10.0.1", 35 | "eslint": "^5.9.0", 36 | "prop-types": "^15.7.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/exercise-lazy-solution/public/favicon.ico -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/src/components/spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './spinner.css'; 4 | 5 | const Spinner = ({children}) => ( 6 | <> 7 |
    8 | {children} 9 | 10 | ); 11 | 12 | Spinner.propTypes = { 13 | children: PropTypes.node 14 | }; 15 | 16 | Spinner.defaultProps = { 17 | children: null 18 | }; 19 | 20 | export default Spinner; 21 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html { 12 | background-color: #282c34; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } 19 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/src/pages/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../logo.svg'; 4 | 5 | const Home = () => ( 6 |
    7 | logo 8 |

    Suspense and Concurrent Mode in React

    9 |

    10 | "Code splitting and improving UX made easy." 11 |

    12 |
    13 | ); 14 | 15 | export default Home; 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy-solution/src/pages/prague-image.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const PragueImage = () => ( 4 |
    5 | This is a page with image just for demonstration purpose:
    6 | Prague 7 |
    8 | ); 9 | 10 | export default PragueImage; 11 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/README.md: -------------------------------------------------------------------------------- 1 | # To run the application 2 | 3 | If you've just downloaded the code and you are running the application 4 | for the first time, go to the project directory and install all packages 5 | 6 | ```shell 7 | npm ci 8 | ``` 9 | 10 | Then, you can run the app in the development mode: 11 | 12 | ```shell 13 | npm start 14 | ``` 15 | 16 | The page will reload if you make edits. 17 | You will also see any lint errors in the console. 18 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "suspense-and-concurrent-mode", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "lodash": "^4.17.11", 7 | "react": "16.7.0-alpha.2", 8 | "react-cache": "^2.0.0-alpha.1", 9 | "react-dom": "16.7.0-alpha.2", 10 | "react-router-dom": "4.4.0-beta.7", 11 | "react-scripts": "2.1.1", 12 | "scheduler": "0.13.3", 13 | "serve": "^10.1.2" 14 | }, 15 | "scripts": { 16 | "build": "SKIP_PREFLIGHT_CHECK=true react-scripts build", 17 | "eject": "react-scripts eject", 18 | "eslint": "eslint --ext .js --ext .jsx .", 19 | "lint": "npm run eslint", 20 | "serve": "./node_modules/serve/bin/serve.js -s build", 21 | "start": "SKIP_PREFLIGHT_CHECK=true PORT=3006 react-scripts start", 22 | "test": "react-scripts test" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ], 33 | "devDependencies": { 34 | "babel-eslint": "^10.0.1", 35 | "eslint": "^5.9.0", 36 | "prop-types": "^15.7.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/exercise-lazy/public/favicon.ico -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/src/components/spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './spinner.css'; 4 | 5 | const Spinner = ({children}) => ( 6 | <> 7 |
    8 | {children} 9 | 10 | ); 11 | 12 | Spinner.propTypes = { 13 | children: PropTypes.node 14 | }; 15 | 16 | Spinner.defaultProps = { 17 | children: null 18 | }; 19 | 20 | export default Spinner; 21 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html { 12 | background-color: #282c34; 13 | } 14 | 15 | code { 16 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 17 | monospace; 18 | } 19 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/src/pages/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../logo.svg'; 4 | 5 | const Home = () => ( 6 |
    7 | logo 8 |

    Suspense and Concurrent Mode in React

    9 |

    10 | "Code splitting and improving UX made easy." 11 |

    12 |
    13 | ); 14 | 15 | export default Home; 16 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/exercise-lazy/src/pages/prague-image.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const PragueImage = () => ( 4 |
    5 | This is a page with image just for demonstration purpose:
    6 | Prague 7 |
    8 | ); 9 | 10 | export default PragueImage; 11 | -------------------------------------------------------------------------------- /04-suspense-and-async-mode/images/concurrent_lifecycle_methods.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/images/concurrent_lifecycle_methods.jpeg -------------------------------------------------------------------------------- /04-suspense-and-async-mode/images/concurrent_mode_explained.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/04-suspense-and-async-mode/images/concurrent_mode_explained.png -------------------------------------------------------------------------------- /05-component-testing/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | *.final.test.tsx 26 | -------------------------------------------------------------------------------- /05-component-testing/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/05-component-testing/public/favicon.ico -------------------------------------------------------------------------------- /05-component-testing/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /05-component-testing/src/App.css: -------------------------------------------------------------------------------- 1 | .movie-list { 2 | padding: 20px; 3 | display: flex; 4 | flex-wrap: wrap; 5 | } 6 | 7 | .movie { 8 | margin: 10px; 9 | } 10 | 11 | .movie__header { 12 | display: flex; 13 | justify-content: space-between; 14 | } 15 | 16 | .movie__image-container { 17 | width: 500px; 18 | height: 281px; 19 | } 20 | -------------------------------------------------------------------------------- /05-component-testing/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Provider } from 'react-redux' 3 | 4 | import './App.css'; 5 | import MovieList from './MovieList'; 6 | import {BrowserRouter as Router, Route, Switch} from 'react-router-dom' 7 | import configureStore from './store'; 8 | 9 | class App extends Component<{}> { 10 | render() { 11 | return ( 12 | 13 |
    14 | 15 | 16 | 17 |
    18 |
    19 | ); 20 | } 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /05-component-testing/src/Fetcher.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | type State = { 4 | data: object | undefined 5 | loading: boolean 6 | } 7 | 8 | type Props = { 9 | url: string 10 | children: (api: { 11 | data?: TData 12 | loading: boolean | null 13 | }) => ReactNode 14 | } 15 | 16 | class Fetcher extends React.Component, State> { 17 | state = { 18 | data: undefined, 19 | loading: false, 20 | }; 21 | 22 | componentDidMount() { 23 | this.setState({ loading: true }); 24 | 25 | fetch(this.props.url) 26 | .then(response => { 27 | if (response.ok) { 28 | return response.json(); 29 | } else { 30 | throw new Error('Something went wrong ...'); 31 | } 32 | }) 33 | .then(data => this.setState({ data, loading: false })) 34 | .catch(error => this.setState({ loading: false })); 35 | } 36 | 37 | render() { 38 | return this.props.children(this.state); 39 | } 40 | } 41 | 42 | export default Fetcher; 43 | -------------------------------------------------------------------------------- /05-component-testing/src/Movie.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | type Props = { 4 | id: number 5 | title: string 6 | includedInWishlist: boolean 7 | image?: string | null 8 | onClick: (id: number) => void 9 | } 10 | 11 | const Movie: React.FunctionComponent = (props) => { 12 | 13 | const handleClick = () => { 14 | props.onClick(props.id) 15 | } 16 | 17 | return ( 18 |
    19 |
    20 | {props.title} 21 | 22 | {props.includedInWishlist ? '❤️' : '🖤'} 23 | 24 |
    25 |
    26 | {props.image && } 27 |
    28 |
    29 | ) 30 | } 31 | 32 | export default Movie 33 | -------------------------------------------------------------------------------- /05-component-testing/src/__tests__/exercise.002.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render, cleanup, fireEvent} from 'react-testing-library' 3 | import chai, {expect} from 'chai' 4 | import sinon from 'sinon' 5 | 6 | import chaiSinon from 'sinon-chai' 7 | 8 | import Movie from '../Movie' 9 | 10 | chai.use(chaiSinon) 11 | 12 | describe('Exercise 002', () => { 13 | const baseProps = { 14 | title: 'New Movie', 15 | id: 12, 16 | includedInWishlist: false, 17 | onClick: () => {} 18 | } 19 | 20 | afterEach(() => { 21 | cleanup() 22 | }) 23 | 24 | it('should handle add wishlist item', () => { 25 | // const props = { 26 | // ...baseProps, 27 | // onClick: sinon.spy() 28 | // } 29 | 30 | // Render component 31 | // Fire click event 32 | // Check if the event was called with param id of the movie 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /05-component-testing/src/__tests__/exercise.004.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render, cleanup, waitForElement, fireEvent} from 'react-testing-library' 3 | import {MemoryRouter} from 'react-router' 4 | import chai, {expect} from 'chai' 5 | import chaiDom from 'chai-dom' 6 | 7 | import App from '../App' 8 | 9 | chai.use(chaiDom) 10 | 11 | describe('Exercise 004', () => { 12 | afterEach(() => { 13 | cleanup() 14 | }) 15 | 16 | it('should by able to add movie to wishlist', async () => { 17 | // Render component - 18 | // Wait for wishlist button to be displayed - await waitForElement(() => find the element) 19 | // Check if the button has black heart 20 | // Click on the button 21 | // Check if the button has red heart 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /05-component-testing/src/actions/wishlistAction.ts: -------------------------------------------------------------------------------- 1 | import { Action, ActionCreator } from "redux"; 2 | export const ADD_WISHLIST_ITEM = 'ADD_WISHLIST_ITEM' 3 | 4 | export interface AddWishlistItemAction extends Action { 5 | payload: number 6 | } 7 | 8 | export type WishlistActions = AddWishlistItemAction 9 | 10 | export const addWishlistItem = (id: number) => ({ 11 | type: ADD_WISHLIST_ITEM, 12 | payload: id 13 | }) 14 | -------------------------------------------------------------------------------- /05-component-testing/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /05-component-testing/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import {BrowserRouter as Router} from 'react-router-dom' 6 | 7 | // import * as serviceWorker from './serviceWorker'; 8 | 9 | ReactDOM.render(, document.getElementById('root')); 10 | 11 | // If you want your app to work offline and load faster, you can change 12 | // unregister() to register() below. Note this comes with some pitfalls. 13 | // Learn more about service workers: https://bit.ly/CRA-PWA 14 | // serviceWorker.unregister(); 15 | -------------------------------------------------------------------------------- /05-component-testing/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /05-component-testing/src/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import wishlistReducer, { WishlistState } from './wishlistReducer'; 3 | 4 | 5 | const rootReducer = combineReducers({ 6 | wishlistReducer: wishlistReducer 7 | }); 8 | 9 | export type AppState = ReturnType 10 | 11 | export default rootReducer 12 | -------------------------------------------------------------------------------- /05-component-testing/src/reducers/wishlistReducer.ts: -------------------------------------------------------------------------------- 1 | import { Reducer } from "redux"; 2 | import {WishlistActions} from '../actions/wishlistAction' 3 | 4 | export interface WishlistState { 5 | readonly ids: number[] 6 | } 7 | 8 | const initialState: WishlistState = { 9 | ids: [] 10 | } 11 | 12 | const wishlistReducer: Reducer = (state = initialState, action): WishlistState => { 13 | switch (action.type) { 14 | case 'ADD_WISHLIST_ITEM': 15 | if (state.ids.includes(action.payload)) { 16 | return state 17 | } 18 | 19 | return { 20 | ids: [...state.ids, action.payload] 21 | } 22 | default: 23 | return state 24 | } 25 | } 26 | 27 | export default wishlistReducer 28 | -------------------------------------------------------------------------------- /05-component-testing/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from 'redux'; 2 | import rootReducer from './reducers'; 3 | 4 | export default function configureStore() { 5 | return createStore(rootReducer); 6 | } 7 | -------------------------------------------------------------------------------- /05-component-testing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /06-graphql/backend/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const cors = require('cors') 3 | 4 | const graphqlHTTP = require("express-graphql"); 5 | 6 | const graphqlSchema = require("./graphqlSchema") 7 | 8 | const app = express() 9 | app.use(cors()) 10 | 11 | const port = 3000 12 | app.listen(port, () => console.log(`Server listening on port ${port}!`)) 13 | 14 | 15 | app.use("/hello", function(req, res){ 16 | res.send("hello!") 17 | }) 18 | 19 | app.use("/graphql", function (req, res) { 20 | const graphqlFunc = graphqlHTTP({ 21 | schema: graphqlSchema, 22 | graphiql: true, 23 | }) 24 | 25 | return graphqlFunc(req, res) 26 | .catch(function (err) { 27 | console.log(err) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /06-graphql/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^0.19.0", 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1", 15 | "express-graphql": "^0.8.0", 16 | "graphql": "^14.3.1", 17 | "https-proxy-agent": "^2.2.1", 18 | "rickmortyapi": "^0.2.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /06-graphql/web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | "@babel/preset-react", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | // see .browserslistrc 8 | useBuiltIns: "entry", 9 | corejs: 3 10 | } 11 | ] 12 | ], 13 | plugins: [ 14 | "@babel/plugin-proposal-class-properties", 15 | "@babel/plugin-transform-regenerator" 16 | ] 17 | }; 18 | -------------------------------------------------------------------------------- /06-graphql/web/src/components/Character.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import "./Character.scss"; 4 | 5 | export default function Character(props) { 6 | 7 | const {id, name, imageUrl} = props.data 8 | 9 | return ( 10 |
    11 |
    {id} {name}
    12 |
    13 | 14 |
    15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /06-graphql/web/src/components/Character.scss: -------------------------------------------------------------------------------- 1 | 2 | .character { 3 | display: flex; 4 | flex-direction: column; 5 | padding: 10px; 6 | } 7 | 8 | .header { 9 | 10 | } 11 | 12 | .image { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /06-graphql/web/src/components/CharacterSheet.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /06-graphql/web/src/graphqlRequest.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default function graphqlRequest(query){ 4 | return axios 5 | .get(`http://localhost:3000/graphql/?query=${query}`) 6 | .then(result => result.data); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /06-graphql/web/src/helpers/getRandomCharacterIds.js: -------------------------------------------------------------------------------- 1 | // adopted from https://github.com/afuh/rick-and-morty-api-site/blob/master/src/utils/hooks/useRandomChars.js 2 | export default function getRandomCharacterIds(total){ 3 | const totalNumberOfCharacters = 493 4 | const arr = [] 5 | const randomNum = () => Math.floor(Math.random() * totalNumberOfCharacters + 1) 6 | 7 | if (total === 1) { 8 | return randomNum() 9 | } 10 | 11 | while (arr.length < total) { 12 | const num = randomNum() 13 | if (arr.indexOf(num) > -1) { 14 | continue 15 | } 16 | arr[arr.length] = num 17 | } 18 | 19 | return arr 20 | } 21 | -------------------------------------------------------------------------------- /06-graphql/web/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React workshop 6 8 | 9 | 10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /06-graphql/web/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | 5 | import CharacterSheet from "./components/CharacterSheet" 6 | 7 | const Index = () => ( 8 |
    9 | 10 |
    11 | ); 12 | 13 | ReactDOM.render(, document.getElementById("index")); 14 | -------------------------------------------------------------------------------- /06-graphql/web/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const merge = require("webpack-merge"); 3 | const common = require("./webpack.common"); 4 | 5 | module.exports = merge(common, { 6 | mode: "development", 7 | devtool: "cheap-module-eval-source-map", 8 | watch: true, 9 | output: { 10 | publicPath: "/" 11 | }, 12 | devServer: { 13 | contentBase: path.join(__dirname, "dist"), 14 | watchContentBase: true, 15 | publicPath: "/", 16 | inline: true, 17 | stats: "errors-only", 18 | proxy: { 19 | "/**": { 20 | target: "http://localhost:8080", 21 | pathRewrite: { "^/.*": "/" } 22 | } 23 | } 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /06-graphql/web/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require("webpack-merge"); 2 | const common = require("./webpack.common"); 3 | 4 | module.exports = merge(common, { 5 | mode: "production", 6 | output: { 7 | publicPath: "/static/" 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /07-firebase/excersise/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # env variables 9 | .env 10 | 11 | # testing 12 | /coverage 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /07-firebase/excersise/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": true, 10 | "arrowParens": "always" 11 | } 12 | -------------------------------------------------------------------------------- /07-firebase/excersise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase-excersise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.4.0", 7 | "firebase": "^6.5.0", 8 | "prettier": "^1.15.3", 9 | "react": "^16.9.0", 10 | "react-dom": "^16.9.0", 11 | "react-router-dom": "^5.0.1", 12 | "react-scripts": "3.1.1", 13 | "recompose": "^0.30.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "prettier": "prettier --write './**/*.{js,json,css,md}'" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [">0.2%", "not dead", "not op_mini all"], 27 | "development": [ 28 | "last 1 chrome version", 29 | "last 1 firefox version", 30 | "last 1 safari version" 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /07-firebase/excersise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/excersise/public/favicon.ico -------------------------------------------------------------------------------- /07-firebase/excersise/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/excersise/public/logo192.png -------------------------------------------------------------------------------- /07-firebase/excersise/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/excersise/public/logo512.png -------------------------------------------------------------------------------- /07-firebase/excersise/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /07-firebase/excersise/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Firebase/context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const FirebaseContext = React.createContext(null) 4 | 5 | // Implement FirebaseContext.Consumer which then pass firebase instance to Component. 6 | export const withFirebase = (Component) => (props) =>
    7 | 8 | export default FirebaseContext 9 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Firebase/firebase.js: -------------------------------------------------------------------------------- 1 | import app from 'firebase/app' 2 | import 'firebase/auth' 3 | import 'firebase/database' 4 | 5 | const config = { 6 | apiKey: process.env.REACT_APP_API_KEY, 7 | authDomain: process.env.REACT_APP_AUTH_DOMAIN, 8 | databaseURL: process.env.REACT_APP_DATABASE_URL, 9 | projectId: process.env.REACT_APP_PROJECT_ID, 10 | storageBucket: process.env.REACT_APP_STORAGE_BUCKET, 11 | messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID 12 | } 13 | 14 | class Firebase { 15 | constructor() { 16 | app.initializeApp(config) 17 | 18 | this.auth = app.auth() 19 | this.db = app.database() 20 | } 21 | 22 | // *** Auth API *** 23 | 24 | // *** User API *** 25 | } 26 | 27 | export default Firebase 28 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Firebase/index.js: -------------------------------------------------------------------------------- 1 | import FirebaseContext, {withFirebase} from './context' 2 | import Firebase from './firebase' 3 | 4 | export default Firebase 5 | 6 | export {FirebaseContext, withFirebase} 7 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Typography from '@material-ui/core/Typography' 3 | 4 | import {withAuthorization} from '../Session' 5 | 6 | const HomePage = () => ( 7 |
    8 | 9 | Home Page 10 | 11 | 12 | The Home page is accesible by every signed in user. 13 | 14 |
    15 | ) 16 | 17 | const condition = (authUser) => authUser !== null 18 | 19 | export default withAuthorization(condition)(HomePage) 20 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Landing/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Typography from '@material-ui/core/Typography' 3 | 4 | const LandingPage = () => ( 5 |
    6 | 7 | Landing Page 8 | 9 | 10 | The Home page is accesible by every user. 11 | 12 |
    13 | ) 14 | 15 | export default LandingPage 16 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Session/context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const AuthUserContext = React.createContext(null) 4 | 5 | export default AuthUserContext 6 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Session/index.js: -------------------------------------------------------------------------------- 1 | import AuthUserContext from './context' 2 | import withAuthentication from './withAuthentication' 3 | import withAuthorization from './withAuthorization' 4 | 5 | export {AuthUserContext, withAuthentication, withAuthorization} 6 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/Session/withAuthentication.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import AuthUserContext from './context' 3 | import {withFirebase} from '../Firebase' 4 | 5 | const withAuthentication = (Component) => { 6 | class WithAuthentication extends React.Component { 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | authUser: null 12 | } 13 | } 14 | 15 | componentDidMount() { 16 | this.listener = this.props.firebase.auth.onAuthStateChanged( 17 | (authUser) => { 18 | authUser ? this.setState({authUser}) : this.setState({authUser: null}) 19 | } 20 | ) 21 | } 22 | 23 | componentWillUnmount() { 24 | this.listener() 25 | } 26 | render() { 27 | return ( 28 | 29 | 30 | 31 | ) 32 | } 33 | } 34 | 35 | return withFirebase(WithAuthentication) 36 | } 37 | 38 | export default withAuthentication 39 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/components/SignOut/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {compose} from 'recompose' 3 | import {withRouter} from 'react-router-dom' 4 | import Button from '@material-ui/core/Button' 5 | import {makeStyles} from '@material-ui/core/styles' 6 | 7 | import * as ROUTES from '../../constants/routes' 8 | import {withFirebase} from '../Firebase' 9 | 10 | const useStyles = makeStyles((theme) => ({ 11 | menuButton: { 12 | marginRight: theme.spacing(2), 13 | color: '#ffffff' 14 | } 15 | })) 16 | 17 | const SignOutButton = ({firebase, history}) => { 18 | const classes = useStyles() 19 | 20 | // Call sign out of our authentication interface and route to landing page. 21 | const onSignOut = () => {} 22 | 23 | return ( 24 | 27 | ) 28 | } 29 | 30 | export default compose( 31 | withRouter, 32 | withFirebase 33 | )(SignOutButton) 34 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/constants/routes.js: -------------------------------------------------------------------------------- 1 | export const LANDING = '/' 2 | export const SIGN_UP = '/signup' 3 | export const SIGN_IN = '/signin' 4 | export const HOME = '/home' 5 | export const ACCOUNT = '/account' 6 | export const ADMIN = '/admin' 7 | export const PASSWORD_FORGET = '/pw-forget' 8 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /07-firebase/excersise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import './index.css' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | import App from './components/App' 8 | import Firebase, {FirebaseContext} from './components/Firebase' 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ) 16 | 17 | // If you want your app to work offline and load faster, you can change 18 | // unregister() to register() below. Note this comes with some pitfalls. 19 | // Learn more about service workers: https://bit.ly/CRA-PWA 20 | serviceWorker.unregister() 21 | -------------------------------------------------------------------------------- /07-firebase/solution/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # env variables 9 | .env 10 | 11 | # testing 12 | /coverage 13 | 14 | # production 15 | /build 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /07-firebase/solution/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": true, 10 | "arrowParens": "always" 11 | } 12 | -------------------------------------------------------------------------------- /07-firebase/solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase-solution", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.4.0", 7 | "firebase": "^6.5.0", 8 | "prettier": "^1.15.3", 9 | "react": "^16.9.0", 10 | "react-dom": "^16.9.0", 11 | "react-router-dom": "^5.0.1", 12 | "react-scripts": "3.1.1", 13 | "recompose": "^0.30.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject", 20 | "prettier": "prettier --write './**/*.{js,json,css,md}'" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /07-firebase/solution/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/solution/public/favicon.ico -------------------------------------------------------------------------------- /07-firebase/solution/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/solution/public/logo192.png -------------------------------------------------------------------------------- /07-firebase/solution/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/07-firebase/solution/public/logo512.png -------------------------------------------------------------------------------- /07-firebase/solution/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /07-firebase/solution/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Firebase/context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const FirebaseContext = React.createContext(null) 4 | 5 | export const withFirebase = (Component) => (props) => ( 6 | 7 | {(firebase) => } 8 | 9 | ) 10 | 11 | export default FirebaseContext 12 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Firebase/index.js: -------------------------------------------------------------------------------- 1 | import FirebaseContext, {withFirebase} from './context' 2 | import Firebase from './firebase' 3 | 4 | export default Firebase 5 | 6 | export {FirebaseContext, withFirebase} 7 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Home/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Typography from '@material-ui/core/Typography' 3 | 4 | import {withAuthorization} from '../Session' 5 | 6 | const HomePage = () => ( 7 |
    8 | 9 | Home Page 10 | 11 | 12 | The Home page is accesible by every signed in user. 13 | 14 |
    15 | ) 16 | 17 | const condition = (authUser) => authUser !== null 18 | 19 | export default withAuthorization(condition)(HomePage) 20 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Landing/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Typography from '@material-ui/core/Typography' 3 | 4 | const LandingPage = () => ( 5 |
    6 | 7 | Landing Page 8 | 9 | 10 | The Home page is accesible by every user. 11 | 12 |
    13 | ) 14 | 15 | export default LandingPage 16 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Session/context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const AuthUserContext = React.createContext(null) 4 | 5 | export default AuthUserContext 6 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Session/index.js: -------------------------------------------------------------------------------- 1 | import AuthUserContext from './context' 2 | import withAuthentication from './withAuthentication' 3 | import withAuthorization from './withAuthorization' 4 | 5 | export {AuthUserContext, withAuthentication, withAuthorization} 6 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/Session/withAuthentication.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import AuthUserContext from './context' 3 | import {withFirebase} from '../Firebase' 4 | 5 | const withAuthentication = (Component) => { 6 | class WithAuthentication extends React.Component { 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | authUser: null 12 | } 13 | } 14 | 15 | componentDidMount() { 16 | this.listener = this.props.firebase.auth.onAuthStateChanged( 17 | (authUser) => { 18 | authUser ? this.setState({authUser}) : this.setState({authUser: null}) 19 | } 20 | ) 21 | } 22 | 23 | componentWillUnmount() { 24 | this.listener() 25 | } 26 | render() { 27 | return ( 28 | 29 | 30 | 31 | ) 32 | } 33 | } 34 | 35 | return withFirebase(WithAuthentication) 36 | } 37 | 38 | export default withAuthentication 39 | -------------------------------------------------------------------------------- /07-firebase/solution/src/components/SignOut/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {compose} from 'recompose' 3 | import {withRouter} from 'react-router-dom' 4 | import Button from '@material-ui/core/Button' 5 | import {makeStyles} from '@material-ui/core/styles' 6 | 7 | import * as ROUTES from '../../constants/routes' 8 | import {withFirebase} from '../Firebase' 9 | 10 | const useStyles = makeStyles((theme) => ({ 11 | menuButton: { 12 | marginRight: theme.spacing(2), 13 | color: '#ffffff' 14 | } 15 | })) 16 | 17 | const SignOutButton = ({firebase, history}) => { 18 | const classes = useStyles() 19 | 20 | const onSignOut = () => { 21 | firebase.doSignOut() 22 | history.push(ROUTES.LANDING) 23 | } 24 | 25 | return ( 26 | 29 | ) 30 | } 31 | 32 | export default compose( 33 | withRouter, 34 | withFirebase 35 | )(SignOutButton) 36 | -------------------------------------------------------------------------------- /07-firebase/solution/src/constants/routes.js: -------------------------------------------------------------------------------- 1 | export const LANDING = '/' 2 | export const SIGN_UP = '/signup' 3 | export const SIGN_IN = '/signin' 4 | export const HOME = '/home' 5 | export const ACCOUNT = '/account' 6 | export const ADMIN = '/admin' 7 | export const PASSWORD_FORGET = '/pw-forget' 8 | -------------------------------------------------------------------------------- /07-firebase/solution/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /07-firebase/solution/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import './index.css' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | import App from './components/App' 8 | import Firebase, {FirebaseContext} from './components/Firebase' 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ) 16 | 17 | // If you want your app to work offline and load faster, you can change 18 | // unregister() to register() below. Note this comes with some pitfalls. 19 | // Learn more about service workers: https://bit.ly/CRA-PWA 20 | serviceWorker.unregister() 21 | -------------------------------------------------------------------------------- /08-testing-hooks/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /08-testing-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "08-testing-hooks", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.1.0", 7 | "@testing-library/react": "^9.2.0", 8 | "enzyme": "^3.10.0", 9 | "enzyme-adapter-react-16": "^1.14.0", 10 | "react": "^16.10.1", 11 | "react-dom": "^16.10.1", 12 | "react-scripts": "3.1.2", 13 | "react-test-renderer": "^16.10.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /08-testing-hooks/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/08-testing-hooks/public/favicon.ico -------------------------------------------------------------------------------- /08-testing-hooks/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/08-testing-hooks/public/logo192.png -------------------------------------------------------------------------------- /08-testing-hooks/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/08-testing-hooks/public/logo512.png -------------------------------------------------------------------------------- /08-testing-hooks/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /08-testing-hooks/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /08-testing-hooks/src/01/01-exercise.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Main = () => { 4 | const defaultColor = 'orange' 5 | 6 | return ( 7 |
    8 |

    Adding State

    9 | 10 |
    11 |
    12 | ) 13 | } 14 | 15 | export default Main 16 | -------------------------------------------------------------------------------- /08-testing-hooks/src/01/01-exercise.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, unmountComponentAtNode } from 'react-dom' 3 | import { act } from "react-dom/test-utils" 4 | import Main from './01-exercise' 5 | 6 | let div 7 | beforeEach(() => { 8 | div = document.createElement('div') 9 | }) 10 | afterEach(() => { 11 | unmountComponentAtNode(div) 12 | }) 13 | 14 | test('default color', () => { 15 | act(() => { 16 | render(
    , div) 17 | }) 18 | expect(div.querySelector('.Main-box').style.backgroundColor).toBe('orange') 19 | }) 20 | -------------------------------------------------------------------------------- /08-testing-hooks/src/01/01-solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const Main = () => { 4 | const defaultColor = 'orange' 5 | 6 | const [color, setColor] = useState('') 7 | const handleChange = (event) => { 8 | setColor(event.target.value) 9 | } 10 | 11 | return ( 12 |
    13 |

    Adding State

    14 | 15 |
    16 |
    17 | ) 18 | } 19 | 20 | export default Main 21 | -------------------------------------------------------------------------------- /08-testing-hooks/src/02/02-exercise.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export const validateColor = () => false 4 | 5 | const Main = () => { 6 | const defaultColor = 'orange' 7 | 8 | const [inputColor, setInputColor] = useState('') 9 | const handleChange = (event) => { 10 | const { value } = event.target 11 | setInputColor(value) 12 | if (validateColor(value)) { 13 | // TODO 14 | } 15 | } 16 | 17 | return ( 18 |
    19 |

    TDD

    20 | 21 |
    22 | {'orange '} 23 |
    24 |
    25 | ) 26 | } 27 | 28 | export default Main 29 | -------------------------------------------------------------------------------- /08-testing-hooks/src/02/02-solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export const validateColor = (value) => { 4 | const div = document.createElement('div') 5 | div.style.color = value 6 | return !!div.style.color 7 | } 8 | 9 | const Main = () => { 10 | const defaultColor = 'orange' 11 | 12 | const [inputColor, setInputColor] = useState('') 13 | const [validColor, setValidColor] = useState(defaultColor) 14 | const handleChange = (event) => { 15 | const { value } = event.target 16 | setInputColor(value) 17 | if (!value) { 18 | setValidColor(defaultColor) 19 | } else if (validateColor(value)) { 20 | setValidColor(value) 21 | } 22 | } 23 | 24 | return ( 25 |
    26 |

    TDD

    27 | 28 |
    29 | {validColor} 30 |
    31 |
    32 | ) 33 | } 34 | 35 | export default Main 36 | -------------------------------------------------------------------------------- /08-testing-hooks/src/04/04-exercise.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const Main = () => { 4 | const defaultColor = 'orange' 5 | 6 | const [color, setColor] = useState('') 7 | const handleChange = (event) => { 8 | setColor(event.target.value) 9 | } 10 | 11 | return ( 12 |
    13 |

    Libraries for testing

    14 | 15 |
    16 |
    17 | ) 18 | } 19 | 20 | export default Main 21 | -------------------------------------------------------------------------------- /08-testing-hooks/src/04/04-solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | const Main = () => { 4 | const defaultColor = 'orange' 5 | 6 | const [color, setColor] = useState('') 7 | const handleChange = (event) => { 8 | setColor(event.target.value) 9 | } 10 | 11 | return ( 12 |
    13 |

    Libraries for testing

    14 | 15 |
    16 |
    17 | ) 18 | } 19 | 20 | export default Main 21 | -------------------------------------------------------------------------------- /08-testing-hooks/src/04/04-solution.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, fireEvent } from '@testing-library/react' 3 | import Main from './04-solution' 4 | 5 | test('default color', () => { 6 | const { container: div } = render(
    ) 7 | expect(div.querySelector('.Main-box').style.backgroundColor).toBe('orange') 8 | }) 9 | 10 | test('modified color', () => { 11 | const { container: div } = render(
    ) 12 | const input = div.querySelector('input') 13 | expect(input).toHaveProperty('value', '') 14 | 15 | fireEvent.change(input, { target: { value: 'green' } }) 16 | expect(input).toHaveProperty('value', 'green') 17 | expect(div.querySelector('.Main-box').style.backgroundColor).toBe('green') 18 | }) 19 | -------------------------------------------------------------------------------- /08-testing-hooks/src/05/05-exercise.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | 3 | const Main = () => { 4 | const [color, setColor] = useState('orange') 5 | 6 | // useEffect(() => { 7 | // window.fetch('https://jsonplaceholder.typicode.com/users/1').then((data) => { 8 | // setColor('green') 9 | // }) 10 | // }, []) 11 | 12 | useEffect(() => { 13 | const fetchPromise = window.fetch('https://jsonplaceholder.typicode.com/users/1') 14 | const timeoutPromise = new Promise((resolve, reject) => { 15 | setTimeout(reject, 1000) 16 | }) 17 | Promise.race([fetchPromise, timeoutPromise]).then((data) => { 18 | setColor('green') 19 | }).catch(() => { 20 | setColor('red') 21 | }) 22 | }, []) 23 | 24 | return ( 25 |
    26 |

    Fetch and timeout

    27 |
    28 |
    29 | ) 30 | } 31 | 32 | export default Main 33 | -------------------------------------------------------------------------------- /08-testing-hooks/src/05/05-exercise.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, unmountComponentAtNode } from 'react-dom' 3 | import { act } from "react-dom/test-utils" 4 | import Main from './05-exercise' 5 | 6 | let div 7 | beforeEach(() => { 8 | div = document.createElement('div') 9 | document.body.appendChild(div) 10 | }) 11 | afterEach(() => { 12 | unmountComponentAtNode(div) 13 | div.remove() 14 | }) 15 | 16 | test('default color', () => { 17 | act(() => { 18 | render(
    , div) 19 | }) 20 | expect(div.querySelector('.Main-box').style.backgroundColor).toBe('orange') 21 | }) 22 | 23 | test('modified color', () => { 24 | // ??? 25 | }) 26 | -------------------------------------------------------------------------------- /08-testing-hooks/src/05/05-solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | 3 | const Main = () => { 4 | const [color, setColor] = useState('orange') 5 | 6 | useEffect(() => { 7 | const fetchPromise = window.fetch('https://jsonplaceholder.typicode.com/users/1') 8 | let cancellable 9 | const timeoutPromise = new Promise((resolve, reject) => { 10 | cancellable = setTimeout(reject, 1000) 11 | }) 12 | Promise.race([fetchPromise, timeoutPromise]).then((data) => { 13 | setColor('green') 14 | }).catch(() => { 15 | setColor('red') 16 | }) 17 | return () => { 18 | clearTimeout(cancellable) 19 | } 20 | }, []) 21 | 22 | return ( 23 |
    24 |

    Fetch and timeout

    25 |
    26 |
    27 | ) 28 | } 29 | 30 | export default Main 31 | -------------------------------------------------------------------------------- /08-testing-hooks/src/06/06-solution.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | // import { useDispatch, useSelector } from 'react-redux' 3 | import { useDispatch, useSelector } from './react-redux' 4 | 5 | const fetchData = (dispatch) => window.fetch('/api/data').then(({ data }) => dispatch({ type: 'DATA_SUCCESS', data })) 6 | const dataSelector = (state) => state.api13.data 7 | 8 | const Main = () => { 9 | const { data } = useSelector(dataSelector) 10 | const dispatch = useDispatch() 11 | useEffect(() => { 12 | dispatch(fetchData) 13 | }, [dispatch]) 14 | 15 | return !data ? ('Loading...') : ( 16 |
      {data.map(({ id }) =>
    • {id}
    • )}
    17 | ) 18 | } 19 | 20 | export default Main -------------------------------------------------------------------------------- /08-testing-hooks/src/06/06-solution.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import Main from './06-solution' 4 | 5 | // jest.mock('react-redux', () => ({ 6 | jest.mock('./react-redux', () => ({ 7 | useDispatch: () => jest.fn(), 8 | useSelector: () => ({ data: [{ id: 2 }] }), 9 | // useSelector: (selectorFn) => { 10 | // switch (selectorFn.name) { 11 | // case 'dataSelector': 12 | // return { data: [{ id: 2 }] } 13 | // } 14 | // }, 15 | connect: () => (Component) => Component 16 | })) 17 | 18 | test('Main', () => { 19 | const { container } = render(
    ) 20 | expect(container.textContent).toBe('2') 21 | }) -------------------------------------------------------------------------------- /08-testing-hooks/src/06/react-redux.js: -------------------------------------------------------------------------------- 1 | export const useDispatch = () => undefined 2 | export const useSelector = () => undefined -------------------------------------------------------------------------------- /08-testing-hooks/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /08-testing-hooks/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /09-typescript-in-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /09-typescript-in-react/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 90, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "bracketSpacing": false, 8 | "trailingComma": "none", 9 | "jsxBracketSameLine": true, 10 | "arrowParens": "always", 11 | "parser": "typescript", 12 | "overrides": [{ 13 | "files": "*.md", 14 | "options": { 15 | "parser": "markdown", 16 | "proseWrap": "always", 17 | "printWidth": 80 18 | } 19 | }] 20 | } -------------------------------------------------------------------------------- /09-typescript-in-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@types/jest": "24.0.23", 7 | "@types/node": "12.12.11", 8 | "@types/react-dom": "16.9.4", 9 | "prop-types": "^15.7.2", 10 | "react": "^16.12.0", 11 | "react-dom": "^16.12.0", 12 | "react-router-dom": "^5.1.2", 13 | "react-scripts": "3.2.0", 14 | "typescript": "3.7.2" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "@types/react": "^16.9.11", 36 | "@types/react-router-dom": "^5.1.2", 37 | "prettier": "^1.19.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /09-typescript-in-react/public/IMG_5437.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/09-typescript-in-react/public/IMG_5437.JPG -------------------------------------------------------------------------------- /09-typescript-in-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/09-typescript-in-react/public/favicon.ico -------------------------------------------------------------------------------- /09-typescript-in-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/09-typescript-in-react/public/logo192.png -------------------------------------------------------------------------------- /09-typescript-in-react/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msd-code-academy/react-workshop/3fba5ae04d3e9e96b7ed20734603dcf1e300261a/09-typescript-in-react/public/logo512.png -------------------------------------------------------------------------------- /09-typescript-in-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /09-typescript-in-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/exercise-1/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './blog'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/exercise-2/fetch-jokes.ts: -------------------------------------------------------------------------------- 1 | export type Joke = { 2 | id: string; 3 | icon_url: string; 4 | value: string; 5 | }; 6 | 7 | const fetchJokes = async (query: string): Promise => { 8 | try { 9 | const result = await fetch(`https://api.chucknorris.io/jokes/search?query=${query}`); 10 | const resultJSON = await result.json(); 11 | return resultJSON.result || []; 12 | } catch (error) { 13 | console.error(error); 14 | return []; 15 | } 16 | }; 17 | 18 | export default fetchJokes; 19 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/exercise-2/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './jokes'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/exercise-3/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './clock'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/homepage/homepage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const HomePage = () => { 4 | // Place for playing with the code: 5 | 6 | 7 | 8 | return

    Home Page

    ; 9 | }; 10 | 11 | export default HomePage; 12 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/homepage/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './homepage'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /09-typescript-in-react/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/sandbox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | enum Color { 4 | Red = 'red', 5 | Green = 'green', 6 | Blue = 'blue' 7 | } 8 | 9 | const myColor: Color = Color.Green; 10 | console.log(myColor); -------------------------------------------------------------------------------- /09-typescript-in-react/src/solution-1/blog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export type BlogPost = { 4 | title: string; 5 | text: string; 6 | id: number; 7 | }; 8 | 9 | // Article component 10 | type ArticleProps = Omit; 11 | 12 | const Article: React.FC = ({title = '', text = ''}) => ( 13 | <> 14 |

    {title}

    15 |
    {text}
    16 | 17 | ); 18 | 19 | // Blog component that displays multiple articles 20 | type BlogProps = {}; 21 | 22 | const Blog: React.FC = () => { 23 | const myArticles: BlogPost[] = [ 24 | { 25 | id: 1, 26 | title: 'TypeScript is awesome', 27 | text: 'Every day I do not use TypeScript a kitten dies.' 28 | }, 29 | { 30 | id: 2, 31 | title: 'Jar Jar Binks was a sith lord', 32 | text: 'It all make sense when you think about it!' 33 | } 34 | ]; 35 | 36 | return ( 37 | <> 38 |

    My Blog

    39 | {myArticles.map((article) => ( 40 |
    41 | ))} 42 | 43 | ); 44 | }; 45 | 46 | export default Blog; 47 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/solution-1/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './blog'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/solution-2/fetch-jokes.ts: -------------------------------------------------------------------------------- 1 | export type Joke = { 2 | id: string; 3 | icon_url: string; 4 | value: string; 5 | }; 6 | 7 | const fetchJokes = async (query: string): Promise => { 8 | try { 9 | const result = await fetch(`https://api.chucknorris.io/jokes/search?query=${query}`); 10 | const resultJSON = await result.json(); 11 | return resultJSON.result || []; 12 | } catch (error) { 13 | console.error(error); 14 | return []; 15 | } 16 | }; 17 | 18 | export default fetchJokes; 19 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/solution-2/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './jokes'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/solution-3/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './clock'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/use-context/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './use-context'; -------------------------------------------------------------------------------- /09-typescript-in-react/src/use-context/use-context.tsx: -------------------------------------------------------------------------------- 1 | import React, {createContext, useContext, useState} from 'react'; 2 | 3 | // Context object initiation 4 | type Theme = 'light' | 'dark'; 5 | const ThemeContext = createContext('dark'); 6 | 7 | 8 | // Consumer component 9 | const MyComponent = () => { 10 | const theme = useContext(ThemeContext); 11 | return

    The theme is {theme}

    ; 12 | } 13 | 14 | 15 | // Provider component 16 | const ComponentWithUseContext = () => { 17 | const [theme, setTheme] = useState('dark'); //<= we are explicitly telling useState to use the Theme type 18 | 19 | return ( 20 | 21 | 22 |
    setTheme(theme === 'dark' ? 'light' : 'dark')}> 23 | Change the theme 24 |
    25 |
    26 | ); 27 | } 28 | 29 | export default ComponentWithUseContext; 30 | -------------------------------------------------------------------------------- /09-typescript-in-react/src/use-reducer/index.tsx: -------------------------------------------------------------------------------- 1 | export {default} from './use-reducer'; 2 | -------------------------------------------------------------------------------- /09-typescript-in-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | 'browser': true, 4 | 'es6': true 5 | }, 6 | extends: [ 7 | 'plugin:react/recommended', 8 | 'prettier/@typescript-eslint', 9 | 'plugin:prettier/recommended' 10 | ], 11 | globals: { 12 | 'Atomics': 'readonly', 13 | 'SharedArrayBuffer': 'readonly' 14 | }, 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | ecmaFeatures: { 18 | 'jsx': true 19 | }, 20 | ecmaVersion: 2018, 21 | sourceType: 'module' 22 | }, 23 | plugins: [ 24 | 'react', 25 | '@typescript-eslint' 26 | ], 27 | rules: { 28 | 'react/prop-types': 0 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | }; 8 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Roman Klos 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 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/01-exercise-suspense/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { fetchPeople } from '../utils/fetch'; 3 | import { Person } from '../utils/types'; 4 | 5 | // TODO: Create resource 6 | 7 | const People: FC<{}> = () => { 8 | // TODO: Read resource 9 | 10 | return
      {/* {people.map((person, index) => ( 11 |
    • {person.name}
    • 12 | ))} */}
    ; 13 | }; 14 | 15 | const Exercise01: FC<{}> = () => { 16 | // TODO: Add Suspense with fallback 17 | return ; 18 | }; 19 | 20 | export default Exercise01; 21 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/02-exercise-error-boundaries/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { People } from './people'; 3 | 4 | // TODO: Create ErrorBoundary component 5 | 6 | const Exercise02: FC<{}> = () => { 7 | // TODO: Add ErrorBoundary 8 | return ( 9 | Loading people...
    }> 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default Exercise02; 16 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/02-exercise-error-boundaries/people.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { fetchPeople } from '../utils/fetch'; 3 | import { Person } from '../utils/types'; 4 | import { wrapPromise } from '../utils/wrapPromise'; 5 | 6 | const peopleResource = wrapPromise(fetchPeople); 7 | 8 | export const People: FC<{}> = () => { 9 | const people = peopleResource.read(); 10 | 11 | return ( 12 |
      13 | {people.map((person, index) => ( 14 |
    • {person.name}
    • 15 | ))} 16 |
    17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/03-exercise-loading-states/people.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'React'; 2 | import { fetchPeople } from '../utils/fetch'; 3 | import { wrapPromise } from '../utils/wrapPromise'; 4 | import { Person } from '../utils/types'; 5 | 6 | const createPeopleResource = () => { 7 | return wrapPromise(() => fetchPeople(100)); 8 | }; 9 | 10 | const peopleResource = createPeopleResource(); 11 | 12 | export const People: FC<{ 13 | onClick: (name: string) => void; 14 | }> = ({ onClick }) => { 15 | const people = peopleResource.read(); 16 | 17 | return ( 18 |
    19 | Suggestions: 20 | 29 |
    30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/03-exercise-loading-states/person.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'React'; 2 | import { Person } from '../utils/types'; 3 | type PersonResource = { read: () => Person }; 4 | 5 | export const PersonInfo: FC<{ 6 | personResource: PersonResource; 7 | }> = ({ personResource }) => { 8 | const person = personResource.read(); 9 | 10 | return ( 11 |
    12 |
    {person.name}
    13 |
    Height: {person.height}
    14 |
    Weight: {person.mass}
    15 |
    16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/03-exercise-loading-states/style.css: -------------------------------------------------------------------------------- 1 | .container--loading { 2 | opacity: 0.6; 3 | transition: opacity 0s; 4 | /* note: the transition delay is the same as the busyDelayMs config */ 5 | transition-delay: 0.4s; 6 | } 7 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/leftNav.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | const LeftNav: FC<{}> = () =>
    LeftNav
    ; 4 | 5 | export default LeftNav; 6 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/mainContent.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | const MainContent: FC<{}> = () =>
    MainContent
    ; 4 | 5 | export default MainContent; 6 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/navBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | const NavBar: FC<{}> = () =>
    NavBar
    ; 4 | 5 | export default NavBar; 6 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/rightNav.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | const RightNav: FC<{}> = () =>
    RightNav
    ; 4 | 5 | export default RightNav; 6 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/spinner.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | export const Spinner: FC<{}> = () => { 4 | return ( 5 |
    6 | Loading... 7 |
    8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/04-exercise-suspense-list/style.css: -------------------------------------------------------------------------------- 1 | /* .nav-bar { 2 | 3 | } 4 | 5 | .main-content { 6 | 7 | } 8 | 9 | .left-nav { 10 | 11 | } 12 | 13 | .right-nav { 14 | 15 | } */ 16 | 17 | .main-container { 18 | display: flex; 19 | flex-direction: column; 20 | } 21 | 22 | .content-container { 23 | display: flex; 24 | flex-direction: row; 25 | } 26 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/app.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | import Exercise01 from './01-exercise-suspense'; 4 | import Exercise02 from './02-exercise-error-boundaries'; 5 | import Exercise03 from './03-exercise-loading-states'; 6 | import Exercise04 from './04-exercise-suspense-list'; 7 | 8 | export const App: FC<{}> = () => { 9 | return ( 10 | <> 11 | 12 | {/* */} 13 | {/* */} 14 | {/* */} 15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import { App } from './app'; 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render(); 7 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/utils/errorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ErrorBoundary extends React.Component { 4 | state = { error: null }; 5 | static getDerivedStateFromError(error) { 6 | return { error }; 7 | } 8 | componentDidCatch() { 9 | // log the error to the server 10 | } 11 | tryAgain = () => this.setState({ error: null }); 12 | render() { 13 | return this.state.error ? ( 14 |
    15 | There was an error. 16 |
    {this.state.error.message}
    17 |
    18 | ) : ( 19 | this.props.children 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/utils/fetch.ts: -------------------------------------------------------------------------------- 1 | import { Person } from './types'; 2 | 3 | export const sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time)); 4 | 5 | export const fetchPerson = async (name: string = '', delay: number = 3000): Promise => { 6 | const people = await fetch(`https://swapi.co/api/people/?search=${name}`); 7 | const peopleJSON = await people.json(); 8 | 9 | const person = peopleJSON.results[0]; 10 | if (!person) { 11 | throw new Error('Could not find person!'); 12 | } 13 | 14 | await sleep(delay); 15 | 16 | return person; 17 | }; 18 | 19 | export const fetchPeople = async (delay: number = 3000): Promise => { 20 | const people = await fetch('https://swapi.co/api/people/'); 21 | const peopleJSON = await people.json(); 22 | 23 | const peopleResults = peopleJSON.results; 24 | 25 | if (!peopleResults) { 26 | throw new Error('Could not find people!'); 27 | } 28 | 29 | await sleep(delay); 30 | 31 | return peopleResults; 32 | }; 33 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface Person { 2 | gender: string; 3 | hair_color: string; 4 | height: string; 5 | mass: string; 6 | name: string; 7 | skin_color: string; 8 | birth_year: string; 9 | eye_color: string; 10 | } 11 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/src/utils/wrapPromise.ts: -------------------------------------------------------------------------------- 1 | type Status = 'pending' | 'complete' | 'error'; 2 | 3 | export const wrapPromise = (promiseFn: () => Promise): { read: () => T } => { 4 | let status: Status = 'pending'; 5 | let result: T; 6 | 7 | const resourcePromise = promiseFn() 8 | .then(r => { 9 | status = 'complete'; 10 | result = r; 11 | }) 12 | .catch(e => { 13 | status = 'error'; 14 | result = e; 15 | }); 16 | 17 | return { 18 | read: () => { 19 | if (status === 'pending') throw resourcePromise; 20 | if (status === 'error') throw result; 21 | 22 | return result; 23 | }, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /10-suspense-for-data-fetching/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "esModuleInterop": true, 5 | "target": "es2018", 6 | "module": "commonjs", 7 | "types": [ 8 | "react/experimental", 9 | "react-dom/experimental" 10 | ] 11 | }, 12 | "include": [ 13 | "src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MSD Code Academy 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 | --------------------------------------------------------------------------------