-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
[NEXTLEVEL 클린코드 리액트 조성륜] 장바구니 미션 Step1
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/dist | ||
*.scss | ||
*.css | ||
/.next | ||
/node_modules | ||
/build | ||
*.js |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
module.exports = { | ||
env: { | ||
browser: true, | ||
es2021: true, | ||
}, | ||
extends: [ | ||
'plugin:react/recommended', | ||
'airbnb', | ||
'airbnb-typescript', | ||
'prettier', | ||
], | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
ecmaVersion: 'latest', | ||
sourceType: 'module', | ||
project: './tsconfig.json', | ||
}, | ||
plugins: ['react', '@typescript-eslint', 'prettier'], | ||
rules: { | ||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }], | ||
'import/prefer-default-export': 0, | ||
'prettier/prettier': 'error', | ||
'jsx-a11y/no-noninteractive-element-interactions': 0, | ||
'jsx-a11y/click-events-have-key-events': 0, | ||
'react/require-default-props': 0, | ||
'react/react-in-jsx-scope': 0, | ||
'react/jsx-filename-extension': [ | ||
1, | ||
{ extensions: ['.js', '.jsx', 'ts', 'tsx'] }, | ||
], | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"semi": true, | ||
"tabWidth": 2, | ||
"singleQuote": true, | ||
"useTabs": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<!DOCTYPE html> | ||
<!--suppress HtmlUnknownTarget --> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite App</title> | ||
</head> | ||
<body class="debug-screens"> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export default { | ||
testEnvironment: 'jsdom', | ||
transform: { | ||
'^.+\\.tsx?$': 'ts-jest', | ||
}, | ||
moduleNameMapper: { | ||
'\\.(css|less|sass|scss)$': 'identity-obj-proxy', | ||
}, | ||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import '@testing-library/jest-dom/extend-expect'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,52 @@ | ||
{ | ||
"name": "shopping-cart-client", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"license": "MIT", | ||
"name": "client", | ||
"private": true, | ||
"version": "0.0.0", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build", | ||
"preview": "vite preview", | ||
"server:prepare": "cd ../server && yarn && cd ../client", | ||
"server": "cd ../server && yarn server && cd ../client", | ||
"server:first": "yarn server:prepare && yarn server" | ||
"server:first": "yarn server:prepare && yarn server", | ||
"test": "jest" | ||
}, | ||
"dependencies": { | ||
"ky": "^0.29.0", | ||
"react": "^17.0.2", | ||
"react-dom": "^17.0.2", | ||
"react-query": "^3.34.16", | ||
"react-router-dom": "6", | ||
"typescript": "^4.5.5" | ||
}, | ||
"devDependencies": { | ||
"@testing-library/jest-dom": "^5.16.2", | ||
"@testing-library/react": "^12.1.3", | ||
"@testing-library/user-event": "^13.5.0", | ||
"@types/jest": "^27.4.1", | ||
"@types/react": "^17.0.33", | ||
"@types/react-dom": "^17.0.10", | ||
"@typescript-eslint/eslint-plugin": "^5.12.1", | ||
"@typescript-eslint/parser": "^5.12.1", | ||
"@vitejs/plugin-react": "^1.0.7", | ||
"autoprefixer": "^10.4.2", | ||
"eslint": "^8.2.0", | ||
"eslint-config-airbnb": "19.0.4", | ||
"eslint-config-airbnb-typescript": "^16.1.0", | ||
"eslint-config-prettier": "^8.4.0", | ||
"eslint-plugin-import": "^2.25.3", | ||
"eslint-plugin-jsx-a11y": "^6.5.1", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"eslint-plugin-react": "^7.28.0", | ||
"eslint-plugin-react-hooks": "^4.3.0", | ||
"identity-obj-proxy": "^3.0.0", | ||
"jest": "^27.5.1", | ||
"postcss": "^8.4.6", | ||
"prettier": "^2.5.1", | ||
"tailwindcss": "^3.0.23", | ||
"tailwindcss-debug-screens": "^2.2.1", | ||
"ts-jest": "^27.1.3", | ||
"ts-node": "^10.5.0", | ||
"vite": "^2.8.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
plugins: { | ||
tailwindcss: {}, | ||
autoprefixer: {}, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
.App { | ||
text-align: center; | ||
} | ||
|
||
.App-logo { | ||
height: 40vmin; | ||
pointer-events: none; | ||
} | ||
|
||
@media (prefers-reduced-motion: no-preference) { | ||
.App-logo { | ||
animation: App-logo-spin infinite 20s linear; | ||
} | ||
} | ||
|
||
.App-header { | ||
background-color: #282c34; | ||
min-height: 100vh; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
font-size: calc(10px + 2vmin); | ||
color: white; | ||
} | ||
|
||
.App-link { | ||
color: #61dafb; | ||
} | ||
|
||
@keyframes App-logo-spin { | ||
from { | ||
transform: rotate(0deg); | ||
} | ||
to { | ||
transform: rotate(360deg); | ||
} | ||
} | ||
|
||
button { | ||
font-size: calc(10px + 2vmin); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import './App.css'; | ||
import { BrowserRouter, Route, Routes } from 'react-router-dom'; | ||
import React from 'react'; | ||
import { QueryClient, QueryClientProvider } from 'react-query'; | ||
import { Home } from './pages/Home'; | ||
import { Cart } from './pages/Cart'; | ||
import { Order } from './pages/Order'; | ||
import { Gnb } from './components/common/Gnb'; | ||
|
||
const queryClient = new QueryClient(); | ||
|
||
function App() { | ||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
<BrowserRouter> | ||
<Gnb /> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/cart" element={<Cart />} /> | ||
<Route path="/order" element={<Order />} /> | ||
</Routes> | ||
</BrowserRouter> | ||
</QueryClientProvider> | ||
); | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import App from '../App'; | ||
|
||
test('App test', () => { | ||
const { container } = render(<App />); | ||
|
||
expect(container).not.toBe(null); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { render } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { ProductCard } from '../../components/ProductCard'; | ||
|
||
describe('ProductCard', () => { | ||
const handleClickItem = jest.fn(); | ||
const handleClickCart = jest.fn(); | ||
|
||
beforeEach(() => { | ||
handleClickItem.mockClear(); | ||
handleClickCart.mockClear(); | ||
}); | ||
|
||
it('render', () => { | ||
const { container } = render( | ||
<ProductCard | ||
item={{ id: 1, name: 'item', imageUrl: '', price: 1000 }} | ||
onClickItem={handleClickItem} | ||
onClickCart={handleClickCart} | ||
/> | ||
); | ||
|
||
expect(container).not.toBeNull(); | ||
expect(container).toHaveTextContent('item'); | ||
expect(container).toHaveTextContent('1000 원'); | ||
}); | ||
|
||
it('Click card item, call handleClickItem', () => { | ||
const { getByRole } = render( | ||
<ProductCard | ||
item={{ id: 1, name: 'item', imageUrl: '', price: 1000 }} | ||
onClickItem={handleClickItem} | ||
onClickCart={handleClickCart} | ||
/> | ||
); | ||
|
||
userEvent.click(getByRole('listitem')); | ||
expect(handleClickItem).toBeCalled(); | ||
}); | ||
|
||
it('Click cart button, call handleClickCart', () => { | ||
const { getByRole } = render( | ||
<ProductCard | ||
item={{ id: 1, name: 'item', imageUrl: '', price: 1000 }} | ||
onClickItem={handleClickItem} | ||
onClickCart={handleClickCart} | ||
/> | ||
); | ||
|
||
const cartButton = getByRole('button'); | ||
userEvent.click(cartButton); | ||
|
||
expect(handleClickCart).toBeCalled(); | ||
}); | ||
}); |