├── material-icons.ttf ├── missing-things.txt ├── LICENSE └── README.md /material-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/equisoide/react-mui-ts-steps/HEAD/material-icons.ttf -------------------------------------------------------------------------------- /missing-things.txt: -------------------------------------------------------------------------------- 1 | - package.json 2 | - redux-devtools 3 | - @date-io/moment 4 | - axios 5 | - axios-mock-adapter 6 | - classnames 7 | - deepmerge 8 | - lodash 9 | - moment 10 | - numeral 11 | - react-helmet 12 | - react-redux 13 | - react-router 14 | - react-router-dom 15 | - redux": "4.0.1", 16 | - redux-logger 17 | - redux-mock-store 18 | - redux-thunk 19 | - Components: 20 | - AppSettings 21 | - Loading Page 22 | - LogViewer 23 | - Main Menu 24 | - MasterData 25 | - Modal Dialog 26 | - Security 27 | - Toast Notifications 28 | - Profile Menu 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Juan Cuartas 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Creating a React-MUI-TypeScript Template 2 | The purpose of this tutorial is to document the step by step on how to create a [React-MUI-TypeScript Template](https://github.com/equisoide/react-mui-ts-template) from scratch, so that it will serve as a reference guide for myself and for others. 3 | 4 | ## 1. Install required libraries 5 | - [React App with TypeScript template](https://github.com/facebook/create-react-app/tree/main/packages/cra-template-typescript): 6 | - `npx create-react-app react-mui-ts-template --template typescript` 7 | - `cd react-mui-ts-template` 8 | - Go to **package.json** file and rearrange **dependencies** as follow (keep your current versions): 9 | ```json 10 | "dependencies": { 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-scripts": "5.0.1", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "devDependencies": { 17 | "@testing-library/jest-dom": "^5.16.4", 18 | "@testing-library/react": "^13.3.0", 19 | "@testing-library/user-event": "^13.5.0", 20 | "@types/jest": "^27.5.2", 21 | "@types/node": "^16.11.42", 22 | "@types/react": "^18.0.14", 23 | "@types/react-dom": "^18.0.5", 24 | "typescript": "^4.7.4" 25 | }, 26 | ``` 27 | - Save 28 | - Delete **node_modules** and **package-lock.json** file 29 | - `npm install` 30 | - [SASS](https://sass-lang.com/install): 31 | - `npm i sass --save-dev` 32 | - [MUI (Material UI)](https://mui.com/material-ui/getting-started/installation): 33 | - `npm i @mui/material @emotion/react @emotion/styled` 34 | - `npm i @fontsource/roboto` 35 | - `npm i @mui/icons-material` 36 | - `npm i @mui/x-date-pickers` 37 | - [react-i18next](https://react.i18next.com/guides/quick-start): 38 | - `npm i i18next react-i18next` 39 | - `npm i i18next-browser-languagedetector` 40 | - [env-cmd](https://github.com/toddbluhm/env-cmd): 41 | - `npm i env-cmd --save-dev` 42 | - [Stylelint](https://stylelint.io/user-guide/get-started): 43 | - `npm i stylelint stylelint-config-standard-scss --save-dev` 44 | - [ESLint](https://eslint.org/docs/user-guide/getting-started): 45 | - `npm i eslint --save-dev` 46 | - `npm init @eslint/config` 47 | - To check syntax, find problems, and enforce code style 48 | - JavaScript modules (import/export) 49 | - React 50 | - Does your project use TypeScript? › **Yes** 51 | - Where does your code run? › **Browser** 52 | - Use a popular style guide › **Standard: https://github.com/standard/eslint-config-standard-with-typescript** 53 | - What format do you want your config file to be in? › **JavaScript** 54 | - Would you like to install them now? › **Yes** 55 | - Which package manager do you want to use? › **npm** 56 | - [Storybook](https://storybook.js.org/docs/react/get-started/install): 57 | - `npx storybook init` 58 | - Do you want to run the 'eslintPlugin' migration on your project? › **y** 59 | - Do you want to run the 'npm7' migration on your project? » (Y/n) › **y** 60 | - `npm i --save-dev react-docgen-typescript-plugin` 61 | - [React Router](https://reactrouter.com/docs/en/v6/getting-started/installation) 62 | - `npm i react-router-dom@6` 63 | 64 | ## 2. Install VS Code recomented extensions 65 | - [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) 66 | - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) 67 | - [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 68 | - [Icons](https://marketplace.visualstudio.com/items?itemName=robertohuertasm.vscode-icons) 69 | - [MDX](https://marketplace.visualstudio.com/items?itemName=silvenon.mdx) 70 | - [NpmIntellisense](https://marketplace.visualstudio.com/items?itemName=christian-kohler.npm-intellisense) 71 | - [SCSS Formatter](https://marketplace.visualstudio.com/items?itemName=sibiraj-s.vscode-scss-formatter) 72 | - [SortLines](https://marketplace.visualstudio.com/items?itemName=Tyriar.sort-lines) 73 | - [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) 74 | 75 | ## 3. Add [.editorconfig](https://editorconfig.org) 76 | - Create **.editorconfig** file at the app's root level 77 | - Add the following configuration: 78 | ```env 79 | # EditorConfig helps maintain consistent coding styles 80 | # for multiple developers working on the same project. 81 | # Learn more: https://editorconfig.org 82 | root = true 83 | 84 | [*] 85 | charset = utf-8 86 | end_of_line = lf 87 | indent_size = 2 88 | indent_style = space 89 | insert_final_newline = true 90 | trim_trailing_whitespace = true 91 | ``` 92 | - Save twice (the 2nd save is to apply coding style to the **.editorconfig** file) 93 | 94 | ## 4. Update [package.json](https://docs.npmjs.com/cli/v7/configuring-npm/package-json) 95 | - Add **description** after **version**: 96 | - `"description": "React, MUI and TypeScript template",` 97 | - Set the package as [public](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#private): 98 | - `"private": false,` 99 | - Add the [homepage](https://stackoverflow.com/questions/43011207/using-homepage-in-package-json-without-messing-up-paths-for-localhost) URL after **private**: 100 | - `"homepage": "./",` 101 | - Add **keywords** after **homepage**: 102 | ```json 103 | "keywords": [ 104 | "emotion", 105 | "es6", 106 | "eslint", 107 | "hooks", 108 | "i18next", 109 | "jest", 110 | "mui", 111 | "react", 112 | "roboto", 113 | "router", 114 | "sass", 115 | "spa", 116 | "storybook", 117 | "stylelint", 118 | "typescript", 119 | "vscode" 120 | ], 121 | ``` 122 | - Replace **scripts** object with the following: 123 | ```json 124 | "scripts": { 125 | "build": "env-cmd --no-override -f ./.env-override/.env.production react-scripts build", 126 | "build:d": "env-cmd --no-override -f ./.env-override/.env.development react-scripts build", 127 | "build:l": "env-cmd --no-override -f ./.env-override/.env.local react-scripts build", 128 | "build:q": "env-cmd --no-override -f ./.env-override/.env.qa react-scripts build", 129 | "build:s": "env-cmd --no-override -f ./.env-override/.env.staging react-scripts build", 130 | "eject": "react-scripts eject", 131 | "init": "npm ci --loglevel=error --no-audit --no-fund", 132 | "lint": "eslint --ext .js,.jsx,.ts,.tsx src/", 133 | "lint:f": "eslint --fix --ext .js,.jsx,.ts,.tsx src/", 134 | "sb-build": "env-cmd --no-override -f ./.env-override/.env.production build-storybook -s public -o ./out/storybook/production", 135 | "sb-build:d": "env-cmd --no-override -f ./.env-override/.env.development build-storybook -s public -o ./out/storybook/development", 136 | "sb-build:l": "env-cmd --no-override -f ./.env-override/.env.local build-storybook -s public -o ./out/storybook/local", 137 | "sb-build:q": "env-cmd --no-override -f ./.env-override/.env.qa build-storybook -s public -o ./out/storybook/qa", 138 | "sb-build:s": "env-cmd --no-override -f ./.env-override/.env.staging build-storybook -s public -o ./out/storybook/staging", 139 | "sbook": "env-cmd --no-override -f ./.env-override/.env.local start-storybook -p 4002 -s public", 140 | "sbook-https": "env-cmd --no-override -f ./.env-override/.env.https.local start-storybook -p 4003 -s public --https --ssl-cert localhost.pem --ssl-key localhost-key.pem", 141 | "slint": "stylelint \"src/**/*.{css,scss}\"", 142 | "slint:f": "stylelint --fix \"src/**/*.{css,scss}\"", 143 | "start": "env-cmd --no-override -f ./.env-override/.env.local react-scripts start", 144 | "start-https": "env-cmd --no-override -f ./.env-override/.env.https.local react-scripts start", 145 | "test": "env-cmd --no-override -f ./.env-override/.env.test react-scripts test --env=jsdom --coverage --coverageDirectory='./out/coverage' --watchAll=false" 146 | }, 147 | ``` 148 | - Remove **eslintConfig** object 149 | - Add [jest](https://jestjs.io/docs/configuration) object after **scripts**: 150 | ```json 151 | "jest": { 152 | "collectCoverageFrom": [ 153 | "src/**/*.ts", 154 | "src/**/*.tsx", 155 | "!src/**/*.stories.tsx", 156 | "!src/index.tsx", 157 | "!src/react-app-env.d.ts", 158 | "!src/util/web-vitals.ts", 159 | "!src/app/index.tsx" 160 | ] 161 | }, 162 | ``` 163 | - Save 164 | 165 | ## 5. Update [tsconfig.json](https://www.typescriptlang.org/tsconfig) 166 | - Add the following comments before **compilerOptions**: 167 | ```js 168 | // Specifies the root files and the compiler options required 169 | // to compile the TypeScript project. 170 | // Learn more: https://www.typescriptlang.org/tsconfig 171 | ``` 172 | - Enable [ES6](http://es6-features.org) features: 173 | - `"target": "es6",` 174 | - Disable **TypeScript** [incremental](https://www.typescriptlang.org/tsconfig#incremental) compilation (I had experienced cache issues with it): 175 | - `"incremental": false,` 176 | - Put **lib** on a single line: 177 | - `"lib": ["dom", "dom.iterable", "esnext"],` 178 | - Sort **compilerOptions** items by selecting all of them and pressing `F9` (SortLines Plugin) 179 | - Remove trailing comma from the last item of **compilerOptions** 180 | - Replace **include** array with the following: 181 | - `"include": ["src/**/*"]` 182 | - Save 183 | 184 | ## 6. Update [.eslintrc](https://eslint.org/docs/user-guide/configuring) 185 | - Rename **.eslintrc.js** to **.eslintrc** 186 | - Remove `module.exports = ` (the file is now a JSON document) 187 | - Remove semicolon at the end of the file 188 | - Replace all **single quotes** by **double quotes** 189 | - Enclose all property names in **double quotes** 190 | - Add the following comments before **env**: 191 | ```js 192 | // EsLint helps to check syntax, find problems, and enforce a code style. 193 | // Learn more: https://stylelint.io/user-guide/configure 194 | ``` 195 | - Add `"jest": true` to the `"env"` object 196 | - Replace **extends** array with the following: 197 | ```json 198 | "extends": [ 199 | "eslint:recommended", 200 | "plugin:react/recommended", 201 | "plugin:@typescript-eslint/recommended", 202 | "plugin:jest/recommended", 203 | "plugin:storybook/recommended" 204 | ], 205 | ``` 206 | - Replace **plugins** array with the following: 207 | ```json 208 | "plugins": [ 209 | "react", 210 | "react-hooks", 211 | "@typescript-eslint" 212 | ], 213 | ``` 214 | - Replace **rules** object with the following: 215 | ```json 216 | "rules": { 217 | "@typescript-eslint/no-empty-function": "off", 218 | "comma-dangle": ["error", "always-multiline"], 219 | "jsx-a11y/alt-text": "off", 220 | "max-len": ["error", { "code": 120, "tabWidth": 2, "ignoreComments": true, "ignoreTrailingComments": true, "ignoreUrls": true, "ignoreStrings": true, "ignoreTemplateLiterals": true, "ignoreRegExpLiterals": true }], 221 | "no-duplicate-imports": "error", 222 | "no-empty": "off", 223 | "no-plusplus": "off", 224 | "no-shadow": "off", 225 | "quotes": ["error", "single"], 226 | "react-hooks/exhaustive-deps": "warn", 227 | "react-hooks/rules-of-hooks": "error", 228 | "react/function-component-definition": "off", 229 | "react/jsx-filename-extension": ["error", { "extensions": [".tsx"] }], 230 | "react/jsx-indent-props": ["error", 2], 231 | "react/jsx-props-no-spreading": "off", 232 | "react/react-in-jsx-scope": "off" 233 | }, 234 | ``` 235 | - Add **settings** object after **rules**: 236 | ```json 237 | "settings": { 238 | "import/resolver": { 239 | "node": { 240 | "extensions": [".js", ".jsx", ".ts", ".tsx"] 241 | } 242 | }, 243 | "react": { 244 | "version": "detect" 245 | } 246 | } 247 | ``` 248 | - Add **parser** after **overrides**, 249 | ```json 250 | "parser": "@typescript-eslint/parser", 251 | ``` 252 | - Add **ecmaFeatures** inside **parserOptions**, 253 | ```json 254 | "parserOptions": { 255 | "ecmaFeatures": { 256 | "jsx": true 257 | }, 258 | }, 259 | ``` 260 | - Save 261 | 262 | ## 7. Add [.eslintignore](https://eslint.org/docs/user-guide/configuring/ignoring-code#the-eslintignore-file) 263 | - Create **.eslintignore** file at the app's root level 264 | - Add the following configuration: 265 | ```gitignore 266 | # Tells ESLint to ignore specific files and directories. 267 | # Learn more: https://eslint.org/docs/user-guide/configuring/ignoring-code 268 | node_modules/ 269 | out/ 270 | ``` 271 | - Save 272 | 273 | ## 8. Add [.stylelintrc](https://stylelint.io/user-guide/configure) 274 | - Create **.stylelintrc** file at the app's root level 275 | - Add the following configuration: 276 | ```json 277 | { 278 | "extends": "stylelint-config-standard-scss", 279 | "rules": { 280 | "declaration-no-important": true, 281 | } 282 | } 283 | ``` 284 | - Save 285 | 286 | ## 9. Add [VS Code Settings](https://code.visualstudio.com/docs/getstarted/settings) 287 | - Create **.vscode** folder at the app's root level 288 | - Inside **.vscode** folder, create **extensions.json** file with the following configuration: 289 | ```js 290 | { 291 | // Workspace recommended extensions. 292 | // Learn more: http://go.microsoft.com/fwlink/?LinkId=827846 293 | "recommendations": [ 294 | "christian-kohler.npm-intellisense", 295 | "dbaeumer.vscode-eslint", 296 | "editorconfig.editorconfig", 297 | "mikestead.dotenv", 298 | "sibiraj-s.vscode-scss-formatter", 299 | "silvenon.mdx", 300 | "stylelint.vscode-stylelint", 301 | "tyriar.sort-lines", 302 | "vscode-icons-team.vscode-icons" 303 | ] 304 | } 305 | ``` 306 | - Save 307 | - Inside **.vscode** folder, create **launch.json** file with the following configuration: 308 | ```js 309 | { 310 | // Launch Chrome when debugging from VS Code. 311 | // Learn more: https://go.microsoft.com/fwlink/?linkid=830387 312 | "version": "0.2.0", 313 | "configurations": [ 314 | { 315 | "type": "chrome", 316 | "request": "launch", 317 | "name": "Launch Chrome against localhost", 318 | "url": "http://localhost:4000", 319 | "webRoot": "${workspaceFolder}" 320 | } 321 | ] 322 | } 323 | ``` 324 | - Save 325 | - Inside **.vscode** folder, create **settings.json** file with the following configuration: 326 | ```js 327 | { 328 | // VS Code workspace settings. 329 | // Learn more: https://code.visualstudio.com/docs/getstarted/settings 330 | "editor.codeActionsOnSave": { 331 | "source.fixAll": true 332 | }, 333 | "files.eol": "\n", 334 | "javascript.preferences.quoteStyle": "single", 335 | "typescript.preferences.quoteStyle": "single", 336 | "search.exclude": { 337 | "node_modules": true, 338 | "out": true, 339 | "package-lock.json": true 340 | }, 341 | "css.validate": false, 342 | "scss.validate": false, 343 | "stylelint.validate": ["css", "scss"], 344 | "[scss]": { 345 | "editor.defaultFormatter": "sibiraj-s.vscode-scss-formatter" 346 | }, 347 | "files.associations": { 348 | ".env.https.local": "dotenv" 349 | } 350 | } 351 | ``` 352 | - Save 353 | 354 | ## 10. Update [.gitignore](https://git-scm.com/docs/gitignore) 355 | - Replace **.gitignore** with the following content: 356 | ```gitignore 357 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 358 | 359 | # dependencies 360 | /node_modules 361 | /.pnp 362 | .pnp.js 363 | 364 | # production 365 | /out 366 | 367 | # misc 368 | .DS_Store 369 | 370 | # certificates 371 | *.pem 372 | 373 | # log 374 | npm-debug.log* 375 | yarn-debug.log* 376 | yarn-error.log* 377 | ``` 378 | - Save 379 | 380 | ## 11. Add [Environment Files](https://create-react-app.dev/docs/adding-custom-environment-variables/#adding-development-environment-variables-in-env) 381 | - Create **.env** file at the app's root level 382 | - Add the following configuration: 383 | ```env 384 | # This file defines variables common to all environments. 385 | # Variables in this file will be overridden by each specific environment. 386 | # Shell variables never get overridden. 387 | # Name variables beginning with "REACT_APP_". 388 | # Access variables from the "process.env." object. 389 | # Restart the development server every time you change a variable. 390 | # Learn more: https://create-react-app.dev/docs/adding-custom-environment-variables 391 | # Advanced config: https://create-react-app.dev/docs/advanced-configuration 392 | 393 | # Path to resolve SASS imports. 394 | # Ref: https://create-react-app.dev/docs/adding-a-sass-stylesheet 395 | SASS_PATH='./src/styles' 396 | 397 | # Package Name and Version come from the package.json file. 398 | # They are used to render App Info in some places, for example 399 | # in the Storybook intro page, or in the App . 400 | REACT_APP_PACKAGE_NAME=${npm_package_name} 401 | REACT_APP_PACKAGE_VERSION=${npm_package_version} 402 | 403 | # Lets you find common bugs in your components early during development. 404 | # Learn more: https://react.dev/reference/react/StrictMode 405 | REACT_APP_SCRICT_MODE=false 406 | ``` 407 | - Save 408 | - Create **.env-override** folder at the app's root level 409 | - Inside **.env-override** folder, create the following files: 410 | - **.env.development** 411 | ```env 412 | # Variables in this file are injected by the following scripts: 413 | # - "build:d" Builds the App to `out/build/development` 414 | # - "sb-build:d" Builds Storybook to `out/storybook/development` 415 | 416 | # Don't touch 417 | BUILD_PATH='./out/build/development' 418 | REACT_APP_ENV='development' 419 | 420 | # Add variables below, starting with REACT_APP_ 421 | REACT_APP_API_URL='https://development.com/api/v1' 422 | REACT_APP_SCRICT_MODE=true 423 | ``` 424 | - **.env.https.local** 425 | ```env 426 | # Variables in this file are injected by the following scripts: 427 | # - "start-https" Runs the App in https://localhost:4001 (HTTPS) 428 | # - "sbook-https" Runs Storybook in https://localhost:4003 (HTTPS) 429 | 430 | # Don't touch 431 | PORT=4001 432 | HTTPS=true 433 | SSL_CRT_FILE=localhost.pem 434 | SSL_KEY_FILE=localhost-key.pem 435 | REACT_APP_ENV='local' 436 | 437 | # Add variables below, starting with REACT_APP_ 438 | REACT_APP_API_URL='https://localhost:5000/api/v1' 439 | REACT_APP_SCRICT_MODE=true 440 | ``` 441 | - **.env.local** 442 | ```env 443 | # Variables in this file are injected by the following scripts: 444 | # - "start" Runs the App in http://localhost:4000 445 | # - "sbook" Runs Storybook in http://localhost:4002 446 | # - "build:l" Builds the App to `out/build/local` 447 | # - "sb-build:l" Builds Storybook to `out/storybook/local` 448 | 449 | # Don't touch 450 | PORT=4000 451 | HTTPS=false 452 | BUILD_PATH='./out/build/local' 453 | REACT_APP_ENV='local' 454 | 455 | # Add variables below, starting with REACT_APP_ 456 | REACT_APP_API_URL='http://localhost:5001/api/v1' 457 | REACT_APP_SCRICT_MODE=true 458 | ``` 459 | - **.env.production** 460 | ```env 461 | # Variables in this file are injected by the following scripts: 462 | # - "build" Builds the App to `out/build/production` 463 | # - "sb-build" Builds Storybook to `out/storybook/production` 464 | 465 | # Don't touch 466 | BUILD_PATH='./out/build/production' 467 | REACT_APP_ENV='production' 468 | 469 | # Add variables below, starting with REACT_APP_ 470 | REACT_APP_API_URL='https://production.com/api/v1' 471 | ``` 472 | - **.env.qa** 473 | ```env 474 | # Variables in this file are injected by the following scripts: 475 | # - "build:q" Builds the App to `out/build/qa` 476 | # - "sb-build:q" Builds Storybook to `out/storybook/qa` 477 | 478 | # Don't touch 479 | BUILD_PATH='./out/build/qa' 480 | REACT_APP_ENV='qa' 481 | 482 | # Add variables below, starting with REACT_APP_ 483 | REACT_APP_API_URL='https://qa.com/api/v1' 484 | ``` 485 | - **.env.staging** 486 | ```env 487 | # Variables in this file are injected by the following scripts: 488 | # - "build:s" Builds the App to `out/build/staging` 489 | # - "sb-build:s" Builds Storybook to `out/storybook/staging` 490 | 491 | # Don't touch 492 | BUILD_PATH='./out/build/staging' 493 | REACT_APP_ENV='staging' 494 | 495 | # Add variables below, starting with REACT_APP_ 496 | REACT_APP_API_URL='https://staging.com/api/v1' 497 | ``` 498 | - **.env.test** 499 | ```env 500 | # Variables in this file are injected by the following scripts: 501 | # - "test" Executes Unit Tests outputting to `out/coverage` 502 | 503 | # Don't touch 504 | REACT_APP_ENV='test' 505 | 506 | # Add variables below, starting with REACT_APP_ 507 | REACT_APP_API_URL=='https://test.com/api/v1' 508 | ``` 509 | 510 | ## 12. Add [LICENSE](https://docs.github.com/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository) 511 | - Create **LICENSE** file at the app's root level 512 | - Add the following terms: 513 | ``` 514 | MIT License 515 | 516 | Copyright (c) 2022 Juan Cuartas 517 | 518 | Permission is hereby granted, free of charge, to any person obtaining a copy 519 | of this software and associated documentation files (the "Software"), to deal 520 | in the Software without restriction, including without limitation the rights 521 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 522 | copies of the Software, and to permit persons to whom the Software is 523 | furnished to do so, subject to the following conditions: 524 | 525 | The above copyright notice and this permission notice shall be included in all 526 | copies or substantial portions of the Software. 527 | 528 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 529 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 530 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 531 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 532 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 533 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 534 | SOFTWARE. 535 | 536 | ``` 537 | - Save 538 | 539 | ## 13. Update [README.md](https://docs.github.com/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes) 540 | - Replace **README.md** with the following content: 541 | ````markdown 542 | # React, MUI and TypeScript Template 543 | This template is intended to help you start a new `React SPA` project from scratch with a comprehensive file structure, required dependencies, built-in configurations, example components and good practices for React Web Development. 544 | 545 | The project was bootstrapped with [Create React App](https://create-react-app.dev) following this [Tutorial](https://github.com/equisoide/react-mui-ts-steps). Below you will find some information about main features and how to perform common tasks. 546 | 547 | ## Supported Language Features 548 | This project supports a superset of the latest `JavaScript`/`TypeScript` standard. In addition to [ES6](http://es6-features.org) syntax features, it also supports: 549 | - [Exponentiation Operator](https://github.com/tc39/proposal-exponentiation-operator) (ES2016) 550 | - [Async/await](https://github.com/tc39/proposal-async-await) (ES2017) 551 | - [Object Rest/Spread Properties](https://github.com/tc39/proposal-object-rest-spread) (ES2018) 552 | - [Dynamic import()](https://github.com/tc39/proposal-dynamic-import) (stage 4 proposal) 553 | - [Class Fields and Static Properties](https://github.com/tc39/proposal-class-public-fields) (part of stage 3 proposal) 554 | - [TSX](https://www.typescriptlang.org/docs/handbook/jsx.html) and [TypeScript](https://www.typescriptlang.org) 555 | 556 | Constant enums and namespaces are not supported, you can learn about the constraints of using [Babel with TypeScript here](https://babeljs.io/docs/en/babel-plugin-transform-modules-amd). 557 | 558 | ## Core Libraries 559 | - [React 18.2.0](https://reactjs.org) with `React Scripts 5.0.1` 560 | - [SASS 1.60.0](https://sass-lang.com) with [CSS Modules](https://github.com/css-modules/css-modules) 561 | - [MUI 5.11.14](https://mui.com) with `Emotion` styling engine, `Roboto Fonts` and `Material Icons` 562 | - [TypeScript 5.0.2](https://www.typescriptlang.org) with [ES6](http://es6-features.org) 563 | - [I18next 22.4.13](https://react.i18next.com) for internationalization 564 | - [React Router 6.9.0](https://reactrouter.com/) for the routing system 565 | 566 | ## Documentation Tools 567 | - [Storybook 6.5.16](https://storybook.js.org) to document components 568 | 569 | ## Code Quality & Performance 570 | - [ESLint 8.36.0](https://eslint.org) with `TypeScript`, `React`, `React Hooks` and `Jest` configuration 571 | - [Stylelint 15.3.0](https://stylelint.io) to analyse `CSS`/`SCSS` files 572 | - [Jest 27.5.2](https://jestjs.io/docs/getting-started) to test `JavaScript`/`TypeScript` files 573 | - [React Testing Library 13.4.0](https://testing-library.com/docs/react-testing-library/intro) to test components 574 | - [Web Vitals 2.1.4](https://web.dev/vitals) to meassure performance 575 | 576 | ## Built-in Settings 577 | - [.editorconfig](https://editorconfig.org) settings to standardize charset, ending of lines and indentation 578 | - [.vscode](https://code.visualstudio.com/docs/getstarted/settings) settings with integrated Chrome Debugger, faster search results and auto-format on save 579 | - [Environment files](https://create-react-app.dev/docs/adding-custom-environment-variables) for `Local`, `Test`, `Development`, `QA`, `Staging` and `Production` 580 | 581 | ## Environment Quick Setup 582 | 1. Install [NodeJs](https://nodejs.org/es/download) 583 | 2. Install [Git](https://git-scm.com/downloads) 584 | 3. Install [VS Code](https://code.visualstudio.com/download) 585 | 4. Install VS Code recomented extensions: 586 | * [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) 587 | * [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) 588 | * [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 589 | * [Icons](https://marketplace.visualstudio.com/items?itemName=robertohuertasm.vscode-icons) 590 | * [MDX](https://marketplace.visualstudio.com/items?itemName=silvenon.mdx) 591 | * [NpmIntellisense](https://marketplace.visualstudio.com/items?itemName=christian-kohler.npm-intellisense) 592 | * [SCSS Formatter](https://marketplace.visualstudio.com/items?itemName=sibiraj-s.vscode-scss-formatter) 593 | * [SortLines](https://marketplace.visualstudio.com/items?itemName=Tyriar.sort-lines) 594 | * [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) 595 | 5. Install [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) for Google Chrome 596 | 597 | ## Running & Debugging the application for the first time 598 | 1. Open a new [VS Code](https://code.visualstudio.com/download) window: 599 | - `File` > `New Window` 600 | 3. Open a parent folder that will host this project (e.g. `~/Projects`): 601 | - `File` > `Open Folder` 602 | 4. Open a new terminal: 603 | - `Terminal` > `New Terminal` 604 | 5. Clone repo: 605 | - `git clone https://github.com/equisoide/react-mui-ts-template.git` 606 | 6. Install project dependencies: 607 | - `cd react-mui-ts-template` 608 | - `npm run init` (performs a [Clean Install](https://docs.npmjs.com/cli/v8/commands/npm-ci)) 609 | 7. Restart VS Code to refresh TypeScript Intellisense, otherwise you might see errors in the editor: 610 | - Close VS Code 611 | - Open a new VS Code window 612 | - Open the folder where the project was cloned 613 | 8. Start the application: 614 | - Open a new terminal 615 | - `npm start` 616 | 9. Start debugging in VS Code: 617 | - Press `F5` or click on `Run and Debug` > `Green Debug Icon` 618 | - You can set breakpoints and inspect components in the [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) 619 | 620 | ## Available Scripts 621 | | Command | Description | Evironment File | 622 | | :--- | :--- | :--- | 623 | | `npm run init` | Installs project dependencies for the first time | N/A | 624 | | `npm run lint` | Analyses **JavaSript**/**TypeScript** code | N/A | 625 | | `npm run lint:f` | Try to fix **JavaSript**/**TypeScript** errors | N/A | 626 | | `npm run slint` | Analyses **CSS**/**SCSS** styles | N/A | 627 | | `npm run slint:f` | Try to fix **CSS**/**SCSS** errors | N/A | 628 | | `npm test` | Executes Unit Tests outputting to `out/coverage` | .env.test | 629 | | `npm start` | Runs the App in http://localhost:4000 | .env.local | 630 | | `npm run start-https` | Runs the App in https://localhost:4001 (HTTPS) | .env.https.local | 631 | | `npm run build:l` | Builds the App to `out/build/local` | .env.local | 632 | | `npm run build:d` | Builds the App to `out/build/development` | .env.development | 633 | | `npm run build:q` | Builds the App to `out/build/qa` | .env.qa | 634 | | `npm run build:s` | Builds the App to `out/build/staging` | .env.staging | 635 | | `npm run build` | Builds the App to `out/build/production` | .env.production | 636 | | `npm run sbook` | Runs Storybook in http://localhost:4002 | .env.local | 637 | | `npm run sbook-https` | Runs Storybook in https://localhost:4003 (HTTPS) | .env.https.local | 638 | | `npm run sb-build:l` | Builds Storybook to `out/storybook/local` | .env.local | 639 | | `npm run sb-build:d` | Builds Storybook to `out/storybook/development` | .env.development | 640 | | `npm run sb-build:q` | Builds Storybook to `out/storybook/qa` | .env.qa | 641 | | `npm run sb-build:s` | Builds Storybook to `out/storybook/staging` | .env.staging | 642 | | `npm run sb-build` | Builds Storybook to `out/storybook/production` | .env.production | 643 | 644 | ## Project Structure 645 | After cloning, your project should look like this: 646 | 647 | ``` 648 | 📦 react-mui-ts-template 649 | ├── 📜 .editorconfig EditorConfig settings 650 | ├── 📜 .env Variables common to all environments 651 | ├── 📜 .eslintignore Folders and files ignored by ESLint 652 | ├── 📜 .eslintrc ESLint configuration 653 | ├── 📜 .gitignore Folders and files ignored by Git 654 | ├── 📜 .npmrc Npm settings 655 | ├── 📜 .stylelintrc Stylelint configuration 656 | ├── 📜 LICENSE License information 657 | ├── 📜 package-lock.json Npm dependency tree to recreate node_modules 658 | ├── 📜 package.json Project dependencies, scripts and more 659 | ├── 📜 README.md Project documentation 660 | ├── 📜 tsconfig.json TypeScript configuration 661 | ├── 📂 .env-override 662 | │ ├── 📜 .env.development Environment variables for Development 663 | │ ├── 📜 .env.https.local Environment variables for Local (HTTPS) 664 | │ ├── 📜 .env.local Environment variables for Local 665 | │ ├── 📜 .env.production Environment variables for Production 666 | │ ├── 📜 .env.qa Environment variables for QA 667 | │ ├── 📜 .env.staging Environment variables for Staging 668 | │ └── 📜 .env.test Environment variables for Unit Test 669 | ├── 📂 .storybook 670 | │ ├── 📜 favicon.svg Favicon for Storybook 671 | │ ├── 📜 main.js Storybook server behavior 672 | │ ├── 📜 manager.js Customize how Storybook App renders 673 | │ └── 📜 preview.js Global code that applies to all stories 674 | ├── 📂 .vscode 675 | │ ├── 📜 extensions.json Recomended extensions to load in VS Code 676 | │ ├── 📜 launch.json Launch Chrome against localhost 677 | │ └── 📜 settings.json Settings for VS Code 678 | ├── 📂 public 679 | │ ├── 📜 favicon.ico The icon found in the URL address bar 680 | │ ├── 📜 index.html HTML where the React App is rendered 681 | │ ├── 📜 logo192.png PWA icon (192x192) 682 | │ ├── 📜 logo512.png PWA icon (512x512) 683 | │ ├── 📜 manifest.json Metadata to install the App as a PWA 684 | │ └── 📜 robots.txt Instructions for search crawlers 685 | └── 📂 src 686 | ├── 📜 index.tsx The application entry point 687 | ├── 📜 react-app-env.d.ts TypeScript declarations for React App 688 | ├── 📜 setupTests.ts Global setup before running tests 689 | ├── 📂 app 690 | │ └── 📜 index.tsx The main App component with routes 691 | ├── 📂 components/HelloWorld 692 | │ ├── 📜 index.module.scss Component styles 693 | │ ├── 📜 index.stories.tsx Storybook documentation 694 | │ ├── 📜 index.test.tsx Jest testing file 695 | │ └── 📜 index.tsx Example component definition 696 | ├── 📂 lang 697 | │ ├── 📜 index.ts i18next configuration 698 | │ ├── 📜 resources.en.json Application texts in English 699 | │ └── 📜 resources.es.json Application texts in Spanish 700 | ├── 📂 pages 701 | │ └── ... React components for each page 702 | ├── 📂 stories 703 | │ └── ... Files for the Storybook intro page 704 | ├── 📂 styles 705 | │ ├── 📜 _reset.scss Simple CSS reset for consistent styles 706 | │ └── 📜 main.scss Main SASS file 707 | └── 📂 util 708 | └── 📜 web-vitals.ts Web Vitals reporting 709 | ``` 710 | For the project to build, these files must exist with exact filenames: 711 | - `public/index.html` is the page template 712 | - `src/index.tsx` is the TypeScript entry point 713 | 714 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by webpack. You need to put any TypeScript and SCSS files inside `src`, otherwise webpack won’t see them. 715 | 716 | Only files inside public can be used from `public/index.html`. 717 | 718 | ## File extensions 719 | Most of the files you will create in the `src` folder will be **TypeScript**, **TypeScript with React** or **SASS**: 720 | - `.ts`: TypeScript files (Don't use `.js`). Use it for: 721 | - Utilities. e.g. `arrays.ts` 722 | - Test of utilities. e.g. `arrays.test.ts` 723 | - `.tsx`: TypeScript with React (Don't use `.jsx`). Use it for: 724 | - Components. e.g. `HelloWorld/index.tsx` 725 | - Test of components. e.g. `HelloWorld/index.test.tsx` 726 | - Storybook stories. e.g. `HelloWorld/index.stories.tsx` 727 | - `.scss`: Superset of CSS (Don't use `.css`). Use it for: 728 | - Global styles. e.g. `main.css` 729 | - Component styles. e.g. `HelloWorld/index.module.scss` 730 | 731 | ## Adding a Stylesheet 732 | This project supports [Sass](https://sass-lang.com/guide) alongside [CSS Modules](https://github.com/css-modules/css-modules): 733 | - `Sass` is CSS with superpowers 734 | - `CSS Modules` scopes CSS by automatically creating a unique **className** 735 | 736 | `Sass` supports two syntaxes: 737 | - `.scss`: Is an extension of CSS where every valid CSS is a valid **.scss** as well 738 | - `.sass`: Is an older indented syntax not recommended for use in new **Sass** files 739 | 740 | In this project we use the `.scss` syntax. 741 | 742 | To express that a component depends on a **.scss module**, you should use the `[name].module.scss` convention: 743 | ```tsx 744 | import styles from './index.module.scss'; 745 | 746 | function MyComponent() { 747 | return <div className={styles.myClass}>My Component</div>; 748 | } 749 | ``` 750 | 751 | In development, expressing dependencies this way allows your styles to be reloaded on the fly as you edit them. In production, all `.scss` files will be concatenated into a single minified `.css` file in the build output. 752 | 753 | To share variables between **Sass** files, you can use Sass's [@use](https://sass-lang.com/documentation/at-rules/use) rule. There is a `SASS_PATH` variable in the `.env` file that is used to locate `.scss` files. Supposing that `SASS_PATH='./src/styles'` and that you have `_colors.scss` in that directory, then you can use it like this: 754 | ```scss 755 | @use 'colors'; 756 | 757 | .info { 758 | color: colors.$primary; 759 | } 760 | ``` 761 | 762 | For information about how to structure a SASS codebase using the **7-1 Pattern** you can read this [article](https://remote.com/blog/how-to-structure-your-sass-project) or take a look to the following [boilerplate](https://github.com/KittyGiraudel/sass-boilerplate). 763 | 764 | ## Adding Images and Files 765 | With webpack, using static assets like images and files works similarly to `SCSS`. 766 | 767 | To reduce the number of requests to the server, importing images that are less than 10,000 bytes returns a data URI instead of a path. This applies to the following file extensions: bmp, gif, jpg, jpeg, and png. You can control the 10,000 byte threshold by setting the `IMAGE_INLINE_SIZE_LIMIT` environment variable as documented in the [advanced configuration](https://create-react-app.dev/docs/advanced-configuration). 768 | 769 | ```tsx 770 | import logo from './logo.png'; 771 | 772 | function Header() { 773 | return <img src={logo} alt="Logo" />; 774 | } 775 | ``` 776 | 777 | ## Using HTTPS in Local Environment 778 | You may require the local server to run the App or Storybook over [HTTPS](https://create-react-app.dev/docs/using-https-in-development): 779 | - Use `npm run start-https` to run the APP over HTTPS 780 | - Use `npm run sbook-https` to run Storybook over HTTPS 781 | 782 | Note that you might get an error in the console telling that `localhost.pem` or `localhost-key.pem` files can't be found. This is because when running the App over HTTPS a valid **Certificate Authority** and an **SSL certificate** are needed. 783 | 784 | To generate those files use [mkcert](https://www.mariokandut.com/how-to-setup-https-ssl-in-localhost-react): 785 | - You need a package manager to install **mkcert**: 786 | - **MacOS**: Use Homebrew (`brew install mkcert`) 787 | - **Linux**: Use Certutil 788 | - **Windows**: Use Chocolatey 789 | - Once installed **mkcert**: 790 | - Open a terminal at the root of the project 791 | - Create a locally trusted CA with `mkcert -install` 792 | - Generate an SSL certificate with `mkcert localhost` 793 | - `localhost.pem` and `localhost-key.pem` will be generated 794 | - Note that these files are included in the `.gitignore` 795 | 796 | ## Working in StrictMode 797 | `<StrictMode>` lets you find common bugs in your components early during development. It also helps you to prepare your app for the future. You can read more about it [here](https://react.dev/reference/react/StrictMode). 798 | 799 | Strict Mode enables the following development-only behaviors: 800 | - Your components will re-render an extra time to find bugs caused by impure rendering 801 | - Your components will re-run Effects an extra time to find bugs caused by missing Effect cleanup 802 | - Your components will be checked for usage of deprecated APIs. 803 | 804 | To enabble/disable StrictMode you can use the `REACT_APP_STRICT_MODE` environment variable. By default it's set to `true` in the following files: 805 | - `.env.development` 806 | - `.env.https.local` 807 | - `.env.local` 808 | 809 | ## Working Guidelines 810 | - Never delete and re-generate the `package-lock.json` file from scratch, it will break the App and Storybook! Let `npm` update that file every time you install a new dependency 811 | - Create reusable components inside the `src/components` folder. Define each component in its own folder with the following structure: 812 | ``` 813 | ├── 📂 src/components/MyComponent Component name in PascalCase 814 | ├── 📜 index.module.cs Component styles (optional) 815 | ├── 📜 index.stories.tsx Storybook documentation 816 | ├── 📜 index.test.tsx Jest testing file 817 | └── 📜 index.tsx Component definition 818 | ``` 819 | - Prefer [Function Components](https://www.robinwieruch.de/react-function-component) over **Class Components**, they offer almost the same: state and lifecycle methods, with the plus they are more lightway, have a sophisticated API and require less code. With the introduction of [React Hooks](https://reactjs.org/docs/hooks-intro.html) it's possible to write your entire application with just functions as React Components: 820 | ```tsx 821 | // External imports 822 | import Box from '@mui/material/Box'; 823 | import { BoxProps } from '@mui/material'; 824 | import { useTranslation } from 'react-i18next'; 825 | 826 | // Local imports 827 | import styles from './index.module.scss'; 828 | 829 | // Component props 830 | export interface MyComponentProps { 831 | /** 832 | * The box container styles. 833 | * See: https://mui.com/material-ui/api/box 834 | */ 835 | box?: BoxProps 836 | } 837 | 838 | // Component definition 839 | function MyComponent({ box } : MyComponentProps) { 840 | const { t } = useTranslation(); 841 | 842 | const defaults = MyComponent.defaultProps; 843 | const boxProps = { ...defaults.box, ...box } as BoxProps; 844 | 845 | return ( 846 | <Box className={styles.box} {...boxProps}> 847 | {t('hello-world')} 848 | </Box> 849 | ); 850 | } 851 | 852 | // Default props 853 | MyComponent.defaultProps = { 854 | box: { 855 | sx: { background: 'blue' }, 856 | }, 857 | }; 858 | 859 | // Default export 860 | export default MyComponent; 861 | ``` 862 | - Use default `imports` and `exports` when a module only exports a single thing (for example, a component). Named exports are useful for utility modules that export several functions. A module may have at most one default export and as many named exports as you like. 863 | - In general use [Trailing Commas](https://blog.logrocket.com/best-practices-using-trailing-commas-javascript) (except on `JSON` files), many coding styles now recommend using them all the time because they make it easier to add new parameters to your functions or copy/paste properties in arrays and objects and also helps with producing cleaner diff output 864 | - Add your own environment variables to the `.env-override/.env.local` file, this file should not be commited 865 | - Before running or building this application always run linters and unit tests 866 | 867 | ## Troubleshooting 868 | - **When running `npm run lint` you get this error: "Expected linebreaks to be 'LF' but found 'CRLF'"** 869 | - This happens because on Windows, Git converts linebreaks from `LF` to `CRLF` when you checkout the code, but esLint is configured to accept valid ending of lines as `LF` (unix style) 870 | - To avoid Git converting from `LF` to `CRLF`, run the following commands: 871 | ```shell 872 | git config --global core.autocrlf false 873 | git config --global core.eol lf 874 | git rm --cached -r . 875 | git reset --hard 876 | ``` 877 | - **On VS Code you get this errors: "JSX element implicitly has type 'any'..."** 878 | - This happens because your Typescript IntelliSense is not working properly 879 | - To fix it reload your code editor: `Ctrl + p` > `Developer: Reload Window` 880 | 881 | ## More Topics 882 | - [Configuring Supported Browsers](https://create-react-app.dev/docs/supported-browsers-features#configuring-supported-browsers) 883 | - [Updating React to New Releases](https://create-react-app.dev/docs/updating-to-new-releases) 884 | 885 | ## Documentation & Training 886 | - [Official React Documentation](https://es.reactjs.org) 887 | - [React Function Components](https://www.robinwieruch.de/react-function-component) 888 | - [The TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html) 889 | - [ES6](http://es6-features.org/#Constants) 890 | - [Sass Basics](https://sass-lang.com/guide) 891 | - [MUI Crash Course](https://www.youtube.com/watch?v=o1chMISeTC0) 892 | - [MUI From Zero to Hero](https://www.youtube.com/playlist?list=PLDxCaNaYIuUlG5ZqoQzFE27CUOoQvOqnQ) 893 | - [MUI Components](https://mui.com/material-ui/react-autocomplete) 894 | - [MUI Templates](https://mui.com/material-ui/getting-started/templates) 895 | 896 | ## Creator 897 | **Juan Cuartas** https://github.com/equisoide 898 | 899 | ## Copyright and License 900 | Code and documentation released under [the MIT license](https://github.com/equisoide/react-mui-ts-template/blob/master/LICENSE) 901 | ```` 902 | - Save 903 | 904 | ## 14. Delete unnecessary files 905 | - Go to **src** folder and delete the following files: 906 | - **App.css** 907 | - **App.test.tsx** 908 | - **App.tsx** 909 | - **index.css** 910 | - **logo.svg** 911 | 912 | ## 15. Update [reportWebVitals.ts](https://web.dev/vitals) 913 | - Create **src/util** folder 914 | - Move **reportWebVitals.ts** file to that folder: 915 | - Update imports for 'reportWebVitals.ts'? > **Yes** (VS Code) 916 | - Rename **reportWebVitals.ts** to **web-vitals.ts**: 917 | - Update imports for 'reportWebVitals.ts'? > **Yes** (VS Code) 918 | - Replace **web-vitals.ts** with the following code: 919 | ```ts 920 | // Web Vitals is an initiative by Google to provide unified guidance 921 | // for quality signals that are essential to delivering a great user 922 | // experience on the web. 923 | // Learn more: https://web.dev/vitals 924 | import { ReportHandler } from 'web-vitals'; 925 | 926 | // Initializes Web Vitals with a custom reporter 927 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 928 | if (onPerfEntry && onPerfEntry instanceof Function) { 929 | import('web-vitals').then(({ 930 | getCLS, 931 | getFID, 932 | getFCP, 933 | getLCP, 934 | getTTFB, 935 | }) => { 936 | getCLS(onPerfEntry); 937 | getFID(onPerfEntry); 938 | getFCP(onPerfEntry); 939 | getLCP(onPerfEntry); 940 | getTTFB(onPerfEntry); 941 | }); 942 | } 943 | }; 944 | 945 | // Default export 946 | export default reportWebVitals; 947 | ``` 948 | - Save 949 | 950 | ## 16. Update index.html 951 | - Open **pubic/index.html** file 952 | - After the `apple-touch-icon` link reference, add the following: 953 | ```html 954 | <!-- 955 | The easiest way to set up icon fonts for use in any web page is through Google Fonts 956 | See: https://developers.google.com/fonts/docs/material_icons 957 | --> 958 | <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> 959 | ``` 960 | - Replace the **title** by the following: 961 | ```html 962 | <title>%REACT_APP_PACKAGE_NAME% 963 | ``` 964 | - Save 965 | 966 | ## 17. Create styles 967 | - Create **src/styles** folder 968 | - Inside **src/styles** folder, create **_reset.scss** file with the following styles: 969 | ```scss 970 | /** 971 | * A simple set of CSS rules that resets the styling 972 | * of all HTML elements to a consistent baseline. 973 | */ 974 | *, 975 | *::before, 976 | *::after { 977 | margin: 0; 978 | padding: 0; 979 | box-sizing: border-box; 980 | } 981 | 982 | html { 983 | font-size: 100%; /* 1rem = 16px */ 984 | } 985 | 986 | body { 987 | font-size: 1rem; /* 16px */ 988 | font-family: Roboto, sans-serif; 989 | -webkit-font-smoothing: antialiased; 990 | -moz-osx-font-smoothing: grayscale; 991 | } 992 | ``` 993 | - Save 994 | - Inside **src/styles** folder, create **main.scss** file with the following styles: 995 | ```scss 996 | // External imports 997 | @import "@fontsource/roboto/300.css"; 998 | @import "@fontsource/roboto/400.css"; 999 | @import "@fontsource/roboto/500.css"; 1000 | @import "@fontsource/roboto/700.css"; 1001 | 1002 | // Local imports 1003 | @import "reset"; 1004 | ``` 1005 | - Save 1006 | 1007 | ## 18. Configure [i18next](https://react.i18next.com) 1008 | - Create **src/lang** folder 1009 | - Inside **src/lang** folder, create **resources.en.json** file with the following content: 1010 | ```json 1011 | { 1012 | "hello-world": "Hello World!" 1013 | } 1014 | - Save 1015 | - Inside **src/lang** folder, create **resources.es.json** file with the following content: 1016 | ```json 1017 | { 1018 | "hello-world": "¡Hola Mundo!" 1019 | } 1020 | - Save 1021 | - Inside **src/lang** folder, create **index.ts** file with the following code: 1022 | ```ts 1023 | /** 1024 | * react-i18next is a powerful internationalization framework for 1025 | * React/ReactNative which is based on i18next. 1026 | * Learn more: https://react.i18next.com 1027 | */ 1028 | 1029 | // External imports 1030 | import LanguageDetector from 'i18next-browser-languagedetector'; 1031 | import i18n from 'i18next'; 1032 | import { initReactI18next } from 'react-i18next'; 1033 | 1034 | // Local imports 1035 | import resourcesEn from './resources.en.json'; 1036 | import resourcesEs from './resources.es.json'; 1037 | 1038 | // Init the i18next module with the resource files 1039 | const initI18n = () => { 1040 | i18n 1041 | .use(LanguageDetector) 1042 | .use(initReactI18next) 1043 | .init({ 1044 | resources: { 1045 | en: { 1046 | translations: { ...resourcesEn }, 1047 | }, 1048 | es: { 1049 | translations: { ...resourcesEs }, 1050 | }, 1051 | }, 1052 | fallbackLng: 'en', 1053 | debug: false, 1054 | ns: ['translations'], 1055 | defaultNS: 'translations', 1056 | keySeparator: false, 1057 | interpolation: { 1058 | escapeValue: false, 1059 | }, 1060 | }); 1061 | }; 1062 | 1063 | // Default export 1064 | export default initI18n; 1065 | ``` 1066 | - Save 1067 | 1068 | ## 19. Configure [Storybook](https://storybook.js.org) 1069 | - Open **.storybook/main.js** file: 1070 | - Add the following comments at the top of the file: 1071 | ```js 1072 | /** 1073 | * This file controls the Storybook server's behavior. 1074 | * You must restart Storybook’s process when you change it. 1075 | * Learn more: https://storybook.js.org/docs/react/configure/overview 1076 | */ 1077 | ``` 1078 | - [Disable Telemetry](https://storybook.js.org/docs/react/configure/telemetry): 1079 | ```js 1080 | "core": { 1081 | ... 1082 | "disableTelemetry": true, 1083 | } 1084 | ``` 1085 | - Add **typescript** and **webpackFinal** after **core**: 1086 | ```js 1087 | "typescript": { 1088 | "reactDocgen": 'react-docgen-typescript-plugin' 1089 | }, 1090 | "webpackFinal": async (config) => { 1091 | injectEnvVariables(config); 1092 | return config; 1093 | }, 1094 | ``` 1095 | - Add **injectEnvVariables** after **module.exports**: 1096 | ```js 1097 | // Manually inject environment variables into the webpack config object. 1098 | // Note that otherwise, only `STORYBOOK_*` prefix env vars are supported. 1099 | // Ref: https://github.com/storybookjs/storybook/issues/12270 1100 | function injectEnvVariables(config) { 1101 | const findPlugin = (name) => config.plugins.find( 1102 | ({ constructor }) => constructor && constructor.name === name, 1103 | ); 1104 | 1105 | const definePlugin = findPlugin('DefinePlugin'); 1106 | const interpolateHtmlPlugin = findPlugin('InterpolateHtmlPlugin'); 1107 | const definitions = Object.keys(definePlugin.definitions); 1108 | const replacements = Object.keys(interpolateHtmlPlugin.replacements); 1109 | const isMissingKey = (key) => !definitions.includes(`process.env.${key}`); 1110 | const missingKeys = replacements.filter(isMissingKey); 1111 | 1112 | missingKeys.forEach((key) => { 1113 | definePlugin.definitions[`process.env.${key}`] = JSON.stringify( 1114 | interpolateHtmlPlugin.replacements[key], 1115 | ); 1116 | }); 1117 | }; 1118 | ``` 1119 | - Add trailing commas where missing 1120 | - Save 1121 | - Open **.storybook/preview.js** file: 1122 | - Add the following code at the top of the file: 1123 | ```js 1124 | /** 1125 | * Use preview.js for global code that applies to all stories. 1126 | * Learn more: https://storybook.js.org/docs/react/configure/overview 1127 | */ 1128 | import { addParameters } from '@storybook/client-api'; 1129 | import initI18n from '../src/lang'; 1130 | import '../src/styles/main.scss'; 1131 | 1132 | // Global initialization 1133 | initI18n(); 1134 | ``` 1135 | - Save 1136 | - Create **.storybook/favicon.svg** file with the following content: 1137 | ```xml 1138 | 1139 | 1140 | storybook-icon/default 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | ``` 1154 | - Save 1155 | - Create file **.storybook/manager.js** with the following code: 1156 | ```js 1157 | /** 1158 | * This file allows you to customize how Storybook’s app UI renders. 1159 | * That is, everything outside of the Canvas (preview iframe). 1160 | * Learn more: https://storybook.js.org/blog/declarative-storybook-configuration 1161 | */ 1162 | import favicon from './favicon.svg'; 1163 | 1164 | // Change Storybook Favicon. 1165 | // Ref: https://github.com/storybookjs/storybook/issues/6155 1166 | const injectFavicon = () => { 1167 | const link = document.createElement('link'); 1168 | 1169 | link.setAttribute('rel', 'icon'); 1170 | link.setAttribute('href', favicon); 1171 | link.setAttribute('type', 'image/svg+xml'); 1172 | 1173 | document.head.appendChild(link); 1174 | }; 1175 | 1176 | injectFavicon(); 1177 | ``` 1178 | - Save 1179 | - Go to **src/stories** folder and delete the following files: 1180 | - **button.css** 1181 | - **Button.stories.tsx** 1182 | - **Button.tsx** 1183 | - **header.css** 1184 | - **Header.stories.tsx** 1185 | - **Header.tsx** 1186 | - **page.css** 1187 | - **Page.stories.tsx** 1188 | - **Page.tsx** 1189 | - Rename **Introduction.stories.mdx** to **introduction.stories.mdx** 1190 | - Open **introduction.stories.mdx** file: 1191 | - Replace `Example/Introduction` by `Introduction` 1192 | - Replace **# Welcome to Storybook** by the following: 1193 | ```html 1194 |

{process.env.REACT_APP_PACKAGE_NAME}

1195 |

{process.env.REACT_APP_PACKAGE_VERSION}

1196 | ``` 1197 | - Replace `stories/Introduction.stories.mdx` by `stories/introduction.stories.mdx` 1198 | - Save 1199 | 1200 | ## 20. Update [setupTests.ts](https://github.com/testing-library/jest-dom) 1201 | - Open **src/setupTests.ts** file and replace with the following code: 1202 | ```ts 1203 | /** 1204 | * jest-dom adds custom jest matchers for asserting on DOM nodes. 1205 | * It allows you to do things like: 1206 | * expect(element).toHaveTextContent(/react/i) 1207 | * Learn more: https://github.com/testing-library/jest-dom 1208 | */ 1209 | import '@testing-library/jest-dom'; 1210 | import initI18n from './lang'; 1211 | 1212 | // Global initialization 1213 | initI18n(); 1214 | ``` 1215 | - Save 1216 | 1217 | ## 21. Create HelloWorld component 1218 | - Create **src/components/HelloWorld** folder 1219 | - Inside **src/components/HelloWorld** folder, create **index.module.scss** file with the following code: 1220 | ```scss 1221 | /** 1222 | * Style file for the HelloWorld component. 1223 | */ 1224 | .info { 1225 | font-size: 0.875rem; /* 14px */ 1226 | } 1227 | ``` 1228 | - Save 1229 | - Inside **src/components/HelloWorld** folder, create **index.tsx** file with the following code: 1230 | ```tsx 1231 | /** 1232 | * The HelloWorld component renders an alert with 1233 | * the package name, version and environment. 1234 | */ 1235 | 1236 | // External imports 1237 | import Alert from '@mui/material/Alert'; 1238 | import AlertTitle from '@mui/material/AlertTitle'; 1239 | import Box from '@mui/material/Box'; 1240 | import { AlertProps, BoxProps } from '@mui/material'; 1241 | import { useTranslation } from 'react-i18next'; 1242 | 1243 | // Local imports 1244 | import styles from './index.module.scss'; 1245 | 1246 | // Component props 1247 | export interface HelloWorldProps { 1248 | /** 1249 | * The alert message styles. 1250 | * See: https://mui.com/material-ui/api/alert 1251 | */ 1252 | alert?: AlertProps, 1253 | /** 1254 | * The box container styles. 1255 | * See: https://mui.com/material-ui/api/box 1256 | */ 1257 | box?: BoxProps, 1258 | } 1259 | 1260 | // Component definition 1261 | function HelloWorld({ alert, box } : HelloWorldProps) { 1262 | const { t } = useTranslation(); 1263 | 1264 | const defaults = HelloWorld.defaultProps; 1265 | const boxProps = { ...defaults.box, ...box } as BoxProps; 1266 | const alertProps = { ...defaults.alert, ...alert } as AlertProps; 1267 | 1268 | const name = process.env.REACT_APP_PACKAGE_NAME; 1269 | const version = process.env.REACT_APP_PACKAGE_VERSION; 1270 | const env = process.env.REACT_APP_ENV; 1271 | 1272 | return ( 1273 | 1274 | 1275 | {t('hello-world')} 1276 |
{name}
1277 |
{version}
1278 |
{env}
1279 |
1280 |
1281 | ); 1282 | } 1283 | 1284 | // Default props 1285 | HelloWorld.defaultProps = { 1286 | alert: { 1287 | severity: 'success', 1288 | sx: { width: 300 }, 1289 | variant: 'filled', 1290 | }, 1291 | box: {}, 1292 | }; 1293 | 1294 | // Default export 1295 | export default HelloWorld; 1296 | ``` 1297 | - Save 1298 | - Inside **src/components/HelloWorld** folder, create **index.test.tsx** file with the following code: 1299 | ```tsx 1300 | /** 1301 | * Testing file for the HelloWorld component. 1302 | */ 1303 | import { render, screen } from '@testing-library/react'; 1304 | import HelloWorld from '.'; 1305 | 1306 | test('Render HelloWorld Component', () => { 1307 | render(); 1308 | const element = screen.getByText(/Hello World!/i); 1309 | expect(element).toBeInTheDocument(); 1310 | }); 1311 | ``` 1312 | - Save 1313 | - Inside **src/components/HelloWorld** folder, create **index.stories.tsx** file with the following code: 1314 | ```tsx 1315 | /** 1316 | * Storybook file for the HelloWorld component. 1317 | */ 1318 | import { ComponentMeta, ComponentStory } from '@storybook/react'; 1319 | import HelloWorld from '.'; 1320 | 1321 | // Story placement in the story list 1322 | export default { 1323 | title: 'Components/HelloWorld', 1324 | component: HelloWorld, 1325 | } as ComponentMeta; 1326 | 1327 | const Template: ComponentStory = (args) => ; 1328 | 1329 | // Story #1 1330 | export const Green = Template.bind({}); 1331 | Green.args = { 1332 | }; 1333 | 1334 | // Story #2 1335 | export const Red = Template.bind({}); 1336 | Red.args = { 1337 | alert: { severity: 'error' }, 1338 | }; 1339 | ``` 1340 | - Save 1341 | 1342 | ## 22. Create Home page 1343 | - Create **src/pages/Home** folder 1344 | - Inside **src/pages/Home** folder, create **index.tsx** file with the following code: 1345 | ```tsx 1346 | // Local imports 1347 | import HelloWorld from '../../components/HelloWorld'; 1348 | 1349 | // Component definition 1350 | function HomePage() { 1351 | return ( 1352 | 1363 | ); 1364 | } 1365 | 1366 | // Default export 1367 | export default HomePage; 1368 | ``` 1369 | - Save 1370 | - Inside **src/pages/Home** folder, create **index.test.tsx** file with the following code: 1371 | ```tsx 1372 | // External imports 1373 | import { render, screen } from '@testing-library/react'; 1374 | 1375 | // Local imports 1376 | import HomePage from '.'; 1377 | 1378 | test('Render HomePage', () => { 1379 | render(); 1380 | const element = screen.getByText(/Hello World!/i); 1381 | expect(element).toBeInTheDocument(); 1382 | }); 1383 | ``` 1384 | - Save 1385 | - Inside **src/pages/Home** folder, create **index.stories.tsx** file with the following code: 1386 | ```tsx 1387 | // External imports 1388 | import { ComponentMeta } from '@storybook/react'; 1389 | 1390 | // Local imports 1391 | import HomePage from '.'; 1392 | 1393 | // Story placement in the story list 1394 | export default { 1395 | title: 'Pages/Home', 1396 | component: HomePage, 1397 | parameters: { 1398 | layout: 'fullscreen', 1399 | }, 1400 | } as ComponentMeta; 1401 | 1402 | // Default export 1403 | export const Default = () => ; 1404 | ``` 1405 | - Save 1406 | 1407 | ## 23. Create App component 1408 | - Create **src/app** folder 1409 | - Inside **src/app** folder, create **index.tsx** file with the following code: 1410 | ```tsx 1411 | // External imports 1412 | import { Routes, Route } from 'react-router-dom'; 1413 | 1414 | // Local imports 1415 | import HomePage from '../pages/Home'; 1416 | 1417 | // Component definition 1418 | function App() { 1419 | return ( 1420 | 1421 | } /> 1422 | } /> 1423 | 1424 | ); 1425 | } 1426 | 1427 | // Default export 1428 | export default App; 1429 | ``` 1430 | - Save 1431 | 1432 | ## 24. Update index.tsx 1433 | - Open **src/index.tsx** file and replace all code with the following: 1434 | ```tsx 1435 | // External imports 1436 | import ReactDOM from 'react-dom/client'; 1437 | import { BrowserRouter } from 'react-router-dom'; 1438 | import { StrictMode } from 'react'; 1439 | 1440 | // Local imports 1441 | import App from './app'; 1442 | import initI18n from './lang'; 1443 | import reportWebVitals from './util/web-vitals'; 1444 | import './styles/main.scss'; 1445 | 1446 | // Global initialization 1447 | initI18n(); 1448 | 1449 | const htmlRoot = document.getElementById('root') as HTMLElement; 1450 | const reactRoot = ReactDOM.createRoot(htmlRoot); 1451 | 1452 | reactRoot.render( 1453 | process.env.REACT_APP_SCRICT_MODE === 'true' ? ( 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | ) : ( 1460 | 1461 | 1462 | 1463 | ), 1464 | ); 1465 | 1466 | if (process.env.REACT_APP_ENV !== 'production') { 1467 | // If you want to start measuring performance in your app, pass a function 1468 | // to log results (for example: reportWebVitals(console.log)) 1469 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 1470 | // eslint-disable-next-line no-console 1471 | reportWebVitals(console.log); 1472 | } 1473 | ``` 1474 | - Save 1475 | 1476 | ## 25. Test everything is working fine 1477 | - Delete **node_modules** folder 1478 | - Install project dependencies for the first time: `npm run init` 1479 | - Restart VS Code in order to refresh **TypeScript Intellisense** 1480 | - Analyse **JavaSript/TypeScript** code: `npm run lint` 1481 | - Try to fix **JavaSript/TypeScript** errors: `npm run lint:f` 1482 | - Analyses **CSS**/**SCSS** styles: `npm run slint` 1483 | - Try to fix **CSS**/**SCSS** errors: `npm run slint:f` 1484 | - Execute Unit Tests outputting to **out/coverage**: `npm test` 1485 | - Run the App in http://localhost:4000: `npm start` 1486 | - Create a locally trusted CA: `mkcert -install` 1487 | - Generate an SSL certificate: `mkcert localhost` 1488 | - Run the App in https://localhost:4001: `npm run start-https` (HTTPS) 1489 | - Build the App to **out/build/local**: `npm run build:l` 1490 | - Build the App to **out/build/development**: `npm run build:d` 1491 | - Build the App to **out/build/qa**: `npm run build:q` 1492 | - Build the App to **out/build/staging**: `npm run build:s` 1493 | - Build the App to **out/build/production**: `npm run build` 1494 | - Run Storybook in http://localhost:4002: `npm run sbook` 1495 | - Run Storybook in https://localhost:4003: `npm run sbook-https` (HTTPS) 1496 | - Build Storybook to **out/storybook/local**: `npm run sb-build:l` 1497 | - Build Storybook to **out/storybook/development**: `npm run sb-build:d` 1498 | - Build Storybook to **out/storybook/qa**: `npm run sb-build:q` 1499 | - Build Storybook to **out/storybook/staging**: `npm run sb-build:s` 1500 | - Build Storybook to **out/storybook/production**: `npm run sb-build` 1501 | 1502 | ## Creator 1503 | **Juan Cuartas** https://github.com/equisoide 1504 | 1505 | ## Copyright and license 1506 | Code and documentation released under [the MIT license](https://github.com/equisoide/react-mui-ts-steps/blob/master/LICENSE) 1507 | --------------------------------------------------------------------------------