From 73a8ac6175890468ac8d117149e063dcc685903d Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:38:24 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8[Feat]=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=B0=BD=20=EA=B5=AC=ED=98=84=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/SearchIcon.tsx | 28 ++++++++++++++++++++++ src/components/search-box/SearchBox.tsx | 31 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 public/icons/SearchIcon.tsx create mode 100644 src/components/search-box/SearchBox.tsx diff --git a/public/icons/SearchIcon.tsx b/public/icons/SearchIcon.tsx new file mode 100644 index 00000000..7a45570d --- /dev/null +++ b/public/icons/SearchIcon.tsx @@ -0,0 +1,28 @@ +import { SVGProps } from 'react'; + +interface SearchIconProps extends SVGProps { + width?: number; + height?: number; +} + +function SearchIcon({ ...props }: SearchIconProps) { + return ( + + + + ); +} + +export default SearchIcon; diff --git a/src/components/search-box/SearchBox.tsx b/src/components/search-box/SearchBox.tsx new file mode 100644 index 00000000..93a3b78e --- /dev/null +++ b/src/components/search-box/SearchBox.tsx @@ -0,0 +1,31 @@ +import React, { ChangeEvent } from 'react'; +import SearchIcon from '../../../public/icons/SearchIcon'; + +interface SearchBoxProps { + value: string; + onChange: (e: ChangeEvent) => void; + placeholder?: string; +} + +const SearchBox = ({ + value, + onChange, + placeholder = '검색어를 입력해주세요', +}: SearchBoxProps) => { + return ( +
+
+ +
+ +
+ ); +}; + +export default SearchBox; From 87be77fcee79647c1f17ae06d1993696cf7e9565 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:39:11 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=85[Test]=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=B0=BD=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1,=20storybook=20=EC=83=9D=EC=84=B1=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search-box/SearchBox.stories.tsx | 22 ++++++++++ src/components/search-box/SearchBox.test.tsx | 44 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/components/search-box/SearchBox.stories.tsx create mode 100644 src/components/search-box/SearchBox.test.tsx diff --git a/src/components/search-box/SearchBox.stories.tsx b/src/components/search-box/SearchBox.stories.tsx new file mode 100644 index 00000000..d4a9446c --- /dev/null +++ b/src/components/search-box/SearchBox.stories.tsx @@ -0,0 +1,22 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import SearchBox from './SearchBox'; + +const meta = { + title: 'Components/SearchBox', + component: SearchBox, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + value: '', + onChange: (e) => console.log('Search value:', e.target.value), + placeholder: '검색어를 입력해주세요', + }, +}; diff --git a/src/components/search-box/SearchBox.test.tsx b/src/components/search-box/SearchBox.test.tsx new file mode 100644 index 00000000..5e990f2b --- /dev/null +++ b/src/components/search-box/SearchBox.test.tsx @@ -0,0 +1,44 @@ +import '@testing-library/jest-dom'; +import { render, screen, fireEvent } from '@testing-library/react'; +import SearchBox from './SearchBox'; + +describe('SearchBox', () => { + const mockOnChange = jest.fn(); + const defaultProps = { + value: '', + onChange: mockOnChange, + }; + + beforeEach(() => { + mockOnChange.mockClear(); + }); + + it('기본 placeholder 텍스트가 올바르게 렌더링되는지 확인', () => { + render(); + expect( + screen.getByPlaceholderText('검색어를 입력해주세요'), + ).toBeInTheDocument(); + }); + + it('사용자 정의 placeholder가 올바르게 렌더링되는지 확인', () => { + const customPlaceholder = 'placeholder 테스트'; + render(); + expect(screen.getByPlaceholderText(customPlaceholder)).toBeInTheDocument(); + }); + + it('입력값이 변경될 때 onChange 핸들러가 호출되는지 확인', () => { + render(); + const input = screen.getByRole('textbox'); + + fireEvent.change(input, { target: { value: '테스트 검색어' } }); + expect(mockOnChange).toHaveBeenCalledTimes(1); + }); + + it('초기 value prop이 올바르게 설정되는지 확인', () => { + const initialValue = '초기 검색어'; + render(); + + const input = screen.getByRole('textbox') as HTMLInputElement; + expect(input.value).toBe(initialValue); + }); +}); From 1eab06e6ee81e17b59a95c47f121053b1d1630a6 Mon Sep 17 00:00:00 2001 From: Sungu Kim <108677235+haegu97@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:48:39 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20width,=20hei?= =?UTF-8?q?ght=20prop=20=EC=A0=9C=EA=B1=B0=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/SearchIcon.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/icons/SearchIcon.tsx b/public/icons/SearchIcon.tsx index 7a45570d..e2612080 100644 --- a/public/icons/SearchIcon.tsx +++ b/public/icons/SearchIcon.tsx @@ -1,11 +1,6 @@ import { SVGProps } from 'react'; -interface SearchIconProps extends SVGProps { - width?: number; - height?: number; -} - -function SearchIcon({ ...props }: SearchIconProps) { +function SearchIcon({ ...props }: SVGProps) { return ( Date: Wed, 11 Dec 2024 10:43:06 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F[Refactor]=20=EB=8F=8B?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EC=95=84=EC=9D=B4=EC=BD=98=20width,=20hei?= =?UTF-8?q?ght=20prop=EC=9C=BC=EB=A1=9C=20=EC=B6=94=EA=B0=80=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/SearchIcon.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/public/icons/SearchIcon.tsx b/public/icons/SearchIcon.tsx index e2612080..81a0d250 100644 --- a/public/icons/SearchIcon.tsx +++ b/public/icons/SearchIcon.tsx @@ -1,10 +1,15 @@ import { SVGProps } from 'react'; -function SearchIcon({ ...props }: SVGProps) { +interface SearchIconProps extends SVGProps { + width?: number; + height?: number; +} + +function SearchIcon({ width = 12, height = 12, ...props }: SearchIconProps) { return (