diff --git a/src/common/component/Input/Input.style.ts b/src/common/component/Input/Input.style.ts index 88710d948..fad9872b3 100644 --- a/src/common/component/Input/Input.style.ts +++ b/src/common/component/Input/Input.style.ts @@ -3,14 +3,27 @@ import { css } from '@emotion/react'; import { InputProps } from '@/common/component/Input/Input'; import { theme } from '@/common/style/theme/theme'; -export const inputContainerStyle = css({ +export const containerStyle = css({ display: 'flex', flexDirection: 'column', - gap: '1.2rem', + gap: '0.8rem', + + width: '100%', + + '& > div': { + marginTop: '0.4rem', + }, +}); + +export const inputSupportStyle = css({ + display: 'flex', + flexDirection: 'column', + + gap: '0.8rem', }); -export const inputWarpperStyle = css({ +export const warpperStyle = css({ display: 'flex', alignItems: 'center', @@ -23,22 +36,26 @@ export const inputStyle = css({ border: 'none', backgroundColor: 'transparent', fontWeight: 400, + ...theme.text.body03, outline: 'none', '::placeholder': { color: theme.colors.gray_500, + ...theme.text.body03, }, }); -export const variantStyle = (variant: Required['variant']) => { +export const variantStyle = ({ variant, isError }: { variant: Required['variant']; isError: boolean }) => { + const borderColor = isError ? `${theme.colors.red}` : `${theme.colors.gray_400}`; + const style = { - outline: { - boxShadow: ` 0px 0px 0px 1px ${theme.colors.gray_400}`, + default: { + boxShadow: `inset 0px 0px 0px 1px ${borderColor}`, borderRadius: '8px', }, underline: { - boxShadow: ` 0px 1px 0px ${theme.colors.gray_400}`, + boxShadow: `0px 1px 0px ${borderColor}`, }, colored: { borderRadius: '100px', @@ -51,9 +68,9 @@ export const variantStyle = (variant: Required['variant']) => { export const sizeStyle = (size: Required['size']) => { const style = { - small: { padding: '0.8rem 1.2rem', ...theme.text.body04 }, - medium: { padding: '1.2rem 1.2rem', ...theme.text.body02 }, - large: { padding: '1.2rem 1.6rem', ...theme.text.body02 }, + small: { padding: '0.8rem 1.2rem' }, + medium: { padding: '1.2rem 1.2rem' }, + large: { padding: '1.2rem 1.6rem' }, }; return style[size]; diff --git a/src/common/component/Input/Input.tsx b/src/common/component/Input/Input.tsx index 0604200f5..c3c7a8e7e 100644 --- a/src/common/component/Input/Input.tsx +++ b/src/common/component/Input/Input.tsx @@ -1,35 +1,56 @@ -import React, { InputHTMLAttributes } from 'react'; +import React, { ForwardedRef, InputHTMLAttributes, forwardRef } from 'react'; import { - inputContainerStyle, + containerStyle, inputStyle, - inputWarpperStyle, + inputSupportStyle, sizeStyle, variantStyle, + warpperStyle, } from '@/common/component/Input/Input.style'; import Label from '@/common/component/Label/Label'; +import SupportingText from '@/common/component/SupportingText/SupportingText'; type InputSize = 'small' | 'medium' | 'large'; -type InputVariant = 'outline' | 'underline' | 'colored'; +type InputVariant = 'default' | 'underline' | 'colored'; export interface InputProps extends Omit, 'size'> { - variant: InputVariant; + variant?: InputVariant; size?: InputSize; //default: medium(p: 1.2rem) label?: string; LeftIcon?: React.FunctionComponent>; //svg 컴포넌트 isError?: boolean; + isNotice?: boolean; + supportingText?: string; } -const Input = ({ variant, size = 'medium', label, LeftIcon, ...props }: InputProps) => { +const Input = ( + { + variant = 'default', + size = 'medium', + label, + LeftIcon, + isError = false, + isNotice = false, + supportingText, + ...props + }: InputProps, + ref: ForwardedRef +) => { return ( -
+
{label && } -
+
{LeftIcon && } - +
+ {supportingText && ( + + {supportingText} + + )}
); }; -export default Input; +export default forwardRef(Input); diff --git a/src/common/component/SupportingText/SupportingText.style.ts b/src/common/component/SupportingText/SupportingText.style.ts new file mode 100644 index 000000000..a7de252b4 --- /dev/null +++ b/src/common/component/SupportingText/SupportingText.style.ts @@ -0,0 +1,9 @@ +import { css } from '@emotion/react'; + +import { theme } from '@/common/style/theme/theme'; + +export const textStyle = (isError: boolean, isNotice: boolean) => { + const textColor = isError ? theme.colors.red : isNotice ? theme.colors.blue_900 : theme.colors.gray_400; + + return css({ color: textColor, wordBreak: 'break-word', ...theme.text.body04 }); +}; diff --git a/src/common/component/SupportingText/SupportingText.tsx b/src/common/component/SupportingText/SupportingText.tsx new file mode 100644 index 000000000..ddf0998c0 --- /dev/null +++ b/src/common/component/SupportingText/SupportingText.tsx @@ -0,0 +1,14 @@ +import { ComponentPropsWithoutRef } from 'react'; + +import { textStyle } from '@/common/component/SupportingText/SupportingText.style'; + +interface SupportingTextProps extends ComponentPropsWithoutRef<'p'> { + isError?: boolean; + isNotice?: boolean; +} + +const SupportingText = ({ isError = false, isNotice = false, children }: SupportingTextProps) => { + return

{children}

; +}; + +export default SupportingText; diff --git a/src/story/common/Input.stories.tsx b/src/story/common/Input.stories.tsx index a9dbd1ab1..6edc032a4 100644 --- a/src/story/common/Input.stories.tsx +++ b/src/story/common/Input.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { useEffect, useRef } from 'react'; + import Input from '@/common/component/Input/Input'; const meta = { @@ -11,9 +13,12 @@ const meta = { args: { type: 'text', placeholder: 'placeholder', - width: 20, label: 'label', variant: 'underline', + size: 'medium', + isError: false, + isNotice: false, + supportingText: '', }, argTypes: {}, } satisfies Meta; @@ -21,4 +26,15 @@ const meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +export const Default: Story = { + render: (args) => { + const inputRef = useRef(null); + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); + + return ; + }, +};