Skip to content

Commit a4cfd34

Browse files
feat(input): Input 컴포넌트 구현 (#23)
* feat(typography): add test scenario * feat(typography): implement test & component * feat(typography): add typography story * fix: lower level - noExplicitAny * fix(typography): exclude dist from typecheck * chore: create Input component folder #7 * chore: add compose-refs util function from radix-ui #7 * feat(input): add utility functions for font size, weight, and line height #7 * test(input): setup and install jest and rtl related packages * chore(input): define constant object and types based on design system * feat(input): write test scenario * feat(input): implement input component and add test code * feat(input): add storybook case * chore: move constants, utils folder into src/ * feat(config): update biome.json to organize imports and ignore dist files * fix(input): correct test case syntax and update className style reference * fix(input): exclude dist folder in tsconfig * fix(input): remove redundant import statement in vitest.setup.ts * fix(input): update test code based on feedback * fix(input): enhance storybook input examples with additional props and ref handling --------- Co-authored-by: noahluftyang <[email protected]>
1 parent 2ae6bd3 commit a4cfd34

18 files changed

+632
-0
lines changed

biome.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"enabled": true,
55
"indentStyle": "space"
66
},
7+
78
"organizeImports": {
89
"enabled": true
910
},
@@ -20,5 +21,8 @@
2021
"formatter": {
2122
"quoteStyle": "single"
2223
}
24+
},
25+
"files": {
26+
"ignore": ["dist/*.*"]
2327
}
2428
}

packages/Input/.storybook/main.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { StorybookConfig } from '@storybook/react-vite';
2+
3+
export default {
4+
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
5+
addons: [
6+
'@storybook/addon-onboarding',
7+
'@storybook/addon-links',
8+
'@storybook/addon-essentials',
9+
'@chromatic-com/storybook',
10+
'@storybook/addon-interactions',
11+
],
12+
framework: {
13+
name: '@storybook/react-vite',
14+
options: {},
15+
},
16+
} satisfies StorybookConfig;

packages/Input/.storybook/preview.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { Preview } from '@storybook/react';
2+
3+
export default {
4+
tags: ['autodocs'],
5+
} satisfies Preview;

packages/Input/package.json

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "@sipe-team/component",
3+
"description": "component for Sipe Design System",
4+
"version": "0.0.0",
5+
"license": "MIT",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/sipe-team/3-1_sds"
9+
},
10+
"type": "module",
11+
"exports": "./src/index.ts",
12+
"files": [
13+
"dist"
14+
],
15+
"scripts": {
16+
"build": "tsup",
17+
"build:storybook": "storybook build",
18+
"dev:storybook": "storybook dev -p 6006",
19+
"lint": "biome lint .",
20+
"test": "vitest",
21+
"typecheck": "tsc",
22+
"prepack": "pnpm run build"
23+
},
24+
"dependencies": {
25+
"@radix-ui/react-slot": "^1.1.0",
26+
"classnames": "^2.5.1"
27+
},
28+
"devDependencies": {
29+
"@biomejs/biome": "catalog:",
30+
"@storybook/addon-essentials": "catalog:",
31+
"@storybook/addon-interactions": "catalog:",
32+
"@storybook/addon-links": "catalog:",
33+
"@storybook/blocks": "catalog:",
34+
"@storybook/react": "catalog:",
35+
"@storybook/react-vite": "catalog:",
36+
"@storybook/test": "catalog:",
37+
"@testing-library/jest-dom": "^6.6.3",
38+
"@testing-library/react": "^16.0.1",
39+
"@testing-library/user-event": "^14.5.2",
40+
"@types/react": "^18.3.12",
41+
"happy-dom": "catalog:",
42+
"react": "^18.3.1",
43+
"storybook": "catalog:",
44+
"tsup": "catalog:",
45+
"typescript": "catalog:",
46+
"vitest": "catalog:"
47+
},
48+
"peerDependencies": {
49+
"react": ">= 18"
50+
},
51+
"publishConfig": {
52+
"access": "public",
53+
"exports": {
54+
".": {
55+
"import": {
56+
"types": "./dist/index.d.ts",
57+
"default": "./dist/index.js"
58+
},
59+
"require": {
60+
"types": "./dist/index.d.cts",
61+
"default": "./dist/index.cjs"
62+
}
63+
}
64+
}
65+
},
66+
"sideEffects": false
67+
}

packages/Input/src/Input.module.css

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
.input-wrapper {
2+
display: flex;
3+
flex: 1;
4+
align-items: center;
5+
font-style: normal;
6+
text-align: start;
7+
8+
padding: var(--input-padding);
9+
border-radius: var(--input-border-radius);
10+
outline: 1px solid var(--input-ring-color);
11+
12+
@supports selector(:has(*)) {
13+
&:where(:has(.input:focus)) {
14+
outline: 2px solid var(--input-ring-color);
15+
outline-offset: -1px;
16+
}
17+
18+
&:where(:has(.input:disabled)) {
19+
background-color: var(--input-disabled-color);
20+
}
21+
}
22+
@supports not selector(:has(*)) {
23+
&:where(:focus-within) {
24+
outline: 2px solid var(--input-ring-color);
25+
outline-offset: -1px;
26+
}
27+
}
28+
}
29+
30+
.input {
31+
width: 100%;
32+
display: flex;
33+
align-items: center;
34+
text-align: inherit;
35+
36+
outline: 1px solid transparent;
37+
border: none;
38+
39+
font-size: var(--font-size);
40+
font-weight: var(--font-weight);
41+
42+
/* 기본 취소 버튼 제거 */
43+
&::-webkit-search-cancel-button {
44+
appearance: none;
45+
}
46+
47+
@supports selector(:has(*)) {
48+
&:where(:autofill, [data-com-onepassword-filled]) {
49+
background-clip: text;
50+
-webkit-text-fill-color: var(--gray-12);
51+
}
52+
}
53+
54+
@supports selector(:has(*)) {
55+
&:where(:disabled) {
56+
background-color: transparent;
57+
}
58+
}
59+
}
60+
61+
62+
63+
.input-action {
64+
all: unset;
65+
66+
width: var(--action-size);
67+
height: var(--action-size);
68+
69+
display: flex;
70+
justify-content: center;
71+
align-items: center;
72+
border-radius: var(--input-border-radius);
73+
74+
@supports selector(:has(*)) {
75+
76+
&:focus {
77+
outline: 2px solid var(--input-ring-color);
78+
outline-offset: 3px;
79+
}
80+
}
81+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
declare const styles: {
3+
readonly "input-wrapper": string;
4+
readonly "input": string;
5+
readonly "input-action": string;
6+
};
7+
export default styles;

packages/Input/src/Input.stories.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { StoryObj } from '@storybook/react';
2+
import { useRef, useState } from 'react';
3+
import { Action, Input } from './Input';
4+
5+
const meta = {
6+
component: Input,
7+
parameters: {
8+
layout: 'centered',
9+
},
10+
args: {
11+
disabled: false,
12+
fontSize: 16,
13+
fontWeight: 'regular',
14+
placeholder: 'placeholder',
15+
type: 'text',
16+
},
17+
argTypes: {
18+
type: {
19+
options: ['email', 'password', 'search', 'tel', 'text', 'url'],
20+
control: { type: 'radio' },
21+
},
22+
},
23+
};
24+
export default meta;
25+
26+
type Story = StoryObj<typeof meta>;
27+
28+
export const Default: Story = {
29+
args: {
30+
type: 'password',
31+
fontWeight: 'regular',
32+
fontSize: 20,
33+
},
34+
35+
render: (args) => {
36+
const [value, setValue] = useState('value');
37+
return (
38+
<Input
39+
value={value}
40+
onChange={(e) => setValue(e.target.value)}
41+
{...args}
42+
/>
43+
);
44+
},
45+
};
46+
47+
export const disabled: Story = {
48+
render: (args) => {
49+
const [value, setValue] = useState(args.value);
50+
return (
51+
<Input
52+
value={value}
53+
onChange={(e) => setValue(e.target.value)}
54+
{...args}
55+
/>
56+
);
57+
},
58+
args: {
59+
disabled: true,
60+
value: 'test',
61+
},
62+
};
63+
64+
export const WithActionButton: Story = {
65+
render: (args) => {
66+
const ref = useRef<HTMLInputElement>(null);
67+
const handleReset = () => {
68+
if (ref.current) {
69+
ref.current.value = '';
70+
}
71+
};
72+
return (
73+
<Input {...args} ref={ref}>
74+
<Action onClick={handleReset}>
75+
<svg
76+
style={{ width: '24px', height: '24px', color: 'currentColor' }}
77+
aria-hidden="true"
78+
xmlns="http://www.w3.org/2000/svg"
79+
width="24"
80+
height="24"
81+
fill="currentColor"
82+
viewBox="0 0 24 24"
83+
>
84+
<path
85+
fillRule="evenodd"
86+
d="M2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Zm7.707-3.707a1 1 0 0 0-1.414 1.414L10.586 12l-2.293 2.293a1 1 0 1 0 1.414 1.414L12 13.414l2.293 2.293a1 1 0 0 0 1.414-1.414L13.414 12l2.293-2.293a1 1 0 0 0-1.414-1.414L12 10.586 9.707 8.293Z"
87+
clipRule="evenodd"
88+
/>
89+
</svg>
90+
</Action>
91+
</Input>
92+
);
93+
},
94+
};

0 commit comments

Comments
 (0)