Skip to content

Commit

Permalink
[FE] refactor: Textarea를 사용하는 공통 장문형 답변 입력 컴포넌트 분리 (#252)
Browse files Browse the repository at this point in the history
* feat: 공통 textarea 컴포넌트 작성

* refactor: longReviewItem의 로직을 커스텀 훅으로 분리

* refactor: longReviewItem으로 이름 변경 및 컴포넌트 구현

* chore: 기존의 ReviewItem 제거 및 리뷰 작성 페이지에 LongReviewItem 적용
  • Loading branch information
chysis authored Aug 8, 2024
1 parent e62a91a commit 556ca56
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 100 deletions.
38 changes: 38 additions & 0 deletions frontend/src/components/common/LongReviewItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { TextareaHTMLAttributes } from 'react';

import useLongReviewItem from '@/hooks/useLongReviewItem';

import * as S from './styles';

interface LongReviewItemProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
minLength: number;
maxLength: number;
initialValue?: string;
}

const LongReviewItem = ({ minLength, maxLength, initialValue = '', style, ...rest }: LongReviewItemProps) => {
const { value, textLength, isError, errorMessage, handleChange, handleBlur } = useLongReviewItem({
minLength,
maxLength,
initialValue,
});

return (
<S.TextareaContainer>
<S.Textarea
value={value}
$isError={isError}
$style={style}
onChange={handleChange}
onBlur={handleBlur}
{...rest}
/>
<S.TextareaInfoContainer>
<S.ReviewTextareaError>{isError && errorMessage}</S.ReviewTextareaError>
<S.ReviewTextLength>{textLength}</S.ReviewTextLength>
</S.TextareaInfoContainer>
</S.TextareaContainer>
);
};

export default LongReviewItem;
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import styled from '@emotion/styled';

export const ReviewItem = styled.article`
export const TextareaContainer = styled.div`
display: flex;
flex-direction: column;
gap: 0.8rem;
& > * {
font-weight: 600;
}
`;

export const ReviewQuestion = styled.div`
&::after {
content: ' (20자 이상)';
}
`;
interface TextareaProps {
$isError: boolean;
$style?: React.CSSProperties;
}

export const ReviewTextarea = styled.textarea<{ $isError: boolean }>`
export const Textarea = styled.textarea<TextareaProps>`
resize: none;
overflow-y: auto;
width: 100%;
max-width: 100%;
height: 15rem;
padding: 1.6rem;
Expand All @@ -34,19 +27,23 @@ export const ReviewTextarea = styled.textarea<{ $isError: boolean }>`
&::placeholder {
font-weight: ${({ theme }) => theme.fontWeight.medium};
}
${({ $style }) => $style && { ...$style }};
`;

export const Container = styled.div`
export const TextareaInfoContainer = styled.div`
display: flex;
justify-content: space-between;
margin-top: 0.8rem;
`;

export const ReviewTextareaError = styled.p`
color: ${({ theme }) => theme.colors.red};
`;

export const ReviewTextLength = styled.p`
display: flex;
justify-content: flex-end;
margin: 0;
`;

export const ReviewTextareaError = styled.p`
color: ${({ theme }) => theme.colors.red};
font-weight: ${({ theme }) => theme.fontWeight.semibold};
`;
11 changes: 0 additions & 11 deletions frontend/src/components/common/Textarea/index.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions frontend/src/components/common/Textarea/styles.ts

This file was deleted.

41 changes: 41 additions & 0 deletions frontend/src/hooks/useLongReviewItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useState } from 'react';

interface UseLongReviewItemProps {
minLength: number;
maxLength: number;
initialValue?: string;
}

const useLongReviewItem = ({ minLength, maxLength, initialValue }: UseLongReviewItemProps) => {
const [value, setValue] = useState(initialValue);
const [isError, setIsError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');

const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
if (value.length <= maxLength) setValue(value);
if (value.length >= minLength) setIsError(false);
};

const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
if (value.length < minLength) {
setIsError(true);
setErrorMessage(`최소 ${minLength}자 이상 작성해 주세요.`);
} else {
setIsError(false);
setErrorMessage('');
}
};

return {
value,
textLength: `${(value && value.length) || 0} / ${maxLength}`,
isError,
errorMessage,
handleChange,
handleBlur,
};
};

export default useLongReviewItem;
58 changes: 0 additions & 58 deletions frontend/src/pages/ReviewWriting/components/ReviewItem/index.tsx

This file was deleted.

13 changes: 3 additions & 10 deletions frontend/src/pages/ReviewWriting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { postReviewApi } from '@/apis/review';
import GithubLogoIcon from '@/assets/githubLogo.svg';
import { ConfirmModal, ErrorAlertModal } from '@/components';
import Button from '@/components/common/Button';
import LongReviewItem from '@/components/common/LongReviewItem';
import useConfirmModal from '@/hooks/useConfirmModal';
import useErrorModal from '@/hooks/useErrorModal';
import useReviewForm from '@/hooks/useReviewForm';
Expand All @@ -15,7 +16,6 @@ import LoadingPage from '../LoadingPage';

import KeywordButton from './components/KeywordButton';
// import RevieweeComment from './components/RevieweeComment';
import ReviewItem from './components/ReviewItem';
import * as S from './styles';

const SUBMIT_CONFIRM_MESSAGE = `리뷰를 제출할까요?
Expand Down Expand Up @@ -78,15 +78,8 @@ const ReviewWritingPage = () => {
</S.ReviewFormHeader>
<S.ReviewFormMain>
<S.ReviewContainer>
{dataToWrite.questions.map((question, index) => {
return (
<ReviewItem
question={`${index + 1}. ${question.content}`}
key={question.id}
answerValue={answers.find((answer) => answer.questionId === question.id)?.answer || ''}
handleWrite={(value) => handleAnswerChange(question.id, value)}
/>
);
{dataToWrite.questions.map((_, index) => {
return <LongReviewItem key={index} minLength={20} maxLength={1000} />;
})}
</S.ReviewContainer>
<S.KeywordContainer>
Expand Down

0 comments on commit 556ca56

Please sign in to comment.