Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1단계 - 페이먼츠 미션] 지그(송지은) 미션 제출합니다. #17

Merged
merged 81 commits into from
May 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
383c2f2
chore: eslint, prettier, storybook 세팅
zigsong Apr 20, 2021
4804a65
docs: 기능 구현 목록 작성
zigsong Apr 20, 2021
965a116
chore: styled components 설치
zigsong Apr 22, 2021
92cc334
feat: page hosting 구현
zigsong Apr 22, 2021
8fdacbb
feat: dummy card data 추가
zigsong Apr 22, 2021
080e632
feat: global font와 색상 팔레트 추가
zigsong Apr 22, 2021
590bca2
feat: Card 컴포넌트 구현
zigsong Apr 22, 2021
8cc2acc
feat: Card 컴포넌트 스토리북 작성
zigsong Apr 22, 2021
75ab307
feat: Card 추가 버튼 구현
zigsong Apr 22, 2021
b5c9dcc
feat: Card 추가 버튼 스토리북 작성
zigsong Apr 22, 2021
0c71377
feat: 보유 카드 목록 페이지 구현
zigsong Apr 22, 2021
a64403e
feat: box-sizing global style 추가
zigsong Apr 23, 2021
517cee4
feat: Card Storybook에 numbers args 추가
zigsong Apr 23, 2021
f8323a8
refactor: CardAddButton 디렉토리 변경
zigsong Apr 23, 2021
f3f3e2e
feat: CardNumbersInput 구현
zigsong Apr 23, 2021
235a69a
feat: ExpirationDateInput 구현
zigsong Apr 23, 2021
fa87729
feat: OwnerNameInput 구현
zigsong Apr 23, 2021
a397e5b
feat: SecureCodeInput 구현
zigsong Apr 23, 2021
9afd7f1
feat: CardPasswordInput 구현
zigsong Apr 23, 2021
459537f
feat: RegisterInputWrapper 구현
zigsong Apr 23, 2021
f1e0eb9
feat: svg 파일 저장
zigsong Apr 23, 2021
6dbeda7
feat: register input 값 객체 상수 분리
zigsong Apr 23, 2021
d2407ce
style: 색상 추가
zigsong Apr 23, 2021
b0cfe8f
fix: global style 적용 범위 수정
zigsong Apr 23, 2021
751e706
feat: 카드 등록 페이지 구현
zigsong Apr 23, 2021
f58776d
chore: storybook default viewport 설정
zigsong Apr 25, 2021
4988e65
feat: PageHost storybook 작성
zigsong Apr 25, 2021
4ae2ea8
refactor: 카드목록 페이지의 카드 크기 고정
zigsong Apr 25, 2021
c5f4ff5
feat: 카드등록 페이지에서 등록 폼 분리
zigsong Apr 25, 2021
8b2ad89
feat: 카드등록 페이지의 빈 카드 구현
zigsong Apr 25, 2021
8e53d39
fix: Array 리턴 요소에 key값 부여
zigsong Apr 25, 2021
99357b2
style: 등록 폼 인풋 헤더 width 설정
zigsong Apr 25, 2021
973a3d5
feat: NavigationBar에 뒤로가기 버튼 추가
zigsong Apr 25, 2021
ebcd4c0
feat: 다음 버튼 구현
zigsong Apr 25, 2021
53c7d7d
feat: 은행 임시 데이터 추가
zigsong Apr 26, 2021
8e78e17
feat: 카드 은행사 옵션 컴포넌트 구현
zigsong Apr 26, 2021
f32713e
feat: 카드 색상 선택창 컴포넌트 구현
zigsong Apr 26, 2021
e048e13
feat: 카드 등록 페이지에서 카드 색상 선택창 로드
zigsong Apr 26, 2021
d6065c7
feat: 카드 번호, 만료일 인풋 핸들링
zigsong Apr 26, 2021
71136e2
feat: 카드 은행사 색상 선택 핸들링
zigsong Apr 26, 2021
6add68b
feat: 색상 선택지 애니메이션 효과 추가
zigsong Apr 27, 2021
f7bc7a7
refactor: RegisterInputWrapper 분리
zigsong Apr 27, 2021
6ca3529
feat: 만료일 인풋 유효성 검사
zigsong Apr 27, 2021
8bc9a43
feat: 카드소유자이름 인풋 유효성 검사
zigsong Apr 27, 2021
ba15e33
feat: CVC 인풋 유효성 검사
zigsong Apr 27, 2021
4162d43
feat: CVC 인풋 기능 구현
zigsong Apr 27, 2021
401da94
feat: 카드 비밀번호 인풋 유효성 검사
zigsong Apr 27, 2021
6c7f442
feat: 카드 비밀번호 인풋 기능 구현
zigsong Apr 27, 2021
c5f7158
feat: 소유자이름 인풋 기능 구현
zigsong Apr 27, 2021
a28131e
feat: 만료일 인풋 기능 구현
zigsong Apr 27, 2021
07d91b7
feat: 카드번호 인풋 기능 구현
zigsong Apr 27, 2021
3c1bb5a
feat: 카드 등록 폼 구현 중
zigsong Apr 27, 2021
3e489b8
feat: 카드 색상 선택 시 배경색 반영
zigsong Apr 27, 2021
15d1cfb
feat: 카드 등록 페이지에서 입력된 정보를 카드에 반영
zigsong Apr 27, 2021
5a51f53
refactor: 카드 색상 반영 로직 수정
zigsong Apr 27, 2021
2e3584b
refactor: 카드 등록 페이지 인풋 width 지정 방식 수정
zigsong Apr 27, 2021
7ae513b
fix: form 관련 스토리북 arguments 주입
zigsong Apr 27, 2021
f9afbfe
fix: propTypes 타입 수정
zigsong Apr 27, 2021
06c8f23
feat: 카드 등록 페이지에서 등록 완료 페이지로의 이동 구현
zigsong Apr 27, 2021
dd120a3
refactor: 카드 번호 앞 2자리 입력시 색상 선택 창 열기
zigsong Apr 28, 2021
7b06701
refactor: 카드 폰트 크기 상대값으로 변경
zigsong Apr 28, 2021
f0c80f8
feat: 카드 등록 완료 페이지 구현
zigsong Apr 28, 2021
feb3fa3
feat: 카드 등록 완료 페이지 스토리북 작성
zigsong Apr 29, 2021
766f8ce
feat: 카드 등록 input들의 proptypes 지정 및 스토리북 인자 전달
zigsong Apr 29, 2021
bc38aa6
refactor: 카드 등록 페이지의 인풋들을 제어 컴포넌트로 변경
zigsong Apr 29, 2021
6103784
feat: 카드 등록 페이지 필수 입력 인풋 지정
zigsong Apr 29, 2021
99f038f
feat: 소유자 이름 유효성 검증
zigsong Apr 29, 2021
ffe790a
fix: 스토리북 전달 인자 수정
zigsong Apr 29, 2021
077a879
refactor: 컴포넌트에 빠진 propTypes 부여
zigsong Apr 29, 2021
693a463
refactor: 리팩토링
zigsong Apr 29, 2021
093ec63
refactor: 리팩토링
zigsong Apr 29, 2021
9bcfda8
refactor: 리팩토링
zigsong Apr 29, 2021
7060d90
refactor: 리팩토링
zigsong Apr 29, 2021
5d62756
fix: propTypes 수정
zigsong Apr 29, 2021
25c9a54
chore: 앱 이름, favicon 변경
zigsong Apr 29, 2021
7352cef
fix: globalStyle 문법 수정
zigsong Apr 29, 2021
09cf3fb
refactor: font size rem으로 변경
zigsong Apr 30, 2021
7e3b60f
feat: 가상 키보드 구현
zigsong Apr 30, 2021
ca7d668
refactor: 일부 태그 수정
zigsong May 1, 2021
f4d4ed8
feat: 가상 키보드 구현
zigsong May 1, 2021
3fcd6bc
feat: 가상 키보드 스토리북 작성
zigsong May 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"react-app",
"plugin:prettier/recommended"

],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"prettier"
],
"rules": {}
}
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"endOfLine": "auto"
}
11 changes: 11 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app"
]
}
24 changes: 24 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const customViewports = {
reactPayments: {
name: 'react-payments',
styles: {
width: '375px',
height: '700px',
},
type: 'mobile',
},
};

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
viewport: {
viewports: customViewports,
defaultViewport: 'reactPayments',
},
};
29 changes: 29 additions & 0 deletions REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## ✅ Todos

### 페이지 호스팅
- 카드목록 페이지에서 새 카드 등록 버튼 클릭 시 카드추가 페이지로 이동
- 카드추가 페이지에서 '다음' 버튼을 누르면 카드추가완료 페이지로 이동
- 카드추가 페이지에서 뒤로가기 버튼 클릭 시 카드목록 페이지로 이동

### 카드목록 페이지
- 카드 추가 UI 구현
- 등록된 카드 컴포넌트 구현
- 4장 이상의 카드가 등록됐을 때 스크롤바 생성

### 카드추가 페이지
- 카드 번호, 만료일, 카드 소유자 이름, 보안코드, 카드 비밀번호 폼 UI 구현
- 카드 번호 입력 시 카드 이미지에 현재 입력된 번호 반영
- 카드 번호 4자리 입력 시 다음 번호 입력 칸으로 이동 (각 항목은 '-'로 구분)
- 16자리 카드 번호 모두 입력 시 카드 색상 선택 모달이 뜨도록 구현
- 카드 색상 선택 시 모달이 닫히도록 구현
- 카드 모양 클릭 시 카드 색상 선택 모달이 뜨도록 구현
- 만료일 월 2자리 입력시 연도 2자리 입력 칸으로 이동 (각 항목은 '/'로 구분)
- 만료일 입력 시 카드 이미지에 현재 입력된 번호 반영
- 카드 소유자 이름 입력시 현재 입력값 길이 계산하여 폼 우측상단에 표시
- 보안코드 입력 폼 옆의 물음표 클릭 시 CVC 설명 팝오버 표시
- 필수 입력 폼을 채우지 않은 상태로 확인 버튼 클릭 시 안내 메시지와 함께 미입력된 인풋으로 포커스
- 카드 소유자 이름 폼 제외

### 카드추가완료 페이지
- 등록된 카드 프리뷰 표시
- 카드 별칭 입력 폼 구현
66 changes: 66 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "react-payments",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"styled-components": "^5.2.3",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"overrides": [
{
"files": [
"**/*.stories.*"
],
"rules": {
"import/no-anonymous-default-export": "off"
}
}
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@storybook/addon-actions": "^6.2.8",
"@storybook/addon-essentials": "^6.2.8",
"@storybook/addon-links": "^6.2.8",
"@storybook/node-logger": "^6.2.8",
"@storybook/preset-create-react-app": "^3.1.7",
"@storybook/react": "^6.2.8",
"eslint-config-prettier": "^8.2.0",
"eslint-plugin-prettier": "^3.4.0",
"prettier": "^2.2.1",
"styled-components.macro": "^1.0.0"
},
"resolutions": {
"babel-loader": "8.1.0"
}
}
Binary file added public/favicon.ico
Binary file not shown.
44 changes: 44 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Woowa-Payments</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="keyboard"></div>
zigsong marked this conversation as resolved.
Show resolved Hide resolved
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
3 changes: 3 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"short_name": "Woowa-Payments"
}
46 changes: 46 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState } from 'react';
import PageHost from './components/PageHost';
import CardList from './components/pages/CardList';
import CardRegister from './components/pages/CardRegister';
import CardCompletion from './components/pages/CardCompletion';
import GlobalStyle from './styles/global';

const PAGES = {
LIST: {
title: '보유카드',
content: (props) => <CardList {...props} />,
backButton: false,
},
REGISTER: {
title: '카드추가',
content: (props) => <CardRegister {...props} />,
backButton: true,
},
COMPLETION: {
title: '',
content: (props) => <CardCompletion {...props} />,
backButton: false,
},
};

const App = () => {
const [currentPage, setCurrentPage] = useState(PAGES.REGISTER);
const [cardData, setCardData] = useState({});

const handleGoNext = () => {
if (currentPage === PAGES.REGISTER) {
setCurrentPage(PAGES.COMPLETION);
}
};

return (
<>
<GlobalStyle />
<PageHost navigationTitle={currentPage.title} hasBackButton={currentPage.backButton}>
{currentPage.content({ handleGoNext, cardData, setCardData })}
zigsong marked this conversation as resolved.
Show resolved Hide resolved
</PageHost>
</>
);
};

export default App;
3 changes: 3 additions & 0 deletions src/assets/backbutton.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/cvc-help.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/cvc_guide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/secure-ellipse-cyan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/secure-ellipse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/components/PageHost/PageHost.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-disable import/no-anonymous-default-export */
import React from 'react';
import PageHost from './index';
import { Default as CardRegister } from '../pages/CardRegister/CardRegister.stories';
import { Default as CardCompletion } from '../pages/CardCompletion/CardCompletion.stories';

export default {
title: 'PageHost',
component: PageHost,
};

const Template = (args) => <PageHost {...args} />;

export const CardRegisterPage = Template.bind({});
CardRegisterPage.args = {
navigationTitle: '카드추가',
children: <CardRegister />,
};

export const CardCompletionPage = Template.bind({});
CardCompletionPage.args = {
navigationTitle: '',
children: <CardCompletion {...CardCompletion.args} />,
};
26 changes: 26 additions & 0 deletions src/components/PageHost/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import backButtonSvg from '../../assets/backbutton.svg';
import * as Style from './style';

const PageHost = (props) => {
const { navigationTitle, hasBackButton, children } = props;

return (
<Style.Root>
<Style.NavigationBar title={navigationTitle}>
{hasBackButton && <img src={backButtonSvg} alt="back-button" />}
<Style.NavigationTitle>{navigationTitle}</Style.NavigationTitle>
</Style.NavigationBar>
<section>{children}</section>
</Style.Root>
);
};

PageHost.propTypes = {
navigationTitle: PropTypes.string.isRequired,
hasBackButton: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
};

export default PageHost;
26 changes: 26 additions & 0 deletions src/components/PageHost/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from 'styled-components/macro';

const Root = styled.main`
display: flex;
position: relative;
flex-direction: column;
width: 375px;
height: 700px;
`;

const NavigationBar = styled.nav`
display: ${({ title }) => (title ? 'flex' : 'none')}};
background-color: #ffffff;
position: sticky;
top: 0;
width: 100%;
padding: 22px 24px;
font-size: 16px;
z-index: 10;
`;

const NavigationTitle = styled.span`
margin-left: 17px;
`;

export { Root, NavigationBar, NavigationTitle };
Loading