Skip to content

Commit

Permalink
Merge pull request #170 from woowacourse-teams/feat/#155
Browse files Browse the repository at this point in the history
코멘트 section 구현
  • Loading branch information
jaeml06 authored Aug 1, 2024
2 parents 6b9ac55 + 49a16aa commit c3e95d2
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 1 deletion.
4 changes: 4 additions & 0 deletions frontend/src/common/assets/submit_message_button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions frontend/src/components/CommentCard/CommentCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Meta, StoryObj } from '@storybook/react';
import CommentCard from './CommentCard';

const meta = {
component: CommentCard,
title: 'Components/CommentCard',
} satisfies Meta<typeof CommentCard>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
comment: {
id: 0,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [
{
id: 0,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [],
},
],
},
},
render: (args) => <CommentCard {...args} />,
};
65 changes: 65 additions & 0 deletions frontend/src/components/CommentCard/CommentCard.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { css, Theme } from '@emotion/react';

export const commentContainer = () => css`
display: flex;
flex-direction: column;
gap: 1rem;
`;
export const commentWrapper = () => css`
display: flex;
align-items: center;
width: 100%;
background-color: #f3f4f6;
`;

export const profileImage = () => css`
width: 3rem;
height: 3rem;
`;

export const commnetBox = () => css`
display: flex;
flex-direction: column;
width: 100%;
margin-left: 1rem;
`;
export const commnetHeader = () => css`
display: flex;
gap: 5px;
align-items: center;
justify-content: space-between;
width: 100%;
`;

export const commentHeaderLeft = () => css`
display: flex;
gap: 0.7rem;
align-items: center;
`;

export const commentHeaderRight = (props: { theme: Theme }) => css`
display: flex;
align-items: center;
button {
${props.theme.typography.c3}
color: ${props.theme.colorPalette.grey[500]};
border: none;
}
button:hover {
${props.theme.typography.c3}
color: ${props.theme.colorPalette.grey[700]};
background-color: ${props.theme.colorPalette.grey[400]};
border: none;
}
`;

export const timestamp = (props: { theme: Theme }) => css`
${props.theme.typography.c3}
color: ${props.theme.colorPalette.grey[500]};
`;
export const commentChildBox = () => css`
margin-left: 3rem;
`;
44 changes: 44 additions & 0 deletions frontend/src/components/CommentCard/CommentCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as S from '@_components/CommentCard/CommentCard.style';
import ProfileFrame from '@_components/Profile/ProfileFrame';
import { Comment } from '@_types/index';
import { useTheme } from '@emotion/react';

import { HTMLProps } from 'react';

export interface CommentCardProps extends HTMLProps<HTMLDivElement> {
comment: Comment;
}

export default function CommentCard(props: CommentCardProps) {
const theme = useTheme();
const { comment } = props;

return (
<div css={S.commentContainer()}>
<div css={S.commentWrapper()}>
<ProfileFrame width={3} height={3} src={comment.src}></ProfileFrame>
<div css={S.commnetBox()}>
<div css={S.commnetHeader}>
<div css={S.commentHeaderLeft}>
<div css={theme.typography.small}>{comment.nickname}</div>
<div css={S.timestamp({ theme })}>{comment.dateTime}</div>
</div>
<div css={S.commentHeaderRight({ theme })}>
<button>수정</button>
<button>삭제</button>
<button>답글쓰기</button>
</div>
</div>
<div>{comment.content}</div>
</div>
</div>
{comment.child && comment.child.length > 0 && (
<div css={S.commentChildBox()}>
{comment.child.map((childComment) => (
<CommentCard key={childComment.id} comment={childComment} />
))}
</div>
)}
</div>
);
}
7 changes: 7 additions & 0 deletions frontend/src/components/CommentList/ComentList.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { css } from '@emotion/react';

export const commentListBox = () => css`
display: flex;
flex-direction: column;
gap: 1rem;
`;
20 changes: 20 additions & 0 deletions frontend/src/components/CommentList/ComentList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import CommentCard from '@_components/CommentCard/CommentCard';
import * as S from '@_components/CommentList/ComentList.style';
import { Comment } from '@_types/index';
import { HTMLProps } from 'react';

interface CommentListProps extends HTMLProps<HTMLDivElement> {
comments: Comment[];
}

export default function ComentList(props: CommentListProps) {
const { comments } = props;

return (
<div css={S.commentListBox()}>
{comments.map((comment) => {
return <CommentCard key={comment.id} comment={comment} />;
})}
</div>
);
}
53 changes: 53 additions & 0 deletions frontend/src/components/CommentList/CommentList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react';
import ComentList from './ComentList';

const meta = {
component: ComentList,
title: 'Components/ComentList',
} satisfies Meta<typeof ComentList>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
comments: [
{
id: 0,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [
{
id: 0,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [],
},
],
},
{
id: 3,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [
{
id: 4,
nickname: 'nickname',
content: 'content',
dateTime: '2023-04-04 14:00',
src: '',
child: [],
},
],
},
],
},
render: (args) => <ComentList {...args} />,
};
19 changes: 19 additions & 0 deletions frontend/src/components/Input/MessagInput/MessageInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';
import MessageInput from './MessageInput';

const meta = {
component: MessageInput,
title: 'Components/MessageInput',
} satisfies Meta<typeof MessageInput>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
placeHolder: '댓글을 입력해주세요',
onSubmit: () => {},
},
render: (args) => <MessageInput {...args} />,
};
29 changes: 29 additions & 0 deletions frontend/src/components/Input/MessagInput/MessageInput.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { theme } from '@_common/theme/theme.style';
import { css, Theme } from '@emotion/react';

export const formBox = (props: { theme: Theme }) => css`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 4rem;
padding: 1rem;
background: ${props.theme.colorPalette.white[100]};
border: 1px solid ${theme.colorPalette.grey[200]};
border-radius: 50px;
`;

export const input = (props: { theme: Theme }) => css`
${props.theme.typography.b4}
width: 100%;
border: none;
outline: none;
`;

export const button = (props: { theme: Theme }) => css`
width: fit-content;
background-color: ${props.theme.colorPalette.white[100]};
border: none;
`;
43 changes: 43 additions & 0 deletions frontend/src/components/Input/MessagInput/MessageInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as S from '@_components/Input/MessagInput/MessageInput.style';
import { useTheme } from '@emotion/react';

import { useState } from 'react';
import SubmitButton from '@_common/assets/submit_message_button.svg';

export interface MessageInputProps {
placeHolder: string;
onSubmit: (message: string) => void;
}

export default function MessageInput(props: MessageInputProps) {
const { placeHolder, onSubmit } = props;
const [message, setMessage] = useState('');

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setMessage(event.target.value);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(message);
setMessage('');
};
const theme = useTheme();
return (
<form css={S.formBox({ theme })} onSubmit={handleSubmit}>
<input
css={S.input({ theme })}
type="text"
placeholder={placeHolder}
value={message}
onChange={handleChange}
/>
<button
css={S.button({ theme })}
type="submit"
disabled={!message.trim()}
>
<SubmitButton />
</button>
</form>
);
}
2 changes: 1 addition & 1 deletion frontend/src/components/Profile/ProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Participation } from '@_types/index';
interface ProfileCardProps {
profile: Participation;
}
export default function ProfileBox(props: ProfileCardProps) {
export default function ProfileCard(props: ProfileCardProps) {
const { profile } = props;
const theme = useTheme();
return (
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ export interface Participation {

export type Role = 'moimer' | 'moimee';

export interface Comment {
id: number;
nickname: string;
content: string;
dateTime: string;
src: string;
child: Comment[];
}
export type MoimInputInfo = Omit<
MoimInfo,
'moimId' | 'currentPeople' | 'participants'
Expand Down

0 comments on commit c3e95d2

Please sign in to comment.