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

refactor: 웹에 맞게 Toast.style 수정 #84

Merged
merged 7 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 9 additions & 2 deletions src/components/Toast/HookSource.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
```typescript
```tsx
import { ToastDuration, useToast, Toast } from '@yourssu/design-system-react';

const ToastWrapper = () => {
Expand All @@ -11,7 +11,14 @@ const ToastWrapper = () => {

return (
<div>
<button onClick={() => { showToast(toastProps.duration); }}> 버튼 </button>
<button
onClick={() => {
showToast(toastProps.duration);
}}
>
{' '}
버튼{' '}
</button>
{isShowToast && <Toast {...toastProps} />}
</div>
);
Expand Down
70 changes: 29 additions & 41 deletions src/components/Toast/Toast.stories.tsx
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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</Title>
<span>Toast 컴포넌트를 사용하기 위한 Custom Hook입니다.</span>
<Markdown>{HookSource}</Markdown>
<br />
<Stories />
</>
),
},
Expand All @@ -41,67 +34,62 @@ export default meta;

const ToastStory = ({ ...toastProps }) => {
return (
<div style={{ display: 'flex', gap: '10px', width: '780px', height: '300px' }}>
<div
style={{
backgroundColor: '#c4c4c4',
width: '50%',
position: 'relative',
}}
>
short duration toast (1.5s)
<Toast {...toastProps} />
</div>
<div
style={{
backgroundColor: '#c4c4c4',
width: '50%',
position: 'relative',
}}
>
long duration toast (3s)
<Toast duration="long" {...toastProps} />
</div>
<div
style={{
width: '500px',
height: '150px',
}}
>
<Toast {...toastProps} />
</div>
);
};

const HookTest = () => {
const toastProps = {
children: 'useToast를 사용한 토스트 메시지',
duration: 'long' as ToastDuration,
};
const HookTest = ({ ...toastProps }) => {
const { showToast, isShowToast } = useToast();

return (
<div
style={{ backgroundColor: '#c4c4c4', width: '390px', height: '300px', position: 'relative' }}
style={{
width: '500px',
height: '200px',
display: 'flex',
justifyContent: 'center',
}}
>
<button
onClick={() => {
showToast(toastProps.duration);
}}
style={{ height: 'fit-content' }}
>
버튼을 누르면 토스트가 발생합니다
Show Toast
</button>
{isShowToast && <Toast {...toastProps} />}
</div>
);
};

type Story = StoryObj<typeof Toast>;
export const ToastHook: Story = {
render: HookTest,
args: {
children: 'useToast를 사용한 토스트 메시지',
duration: 'long',
},
};
export const SingleLine: Story = {
args: {
children: '토스트 메시지',
duration: 'short',
Copy link
Member

Choose a reason for hiding this comment

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

스토리북에서 토스트가 한 번 뜬 이후 다시 나타나지 않는 게 살짝 불편한데, 사용처에 duration 설정을 온전히 맡기고 기본 값은 무한으로 설정해두는 거 어떠신가요?
ToastHook만 duration long 유지하고, SingleLine, MultiLine은 설정하지 않으면 좋을 것 같아요

Copy link
Collaborator Author

@nijuy nijuy May 8, 2024

Choose a reason for hiding this comment

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

내부에 삭제 버튼이 존재하지 않는다는 점에서 YDS의 Toast는 사용자에게 피드백을 제공한 뒤 자체적으로 사라져야 하는 컴포넌트이고, 이러한 역할을 생각했을 때 duration의 기본값을 무한대로 설정하는 것이 어색하게 느껴집니다. 문서 조회에서 발생한 문제를 컴포넌트 코드의 수정으로 해결하는 것도 적절하지 않다는 생각이 들어요

말씀하신 불편함에는 저 역시도 공감합니다! 문서를 좀 다듬어보고 리뷰 요청 다시 넣을게요~

toast

Copy link
Member

@Hanna922 Hanna922 May 8, 2024

Choose a reason for hiding this comment

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

토스트가 생겼다 사라지는 것을 무한으로 반복해도 좋을 것 같네요 (일정 간격을 두고)

Copy link
Member

Choose a reason for hiding this comment

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

혹시 Single Line, Multi Line은 무한 반복이 불가능할까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

사용자가 원하는 시점에 원하는 옵션으로 다시 조회하는 기능이 낫다고 생각했고, 그 기능은 현재 PrimaryBlock에서 충분히 가능하기 때문에 필요성을 잘 모르겠어서 일단 추가하지 않았습니다

width: '300px',
},
render: ToastStory,
};
export const MultiLine: Story = {
args: {
children: '줄 수가 두 줄 이상이 되는 토스트 메시지입니다. 좌측 정렬을 해주세요.',
children: '줄 수가 두 줄 이상이 되는 토스트 메시지입니다.\n좌측 정렬을 해주세요.',
duration: 'short',
},
render: ToastStory,
};
export const ToastHook: Story = {
render: HookTest,
};
28 changes: 21 additions & 7 deletions src/components/Toast/Toast.style.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<StyledToastProps>`
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)};
`;
4 changes: 2 additions & 2 deletions src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<StyledToastWrapper>
<StyledToast $duration={duration} {...props}>
<StyledToast $duration={duration} $width={width} {...props}>
{children}
</StyledToast>
</StyledToastWrapper>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Toast/Toast.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ export interface ToastProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
/** 지속 시간 (1.5s | 3s)*/
duration?: ToastDuration;
/** Toast의 width */
width?: string;
}