From d34bd6aa3cfa825480bf86f8ae2110e8e4465d61 Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Thu, 18 Apr 2024 16:36:45 +0900 Subject: [PATCH 01/16] feat: create SuffixTextField --- .../SuffixTextField.stories.tsx | 77 +++++++++++++++++++ .../SuffixTextField/SuffixTextField.tsx | 7 ++ .../SuffixTextField/SuffixTextField.type.ts | 3 + src/components/TextField/TextField.style.ts | 7 ++ src/components/TextField/TextField.tsx | 7 +- src/components/TextField/index.ts | 3 + src/components/index.ts | 3 + 7 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/components/TextField/SuffixTextField/SuffixTextField.stories.tsx create mode 100644 src/components/TextField/SuffixTextField/SuffixTextField.tsx create mode 100644 src/components/TextField/SuffixTextField/SuffixTextField.type.ts diff --git a/src/components/TextField/SuffixTextField/SuffixTextField.stories.tsx b/src/components/TextField/SuffixTextField/SuffixTextField.stories.tsx new file mode 100644 index 0000000..2757995 --- /dev/null +++ b/src/components/TextField/SuffixTextField/SuffixTextField.stories.tsx @@ -0,0 +1,77 @@ +import { useState } from 'react'; + +import { Meta, StoryObj } from '@storybook/react'; + +import { SuffixTextField } from './SuffixTextField'; + +const meta: Meta = { + title: 'Atoms/TextField/SuffixTextField', + component: SuffixTextField, + parameters: { + layout: 'centered', + }, +}; +export default meta; + +const TextFieldStory = ({ ...textFieldProps }) => { + const [value, setValue] = useState(''); + const onChange = (e: React.ChangeEvent) => { + setValue(e.target.value); + }; + + const newProps = { ...textFieldProps, value, onChange }; + return ; +}; + +type Story = StoryObj; +export const Primary: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isPositive: false, + isNegative: false, + width: '350px', + suffix: '@soongsil.ac.kr', + }, + render: TextFieldStory, +}; + +export const Disabled: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: true, + width: '350px', + suffix: '@soongsil.ac.kr', + }, + render: TextFieldStory, +}; + +export const Positive: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isPositive: true, + width: '350px', + suffix: '@soongsil.ac.kr', + }, + render: TextFieldStory, +}; + +export const Negative: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isNegative: true, + width: '350px', + suffix: '@soongsil.ac.kr', + }, + render: TextFieldStory, +}; diff --git a/src/components/TextField/SuffixTextField/SuffixTextField.tsx b/src/components/TextField/SuffixTextField/SuffixTextField.tsx new file mode 100644 index 0000000..5b93ea1 --- /dev/null +++ b/src/components/TextField/SuffixTextField/SuffixTextField.tsx @@ -0,0 +1,7 @@ +import { TextField } from '../TextField'; + +import { SuffixTextFieldProps } from './SuffixTextField.type'; + +export const SuffixTextField = ({ ...props }: SuffixTextFieldProps) => { + return ; +}; diff --git a/src/components/TextField/SuffixTextField/SuffixTextField.type.ts b/src/components/TextField/SuffixTextField/SuffixTextField.type.ts new file mode 100644 index 0000000..fdc7938 --- /dev/null +++ b/src/components/TextField/SuffixTextField/SuffixTextField.type.ts @@ -0,0 +1,3 @@ +import { TextFieldProps } from '../TextField.type'; + +export interface SuffixTextFieldProps extends TextFieldProps {} diff --git a/src/components/TextField/TextField.style.ts b/src/components/TextField/TextField.style.ts index 7ad4bf5..61fe7ab 100644 --- a/src/components/TextField/TextField.style.ts +++ b/src/components/TextField/TextField.style.ts @@ -25,6 +25,7 @@ export const StyledTextFieldWrapper = styled.div` margin: 8px 0 0 0; padding: 12px 16px; + gap: 4px; .suffix-icon { visibility: hidden; @@ -81,6 +82,12 @@ export const StyledTextField = styled.input` } `; +export const StyledSuffixText = styled.span` + ${({ theme }) => theme.typo.body2}; + color: ${({ theme, $isDisabled }) => + $isDisabled ? theme.color.textDisabled : theme.color.textTertiary}; +`; + export const StyledFieldLabel = styled.label` ${({ theme }) => theme.typo.subtitle6}; color: ${({ theme, $isDisabled }) => diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx index 25c50ae..befe079 100644 --- a/src/components/TextField/TextField.tsx +++ b/src/components/TextField/TextField.tsx @@ -1,6 +1,7 @@ import { StyledFieldLabel, StyledHelperLabel, + StyledSuffixText, StyledTextField, StyledTextFieldWrapper, } from './TextField.style'; @@ -31,7 +32,11 @@ export const TextField = ({ > {searchPrefix} - {suffix} + {typeof suffix === 'string' ? ( + {suffix} + ) : ( + suffix + )} {helperLabel && ( diff --git a/src/components/TextField/index.ts b/src/components/TextField/index.ts index a2cc4f0..0232aa1 100644 --- a/src/components/TextField/index.ts +++ b/src/components/TextField/index.ts @@ -1,2 +1,5 @@ export { SimpleTextField } from './SimpleTextField/SimpleTextField'; export type { SimpleTextFieldProps } from './SimpleTextField/SimpleTextField.type'; + +export { SuffixTextField } from './SuffixTextField/SuffixTextField'; +export type { SuffixTextFieldProps } from './SuffixTextField/SuffixTextField.type'; diff --git a/src/components/index.ts b/src/components/index.ts index e3524fc..416cd9b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -27,3 +27,6 @@ export type { ToastProps, ToastDuration } from './Toast'; export { SimpleTextField } from './TextField'; export type { SimpleTextFieldProps } from './TextField'; + +export { SuffixTextField } from './TextField'; +export type { SuffixTextFieldProps } from './TextField'; From 63c98fb38f9650898e9fed83d35583222c3b93b3 Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Wed, 24 Apr 2024 10:29:53 +0900 Subject: [PATCH 02/16] feat: create PasswordTextField --- .../PasswordTextField.stories.tsx | 76 +++++++++++++++++++ .../PasswordTextField/PasswordTextField.tsx | 34 +++++++++ .../PasswordTextField.type.ts | 6 ++ 3 files changed, 116 insertions(+) create mode 100644 src/components/TextField/PasswordTextField/PasswordTextField.stories.tsx create mode 100644 src/components/TextField/PasswordTextField/PasswordTextField.tsx create mode 100644 src/components/TextField/PasswordTextField/PasswordTextField.type.ts diff --git a/src/components/TextField/PasswordTextField/PasswordTextField.stories.tsx b/src/components/TextField/PasswordTextField/PasswordTextField.stories.tsx new file mode 100644 index 0000000..334e63e --- /dev/null +++ b/src/components/TextField/PasswordTextField/PasswordTextField.stories.tsx @@ -0,0 +1,76 @@ +import { useState } from 'react'; + +import { Meta, StoryObj } from '@storybook/react'; + +import { PasswordTextField } from './PasswordTextField'; + +const meta: Meta = { + title: 'Atoms/TextField/PasswordTextField', + component: PasswordTextField, + parameters: { + layout: 'centered', + }, +}; +export default meta; + +const TextFieldStory = ({ ...textFieldProps }) => { + const [value, setValue] = useState(''); + const onChange = (e: React.ChangeEvent) => { + setValue(e.target.value); + }; + + const newProps = { ...textFieldProps, value, onChange }; + return ; +}; + +type Story = StoryObj; +export const Primary: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isPositive: false, + isNegative: false, + isMarked: true, + width: '350px', + }, + render: TextFieldStory, +}; + +export const Disabled: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: true, + width: '350px', + }, + render: TextFieldStory, +}; + +export const Positive: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isPositive: true, + isMarked: false, + width: '350px', + }, + render: TextFieldStory, +}; + +export const Negative: Story = { + args: { + fieldLabel: '필드 라벨', + helperLabel: '도움말 텍스트', + placeholder: '플레이스 홀더', + disabled: false, + isNegative: true, + isMarked: true, + width: '350px', + }, + render: TextFieldStory, +}; diff --git a/src/components/TextField/PasswordTextField/PasswordTextField.tsx b/src/components/TextField/PasswordTextField/PasswordTextField.tsx new file mode 100644 index 0000000..6b8c4aa --- /dev/null +++ b/src/components/TextField/PasswordTextField/PasswordTextField.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; + +import { useTheme } from 'styled-components'; + +import { IcEyeclosedLine, IcEyeopenLine, IconContext } from '@/style'; + +import { TextField } from '../TextField'; + +import { PasswordTextFieldProps } from './PasswordTextField.type'; + +export const PasswordTextField = ({ isMarked, ...props }: PasswordTextFieldProps) => { + const [isMarkedValue, setIsMarkedValue] = useState(isMarked); + const onClickEyeButton = () => { + setIsMarkedValue((prev) => !prev); + }; + return ( + +
+ {isMarkedValue ? : } +
+ + } + {...props} + >
+ ); +}; diff --git a/src/components/TextField/PasswordTextField/PasswordTextField.type.ts b/src/components/TextField/PasswordTextField/PasswordTextField.type.ts new file mode 100644 index 0000000..7ccca79 --- /dev/null +++ b/src/components/TextField/PasswordTextField/PasswordTextField.type.ts @@ -0,0 +1,6 @@ +import { TextFieldProps } from '../TextField.type'; + +export interface PasswordTextFieldProps extends TextFieldProps { + /** 입력된 내용을 보지 못하게 할 것인지 나타내는 속성 */ + isMarked?: boolean; +} From b9a45607909b1a6aa259e5d6a823a4223e6c8159 Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Wed, 24 Apr 2024 10:31:59 +0900 Subject: [PATCH 03/16] fix: icon color in SimpleTextField --- src/components/TextField/SimpleTextField/SimpleTextField.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/TextField/SimpleTextField/SimpleTextField.tsx b/src/components/TextField/SimpleTextField/SimpleTextField.tsx index a49bf1c..ea2fe6a 100644 --- a/src/components/TextField/SimpleTextField/SimpleTextField.tsx +++ b/src/components/TextField/SimpleTextField/SimpleTextField.tsx @@ -1,3 +1,5 @@ +import { useTheme } from 'styled-components'; + import { IcXLine, IconContext } from '@/style'; import { TextField } from '../TextField'; @@ -10,7 +12,7 @@ export const SimpleTextField = ({ onClickClearButton, ...props }: SimpleTextFiel suffix={ From 39a0a5fd7e5ce221ba606667a0ae23eabd16c144 Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Wed, 24 Apr 2024 10:36:40 +0900 Subject: [PATCH 04/16] feat: export PasswordTextField --- src/components/TextField/index.ts | 3 +++ src/components/index.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/components/TextField/index.ts b/src/components/TextField/index.ts index 0232aa1..60869e3 100644 --- a/src/components/TextField/index.ts +++ b/src/components/TextField/index.ts @@ -3,3 +3,6 @@ export type { SimpleTextFieldProps } from './SimpleTextField/SimpleTextField.typ export { SuffixTextField } from './SuffixTextField/SuffixTextField'; export type { SuffixTextFieldProps } from './SuffixTextField/SuffixTextField.type'; + +export { PasswordTextField } from './PasswordTextField/PasswordTextField'; +export type { PasswordTextFieldProps } from './PasswordTextField/PasswordTextField.type'; diff --git a/src/components/index.ts b/src/components/index.ts index 416cd9b..9b44497 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -30,3 +30,6 @@ export type { SimpleTextFieldProps } from './TextField'; export { SuffixTextField } from './TextField'; export type { SuffixTextFieldProps } from './TextField'; + +export { PasswordTextField } from './TextField'; +export type { PasswordTextFieldProps } from './TextField'; From 5e46b05ef87f206fbf4da6d8c62f5fb1fee40835 Mon Sep 17 00:00:00 2001 From: nijuy Date: Sun, 28 Apr 2024 01:17:38 +0900 Subject: [PATCH 05/16] =?UTF-8?q?fix:=20ms-reveal=20display:none=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 엣지에서 password type input에 기본적으로 포함하는 버튼 --- src/components/TextField/TextField.style.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/TextField/TextField.style.ts b/src/components/TextField/TextField.style.ts index 61fe7ab..2f11e7a 100644 --- a/src/components/TextField/TextField.style.ts +++ b/src/components/TextField/TextField.style.ts @@ -80,6 +80,10 @@ export const StyledTextField = styled.input` color: ${({ theme, disabled }) => disabled ? theme.color.textDisabled : theme.color.textTertiary}; } + + &::-ms-reveal { + display: none; + } `; export const StyledSuffixText = styled.span` From 492104b7f359fb1e871e68e24f8ec3251330f8c3 Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Mon, 29 Apr 2024 20:48:26 +0900 Subject: [PATCH 06/16] refactor: overwritten suffix props & omit searchPrefix props --- src/components/TextField/SuffixTextField/SuffixTextField.tsx | 4 ++-- .../TextField/SuffixTextField/SuffixTextField.type.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/TextField/SuffixTextField/SuffixTextField.tsx b/src/components/TextField/SuffixTextField/SuffixTextField.tsx index 5b93ea1..5984f1e 100644 --- a/src/components/TextField/SuffixTextField/SuffixTextField.tsx +++ b/src/components/TextField/SuffixTextField/SuffixTextField.tsx @@ -2,6 +2,6 @@ import { TextField } from '../TextField'; import { SuffixTextFieldProps } from './SuffixTextField.type'; -export const SuffixTextField = ({ ...props }: SuffixTextFieldProps) => { - return ; +export const SuffixTextField = ({ suffix, ...props }: SuffixTextFieldProps) => { + return ; }; diff --git a/src/components/TextField/SuffixTextField/SuffixTextField.type.ts b/src/components/TextField/SuffixTextField/SuffixTextField.type.ts index fdc7938..4667363 100644 --- a/src/components/TextField/SuffixTextField/SuffixTextField.type.ts +++ b/src/components/TextField/SuffixTextField/SuffixTextField.type.ts @@ -1,3 +1,6 @@ import { TextFieldProps } from '../TextField.type'; -export interface SuffixTextFieldProps extends TextFieldProps {} +export interface SuffixTextFieldProps extends Omit { + /** TextField 오른쪽에 들어갈 텍스트 */ + suffix?: string; +} From bce63ccd669bceb38d3afec6512c39790f9d454d Mon Sep 17 00:00:00 2001 From: Hanna922 Date: Mon, 29 Apr 2024 20:51:52 +0900 Subject: [PATCH 07/16] fix: omit suffix, searchPrefix props --- .../TextField/PasswordTextField/PasswordTextField.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextField/PasswordTextField/PasswordTextField.type.ts b/src/components/TextField/PasswordTextField/PasswordTextField.type.ts index 7ccca79..879bc71 100644 --- a/src/components/TextField/PasswordTextField/PasswordTextField.type.ts +++ b/src/components/TextField/PasswordTextField/PasswordTextField.type.ts @@ -1,6 +1,6 @@ import { TextFieldProps } from '../TextField.type'; -export interface PasswordTextFieldProps extends TextFieldProps { +export interface PasswordTextFieldProps extends Omit { /** 입력된 내용을 보지 못하게 할 것인지 나타내는 속성 */ isMarked?: boolean; } From 8a3c63e96effc48e4ead7ca8ac465bb0a5b20181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9C=A0=EC=A7=84?= Date: Mon, 29 Apr 2024 21:00:57 +0900 Subject: [PATCH 08/16] =?UTF-8?q?fix:=20SimpleTextFieldProps=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: `SimpleTextFieldProps`의 부모 타입 수정 - TextFieldProps에서 suffix, searchPrefix를 Omit으로 제거함 * fix: icon color in SimpleTextField --------- Co-authored-by: Hanna922 --- .../TextField/SimpleTextField/SimpleTextField.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TextField/SimpleTextField/SimpleTextField.type.ts b/src/components/TextField/SimpleTextField/SimpleTextField.type.ts index bb0ec8d..bb6d7fe 100644 --- a/src/components/TextField/SimpleTextField/SimpleTextField.type.ts +++ b/src/components/TextField/SimpleTextField/SimpleTextField.type.ts @@ -1,6 +1,6 @@ import { TextFieldProps } from '../TextField.type'; -export interface SimpleTextFieldProps extends TextFieldProps { +export interface SimpleTextFieldProps extends Omit { /** x 버튼을 클릭했을 때 이벤트 핸들러 */ onClickClearButton?: () => void; } From 34b2d82a74817f72f7621a9abc740e30f588d22f Mon Sep 17 00:00:00 2001 From: hanna Date: Mon, 29 Apr 2024 22:31:35 +0900 Subject: [PATCH 09/16] refactor: text field props (#72) * docs: separate TextField basic docs * @Hanna922 Merge branch 'develop' into feat/#70-TextField-props * docs: add Controls in base TextField --- .../SimpleTextField.stories.tsx | 53 -------------- .../TextField/TextField.stories.tsx | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/components/TextField/TextField.stories.tsx diff --git a/src/components/TextField/SimpleTextField/SimpleTextField.stories.tsx b/src/components/TextField/SimpleTextField/SimpleTextField.stories.tsx index 1d65986..d3f5172 100644 --- a/src/components/TextField/SimpleTextField/SimpleTextField.stories.tsx +++ b/src/components/TextField/SimpleTextField/SimpleTextField.stories.tsx @@ -1,6 +1,5 @@ import { useState } from 'react'; -import { Stories, Primary as PrimaryBlock, Controls, Title } from '@storybook/blocks'; import { Meta, StoryObj } from '@storybook/react'; import { SimpleTextField } from './SimpleTextField'; @@ -10,58 +9,6 @@ const meta: Meta = { component: SimpleTextField, parameters: { layout: 'centered', - docs: { - page: () => ( - <> - - <PrimaryBlock /> - <Controls /> - <h2> 주의사항 </h2> - <ol> - <li> - TextField의 종류에 따라 suffix, searchPrefix 속성 값이 일부 정해져 있습니다. - <br /> - <br /> - <table> - <tr> - <th>종류</th> - <th>suffix</th> - <th>searchPrefix</th> - </tr> - <tr> - <td>SimpleTextField</td> - <td>IcXLine</td> - <td>설정 불가</td> - </tr> - <tr> - <td>SuffixTextField</td> - <td>사용자가 설정한 값</td> - <td>설정 불가</td> - </tr> - <tr> - <td>SearchTextField</td> - <td>IcXLine</td> - <td>IcSearchLine</td> - </tr> - <tr> - <td>PasswordTextField</td> - <td>IcEyeclosedLine 또는 IcEyeopenLine</td> - <td>설정 불가</td> - </tr> - </table> - </li> - <br /> - <li> - boolean 타입 속성의 우선순위는 아래와 같습니다. - <br /> - disabled > isNegative > isPositive - </li> - </ol> - <br /> - <Stories /> - </> - ), - }, }, }; export default meta; diff --git a/src/components/TextField/TextField.stories.tsx b/src/components/TextField/TextField.stories.tsx new file mode 100644 index 0000000..0165025 --- /dev/null +++ b/src/components/TextField/TextField.stories.tsx @@ -0,0 +1,72 @@ +import { Controls, Title } from '@storybook/blocks'; +import { Meta } from '@storybook/react'; + +import { TextField } from './TextField'; + +const meta: Meta = { + title: 'Atoms/TextField', + component: TextField, + parameters: { + layout: 'centered', + docs: { + page: () => ( + <> + <Title /> + <h2>주의사항</h2> + <ol> + <li> + TextField의 종류에 따라 suffix, searchPrefix 속성 값이 일부 정해져 있습니다. + <br /> + <br /> + <table> + <tbody> + <tr> + <th>종류</th> + <th>suffix</th> + <th>searchPrefix</th> + </tr> + <tr> + <td>SimpleTextField</td> + <td>IcXLine</td> + <td>설정 불가</td> + </tr> + <tr> + <td>SuffixTextField</td> + <td>사용자가 설정한 값</td> + <td>설정 불가</td> + </tr> + <tr> + <td>SearchTextField</td> + <td>IcXLine</td> + <td>IcSearchLine</td> + </tr> + <tr> + <td>PasswordTextField</td> + <td>IcEyeclosedLine 또는 IcEyeopenLine</td> + <td>설정 불가</td> + </tr> + </tbody> + </table> + </li> + <br /> + <li> + boolean 타입 속성의 우선순위는 아래와 같습니다. + <br /> + disabled > isNegative > isPositive + </li> + </ol> + <br /> + <h2>TextField 속성</h2> + <Controls /> + </> + ), + }, + }, +}; +export default meta; + +const TextFieldStory = () => {}; + +export const Primary = { + render: TextFieldStory, +}; From 58a90c62f10db6b4037be0f141470d0c5b075b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9C=A0=EC=A7=84?= <youjin6325@naver.com> Date: Mon, 29 Apr 2024 23:33:10 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20SearchTextField=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: SearchTextField.type.ts 추가 * feat: SearchTextField 컴포넌트 추가 * docs: SearchTextField.stories.tsx 추가 * feat: export SearchTextField --- .../SearchTextField.stories.tsx | 58 +++++++++++++++++++ .../SearchTextField/SearchTextField.tsx | 39 +++++++++++++ .../SearchTextField/SearchTextField.type.ts | 10 ++++ src/components/TextField/index.ts | 3 + src/components/index.ts | 3 + 5 files changed, 113 insertions(+) create mode 100644 src/components/TextField/SearchTextField/SearchTextField.stories.tsx create mode 100644 src/components/TextField/SearchTextField/SearchTextField.tsx create mode 100644 src/components/TextField/SearchTextField/SearchTextField.type.ts diff --git a/src/components/TextField/SearchTextField/SearchTextField.stories.tsx b/src/components/TextField/SearchTextField/SearchTextField.stories.tsx new file mode 100644 index 0000000..9f1a8b8 --- /dev/null +++ b/src/components/TextField/SearchTextField/SearchTextField.stories.tsx @@ -0,0 +1,58 @@ +import { useState } from 'react'; + +import { Meta, StoryObj } from '@storybook/react'; + +import { SearchTextField } from './SearchTextField'; + +const meta: Meta<typeof SearchTextField> = { + title: 'Atoms/TextField/SearchTextField', + component: SearchTextField, + parameters: { + layout: 'centered', + }, +}; +export default meta; + +const TextFieldStory = ({ ...textFieldProps }) => { + const [value, setValue] = useState(''); + const onChange = (e: React.ChangeEvent<HTMLInputElement>) => { + setValue(e.target.value); + }; + const onClickClearButton = () => { + setValue(''); + }; + + const newProps = { ...textFieldProps, value, onChange, onClickClearButton }; + return <SearchTextField {...newProps} />; +}; + +type Story = StoryObj<typeof SearchTextField>; +export const Primary: Story = { + args: { + isFocused: false, + isTyping: false, + placeholder: '플레이스 홀더', + disabled: false, + width: '350px', + }, + render: TextFieldStory, +}; + +export const Disabled: Story = { + args: { + placeholder: '플레이스 홀더', + disabled: true, + width: '350px', + }, + render: TextFieldStory, +}; + +export const Focused: Story = { + args: { + isFocused: true, + placeholder: '플레이스 홀더', + disabled: false, + width: '350px', + }, + render: TextFieldStory, +}; diff --git a/src/components/TextField/SearchTextField/SearchTextField.tsx b/src/components/TextField/SearchTextField/SearchTextField.tsx new file mode 100644 index 0000000..3a15227 --- /dev/null +++ b/src/components/TextField/SearchTextField/SearchTextField.tsx @@ -0,0 +1,39 @@ +import { useTheme } from 'styled-components'; + +import { IcSearchLine, IcXLine, IconContext } from '@/style'; + +import { TextField } from '../TextField'; + +import { SearchTextFieldProps } from './SearchTextField.type'; + +export const SearchTextField = ({ onClickClearButton, ...props }: SearchTextFieldProps) => { + const theme = useTheme(); + + return ( + <TextField + suffix={ + <IconContext.Provider + value={{ + color: theme.color.buttonNormal, + size: '1rem', + }} + > + <div className="suffix-icon clear-icon" onClick={onClickClearButton}> + <IcXLine /> + </div> + </IconContext.Provider> + } + searchPrefix={ + <IconContext.Provider + value={{ + color: theme.color.textTertiary, + size: '1.5rem', + }} + > + <IcSearchLine /> + </IconContext.Provider> + } + {...props} + /> + ); +}; diff --git a/src/components/TextField/SearchTextField/SearchTextField.type.ts b/src/components/TextField/SearchTextField/SearchTextField.type.ts new file mode 100644 index 0000000..1ca448f --- /dev/null +++ b/src/components/TextField/SearchTextField/SearchTextField.type.ts @@ -0,0 +1,10 @@ +import { TextFieldProps } from '../TextField.type'; + +export interface SearchTextFieldProps + extends Omit< + TextFieldProps, + 'isNegative' | 'isPositive' | 'fieldLabel' | 'helperLabel' | 'suffix' | 'searchPrefix' + > { + /** x 버튼을 클릭했을 때 이벤트 핸들러 */ + onClickClearButton?: () => void; +} diff --git a/src/components/TextField/index.ts b/src/components/TextField/index.ts index 60869e3..1aeb192 100644 --- a/src/components/TextField/index.ts +++ b/src/components/TextField/index.ts @@ -6,3 +6,6 @@ export type { SuffixTextFieldProps } from './SuffixTextField/SuffixTextField.typ export { PasswordTextField } from './PasswordTextField/PasswordTextField'; export type { PasswordTextFieldProps } from './PasswordTextField/PasswordTextField.type'; + +export { SearchTextField } from './SearchTextField/SearchTextField'; +export type { SearchTextFieldProps } from './SearchTextField/SearchTextField.type'; diff --git a/src/components/index.ts b/src/components/index.ts index 9b44497..a173ddf 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -33,3 +33,6 @@ export type { SuffixTextFieldProps } from './TextField'; export { PasswordTextField } from './TextField'; export type { PasswordTextFieldProps } from './TextField'; + +export { SearchTextField } from './TextField'; +export type { SearchTextFieldProps } from './TextField'; From 4f4b46148c5f62885c9a354a57d60a54de191b13 Mon Sep 17 00:00:00 2001 From: hanna <hanna.urssu@gmail.com> Date: Sun, 5 May 2024 16:55:44 +0900 Subject: [PATCH 11/16] feat: add extension recommendations (#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add extensions recommendations * fix: apply vscode-styled-components extensions * fix: `setToastAnimation` 호출 수정 - `animation:` 이 함수 반환값으로 들어가니까 여기선 없어야 함 --------- Co-authored-by: nijuy <youjin6325@naver.com> --- .vscode/extensions.json | 7 +++++++ src/components/Toast/Toast.style.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..593b87f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "jpoissonnier.vscode-styled-components" + ] +} diff --git a/src/components/Toast/Toast.style.ts b/src/components/Toast/Toast.style.ts index 15a0433..37f7a27 100644 --- a/src/components/Toast/Toast.style.ts +++ b/src/components/Toast/Toast.style.ts @@ -26,13 +26,15 @@ const setToastAnimation = ($duration: ToastDuration) => { switch ($duration) { case 'short': return css` - ${ToastFadeIn} ${FADE_DURATION}s ease-in forwards, - ${ToastFadeOut} ${FADE_DURATION}s ${SHORT_DURATION + FADE_DURATION}s ease-out forwards + animation: + ${ToastFadeIn} ${FADE_DURATION}s ease-in forwards, + ${ToastFadeOut} ${FADE_DURATION}s ${SHORT_DURATION + FADE_DURATION}s ease-out forwards; `; case 'long': return css` - ${ToastFadeIn} ${FADE_DURATION}s ease-in forwards, - ${ToastFadeOut} ${FADE_DURATION}s ${LONG_DURATION + FADE_DURATION}s ease-out forwards + animation: + ${ToastFadeIn} ${FADE_DURATION}s ease-in forwards, + ${ToastFadeOut} ${FADE_DURATION}s ${LONG_DURATION + FADE_DURATION}s ease-out forwards; `; } }; @@ -55,5 +57,5 @@ export const StyledToast = styled.div<StyledToastProps>` color: ${({ theme }) => theme.color.textBright}; ${({ theme }) => theme.typo.body2}; - animation: ${({ $duration }) => setToastAnimation($duration)}; + ${({ $duration }) => setToastAnimation($duration)}; `; From a20c795d9fd0a4e71c449c27ed5ff68b67d71814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9C=A0=EC=A7=84?= <youjin6325@naver.com> Date: Wed, 8 May 2024 19:20:49 +0900 Subject: [PATCH 12/16] =?UTF-8?q?refactor:=20=EC=9B=B9=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20Toast.style=20=EC=88=98=EC=A0=95=20(#84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * design: `StyledToastWrapper` `StyledToast` 스타일 수정 * docs: Toast.stories.tsx 수정 * docs: `HookTest` 스토리 수정 - args를 내부에서 정의하지 않고 외부에서 전달하도록 함 * feat: `width` props 추가 및 문서 수정 * chore: css 속성 순서 정리 - 관련 있는 것끼리 묶고 중간에 개행 추가함 * docs: 스토리 순서 변경 - ToastHook 스토리를 Primary로 취급하게끔 정의 순서를 변경함 - 버튼 텍스트 height 수정 - 불필요한 Docs block 삭제 * fix: (ts -> tsx) HookSource md file extension --------- Co-authored-by: Hanna922 <hanna.urssu@gmail.com> --- src/components/Toast/HookSource.md | 11 +++- src/components/Toast/Toast.stories.tsx | 70 +++++++++++--------------- src/components/Toast/Toast.style.ts | 28 ++++++++--- src/components/Toast/Toast.tsx | 4 +- src/components/Toast/Toast.type.ts | 2 + 5 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/components/Toast/HookSource.md b/src/components/Toast/HookSource.md index 680d789..1a1a9a7 100644 --- a/src/components/Toast/HookSource.md +++ b/src/components/Toast/HookSource.md @@ -1,4 +1,4 @@ -```typescript +```tsx import { ToastDuration, useToast, Toast } from '@yourssu/design-system-react'; const ToastWrapper = () => { @@ -11,7 +11,14 @@ const ToastWrapper = () => { return ( <div> - <button onClick={() => { showToast(toastProps.duration); }}> 버튼 </button> + <button + onClick={() => { + showToast(toastProps.duration); + }} + > + {' '} + 버튼{' '} + </button> {isShowToast && <Toast {...toastProps} />} </div> ); diff --git a/src/components/Toast/Toast.stories.tsx b/src/components/Toast/Toast.stories.tsx index c478976..3824a7e 100644 --- a/src/components/Toast/Toast.stories.tsx +++ b/src/components/Toast/Toast.stories.tsx @@ -1,11 +1,10 @@ -import { Stories, Primary as PrimaryBlock, Controls, Title, Markdown } from '@storybook/blocks'; +import { Primary as PrimaryBlock, Controls, Title, Markdown } from '@storybook/blocks'; import { Meta, StoryObj } from '@storybook/react'; import { useToast } from '@/hooks/useToast'; import HookSource from './HookSource.md?raw'; import { Toast } from './Toast'; -import { ToastDuration } from './Toast.type'; const meta: Meta<typeof Toast> = { title: 'Component/Toast', @@ -20,18 +19,12 @@ const meta: Meta<typeof Toast> = { <Controls /> <h2> 주의사항 </h2> <ol> - <li>Toast의 width는 Toast 를 감싸는 컴포넌트의 width에 영향을 받습니다.</li> - <li> - Toast의 위치는 position: relative 속성이 설정된 가장 가까운 부모 컴포넌트에 의해 - 결정됩니다. - </li> + <li>width props 값이 fit-content보다 작을 경우 적용되지 않습니다.</li> </ol> <br /> <Title>useToast Toast 컴포넌트를 사용하기 위한 Custom Hook입니다. {HookSource} -
- ), }, @@ -41,48 +34,36 @@ export default meta; const ToastStory = ({ ...toastProps }) => { return ( -
-
- short duration toast (1.5s) - -
-
- long duration toast (3s) - -
+
+
); }; -const HookTest = () => { - const toastProps = { - children: 'useToast를 사용한 토스트 메시지', - duration: 'long' as ToastDuration, - }; +const HookTest = ({ ...toastProps }) => { const { showToast, isShowToast } = useToast(); return (
{isShowToast && }
@@ -90,18 +71,25 @@ const HookTest = () => { }; type Story = StoryObj; +export const ToastHook: Story = { + render: HookTest, + args: { + children: 'useToast를 사용한 토스트 메시지', + duration: 'long', + }, +}; export const SingleLine: Story = { args: { children: '토스트 메시지', + duration: 'short', + width: '300px', }, render: ToastStory, }; export const MultiLine: Story = { args: { - children: '줄 수가 두 줄 이상이 되는 토스트 메시지입니다. 좌측 정렬을 해주세요.', + children: '줄 수가 두 줄 이상이 되는 토스트 메시지입니다.\n좌측 정렬을 해주세요.', + duration: 'short', }, render: ToastStory, }; -export const ToastHook: Story = { - render: HookTest, -}; diff --git a/src/components/Toast/Toast.style.ts b/src/components/Toast/Toast.style.ts index 37f7a27..2c3a926 100644 --- a/src/components/Toast/Toast.style.ts +++ b/src/components/Toast/Toast.style.ts @@ -1,9 +1,10 @@ import { css, keyframes, styled } from 'styled-components'; -import { ToastDuration } from './Toast.type'; +import { ToastDuration, ToastProps } from './Toast.type'; interface StyledToastProps { $duration: ToastDuration; + $width: ToastProps['width']; } const SHORT_DURATION = 1.5; @@ -40,22 +41,35 @@ const setToastAnimation = ($duration: ToastDuration) => { }; export const StyledToastWrapper = styled.div` - position: absolute; - bottom: 66px; + position: fixed; + inset: 0px; width: 100%; + height: 100%; padding: 0px 8px; + + display: flex; + justify-content: center; + + pointer-events: none; `; export const StyledToast = styled.div` - opacity: 0; - border-radius: 8px; - width: 100%; - padding: 16px 24px; + position: absolute; + bottom: 66px; + min-width: fit-content; + width: ${({ $width }) => $width}; + max-width: 100%; + display: flex; justify-content: center; + padding: 16px 24px; + opacity: 0; + background-color: ${({ theme }) => theme.color.toastBG}; + border-radius: 8px; color: ${({ theme }) => theme.color.textBright}; ${({ theme }) => theme.typo.body2}; + white-space: pre-line; ${({ $duration }) => setToastAnimation($duration)}; `; diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index 3f7a519..3065180 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -1,12 +1,12 @@ import { StyledToast, StyledToastWrapper } from './Toast.style'; import { ToastProps } from './Toast.type'; -export const Toast = ({ children, duration = 'short', ...props }: ToastProps) => { +export const Toast = ({ children, duration = 'short', width, ...props }: ToastProps) => { if (!children) return; return ( - + {children} diff --git a/src/components/Toast/Toast.type.ts b/src/components/Toast/Toast.type.ts index 3d3b12d..aec2e12 100644 --- a/src/components/Toast/Toast.type.ts +++ b/src/components/Toast/Toast.type.ts @@ -5,4 +5,6 @@ export interface ToastProps extends React.HTMLAttributes { children?: React.ReactNode; /** 지속 시간 (1.5s | 3s)*/ duration?: ToastDuration; + /** Toast의 width */ + width?: string; } From 6bd4f4a2a5761bf5db7350ec2e1d8acb939ed2dd Mon Sep 17 00:00:00 2001 From: hanna Date: Fri, 10 May 2024 00:01:28 +0900 Subject: [PATCH 13/16] feat: config react docgen typescript (#85) * feat: config react docgen typescript * refactor: add parser option (remove undefined optional) --- .storybook/main.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.storybook/main.ts b/.storybook/main.ts index 998bbf4..d51b64d 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -2,12 +2,26 @@ import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + typescript: { + reactDocgen: 'react-docgen-typescript', + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + shouldRemoveUndefinedFromOptional: true, + propFilter: (prop) => { + if (prop.parent) { + return !prop.parent.fileName.includes('node_modules'); + } + return true; + }, + }, + check: false, + }, addons: [ '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-onboarding', '@storybook/addon-interactions', - '@storybook/addon-mdx-gfm' + '@storybook/addon-mdx-gfm', ], framework: { name: '@storybook/react-vite', From eed14aaab4bafb81fab581a83ead30633ade8dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9C=A0=EC=A7=84?= Date: Sat, 11 May 2024 19:36:41 +0900 Subject: [PATCH 14/16] rename: OnlyBGColor -> OnlyItemBGColor (#87) --- src/components/Badge/Badge.type.ts | 4 ++-- .../foundation/color/semanticColor/semanticColor.type.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Badge/Badge.type.ts b/src/components/Badge/Badge.type.ts index 62a5bf9..4ac49af 100644 --- a/src/components/Badge/Badge.type.ts +++ b/src/components/Badge/Badge.type.ts @@ -1,8 +1,8 @@ -import { SemanticBGColor } from '@/style'; +import { SemanticItemBGColor } from '@/style'; export interface BadgeProps extends React.HTMLAttributes { /** 배경 색상 */ - color?: SemanticBGColor; + color?: SemanticItemBGColor; /** Badge 안에 들어갈 텍스트 */ children?: React.ReactNode; /** 텍스트 왼쪽에 들어갈 아이콘 */ diff --git a/src/style/foundation/color/semanticColor/semanticColor.type.ts b/src/style/foundation/color/semanticColor/semanticColor.type.ts index 0ec4d27..5302054 100644 --- a/src/style/foundation/color/semanticColor/semanticColor.type.ts +++ b/src/style/foundation/color/semanticColor/semanticColor.type.ts @@ -99,5 +99,5 @@ export type SemanticColor = | SemanticItemColor; // Utility Types -type OnlyBGColor = T extends `${string}BG` ? T : never; -export type SemanticBGColor = OnlyBGColor; +type OnlyItemBGColor = T extends `${string}ItemBG` ? T : never; +export type SemanticItemBGColor = OnlyItemBGColor; From 90e0c89dca1b9059c0bf1a0e45f1ce06b9d3735c Mon Sep 17 00:00:00 2001 From: hanna Date: Sat, 11 May 2024 23:48:10 +0900 Subject: [PATCH 15/16] fix: release template (#88) --- .github/release.yml | 69 ++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index d7fd83a..a8f6cf1 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,37 +1,36 @@ # 이 파일은 release note 작성에 사용되는 파일입니다. -name-template: '@yourssu/design-system-react@$RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -categories: - - title: '🆕 새로운 기능이 추가되었어요!' - label: 'feat' - - title: '🐞 자잘한 버그를 수정했습니다.' - labels: - - 'bug' - - 'fix' - - title: '🫶🏻 사용성 개선에 힘썼습니다.' - label: 'docs' - - title: '🛠️ 더 나은 코드를 위해 노력하고 있습니다.' - labels: - - 'refactor' - - 'chore' - - title: 'ETC' - labels: - - '*' -change-template: '* $TITLE (#$NUMBER) by @$AUTHOR' -change-title-escapes: '\<*_&#@`' -exclude-labels: - - 'Main' -version-resolver: - major: - labels: - - 'Major' - minor: - labels: - - 'Minor' - patch: - labels: - - 'Patch' - default: patch -template: | - $CHANGES +changelog: + name-template: '@Yourssu Design/design-system-react@$RESOLVED_VERSION' + tag-template: 'v$RESOLVED_VERSION' + categories: + - title: ':new: Exciting New Features!' + label: 'feat' + - title: ':ladybug: Fixed a Bug' + labels: + - 'bug' + - 'fix' + - title: ':heart_hands::skin-tone-2: Improve User Experience' + label: 'docs' + - title: ':hammer_and_wrench: Strive for Better Code' + label: 'refactor' + - title: 'ETC' + labels: + - '*' + change-template: '* $TITLE (#$NUMBER) by @$AUTHOR' + change-title-escapes: '\<*_&#@`' + exclude-labels: + - 'Main' + version-resolver: + major: + labels: + - 'Major' + minor: + labels: + - 'Minor' + patch: + labels: + - 'Patch' + default: patch + template: | + $CHANGES From e7b038c201df5922ae2e7117f028b3eb47314d75 Mon Sep 17 00:00:00 2001 From: hanna Date: Sun, 12 May 2024 00:03:00 +0900 Subject: [PATCH 16/16] chore: update-version (#89) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f66306c..d28c4ac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@yourssu/design-system-react", "private": false, - "version": "1.0.2", + "version": "1.1.0", "description": "Yourssu Design System for React", "keywords": [ "yourssu",