Skip to content

Commit

Permalink
refactor: search input 컴포넌트 추가 (#ATR-603)
Browse files Browse the repository at this point in the history
  • Loading branch information
LC-02s committed Aug 21, 2024
1 parent c558284 commit c57742f
Show file tree
Hide file tree
Showing 6 changed files with 388 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
@import 'switch';
@import 'chip';
@import 'text-input';
@import 'search-input';
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './switch'
export * from './chip'
export * from './text-input'
export * from './hidden-text'
export * from './search-input'
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* eslint-disable no-alert */
/* eslint-disable tailwindcss/no-custom-classname */
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react'
import SearchInput from './SearchInput'
import { $variable } from '../../token'

const meta: Meta<typeof SearchInput> = {
title: 'Inputs/SearchInput',
component: SearchInput,
tags: ['autodocs'],
parameters: {
componentSubtitle: 'Attraction에서 사용되는 검색 인풋 컴포넌트입니다.',
},
argTypes: {
size: {
description: '인풋의 크기를 지정합니다.',
control: 'select',
options: ['md', 'lg'],
table: {
type: { summary: ['md', 'lg'].join(' | ') },
defaultValue: { summary: 'md' },
},
},
round: {
description: '인풋의 모서리 형태를 지정합니다.',
control: 'select',
options: ['xs', 'sm', 'md', 'lg', 'full'],
table: {
type: { summary: ['xs', 'sm', 'md', 'lg', 'full'].join(' | ') },
defaultValue: { summary: 'sm' },
},
},
disabled: {
description: '인풋의 비활성화 상태를 지정합니다.',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'undefined' },
},
},
withBackground: {
description: '인풋 배경색의 기본 상태를 지정합니다.',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'undefined' },
},
},
withClearButton: {
description: '검색어를 지우는 버튼을 렌더링합니다.',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'undefined' },
},
},
placeholder: {
description: '인풋의 placeholder를 지정합니다.',
control: 'text',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
},
},
},
}

export default meta

type Story = StoryObj<typeof meta>

const style: React.CSSProperties = {
display: 'block',
padding: '30px',
width: '50%',
}

export const SearchInputDefault: Story = {
render: (props) => (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<div style={style}>
<SearchInput {...props} />
</div>
<div
className="dark"
style={{ ...style, backgroundColor: $variable.color.gray800 }}>
<SearchInput {...props} />
</div>
</div>
),
args: {
onEnter: () => alert('on enter !!!'),
onClear: () => alert('on clear !!!'),
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cva } from 'class-variance-authority'

const searchInputClassName = 'ds-search-input'
const getSearchInputModifier = (name: string) => {
return `${searchInputClassName}--${name}`
}

export const variants = {
size: {
md: '',
lg: getSearchInputModifier('size-lg'),
},
round: {
xs: getSearchInputModifier('round-xs'),
sm: '',
md: getSearchInputModifier('round-md'),
lg: getSearchInputModifier('round-lg'),
full: getSearchInputModifier('round-full'),
},
background: {
none: '',
with: getSearchInputModifier('background'),
},
}

export const searchInputVariants = cva(searchInputClassName, {
variants,
defaultVariants: { size: 'md', round: 'sm', background: 'none' },
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,75 @@
import React from 'react'
import { BackspaceOutline, MagnifyingGlassOutline } from '@attraction/icons'
import { cn } from '@attraction/utils'
import { HiddenText } from '../hidden-text'
import { searchInputVariants, variants } from './SearchInput.style'

const SearchInput = React.forwardRef(() => <input type="search" />)
type SearchInputVariant = typeof variants
interface SearchInputProps
extends Omit<
React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>,
'size'
> {
size?: keyof SearchInputVariant['size']
round?: keyof SearchInputVariant['round']
withBackground?: boolean
onEnter?: React.KeyboardEventHandler<HTMLInputElement>
withClearButton?: boolean
onClear?: React.MouseEventHandler<HTMLButtonElement>
}

const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
(
{
className,
style,
placeholder,
onKeyDown,
size,
round,
withBackground,
onEnter,
withClearButton,
onClear,
...props
},
ref,
) => (
<div
className={cn(
searchInputVariants({
size,
round,
background: withBackground ? 'with' : null,
}),
className,
)}
style={style}>
<MagnifyingGlassOutline />
<input
ref={ref}
type="search"
autoComplete="off"
placeholder={placeholder || '검색어를 입력해주세요'}
onKeyDown={(e) => {
if (e.key === 'Enter') {
onEnter?.(e)
}
onKeyDown?.(e)
}}
{...props}
/>
{withClearButton && (
<button type="button" onClick={onClear}>
<BackspaceOutline />
<HiddenText>검색어 지우기</HiddenText>
</button>
)}
</div>
),
)

export default SearchInput
Loading

0 comments on commit c57742f

Please sign in to comment.