From a1673f6446b3b2ffc0d2ae5b4390380dcfa8424d Mon Sep 17 00:00:00 2001
From: kyuran kim <57716832+gxxrxn@users.noreply.github.com>
Date: Mon, 15 Apr 2024 23:28:47 +0900
Subject: [PATCH] =?UTF-8?q?[#521]=20TextArea=20=EC=BB=B4=ED=8F=AC=EB=84=8C?=
=?UTF-8?q?=ED=8A=B8=20(#522)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: textarea 컴포넌트 구현
* wip: textarea 개선 작업 중
* refactor: Textarea 고도화
- InputLength, ErrorMessage 구조 수정
- 컴포넌트 파일명 대소문자 수정을 위한 더미커밋 포함
* chore: TextArea 파일명 변경
---
src/stories/base/TextArea.stories.tsx | 54 ++++++++++++++
src/v1/base/ErrorMessage.tsx | 20 +++---
src/v1/base/InputLength.tsx | 4 +-
src/v1/base/TextArea.tsx | 100 ++++++++++++++++++++++++++
4 files changed, 168 insertions(+), 10 deletions(-)
create mode 100644 src/stories/base/TextArea.stories.tsx
create mode 100644 src/v1/base/TextArea.tsx
diff --git a/src/stories/base/TextArea.stories.tsx b/src/stories/base/TextArea.stories.tsx
new file mode 100644
index 00000000..b89d7cff
--- /dev/null
+++ b/src/stories/base/TextArea.stories.tsx
@@ -0,0 +1,54 @@
+import { Meta, StoryObj } from '@storybook/react';
+import { SubmitHandler, useForm } from 'react-hook-form';
+import TextArea from '@/v1/base/TextArea';
+import Button from '@/v1/base/Button';
+
+const meta: Meta = {
+ title: 'Base/TextArea',
+ component: TextArea,
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: { placeholder: '어떤 이야기를 모임에서 나누면 좋을까요?' },
+};
+
+type FormValue = {
+ content: string;
+};
+
+const TextAreaWithForm = () => {
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({ mode: 'all' });
+
+ const handleTextAreaSubmit: SubmitHandler = value => {
+ alert(value.content);
+ };
+
+ return (
+
+ );
+};
+
+export const UseWithForm: Story = {
+ render: () => ,
+};
diff --git a/src/v1/base/ErrorMessage.tsx b/src/v1/base/ErrorMessage.tsx
index 3a40d9c5..1bfc5490 100644
--- a/src/v1/base/ErrorMessage.tsx
+++ b/src/v1/base/ErrorMessage.tsx
@@ -1,14 +1,18 @@
-import { IconWarningCircle } from '@public/icons';
import { ReactNode } from 'react';
+import { IconWarningCircle } from '@public/icons';
-const ErrorMessage = ({ children }: { children: ReactNode }) => {
+const ErrorMessage = ({ children }: { children?: ReactNode }) => {
return (
-
+ <>
+ {children && (
+
+ )}
+ >
);
};
diff --git a/src/v1/base/InputLength.tsx b/src/v1/base/InputLength.tsx
index b66db197..d0518a22 100644
--- a/src/v1/base/InputLength.tsx
+++ b/src/v1/base/InputLength.tsx
@@ -12,10 +12,10 @@ const InputLength = ({
const textColor = isError ? 'text-warning-800 ' : 'text-main-900';
return (
-
+
{currentLength ? currentLength : 0}/
{maxLength}
-
+
);
};
diff --git a/src/v1/base/TextArea.tsx b/src/v1/base/TextArea.tsx
new file mode 100644
index 00000000..03ea3f69
--- /dev/null
+++ b/src/v1/base/TextArea.tsx
@@ -0,0 +1,100 @@
+import {
+ ChangeEventHandler,
+ ForwardedRef,
+ PropsWithChildren,
+ TextareaHTMLAttributes,
+ forwardRef,
+ useState,
+ ReactNode,
+ isValidElement,
+ Children,
+} from 'react';
+
+import ErrorMessage from './ErrorMessage';
+import InputLength from './InputLength';
+
+interface BaseTextAreaProps
+ extends TextareaHTMLAttributes {
+ error?: boolean;
+}
+interface TextAreaProps extends BaseTextAreaProps {
+ count?: boolean;
+}
+
+const _TextArea = (
+ {
+ maxLength = 500,
+ count = false,
+ error = false,
+ onChange,
+ children,
+ ...props
+ }: PropsWithChildren,
+ ref: ForwardedRef
+) => {
+ const [value, setValue] = useState('');
+
+ const handleChange: ChangeEventHandler = e => {
+ setValue(e.target.value);
+ onChange && onChange(e);
+ };
+
+ return (
+
+
+
+ {/** 에러 메세지 */}
+
{getErrorChildren(children)}
+ {/** 글자수 카운트 */}
+ {count && (
+
+ )}
+
+
+ );
+};
+
+const TextArea = Object.assign(forwardRef(_TextArea), {
+ Error: ErrorMessage,
+});
+
+const ErrorMeesageType = ().type;
+
+const getErrorChildren = (children: ReactNode) => {
+ const childrenArray = Children.toArray(children);
+
+ return childrenArray.find(
+ child => isValidElement(child) && child.type === ErrorMeesageType
+ );
+};
+
+export default TextArea;
+
+const BaseTextArea = forwardRef(
+ ({ placeholder, error, ...props }, ref) => {
+ const borderColor = error
+ ? 'border-warning-800 focus:border-warning-800'
+ : 'border-black-400 focus:border-main-900';
+
+ return (
+
+ );
+ }
+);
+
+BaseTextArea.displayName = 'BaseTextArea';