Skip to content

Commit 096b2fd

Browse files
[1단계 - 페이먼츠] 카멜(진나영) 미션 제출합니다. (woowacourse#435)
* feat: 페어 프로그래밍 초기 세팅 Co-authored-by: dev-dino22 <[email protected]> * docs: 미션의 기능목록, 설계 구조 등 작성 Co-authored-by: dev-dino22 <[email protected]> * style: 전역 스타일링 적용 Co-authored-by: dev-dino22 <[email protected]> * feat: Input 컴포넌트 생성 Co-authored-by: dev-dino22 <[email protected]> * feat: InputForm 컴포넌트 생성 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 번호 입력 컴포넌트 작성 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 유효기간 입력 컴포넌트 작성 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 CVC 입력 컴포넌트 작성 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 입력 form 컴포넌트 작성 Co-authored-by: dev-dino22 <[email protected]> * refactor: InputForm이 Input을 children으로 받도록 리팩토링 Co-authored-by: dev-dino22 <[email protected]> * style: 전역 변수로 관리하는 색상 추가 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 브랜드 로고 이미지 생성 Co-authored-by: dev-dino22 <[email protected]> * style: 카드 입력 form 컴포넌트 스타일 적용 Co-authored-by: dev-dino22 <[email protected]> * style: 기본 스타일을 초기화하는 파일 수정 Co-authored-by: dev-dino22 <[email protected]> * style: 카드 입력 컴포넌트의 스타일 수정 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 정보를 입력하는 UI를 렌더링하는 함수 생성 Co-authored-by: dev-dino22 <[email protected]> * style: 카드 정보를 입력하는 UI에 스타일 추가 Co-authored-by: dev-dino22 <[email protected]> * style: 카드 입력 form의 스타일 조정 Co-authored-by: dev-dino22 <[email protected]> * feat: 사용자가 입력한 카드 정보를 프리뷰에 렌더링하는 기능 구현 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 번호와 유효기간 정보를 상태관리 하도록 코드 추가 Co-authored-by: dev-dino22 <[email protected]> * refactor: 사용자 카드 정보 등록 페이지가 렌더링되도록 app 수정 Co-authored-by: dev-dino22 <[email protected]> * feat: 사용자 입력값의 예외 처리 로직 생성 Co-authored-by: dev-dino22 <[email protected]> * feat: 사용자 입력값에 대한 피드백 메시지 렌더링 로직 추가 Co-authored-by: dev-dino22 <[email protected]> * refactor: 유효기간 검증 로직 수정 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 유효기간 검증 로직 추가 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 CVC 검증 로직 추가 Co-authored-by: dev-dino22 <[email protected]> * feat: 카드 번호 검증 로직 추가 Co-authored-by: dev-dino22 <[email protected]> * docs: 구현한 기능 목록 체크 * refactor: 상태 변경 함수의 타입 추가 * style: storybook에 css 적용 Co-authored-by: dev-dino22 <[email protected]> * refactor: 불필요한 props 제거 Co-authored-by: dev-dino22 <[email protected]> * test: storybook 비주얼 테스트 추가 Co-authored-by: dev-dino22 <[email protected]> * fix: 웹 배포 환경에 따라 이미지 경로 수정 * refactor: 불필요한 fragment 제거 * refactor: 카드 정보 상수 분리 * refactor: checkBrand 조건문 startsWith()로 가독성 개선 * refactor: 다시 유효한 값 입력 시 오류 스타일 삭제 * refactor: 유효기간 month, year로 나누고 전용 validator 분리 * refactor: 전역 상태 대신 ref로 유효기간 검증 관리 * refactor: 카드 유효기간입력 컴포넌트의 inputs를 map 함수 렌더링으로 변경 * refactor: CardExpirationDateInput 의 onChangeHandler 함수/상수 분리 및 추상화 * refactor: 카드프리뷰의 let 을 사용한 함수 내 상태관리에서 useMemo()훅 활용 * refactor: Input 컴포넌트에서 이제 불필요한 forwardRef 제거 및 props 전개 방식으로 변경 * chore: Input컴포넌트 폴명 파스칼케이스 수정을 위한 중간임시 폴더명 * feat: input 폴더명 카멜케이스 수정 * refactor: validateCardInput.ts 의 숫자문자열 검사 메서드명 수정 * refactor: validateCardInput.ts의 반환값 에러결과불리언객체로 통일 * refactor: Input 컴포넌트는 isValid 프롭을 전달받고 각 유효 상태를 사용처에서 제어하도록 변경 및 에러메세지 상수분리와 에러메세지 함수 작성 * chore: 불필요한 import 문 제거 --------- Co-authored-by: Jeongeun Lee <[email protected]> Co-authored-by: dev-dino22 <[email protected]>
1 parent bd4402c commit 096b2fd

23 files changed

+804
-0
lines changed

.storybook/preview-head.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
7+
<link href="../src/index.css" rel="stylesheet" />
8+
9+
<title>Document</title>
10+
</head>
11+
<body></body>
12+
</html>

src/color.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:root {
2+
--grey: #acacac;
3+
--black: #000000;
4+
--white: #ffffff;
5+
--red: #ff3d3d;
6+
--gold: #ddcd78;
7+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 16px;
5+
}
6+
7+
.inputForm {
8+
display: flex;
9+
justify-content: space-between;
10+
align-items: center;
11+
}
12+
13+
.inputContainer {
14+
display: flex;
15+
justify-content: space-between;
16+
align-items: center;
17+
gap: 10px;
18+
}
19+
20+
.inputBox {
21+
display: flex;
22+
flex-direction: column;
23+
gap: 8px;
24+
}
25+
26+
label {
27+
color: var(--black);
28+
}
29+
30+
.titleBox {
31+
display: flex;
32+
flex-direction: column;
33+
gap: 8px;
34+
35+
& p {
36+
color: var(--grey);
37+
}
38+
}
39+
40+
.feedbackMessage {
41+
color: var(--red);
42+
height: 18px;
43+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import styles from './InputForm.module.css';
2+
3+
export interface InputFormProps {
4+
title: string;
5+
label: string;
6+
description?: string;
7+
feedbackMessage?: string;
8+
children: React.ReactNode;
9+
}
10+
11+
function InputForm(props: InputFormProps) {
12+
const { title, label, description, feedbackMessage } = props;
13+
14+
return (
15+
<div className={styles.container}>
16+
<div className={styles.titleBox}>
17+
<h3 className='tx-xl'>{title}</h3>
18+
{description && <p className='tx-md'>{description}</p>}
19+
</div>
20+
<div className={styles.inputBox}>
21+
<label className='tx-lg'>{label}</label>
22+
<div className={styles.inputContainer}>{props.children}</div>
23+
<p
24+
style={
25+
feedbackMessage
26+
? { visibility: 'visible' }
27+
: { visibility: 'hidden' }
28+
}
29+
className={`${styles.feedbackMessage}`}
30+
>
31+
{feedbackMessage}
32+
</p>
33+
</div>
34+
</div>
35+
);
36+
}
37+
38+
export default InputForm;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.input {
2+
width: 100%;
3+
border: 1px solid var(--grey);
4+
border-radius: 4px;
5+
color: var(--black);
6+
padding: 12px 8px;
7+
&::placeholder {
8+
color: var(--grey);
9+
}
10+
11+
&:focus {
12+
border: 1px solid var(--black);
13+
}
14+
&.isNotValid {
15+
border: 1px solid var(--red);
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ComponentProps } from "react";
2+
import styles from "./Input.module.css";
3+
4+
export interface InputProps extends ComponentProps<"input"> {
5+
isValid?: boolean;
6+
}
7+
8+
function Input({ isValid, ...props }: InputProps) {
9+
return (
10+
<input
11+
{...props}
12+
className={`${styles.input} ${isValid ? "" : styles.isNotValid} tx-md`}
13+
/>
14+
);
15+
}
16+
17+
export default Input;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.section {
2+
display: flex;
3+
width: 100%;
4+
justify-content: center;
5+
}
6+
.container {
7+
width: 390px;
8+
height: fit-content;
9+
display: flex;
10+
flex-direction: column;
11+
align-items: center;
12+
border: 1px solid var(--grey);
13+
border-radius: 4px;
14+
padding: 28px;
15+
gap: 40px;
16+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import CardPreview from './cardPreview/CardPreview';
2+
import CardInputForm from './cardInputForm/CardInputForm';
3+
import styles from './PaymentInputPage.module.css';
4+
import { useState } from 'react';
5+
6+
function PaymentInputPage() {
7+
const [cardNumbers, setCardNumbers] = useState<string[]>([]);
8+
const [expirationDate, setExpirationDate] = useState<string[]>([]);
9+
10+
return (
11+
<section className={styles.section}>
12+
<div className={styles.container}>
13+
<CardPreview
14+
cardNumbers={cardNumbers}
15+
expirationDate={expirationDate}
16+
/>
17+
<CardInputForm
18+
setCardNumbers={setCardNumbers}
19+
setExpirationDate={setExpirationDate}
20+
/>
21+
</div>
22+
</section>
23+
);
24+
}
25+
26+
export default PaymentInputPage;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.cardInputForm {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 26px;
5+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Dispatch, SetStateAction } from 'react';
2+
import CardCVCInput from './cardInput/CardCVCInput';
3+
import CardExpirationDateInput from './cardInput/CardExpirationDateInput';
4+
import CardNumberInput from './cardInput/CardNumberInput';
5+
import styles from './cardInputForm.module.css';
6+
7+
interface CardInputFormProps {
8+
setCardNumbers: Dispatch<SetStateAction<string[]>>;
9+
setExpirationDate: Dispatch<SetStateAction<string[]>>;
10+
}
11+
12+
function CardInputForm({
13+
setCardNumbers,
14+
setExpirationDate,
15+
}: CardInputFormProps) {
16+
return (
17+
<div className={styles.cardInputForm}>
18+
<CardNumberInput setCardNumbers={setCardNumbers} />
19+
<CardExpirationDateInput setExpirationDate={setExpirationDate} />
20+
<CardCVCInput />
21+
</div>
22+
);
23+
}
24+
export default CardInputForm;

0 commit comments

Comments
 (0)