;
+
+export const Default: Story = {
+ args: {
+ placeholder: 'Enter text...',
+ },
+};
+
+export const Widths: Story = {
+ decorators: [
+ (Story) => (
+
+
+
+
Full width (default)
+
+
+
+
+
+
+
+ ),
+ ],
+ args: {
+ placeholder: 'Full width input...',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ placeholder: 'Disabled input',
+ disabled: true,
+ },
+};
+
+export const WithValue_Controlled: Story = {
+ render: () => {
+ const [value, setValue] = React.useState('Sample text');
+
+ return (
+
+ setValue(e.target.value)}
+ placeholder="Type to change..."
+ />
+
+
+ );
+ },
+};
+
+export const WithType: Story = {
+ args: {
+ type: 'password',
+ placeholder: 'Enter password',
+ },
+};
+
+export const WithError: Story = {
+ args: {
+ className: 'border-error',
+ placeholder: 'Error state',
+ },
+};
diff --git a/packages/ui/src/components/Input/input.test.tsx b/packages/ui/src/components/Input/input.test.tsx
new file mode 100644
index 0000000..3426108
--- /dev/null
+++ b/packages/ui/src/components/Input/input.test.tsx
@@ -0,0 +1,39 @@
+// Input.test.tsx
+import { render, screen, fireEvent } from '@testing-library/react';
+import React from 'react';
+
+import { Input } from './input';
+
+describe('Input Component', () => {
+ it('renders correctly', () => {
+ render();
+ const input = screen.getByPlaceholderText('Test input');
+ expect(input).toBeInTheDocument();
+ });
+
+ it('accepts value changes', () => {
+ render();
+ const input = screen.getByRole('textbox');
+
+ fireEvent.change(input, { target: { value: 'Test value' } });
+ expect(input).toHaveValue('Test value');
+ });
+
+ it('forwards ref correctly', () => {
+ const ref = React.createRef();
+ render();
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
+ });
+
+ it('handles disabled state', () => {
+ render();
+ const input = screen.getByRole('textbox');
+ expect(input).toBeDisabled();
+ });
+
+ it('handles different input types', () => {
+ render();
+ const input = screen.getByRole('textbox');
+ expect(input).toHaveAttribute('type', 'email');
+ });
+});
diff --git a/packages/ui/src/components/Input/input.tsx b/packages/ui/src/components/Input/input.tsx
new file mode 100644
index 0000000..1969bb3
--- /dev/null
+++ b/packages/ui/src/components/Input/input.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+
+import { cn } from '@/lib/utils';
+
+const Input = React.forwardRef>(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = 'Input';
+
+export { Input };
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index 7d2b0dc..6ccf0fa 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -1 +1,2 @@
-export { Button as default } from './Button/button';
+export { Button } from './Button/button';
+export { Input } from './Input/input';
diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js
index 54449c5..620886f 100644
--- a/packages/ui/tailwind.config.js
+++ b/packages/ui/tailwind.config.js
@@ -40,6 +40,7 @@ export default {
DEFAULT: 'hsl(var(--brand-600))',
foreground: 'hsl(var(--brand-600-foreground))', // error-foreground가 추가되었을 경우
},
+ error: 'hsl(var(--error))',
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts
index 3bd6c73..47947fb 100644
--- a/packages/ui/vite.config.ts
+++ b/packages/ui/vite.config.ts
@@ -20,5 +20,10 @@ export default defineConfig({
rollupOptions: {
external: ['react', 'react-dom'],
},
+
+ sourcemap: true,
+ reportCompressedSize: false,
+ chunkSizeWarningLimit: 1000,
+ manifest: true,
},
});