Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Textarea 컴포넌트 구현 #150

Merged
merged 18 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
43ef88c
Merge branch 'develop' into feat/#146-textarea
seocylucky Aug 10, 2024
afb1018
feat: Textarea 컴포넌트 구현 (#146)
seocylucky Aug 21, 2024
39342f1
Revert "feat: Textarea 컴포넌트 구현 (#146)"
seocylucky Aug 21, 2024
1896852
Merge branch 'develop' into feat/#146-textarea
seocylucky Aug 21, 2024
9346d0c
feat: Textarea 컴포넌트 구현 (#146)
seocylucky Aug 21, 2024
360a374
fix: 사라져버린 Pagination, useRadioGroup export 살리기
seocylucky Aug 21, 2024
3a106d8
refactor: currentLength 코드 삭제 -> value.length로 처리
seocylucky Aug 21, 2024
b4ade14
refactor: Textarea 타입 중복 정의 제거
seocylucky Aug 21, 2024
68640e1
refactor: StyledContainer에서 사용 안하는 StyledTextareaProps 삭제
seocylucky Aug 21, 2024
31b354b
refactor: e.target.value -> 선언한 newValue로 변경
seocylucky Aug 21, 2024
edf0eca
style: textStatusNegative -> lineStatusPositive
seocylucky Aug 22, 2024
e16a681
style: border 관련 스타일 line 스타일로 적용
seocylucky Aug 23, 2024
d3cc9ff
style: error 상황일 때와 focus 상황일 때 border 코드 수정
seocylucky Aug 23, 2024
ae0e28e
feat: value prop 삭제, width height 타입 변경
seocylucky Aug 28, 2024
c92abeb
feat: 스크롤바 커스텀 작업
seocylucky Aug 28, 2024
f878050
docs: onValueChange prop 관련 문서 작성
seocylucky Aug 28, 2024
8c9b2ad
feat: helperText 로직 수정
seocylucky Aug 28, 2024
c0a8521
style: 스크롤바와 텍스트 여백 padding: 6px 추가
seocylucky Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions src/components/Textarea/Textarea.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { Canvas, Meta, Controls } from '@storybook/blocks';
import * as TextareaStories from './Textarea.stories';
import { Textarea } from './Textarea';
import React from 'react';

<Meta of={TextareaStories} />

# Textarea

사용자가 텍스트를 입력하는 필드로, 여러 줄의 텍스트 입력이 필요한 경우 사용됩니다. 다양한 상태와 속성을 지원하여 사용자 경험을 향상시킬 수 있습니다.
seocylucky marked this conversation as resolved.
Show resolved Hide resolved

<Canvas of={TextareaStories.Default} />
<Controls />

<br />
<br />

## 사용법

Textarea의 기본 사용법입니다.

필수 프로퍼티인 `width`와 `height`를 사용하여 Textarea의 크기를 설정해주세요.

```tsx
import { Textarea } from '@yourssu/design-system-react';
```

```tsx
<Textarea width="343px" height="187px" />
```

이외의 프로퍼티들은 하단의 예시에서 확인할 수 있습니다.

<br />
<br />

## 예시

### placeholder

Textarea에 표시되는 짧은 안내 문구로, 사용자가 입력해야 할 내용의 예시를 보여줍니다.

```tsx
<Textarea width="343px" height="187px" placeholder="Enter text here..." />
```

<Canvas of={TextareaStories.Placeholder} withSource="none" />

<br />
<br />

### helperText

Textarea에 사용자가 올바르게 입력할 수 있도록 돕는 텍스트입니다.

```tsx
<Textarea
width="343px"
height="187px"
helperText="helperText입니다."
placeholder="Enter text here..."
/>
```

<Canvas of={TextareaStories.HelperText} withSource="none" />

<br />
<br />

### maxLength

Textarea에 사용자가 입력할 수 있는 최대 글자 수를 제한합니다.

```tsx
<Textarea width="343px" height="187px" maxLength={50} placeholder="Max 50 characters" />
```

<Canvas of={TextareaStories.MaxLength} withSource="none" />

<br />
<br />

### disabled

Textarea에 어떠한 입력을 할 수 없도록 막습니다.

```tsx
<Textarea
width="343px"
height="187px"
disabled={true}
helperText="Text Inputting"
placeholder="This field is disabled"
/>
```

<Canvas of={TextareaStories.Disabled} withSource="none" />

<br />
<br />

### error

Textarea에 잘못된 값이 입력되었을 때 사용자에게 오류 상태를 표시하는 데 사용됩니다.

```tsx
<Textarea
width="343px"
height="187px"
error={true}
helperText="Text Inputting"
placeholder="There is an error"
/>
```

<Canvas of={TextareaStories.Error} withSource="none" />

<br />
<br />

### onValueChange

Textarea에 텍스트를 입력하거나 변경할 때 호출되는 콜백 함수입니다. 이 함수는 현재 입력된 텍스트 값을 매개변수로 전달하여, 외부에서 상태를 관리하거나 추가적인 로직을 실행할 수 있게 합니다.

```tsx
const [text, setText] = useState('');

const handleValueChange = (newValue: string) => {
setText(newValue);
};

return (
<Textarea
{...args}
value={text}
onValueChange={handleValueChange}
maxLength={args.maxLength}
placeholder={args.placeholder}
/>
);
```

<Canvas of={TextareaStories.OnValueChange} withSource="none" />

<br />
<br />
146 changes: 146 additions & 0 deletions src/components/Textarea/Textarea.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { useState } from 'react';

import { Meta, StoryObj } from '@storybook/react';

import { Textarea } from './Textarea';
import { TextareaProps } from './Textarea.type';

const meta: Meta<typeof Textarea> = {
title: 'Components/Textarea',
component: Textarea,
argTypes: {
width: {
control: 'text',
description: 'Textarea의 가로 길이',
},
height: {
control: 'text',
description: 'Textarea의 세로 길이',
},
placeholder: {
control: 'text',
description: 'Textarea의 placeholder 텍스트',
},
maxLength: {
control: 'number',
description: 'Textarea의 최대 입력 가능 글자 수',
},
helperText: {
control: 'text',
description: 'Textarea에 올바르게 입력할 수 있도록 돕는 텍스트',
},
disabled: {
control: 'boolean',
description: 'Textarea의 비활성화 여부',
},
error: {
control: 'boolean',
description: 'Textarea의 에러 여부',
},
},
parameters: {
layout: 'centered',
},
};

export default meta;
type Story = StoryObj<typeof Textarea>;

const ControlledComponent = (args: TextareaProps) => {
return <Textarea {...args} maxLength={args.maxLength} placeholder={args.placeholder} />;
};

const OnChangeComponent = (args: TextareaProps) => {
const [text, setText] = useState('');

const handleValueChange = (newValue: string) => {
setText(newValue);
};

return (
<Textarea
{...args}
value={text}
onValueChange={handleValueChange}
maxLength={args.maxLength}
placeholder={args.placeholder}
/>
);
};

export const Default: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '100%',
height: 'auto',
placeholder: 'Enter text here...',
helperText: 'Text Inputting',
},
};

export const Placeholder: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '343px',
height: '187px',
placeholder: 'Enter text here...',
},
};

export const HelperText: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '343px',
height: '187px',
placeholder: 'Enter text here...',
helperText: 'helperText입니다.',
},
};

export const Disabled: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '343px',
height: '187px',
placeholder: 'This field is disabled',
helperText: 'Text Inputting',
disabled: true,
},
};

export const Error: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '343px',
height: '187px',
error: true,
placeholder: 'There is an error',
helperText: 'Text Inputting',
},
};

export const MaxLength: Story = {
render: (args) => <ControlledComponent {...args} />,
args: {
width: '343px',
height: '187px',
maxLength: 50,
placeholder: 'Max 50 characters',
helperText: '',
disabled: false,
error: false,
},
};

export const OnValueChange: Story = {
render: (args) => <OnChangeComponent {...args} />,
args: {
width: '343px',
height: '187px',
maxLength: 50,
placeholder: 'onValueChange...',
helperText: '',
disabled: false,
error: false,
},
};
85 changes: 85 additions & 0 deletions src/components/Textarea/Textarea.style.ts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크롤이 생겼을 때 디자인이 아직 적용 전인거 같네요!

스토리북 피그마
image image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스타일 적용해서 반영해두겠습니당! 근데 제가 맥인데 보리가 윈도우 스타일 잘 적용됐는지 추후 수정되면 한번 확인해주심.. 감사하겠숨다😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nijuy 스크롤바 적용 완료하였습니다! 이게 스크롤바 적용이 패딩값을 무시하고 textarea 전체에 맞게 스크롤바가 생겨서 StyledTextareaWrapper로 감싸서 패딩값 적용된 컨테이너 안에 스크롤바가 생긴 형태(피그마와 같은 형태)로 작업하였습니다!

윈도우 환경 확인 부탁드려욥!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잘 보입니다!
image

다만 첫번째 사진은 단어 길이 때문에 줄바꿈이 생겨서 스크롤과 단어 사이 여백이 충분한데,
그렇지 않은 경우에 글자랑 스크롤바 사이가 너무 딱 붙어있어서..🥲

디자인 자체가 1로 되어있어서 줄바꿈이 안 일어나는 경우 글자와 스크롤 사이 여백이 고려됐는지 잘 모르겠네요!
한번 물어보는 건 어떨까요??
image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6px이라네용 굿.........
바로 머지할 수 있게 미리 어푸룹 해둘게요 이거 말곤 진짜진짜 안보임!!!!

image

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { styled } from 'styled-components';

import { TextareaProps } from './Textarea.type';

interface StyledTextareaProps {
$width?: TextareaProps['width'];
$height?: TextareaProps['height'];
$error?: TextareaProps['error'];
$isFocused?: boolean;
}

export const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const StyledTextareaWrapper = styled.div<StyledTextareaProps>`
width: ${({ $width }) => $width ?? '100%'};
height: ${({ $height }) => $height ?? 'auto'};
padding: 16px;
background-color: ${({ theme }) => theme.semantic.color.bgBasicLight};
border: ${({ theme, $error }) =>
$error ? `1px solid ${theme.semantic.color.lineStatusNegative}` : 'none'};
border-radius: ${({ theme }) => theme.semantic.radius.s}px;
box-sizing: border-box;
border: ${({ theme, $error, $isFocused }) =>
$error
? `1px solid ${theme.semantic.color.lineStatusNegative}`
: $isFocused
? `1px solid ${theme.semantic.color.lineStatusPositive}`
: 'none'};
`;

export const StyledTextarea = styled.textarea<StyledTextareaProps>`
width: 100%;
height: 100%;
resize: none;
${({ theme }) => theme.typo.B3_Rg_14}

box-sizing: border-box;
border: none;
background-color: transparent;
color: ${({ theme }) => theme.semantic.color.textBasicPrimary};

caret-color: ${({ theme, $error }) =>
$error ? theme.semantic.color.lineStatusNegative : theme.semantic.color.lineStatusPositive};

&:focus {
outline: none;
}

&::placeholder {
color: ${({ theme }) => theme.semantic.color.textBasicTertiary};
}

&:disabled {
background-color: transparent;
cursor: not-allowed;

&::placeholder {
color: ${({ theme }) => theme.semantic.color.textBasicDisabled};
}
}

&::-webkit-scrollbar {
width: 2px;
border-radius: 2px;
}

&::-webkit-scrollbar-track {
background: ${({ theme }) => theme.semantic.color.lineBasicMedium};
}

&::-webkit-scrollbar-thumb {
background-color: ${({ theme }) => theme.semantic.color.lineBasicStrong};
}
`;

export const StyledHelperText = styled.div<StyledTextareaProps>`
margin-left: 4px;
${({ theme }) => theme.typo.C2_Rg_12}
color: ${({ theme, $error }) =>
$error ? theme.semantic.color.textStatusNegative : theme.semantic.color.textBasicTertiary};
`;
Loading