Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
27b3cb5
feat: TextField 컴포넌트 기본 로직 구현
YeBeenChoi Jan 13, 2026
326fa12
feat: TextField props 타입 정의
YeBeenChoi Jan 13, 2026
649827c
feat: TextField 스타일 variants 구성
YeBeenChoi Jan 13, 2026
66dd3e7
docs: TextField 스토리북 스토리 추가
YeBeenChoi Jan 13, 2026
d97ecb9
test: TextField 기본 동작 테스트 추가
YeBeenChoi Jan 13, 2026
d5ad675
chore: TextField export 추가
YeBeenChoi Jan 13, 2026
69399ff
feat: TextField 컴포넌트 예제 추가
YeBeenChoi Jan 13, 2026
7cc1637
feat: price 스타일 추가
YeBeenChoi Jan 14, 2026
50098ef
feat: TextField 컴포넌트 로직 수정
YeBeenChoi Jan 14, 2026
3e30a16
feat: Disabled인 경우 hover / focus 차단
YeBeenChoi Jan 14, 2026
9bcc70c
chore: Playground 예제 수정
YeBeenChoi Jan 14, 2026
ea5fcc5
chore: Playground 예제 픽셀값 수정
YeBeenChoi Jan 14, 2026
afe210f
fix: Playground 충돌 해결
YeBeenChoi Jan 14, 2026
b2661ab
docs: 불필요한 주석 제거
YeBeenChoi Jan 14, 2026
f470e00
docs: 스토리북 수정
YeBeenChoi Jan 14, 2026
a1bdd06
chore: 누락된 타입 임포트 추가
YeBeenChoi Jan 14, 2026
357654d
refactor: 중복 정의된 스타일 값 수정
YeBeenChoi Jan 14, 2026
6094651
refactor: 스타일 구조 수정
YeBeenChoi Jan 14, 2026
cdb0ae5
refactor: TextField 값 정규화/상수화 및 onChange 타입 정리
YeBeenChoi Jan 14, 2026
5fc7f49
refactor: TextField onChange 타입 통일
YeBeenChoi Jan 14, 2026
d8f5104
feat: CharField 래퍼 추가
YeBeenChoi Jan 14, 2026
21e6728
feat: TextAreaField 래퍼 추가
YeBeenChoi Jan 14, 2026
dd3d6cf
feat: PriceField 래퍼 추가
YeBeenChoi Jan 14, 2026
e9b4afb
refactor: TextField 타입 분리/정리
YeBeenChoi Jan 14, 2026
86b3829
refactor: TextField export 정리
YeBeenChoi Jan 14, 2026
e2d3275
refactor: TextField 내부 로직 정리
YeBeenChoi Jan 14, 2026
023e7cf
refactor: 하드코딩된 상수 값 분리
YeBeenChoi Jan 14, 2026
c90dd30
refactor: TextField 상수 분리 및 포맷 정리
YeBeenChoi Jan 14, 2026
84cbfac
fix: Price TextField 숫자 기준 maxLength 적용하도록 수정
YeBeenChoi Jan 15, 2026
f8bcd4a
refactor: TextField maxLength 계산 로직 함수로 분리
YeBeenChoi Jan 15, 2026
4b088d4
Merge branch 'main' into 14-feat/TextField-컴포넌트-제작
KyeongJooni Jan 16, 2026
b112e0b
Merge branch 'main' into 14-feat/TextField-컴포넌트-제작
KyeongJooni Jan 16, 2026
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
119 changes: 119 additions & 0 deletions src/pages/playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Card } from '@shared/ui/Card/Card';
import { Checkbox } from '@shared/ui/Checkbox/Checkbox';
import { Profile } from '@shared/ui/Profile/Profile';
import { RadioButton } from '@shared/ui/RadioButton/RadioButton';
import { TextField } from '@shared/ui/TextField/TextField';

export default function Playground() {
return (
Expand Down Expand Up @@ -98,6 +99,124 @@ export default function Playground() {
dateText="1일 전"
/>
</section>

{/* TextField 예시 */}
<section className="flex flex-col gap-4">
<h2 className="typo-body-1">TextField</h2>

<div className="flex flex-wrap gap-10">
{/* Char Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">Char Field</h3>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
disabled
helperText="Disabled"
/>
</div>
</div>

{/* TextArea Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">TextArea Field</h3>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
disabled
helperText="Disabled"
/>
</div>
</div>

{/* Price Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">Price Field</h3>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
disabled
helperText="Disabled"
/>
</div>
</div>
</div>
</section>
</div>
);
}
180 changes: 180 additions & 0 deletions src/shared/ui/TextField/TextField.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { TextField } from '@shared/ui/TextField/TextField';
import type { Meta, StoryObj } from '@storybook/react-vite';

const meta = {
title: 'Shared/UI/TextField',
component: TextField,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
type: {
control: 'select',
options: ['text', 'char', 'textarea', 'price'],
description: 'TextField type',
},
label: { control: 'text' },
placeholder: { control: 'text' },
disabled: { control: 'boolean' },
error: { control: 'boolean' },
helperText: { control: 'text' },
showCharacterCount: { control: 'boolean' },
value: { control: 'text' },
defaultValue: { control: 'text' },
},
} satisfies Meta<typeof TextField>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Showcase: Story = {
name: 'Playground Showcase (9)',
render: () => (
<div className="flex flex-wrap gap-10">
{/* Char Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">Char Field</h3>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="char"
label="Char Field"
placeholder="Enter your username"
showCharacterCount
disabled
helperText="Disabled"
/>
</div>
</div>

{/* TextArea Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">TextArea Field</h3>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="textarea"
label="TextArea Field"
placeholder="Enter a description"
showCharacterCount
disabled
helperText="Disabled"
/>
</div>
</div>

{/* Price Field */}
<div className="flex flex-col gap-4">
<h3 className="typo-caption-1 font-semibold">Price Field</h3>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
helperText="Default"
/>
</div>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
error
helperText="Error"
/>
</div>

<div className="w-127.25">
<TextField
type="price"
label="Price Field"
placeholder="Enter price"
defaultValue="10000"
disabled
helperText="Disabled"
/>
</div>
</div>
</div>
),
};

/**
* (옵션) 단일 스토리들도 기본적인 확인용으로 남겨두고 싶으면 최소만 유지
*/
export const CharDefault: Story = {
args: {
type: 'char',
label: 'Char Field',
placeholder: 'Enter your username',
showCharacterCount: true,
helperText: 'Default',
},
};

export const TextareaDefault: Story = {
args: {
type: 'textarea',
label: 'TextArea Field',
placeholder: 'Enter a description',
showCharacterCount: true,
helperText: 'Default',
},
};

export const PriceDefault: Story = {
args: {
type: 'price',
label: 'Price Field',
placeholder: 'Enter price',
defaultValue: '10000',
helperText: 'Default',
},
};
Loading