├── 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
My Component
;
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 ;
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 | `` 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 |
847 | {t('hello-world')}
848 |
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 |
958 |
959 | ```
960 | - Replace the **title** by the following:
961 | ```html
962 | %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 |
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 |
--------------------------------------------------------------------------------