├── .gitignore
├── README.md
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.js
├── App.test.js
├── challenges
│ └── templates.js
├── deprecated
│ ├── constants.js
│ ├── getDemoConfig.js
│ └── helpers.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
├── tailwind.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Learn refatoring
2 |
3 | 👋 สวัสดี, repo นี้จะเป็นการฝึก refactor โค้ดคับ เหมาะทั้ง frontend และ backend engineer เลย
4 |
5 | - เขียน javascript พอได้
6 | - ไม่จำเป็นต้องมีความรู้ React
7 | - ไม่จำเป็นต้องมีความรู้ CSS
8 |
9 | ## Introduction
10 |
11 | ผมตั้งใจสร้าง repo นี้ขึ้นมาเนื่องจากเป็นโค้ดที่ผ่านการใช้งานมาจริงๆ และเป็น business requirement ที่เกิดขึ้นจริง เหมาะสำหรับการฝึกฝนมากๆ หวังว่าจะเป็นประโยชน์กับทุกๆท่านที่ผ่านมาเห็นนะครับ
12 |
13 | โค้ดทั้งหมดผมได้มาจาก [MUI repository](https://github.com/mui/material-ui/blob/master/docs/src/modules/utils/getDemoConfig.js)
14 |
15 | หลังจาก clone โปรเจคแล้วลองรัน `yarn && yarn start`
16 |
17 | จะได้หน้าจอแบบนี้ขึ้นมา
18 |
19 |
20 |
21 | ## How to practice
22 |
23 | เพื่อให้เสมือนจริงมากที่สุด ให้เริ่มทำความเข้าใจโค้ดเองทั้งหมดว่า application นี้เกี่ยวกับอะไร ทำงานอย่างไร จากนั้นให้อ่าน [Challenges](#challenges) แล้วเริ่มลงมือทำ
24 |
25 | ถ้าไม่เข้าใจจริงๆ ให้อ่าน hint ได้
26 |
27 |
28 |
29 | 💡 hint
30 |
31 | Application นี้เป็นเว็บไซต์สำหรับนักพัฒนาเพื่อเข้ามาดูโค้ดตัวอย่างแล้วนำไปใช้ ในกรณีที่ต้องการทดสอบโค้ดตัวอย่างสามารถเลือกเปิด interactive demo ได้สองแบบผ่าน [CodeSandbox](https://codesandbox.io/) หรือ [StackBlitz](https://stackblitz.com/).
32 |
33 | **การทำงาน**
34 |
35 | - การทำงานทั้งหมดจะเริ่มจากไฟล์ `src/App.js` เมื่อ user กดปุ่ม ข้อมูลจะถูกประมวลผลให้อยู่ในรูปแบบที่ CodeSandbox หรือ StackBlitz ต้องการ
36 | - ข้อมูลนั้นจะนำมาใช้ในการเรียก API เพื่อสร้าง sandbox
37 | - [CodeSanbox API](https://codesandbox.io/docs/api/#define-api)
38 | - [StackBlitz API](https://developer.stackblitz.com/docs/platform/post-api/)
39 |
40 |
41 | ## Challenges
42 |
43 | ### 1. Bug fix
44 |
45 | เมื่อกดปุ่ม StackBlitz จะเห็นว่า demo ไม่สามารถทำงานได้
46 |
47 | https://user-images.githubusercontent.com/18292247/168430133-8f3f5f06-65dc-4997-b5b2-549eadcc6899.mov
48 |
49 | **Requirement**
50 |
51 | ให้เพิ่ม `@babel/runtime` เข้าไปใน dependencies ของ StackBlitz เท่านั้น เมื่อกดปุ่ม StackBlitz จะต้องเห็น demo ที่ทำงานได้ถูกต้องทันที
52 |
53 | ### 2. Multiple products
54 |
55 | โค้ดตัวอย่างที่เห็นอยู่นั้นเป็น demo ของ Material UI ซึ่งเป็นหนึ่งในไลบรารี่ที่เรามี ทางบริษัทต้องการเพิ่มไลบรารี่ให้มากขึ้นโดยแต่ละไลบรารี่จะต้องมีไฟล์ template ที่แตกต่างกัน หากเปิดดูโค้ดที่อยู่ในไฟล์ `getDemoConfig.js` จะเห็นว่าฟังก์ชัน `tsDemo()` และ `jsDemo()` มี config เกี่ยวกับไฟล์ `index.js` ด้วยซึ่งเป็นส่วนหนึ่งของ template.
56 |
57 | ไฟล์ `src/challenges/templates.js` มีโค้ดตัวอย่างของอีกหนึ่งไลบรารี่ที่ชื่อ Joy UI.
58 |
59 | โจทย์คือให้ refactor โค้ด เพื่อนำ Joy UI มาแสดงเป็น demo และเปิด CodeSandbox กับ StackBlitz ได้ถูกต้อง
60 |
61 | > ในอนาคตจะมีไลบรารี่เพิ่มขึ้นอีกแน่นอน ฉะนั้นให้คำนึงถึงเรื่องนี้ตอน refactor โค้ดด้วย
62 |
63 |
64 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-refactoring",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.14.1",
7 | "@testing-library/react": "^13.0.0",
8 | "@testing-library/user-event": "^13.2.1",
9 | "@types/lz-string": "1.3.34",
10 | "lz-string": "1.4.4",
11 | "prism-react-renderer": "1.3.1",
12 | "react": "^18.1.0",
13 | "react-dom": "^18.1.0",
14 | "react-scripts": "5.0.1",
15 | "web-vitals": "^2.1.0"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "devDependencies": {
42 | "autoprefixer": "10.4.7",
43 | "postcss": "8.4.13",
44 | "tailwindcss": "3.0.24"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LZString from "lz-string";
3 | import Highlight, { defaultProps } from "prism-react-renderer";
4 | import getDemoConfig from "./deprecated/getDemoConfig";
5 |
6 | function compress(object) {
7 | return LZString.compressToBase64(JSON.stringify(object))
8 | .replace(/\+/g, "-") // Convert '+' to '-'
9 | .replace(/\//g, "_") // Convert '/' to '_'
10 | .replace(/=+$/, ""); // Remove ending '='
11 | }
12 |
13 | function addHiddenInput(form, name, value) {
14 | const input = document.createElement("input");
15 | input.type = "hidden";
16 | input.name = name;
17 | input.value = value;
18 | form.appendChild(input);
19 | }
20 |
21 | const jsCode = `import * as React from 'react';
22 | import Stack from '@mui/material/Stack';
23 | import Button from '@mui/material/Button';
24 |
25 | export default function BasicButtons() {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }`;
34 |
35 | const tsCode = `import * as React from 'react';
36 | import Stack from '@mui/material/Stack';
37 | import Button from '@mui/material/Button';
38 |
39 | export default function BasicButtons(): React.ReactElement {
40 | return (
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }`;
48 |
49 | const CODE = {
50 | JS: jsCode,
51 | TS: tsCode,
52 | };
53 |
54 | function App() {
55 | const [codeVariant, setCodeVariant] = React.useState("JS");
56 | const demoData = {
57 | title: "A demo",
58 | githubLocation: "",
59 | language: "en",
60 | codeVariant,
61 | raw: CODE[codeVariant],
62 | };
63 | const handleCodeSandboxClick = () => {
64 | const demoConfig = getDemoConfig(demoData);
65 | const parameters = compress({
66 | files: {
67 | "package.json": {
68 | content: {
69 | name: demoConfig.title,
70 | description: demoConfig.description,
71 | dependencies: demoConfig.dependencies,
72 | devDependencies: {
73 | "react-scripts": "latest",
74 | ...demoConfig.devDependencies,
75 | },
76 | main: demoConfig.main,
77 | scripts: demoConfig.scripts,
78 | // We used `title` previously but only inference from `name` is documented.
79 | // TODO revisit once https://github.com/codesandbox/codesandbox-client/issues/4983 is resolved.
80 | title: demoConfig.title,
81 | },
82 | },
83 | ...Object.keys(demoConfig.files).reduce((files, name) => {
84 | files[name] = { content: demoConfig.files[name] };
85 | return files;
86 | }, {}),
87 | },
88 | });
89 |
90 | const form = document.createElement("form");
91 | form.method = "POST";
92 | form.target = "_blank";
93 | form.action = "https://codesandbox.io/api/v1/sandboxes/define";
94 | addHiddenInput(form, "parameters", parameters);
95 | addHiddenInput(
96 | form,
97 | "query",
98 | codeVariant === "TS" ? "file=/demo.tsx" : "file=/demo.js"
99 | );
100 | document.body.appendChild(form);
101 | form.submit();
102 | document.body.removeChild(form);
103 | };
104 |
105 | const handleStackBlitzClick = () => {
106 | const demoConfig = getDemoConfig(demoData, {
107 | indexPath: "index.html",
108 | previewPackage: false,
109 | });
110 | const form = document.createElement("form");
111 | form.method = "POST";
112 | form.target = "_blank";
113 | form.action = "https://stackblitz.com/run";
114 | addHiddenInput(form, "project[template]", "create-react-app");
115 | addHiddenInput(form, "project[title]", demoConfig.title);
116 | addHiddenInput(
117 | form,
118 | "project[description]",
119 | `# ${demoConfig.title}\n${demoConfig.description}`
120 | );
121 | addHiddenInput(
122 | form,
123 | "project[dependencies]",
124 | JSON.stringify(demoConfig.dependencies)
125 | );
126 | addHiddenInput(
127 | form,
128 | "project[devDependencies]",
129 | JSON.stringify(demoConfig.devDependencies)
130 | );
131 | Object.keys(demoConfig.files).forEach((key) => {
132 | const value = demoConfig.files[key];
133 | addHiddenInput(form, `project[files][${key}]`, value);
134 | });
135 | document.body.appendChild(form);
136 | form.submit();
137 | document.body.removeChild(form);
138 | };
139 | return (
140 |
141 |
{demoData.title}
142 |
143 | {({ className, style, tokens, getLineProps, getTokenProps }) => (
144 |
145 | {tokens.map((line, i) => (
146 |
147 | {line.map((token, key) => (
148 |
149 | ))}
150 |
151 | ))}
152 |
153 | )}
154 |
155 |
156 |
157 |
168 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | );
187 | }
188 |
189 | export default App;
190 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/challenges/templates.js:
--------------------------------------------------------------------------------
1 | export const joy = {
2 | JS: {
3 | "index.js": `import * as React from 'react';
4 | import ReactDOM from 'react-dom/client';
5 | import { StyledEngineProvider, CssVarsProvider } from '@mui/joy/styles';
6 | import Demo from './demo';
7 |
8 | ReactDOM.createRoot(document.querySelector("#root")).render(
9 |
10 |
11 |
12 |
13 |
14 | );`,
15 | "demo.js": `import * as React from 'react';
16 | import Button from '@mui/joy/Button';
17 |
18 | export default function BasicButtons() {
19 | return (
20 |
21 | );
22 | }`,
23 | },
24 | TS: {
25 | "index.js": `import * as React from 'react';
26 | import ReactDOM from 'react-dom/client';
27 | import { StyledEngineProvider, CssVarsProvider } from '@mui/joy/styles';
28 | import Demo from './demo';
29 |
30 | ReactDOM.createRoot(document.querySelector("#root")).render(
31 |
32 |
33 |
34 |
35 |
36 | );`,
37 | "demo.js": `import * as React from 'react';
38 | import Button from '@mui/joy/Button';
39 |
40 | export default function BasicButtons(): React.ReactElement {
41 | return (
42 |
43 | );
44 | }`,
45 | },
46 | };
47 |
--------------------------------------------------------------------------------
/src/deprecated/constants.js:
--------------------------------------------------------------------------------
1 | export const CODE_VARIANTS = {
2 | JS: "JS",
3 | TS: "TS",
4 | };
5 |
--------------------------------------------------------------------------------
/src/deprecated/getDemoConfig.js:
--------------------------------------------------------------------------------
1 | import { CODE_VARIANTS } from "./constants";
2 | import { getDependencies } from "./helpers";
3 |
4 | function jsDemo(demoData, options) {
5 | return {
6 | dependencies: getDependencies(demoData.raw, {
7 | codeLanguage: CODE_VARIANTS.JS,
8 | muiCommitRef:
9 | process.env.PULL_REQUEST && options.previewPackage
10 | ? process.env.COMMIT_REF
11 | : undefined,
12 | }),
13 | files: {
14 | "demo.js": demoData.raw,
15 | "index.js": `
16 | import * as React from 'react';
17 | import ReactDOM from 'react-dom/client';
18 | import { StyledEngineProvider } from '@mui/material/styles';
19 | import Demo from './demo';
20 |
21 | ReactDOM.createRoot(document.querySelector("#root")).render(
22 |
23 |
24 |
25 | );
26 | `.trim(),
27 | },
28 | };
29 | }
30 |
31 | function tsDemo(demoData, options) {
32 | return {
33 | dependencies: getDependencies(demoData.raw, {
34 | codeLanguage: CODE_VARIANTS.TS,
35 | muiCommitRef:
36 | process.env.PULL_REQUEST && options.previewPackage
37 | ? process.env.COMMIT_REF
38 | : undefined,
39 | }),
40 | files: {
41 | "demo.tsx": demoData.raw,
42 | "index.tsx": `
43 | import * as React from 'react';
44 | import ReactDOM from 'react-dom/client';
45 | import { StyledEngineProvider } from '@mui/material/styles';
46 | import Demo from './demo';
47 |
48 | ReactDOM.createRoot(document.querySelector("#root")).render(
49 |
50 |
51 |
52 | );
53 | `.trim(),
54 | "tsconfig.json": `{
55 | "compilerOptions": {
56 | "target": "es5",
57 | "lib": [
58 | "dom",
59 | "dom.iterable",
60 | "esnext"
61 | ],
62 | "allowJs": true,
63 | "skipLibCheck": true,
64 | "esModuleInterop": true,
65 | "allowSyntheticDefaultImports": true,
66 | "strict": true,
67 | "forceConsistentCasingInFileNames": true,
68 | "module": "esnext",
69 | "moduleResolution": "node",
70 | "resolveJsonModule": true,
71 | "isolatedModules": true,
72 | "noEmit": true,
73 | "jsx": "react"
74 | },
75 | "include": [
76 | "src"
77 | ]
78 | }
79 | `,
80 | },
81 | main: "index.tsx",
82 | scripts: {
83 | start: "react-scripts start",
84 | },
85 | };
86 | }
87 |
88 | function getLanguageConfig(demoData, options) {
89 | switch (demoData.codeVariant) {
90 | case CODE_VARIANTS.TS:
91 | return tsDemo(demoData, options);
92 | case CODE_VARIANTS.JS:
93 | return jsDemo(demoData, options);
94 | default:
95 | throw new Error(`Unsupported codeVariant: ${demoData.codeVariant}`);
96 | }
97 | }
98 |
99 | export default function getDemoConfig(demoData, options = {}) {
100 | const { indexPath = "public/index.html", previewPackage = true } = options;
101 | const baseConfig = {
102 | title: demoData.title,
103 | description: demoData.githubLocation,
104 | files: {
105 | [indexPath]: `
106 |
107 |
108 |
109 | ${demoData.title}
110 |
111 |
115 |
116 |
120 |
121 |
122 |
123 |
124 |
125 | `.trim(),
126 | },
127 | };
128 | const languageConfig = getLanguageConfig(demoData, {
129 | previewPackage,
130 | });
131 |
132 | return {
133 | ...baseConfig,
134 | ...languageConfig,
135 | files: {
136 | ...baseConfig.files,
137 | ...languageConfig.files,
138 | },
139 | };
140 | }
141 |
--------------------------------------------------------------------------------
/src/deprecated/helpers.js:
--------------------------------------------------------------------------------
1 | import { CODE_VARIANTS } from "./constants";
2 |
3 | /**
4 | * Mapping from the date adapter sub-packages to the npm packages they require.
5 | * @example `@mui/lab/AdapterDateFns` has a peer dependency on `date-fns`.
6 | */
7 | const dateAdapterPackageMapping = {
8 | AdapterDateFns: "date-fns",
9 | AdapterDayjs: "dayjs",
10 | AdapterLuxon: "luxon",
11 | AdapterMoment: "moment",
12 | };
13 |
14 | /**
15 | * @var
16 | * set of packages that ship their own typings instead of using @types/ namespace
17 | * Array because Set([iterable]) is not supported in IE11
18 | */
19 | const packagesWithBundledTypes = [
20 | "date-fns",
21 | "@emotion/react",
22 | "@emotion/styled",
23 | ];
24 |
25 | /**
26 | * WARNING: Always uses `latest` typings.
27 | *
28 | * Adds dependencies to @types packages only for packages that are not listed
29 | * in packagesWithBundledTypes
30 | *
31 | * @see packagesWithBundledTypes in this module namespace
32 | * @param deps - list of dependency as `name => version`
33 | */
34 | function addTypeDeps(deps) {
35 | const packagesWithDTPackage = Object.keys(deps)
36 | .filter((name) => packagesWithBundledTypes.indexOf(name) === -1)
37 | // All the MUI packages come with bundled types
38 | .filter((name) => name.indexOf("@mui/") !== 0);
39 |
40 | packagesWithDTPackage.forEach((name) => {
41 | let resolvedName = name;
42 | // scoped package?
43 | if (name.startsWith("@")) {
44 | // https://github.com/DefinitelyTyped/DefinitelyTyped#what-about-scoped-packages
45 | resolvedName = name.slice(1).replace("/", "__");
46 | }
47 |
48 | deps[`@types/${resolvedName}`] = "latest";
49 | });
50 | }
51 |
52 | function includePeerDependencies(deps, versions) {
53 | let newDeps = {
54 | ...deps,
55 | "react-dom": versions["react-dom"],
56 | react: versions.react,
57 | "@emotion/react": versions["@emotion/react"],
58 | "@emotion/styled": versions["@emotion/styled"],
59 | };
60 |
61 | if (newDeps["@mui/lab"]) {
62 | newDeps["@mui/material"] = versions["@mui/material"];
63 | }
64 |
65 | if (newDeps["@material-ui/data-grid"]) {
66 | newDeps["@mui/material"] = versions["@mui/material"];
67 | newDeps["@mui/styles"] = versions["@mui/styles"];
68 | }
69 |
70 | // TODO: Where is this coming from and why does it need to be injected this way.
71 | if (window.muiDocConfig) {
72 | newDeps = window.muiDocConfig.csbIncludePeerDependencies(newDeps, {
73 | versions,
74 | });
75 | }
76 |
77 | return newDeps;
78 | }
79 |
80 | /**
81 | * @param packageName - The name of a package living inside this repository.
82 | * @param commitRef
83 | * @return string - A valid version for a dependency entry in a package.json
84 | */
85 | function getMuiPackageVersion(packageName, commitRef) {
86 | if (
87 | commitRef === undefined ||
88 | process.env.SOURCE_CODE_REPO !== "https://github.com/mui/material-ui"
89 | ) {
90 | // #default-branch-switch
91 | return "latest";
92 | }
93 | const shortSha = commitRef.slice(0, 8);
94 | return `https://pkg.csb.dev/mui/material-ui/commit/${shortSha}/@mui/${packageName}`;
95 | }
96 |
97 | /**
98 | * @param raw - ES6 source with es module imports
99 | * @param options
100 | * @returns map of packages with their required version
101 | */
102 | export function getDependencies(raw, options = {}) {
103 | const { codeLanguage, muiCommitRef } = options;
104 |
105 | let deps = {};
106 | let versions = {
107 | react: "latest",
108 | "react-dom": "latest",
109 | "@emotion/react": "latest",
110 | "@emotion/styled": "latest",
111 | "@mui/material": "5.7.0",
112 | "@mui/icons-material": getMuiPackageVersion("icons-material", muiCommitRef),
113 | "@mui/lab": getMuiPackageVersion("lab", muiCommitRef),
114 | "@mui/styled-engine": getMuiPackageVersion("styled-engine", muiCommitRef),
115 | "@mui/styles": getMuiPackageVersion("styles", muiCommitRef),
116 | "@mui/system": getMuiPackageVersion("system", muiCommitRef),
117 | "@mui/private-theming": getMuiPackageVersion("theming", muiCommitRef),
118 | "@mui/private-classnames": getMuiPackageVersion("classnames", muiCommitRef),
119 | "@mui/base": getMuiPackageVersion("base", muiCommitRef),
120 | "@mui/utils": getMuiPackageVersion("utils", muiCommitRef),
121 | "@mui/material-next": getMuiPackageVersion("material-next", muiCommitRef),
122 | "@mui/joy": getMuiPackageVersion("joy", muiCommitRef),
123 | };
124 |
125 | // TODO: Where is this coming from and why does it need to be injected this way.
126 | if (window.muiDocConfig) {
127 | versions = window.muiDocConfig.csbGetVersions(versions, { muiCommitRef });
128 | }
129 |
130 | const re = /^import\s'([^']+)'|import\s[\s\S]*?\sfrom\s+'([^']+)/gm;
131 | let m = null;
132 | // eslint-disable-next-line no-cond-assign
133 | while ((m = re.exec(raw))) {
134 | const fullName = m[2] ?? m[1];
135 | // handle scope names
136 | const name =
137 | fullName.charAt(0) === "@"
138 | ? fullName.split("/", 2).join("/")
139 | : fullName.split("/", 1)[0];
140 |
141 | if (!deps[name]) {
142 | deps[name] = versions[name] ? versions[name] : "latest";
143 | }
144 |
145 | // e.g date-fns
146 | const dateAdapterMatch = fullName.match(/^@mui\/lab\/(Adapter.*)/);
147 | if (dateAdapterMatch !== null) {
148 | const packageName = dateAdapterPackageMapping[dateAdapterMatch[1]];
149 | if (packageName === undefined) {
150 | throw new TypeError(
151 | `Can't determine required npm package for adapter '${dateAdapterMatch[1]}'`
152 | );
153 | }
154 | deps[packageName] = "latest";
155 | }
156 | }
157 |
158 | deps = includePeerDependencies(deps, versions);
159 |
160 | if (codeLanguage === CODE_VARIANTS.TS) {
161 | addTypeDeps(deps);
162 | deps.typescript = "latest";
163 | }
164 |
165 | if (!deps["@mui/material"]) {
166 | // The `index.js` imports StyledEngineProvider from '@mui/material', so we need to make sure we have it as a dependency
167 | const name = "@mui/material";
168 | deps[name] = versions[name] ? versions[name] : "latest";
169 | }
170 |
171 | return deps;
172 | }
173 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | margin: 0;
7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
8 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
9 | sans-serif;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
14 | code {
15 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
16 | monospace;
17 | }
18 |
19 | button {
20 | padding: 0.75rem 1rem;
21 | border: 2px solid #121212;
22 | border-radius: 4px;
23 | outline: none;
24 | font-size: 1rem;
25 | background-color: #f5f5f5;
26 | }
27 |
28 | button:hover {
29 | background-color: antiquewhite;
30 | }
31 |
32 | pre {
33 | padding: 1rem;
34 | border-radius: 4px;
35 | overflow: auto;
36 | }
37 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ["./src/**/*.{js,jsx,ts,tsx}"],
3 | theme: {
4 | extend: {},
5 | },
6 | plugins: [],
7 | };
8 |
--------------------------------------------------------------------------------