Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions public/icons/Image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable @next/next/no-img-element */
import React from "react";

type ImageProps = React.ImgHTMLAttributes<HTMLImageElement> & {
src: string;
alt: string;
};

/**
* 테스트용 Image 컴포넌트
* - src는 필수
* - alt 기본값은 빈 문자열
*/
export default function Image({ src, alt = "", ...rest }: ImageProps) {
return <img src={src} alt={alt} {...rest} />;
}
50 changes: 50 additions & 0 deletions src/components/wrappers/SearchInput/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import IconButton from '@/components/basics/IconButton/IconButton';
import clsx from 'clsx';
import React from 'react';

import CustomImage from '../../Icons/CustomImage';

interface SearchInputProps {
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onSubmit: (e: React.FormEvent) => void;
placeholder?: string;
size?: 'md' | 'lg';
}
Comment on lines +9 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

이슈 요구사항과 props 미스매치(size, radius, variant, icon).

Linked issue(#69)에 정의된 radius, variant, icon이 누락되었습니다. 재사용성을 위해 API를 정합화하세요(현 PR에 포함하거나 후속 PR로 트래킹).

간단한 방향:

  • radius?: 'none'|'sm'|'md'|'lg'|'full' → 클래스 매핑
  • variant?: 'classic'|'surface'|'soft' → 배경/테두리 클래스 매핑
  • leftIcon?: React.ReactNode, submitIcon?: React.ReactNode → 현재 하드코딩된 SVG 대체

원하시면 최소 구현 패치(diff) 제안 드리겠습니다.

🤖 Prompt for AI Agents
In src/components/SearchInput/SearchInput.tsx around lines 9 to 15, the
component props are missing radius, variant and icon options required by issue
#69; add the following props to the interface: radius?: 'none' | 'sm' | 'md' |
'lg' | 'full'; variant?: 'classic' | 'surface' | 'soft'; leftIcon?:
React.ReactNode; submitIcon?: React.ReactNode; then update the component to map
radius to corresponding border-radius utility classes, map variant to
background/border/hover classes, and replace the hardcoded SVGs with the
supplied leftIcon and submitIcon (fall back to existing SVGs if icons are
undefined); ensure className composition includes the mapped classes and
existing size prop behavior and update any usage/tests or note a follow-up PR if
you prefer to add consumers later.


export default function SearchInput({
value,
onChange,
onSubmit,
placeholder = '검색하세요',
size = 'md',
}: SearchInputProps) {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(e);
};

const baseStyle =
'flex justify-between pl-4 h-10 text-gray-800 bg-white placeholder-gray-400 rounded-full';
const sizes = {
md: 'w-70',
lg: 'w-full',
};
const classes = clsx(baseStyle, sizes[size]);
Comment on lines +29 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

하드코딩된 스타일을 props로 제어 가능하게 리팩토링 필요.

baseStyle에서 rounded-full, bg-white, text-gray-800 등이 하드코딩되어 있어 Issue #69에서 요구한 재사용성이 떨어집니다. radiusvariant props를 추가하여 다음과 같이 매핑하는 것을 권장합니다:

  • radius: 'none' | 'sm' | 'md' | 'lg' | 'full'rounded-* 클래스
  • variant: 'classic' | 'surface' | 'soft' → 배경/테두리 클래스 조합
🤖 Prompt for AI Agents
In src/components/wrappers/SearchInput/SearchInput.tsx around lines 29 to 35,
the baseStyle currently hardcodes rounded-full, bg-white and text-gray-800 which
reduces reusability; add two new props to the component: radius:
'none'|'sm'|'md'|'lg'|'full' and variant: 'classic'|'surface'|'soft', remove the
hardcoded rounded/bg/text classes from baseStyle and instead map radius values
to the corresponding rounded-* Tailwind classes and map variant values to the
appropriate background/border/text class combinations (e.g. classic -> bg-white
text-gray-800, surface -> bg-gray-50 border border-gray-200, soft -> bg-gray-100
text-gray-800), then compute classes via clsx(baseStyleWithoutHardcodedBits,
sizes[size], radiusClass, variantClass), provide sensible defaults for radius
and variant in the props/type definitions, and update any usages/tests to pass
or rely on the new defaults.


return (
<form onSubmit={handleSubmit} className={classes}>
<CustomImage src="file.svg" alt="img" />
<input
type="text"
value={value}
onChange={onChange}
placeholder={placeholder}
className="ml-1 flex-1 bg-transparent outline-none"
/>
<IconButton icon="IC_Chat" ariaLabel="search button" />
</form>
);
}
45 changes: 45 additions & 0 deletions src/stories/SearchInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SearchInput.stories.tsx
import SearchInput from '@/components/wrappers/SearchInput/SearchInput';
import type { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';

const meta = {
title: 'Components/SearchInput',
component: SearchInput,
tags: ['autodocs'],
argTypes: {
size: {
control: { type: 'select' },
options: ['md', 'lg'],
},
},
} satisfies Meta<typeof SearchInput>;

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

function ControlledSearchInput(
args: typeof SearchInput extends React.ComponentType<infer P> ? P : never
) {
const [value, setValue] = useState('');

return (
<SearchInput
{...args}
value={value}
onChange={e => setValue(e.target.value)}
onSubmit={e => {
e.preventDefault();
console.log('Submit:', value);
}}
/>
);
}

export const Default: Story = {
render: args => <ControlledSearchInput {...args} />,
args: {
placeholder: '검색하세요',
size: 'md',
},
};