Skip to content

Commit

Permalink
feat : EmotionButton, EmotionButtonList 컴포넌트 구현 (#53)
Browse files Browse the repository at this point in the history
* fix : 파일 경로 수정

* fix : 변경된 경로 반영

* fix : 세미콜론 추가

* chore : theme값 추가

* feat : EmotionButton 컴포넌트 구현

* fix : 파일명수정

* chore : 불필요 주석 삭제

* chore : 버튼 스타일 수정

* feat : EmotionButtonList 컴포넌트 구현

* chore : StoryBook 설명 수정

* chore : shared 폴더로 이동
  • Loading branch information
jymaeng1234 authored Oct 29, 2024
1 parent 0566f3f commit 41538fd
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 3 deletions.
File renamed without changes
4 changes: 4 additions & 0 deletions src/app/styles/styled.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ declare module 'styled-components' {
orange_warn: string;
orange_button: string;
orange_reaction: string;
orange_selected: string;
green: string;
white: string;
white_bg: string;
};
border: {
default: string;
normal: string;
not_selected: string;
selected: string;
};
}
}
6 changes: 5 additions & 1 deletion src/app/styles/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ const theme: DefaultTheme = {
orange_warn: '#FF4949',
orange_button: '#FF480E',
orange_reaction: '#FFBB80',
orange_selected: '#FFF4F166',
green: '#3BDE86',
white: '#FFFFFF',
white_bg: '#F8F8F8'
},
border: {
default: '1px solid #E0E0E0'
default: '1px solid #E0E0E0',
normal: '1px solid #00000066',
not_selected: '1px solid #0000001A',
selected: '1px solid #FF480E'
}
};

Expand Down
36 changes: 36 additions & 0 deletions src/shared/EmotionButton/ui/EmotionButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import EmotionButton from './EmotionButton';
import { Emotions } from '../../model/EmotionEnum'; // Emotions 열거형 import

const meta: Meta<typeof EmotionButton> = {
component: EmotionButton,
title: 'Shared/EmotionButton',
tags: ['autodocs'],
argTypes: {
onClick: { action: 'clicked' }, // 클릭 액션을 로그로 남기기
},
};

export default meta;

type Story = StoryObj<typeof EmotionButton>;

// 기본 스토리
export const Default: Story = {
args: {
emotion: Emotions.Happy, // Emotions 열거형에서 감정 값 사용
initialClicked: false, // 기본값으로 클릭되지 않은 상태
onClick: () => {}, // 기본 onClick 핸들러
},
};

// 클릭된 상태 스토리
export const Clicked: Story = {
args: {
emotion: Emotions.Sad, // Emotions 열거형에서 감정 값 사용
initialClicked: true, // 클릭된 상태
onClick: () => {}, // 기본 onClick 핸들러
},
};
37 changes: 37 additions & 0 deletions src/shared/EmotionButton/ui/EmotionButton.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import theme from '../../../app/styles/theme';
import styled from 'styled-components';

interface IsClicked {
clicked: boolean;
}

export const StyledEmotionButton = styled.button<IsClicked>`
width: auto;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
background-color: ${({ clicked }) =>
clicked ? theme.colors.orange_selected : theme.colors.white_bg};
border: 1px solid
${({ clicked }) =>
clicked ? theme.colors.orange_primary : theme.colors.gray_normal};
border-radius: 20px;
padding: 10px 20px;
cursor: pointer;
transition:
background-color 0.2s,
border-color 0.2s;
&:hover {
background-color: ${({ clicked }) =>
clicked
? theme.colors.orange_selected
: theme.colors.orange_selected};
border-color: ${({ clicked }) =>
clicked
? theme.colors.orange_primary
: theme.colors.orange_primary};
}
`;
35 changes: 35 additions & 0 deletions src/shared/EmotionButton/ui/EmotionButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useEffect, useState } from 'react';

import EmotionIcon from '../../EmotionIcon/ui/EmotionIcon';
import { Emotions, getEmotionInfo } from '../../model/EmotionEnum';
import { StyledEmotionButton } from './EmotionButton.styled';

interface EmotionButtonProps {
emotion: Emotions;
initialClicked: boolean;
onClick: () => void;
}

const EmotionButton = ({
emotion,
onClick,
initialClicked = false
}: EmotionButtonProps) => {
const [isClicked, setIsClicked] = useState(initialClicked);
useEffect(() => {
setIsClicked(initialClicked);
}, [initialClicked]);

const handleClick = () => {
setIsClicked((prev) => !prev);
onClick();
};
return (
<StyledEmotionButton clicked={isClicked} onClick={handleClick}>
<EmotionIcon emotion={emotion} width="20px" height="20px" />
<p>{getEmotionInfo(emotion).description}</p>
</StyledEmotionButton>
);
};

export default EmotionButton;
2 changes: 1 addition & 1 deletion src/shared/EmotionIcon/ui/EmotionIcon.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import EmotionIcon from './EmotionIcon';
import { Emotions } from '../model/EmotionEnum';
import { Emotions } from '../../model/EmotionEnum';

const meta: Meta<typeof EmotionIcon> = {
title: 'Shared/EmotionIcon',
Expand Down
2 changes: 1 addition & 1 deletion src/shared/EmotionIcon/ui/EmotionIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react/require-default-props */
import React from 'react';
import { Emotions, getEmotionInfo } from '../model/EmotionEnum';
import { Emotions, getEmotionInfo } from '../../model/EmotionEnum';
import { StyledEmotionIcon } from './EmotionIcon.styled';

interface EmotionProps {
Expand Down
26 changes: 26 additions & 0 deletions src/shared/EmotionList/ui/EmotionButtonList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import EmotionButtonList from './EmotionButtonList';

const meta: Meta<typeof EmotionButtonList> = {
component: EmotionButtonList,
title: 'Shared/EmotionButtonList',
tags: ['autodocs'],
argTypes: {
onSelectionChange: {
description: '선택된 감정 리스트가 변경될 때 호출되는 함수입니다.\n 이를 활용해 선택된 버튼 목록을 확인할 수있습니다.\n (StoryBook에서는 콘솔로 출력 확인 가능)',
},
},
};
export default meta;

type Story = StoryObj<typeof EmotionButtonList>;

export const Default: Story = {
args: {
onSelectionChange: (selectedEmotions) => {
console.log('selected:', selectedEmotions);
},
},
};
8 changes: 8 additions & 0 deletions src/shared/EmotionList/ui/EmotionButtonList.styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import styled from 'styled-components';

export const StyledEmotionButtonList = styled.div`
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
`;
39 changes: 39 additions & 0 deletions src/shared/EmotionList/ui/EmotionButtonList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { StyledEmotionButtonList } from './EmotionButtonList.styled';
import EmotionButton from '../../EmotionButton/ui/EmotionButton';
import { Emotions } from '../../model/EmotionEnum';

interface EmotionListProps {
onSelectionChange: (selectedEmotions: Emotions[]) => void; // prop 추가
}

/** 게시글에 대한 반응을 나타내기 위한 버튼입니다. */
const EmotionList = ({ onSelectionChange }: EmotionListProps) => {
const [selectedEmotions, setSelectedEmotions] = useState<Emotions[]>([]);

const handleEmotionClick = (emotion: Emotions) => {
setSelectedEmotions((prev) => {
const newSelection = prev.includes(emotion)
? prev.filter((e) => e !== emotion)
: [...prev, emotion];

onSelectionChange(newSelection);
return newSelection;
});
};

return (
<StyledEmotionButtonList>
{Object.values(Emotions).map((emotion) => (
<EmotionButton
key={emotion}
emotion={emotion}
initialClicked={selectedEmotions.includes(emotion)}
onClick={() => handleEmotionClick(emotion)}
/>
))}
</StyledEmotionButtonList>
);
};

export default EmotionList;
File renamed without changes.

0 comments on commit 41538fd

Please sign in to comment.