Skip to content

Commit

Permalink
Post-Detail-Page-퍼블리싱 (#14)
Browse files Browse the repository at this point in the history
* New : tailwind css 적용

* Refactor : 모달 패딩 제거

* Fix : 모달 컴포넌트 이벤트 버블링 방지

* new: 디테일페이지 퍼블리싱

* new : 클라이언트 패스 추가

* Refactor: 해시태그 컴포넌트 분리

* new : 모달페이지 이동 hooks 추가

* New : 검색페이지 라우트 추가

* Fix : 해시태그 클릭시 모달이 닫히지 않는 버그 수정

* Fix : useRouter Mock 추가
  • Loading branch information
jobkaeHenry authored Nov 7, 2023
1 parent 335a186 commit c852214
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 90 deletions.
8 changes: 8 additions & 0 deletions client/src/__test__/post/PostCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const mockData = {
tags: [],
};

jest.mock("next/navigation", () => ({
useRouter() {
return {
prefetch: () => null,
};
},
}));

describe("버튼 컴포넌트 스펙", () => {
beforeEach(() => render(<PostCard {...mockData} />));
it("@유저아이디 형태의 헤더가 존재하는지 체크", () => {
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/@Modal/(.)post/[userId]/[postId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const mockData = {

const page = () => {
return (
<ModalWrapper>
<ModalWrapper disableBox>
<PostDetail {...mockData} />
</ModalWrapper>
);
Expand Down
8 changes: 8 additions & 0 deletions client/src/app/@Modal/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use client";

import { usePathname } from "next/navigation";

export default function Layout({ children }: any) {
const pathname = usePathname();
return pathname.startsWith("/post/") ? children : null;
}
1 change: 1 addition & 0 deletions client/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import OverrideCSS from "@/const/overrideCSS";
import { Box, GlobalStyles } from "@mui/material";
import Pretendard from "~/assets/font/Pretendard";
import NavigationBar from "~/components/NavigationBar";
import "./globals.css";

export const metadata: Metadata = {
title: `${nameOfApp} | ${oneLineMessage}`,
Expand Down
9 changes: 9 additions & 0 deletions client/src/app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const SearchPage = ({
searchParams,
}: {
searchParams?: { [key: string]: string | string[] | undefined };
}) => {
return <div>{searchParams?.keyword}</div>;
};

export default SearchPage;
1 change: 1 addition & 0 deletions client/src/components/ModalWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const ModalWrapper = ({ children, disableBox }: ModalInterface) => {
>
{
<Box
onClick={(e) => e.stopPropagation()}
sx={{
bgcolor: disableBox ? undefined : "background.paper",
p: disableBox ? 0 : 4,
Expand Down
164 changes: 79 additions & 85 deletions client/src/components/post/PostCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { POST_DETAIL } from "@/const/clientPath";

import { PostInterface } from "@/types/post/PostInterface";

import { MoreVertOutlined } from "@mui/icons-material";
Expand All @@ -13,7 +13,9 @@ import {
Typography,
ButtonBase,
} from "@mui/material";
import Link from "next/link";
import PostHashTagList from "./PostHashtagList";
import { useOpenPostDetailPage } from "@/hooks/useOpenPostDetailPage";
import { useMemo } from "react";

const PostCard = ({
image,
Expand All @@ -25,100 +27,92 @@ const PostCard = ({
tags,
id,
}: PostInterface) => {
const openPostDetailPage = useOpenPostDetailPage();
const hasImage = useMemo(() => image.length !== 0, [image]);

return (
<Link href={`${POST_DETAIL(userId, id)}`}>
<Card sx={{ display: "flex", gap: 2, p: 2 }}>
<Avatar
sx={{ bgcolor: "secondary.main" }}
sizes="40"
src={userImage}
data-testid="avatar"
<Card sx={{ display: "flex", gap: 2, p: 2 }}>
<Avatar
sx={{ bgcolor: "secondary.main" }}
sizes="40"
src={userImage}
data-testid="avatar"
>
{userImage || userId[0].toUpperCase()}
</Avatar>
<Box>
{/* Header */}
<Box
data-testid="mui-header"
sx={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
px: 0,
width: "100%",
}}
>
{userImage || userId[0].toUpperCase()}
</Avatar>
<Box>
{/* Header */}
<Box
data-testid="mui-header"
sx={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
px: 0,
width: "100%",
gap: 1,
alighItems: "center",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "row",
gap: 1,
alighItems: "center",
}}
>
<Typography sx={{ fontWeight: "bold" }}>{nickname}</Typography>
<Typography>{`@${userId}`}</Typography>
<Typography>{createdAt}</Typography>
</Box>

<ButtonBase aria-label="settings" sx={{ p: 0 }}>
<MoreVertOutlined />
</ButtonBase>
{/* 타이틀 */}
<Typography sx={{ fontWeight: "bold" }}>{nickname}</Typography>
<Typography>{`@${userId}`}</Typography>
<Typography>{createdAt}</Typography>
</Box>

{/* Contents */}
<CardContent sx={{ px: 0 }}>
<Typography variant="body1">{content}</Typography>
{/* Hash tags */}
{tags?.length > 0 && (
<Box
data-testid="tags"
sx={{
pt: 2,
display: "flex",
flexDirection: "row",
gap: "8px",
}}
>
{tags.map((tag, i) => (
<Typography
component={"span"}
variant={"label"}
color="text.secondary"
key={i}
>
{`#${tag}`}
</Typography>
))}
</Box>
)}
</CardContent>
{/* image */}
{image.length !== 0 && (
<CardMedia
data-testid="postImg"
component="img"
height="142"
image={image[0]}
alt={`${userId}의 포스트`}
sx={{ borderRadius: 2, bgcolor: "background.default" }}
/>
)}
{/* CTA */}
<CardActions sx={{ px: 0, justifyContent: "end", gap: 2 }}>
<ButtonBase data-testid="commentBtn" aria-label="share">
<Typography>댓글</Typography>
</ButtonBase>
<ButtonBase data-testid="likeBtn" aria-label="add to favorites">
<Typography>좋아요</Typography>
</ButtonBase>
<ButtonBase data-testid="shareBtn" aria-label="share">
<Typography>공유하기</Typography>
</ButtonBase>
</CardActions>
<ButtonBase aria-label="settings" sx={{ p: 0 }}>
<MoreVertOutlined />
</ButtonBase>
</Box>
</Card>
</Link>

<CardContent sx={{ px: 0 }}>
{/* Contents */}
<Typography
variant="body1"
className={hasImage ? "line-clamp-2" : "line-clamp-5"}
onClick={() => openPostDetailPage(userId, id)}
>
{content}
</Typography>

{/* Hash tags */}
<PostHashTagList tags={tags} />
</CardContent>

{/* image */}
{hasImage && (
<CardMedia
data-testid="postImg"
component="img"
height="142"
onClick={() => openPostDetailPage(userId, id)}
image={image[0]}
alt={`${userId}의 포스트`}
sx={{ borderRadius: 2, bgcolor: "background.default" }}
/>
)}
{/* CTA */}
<CardActions sx={{ px: 0, justifyContent: "end", gap: 2 }}>
<ButtonBase data-testid="commentBtn" aria-label="share">
<Typography onClick={() => openPostDetailPage(userId, id)}>
댓글
</Typography>
</ButtonBase>
<ButtonBase data-testid="likeBtn" aria-label="add to favorites">
<Typography>좋아요</Typography>
</ButtonBase>
<ButtonBase data-testid="shareBtn" aria-label="share">
<Typography>공유하기</Typography>
</ButtonBase>
</CardActions>
</Box>
</Card>
);
};

Expand Down
55 changes: 54 additions & 1 deletion client/src/components/post/PostDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { PostInterface } from "@/types/post/PostInterface";
import {
Avatar,
ButtonBase,
Card,
CardActions,
CardContent,
CardHeader,
CardMedia,
TextField,
Typography,
} from "@mui/material";
import PostHashTagList from "./PostHashtagList";

const PostDetail = ({
image,
Expand All @@ -10,6 +22,47 @@ const PostDetail = ({
tags,
id,
}: PostInterface) => {
return <>{userId}</>;
return (
<Card sx={{ p: 4 }}>
<CardHeader
avatar={
<Avatar
sx={{ bgcolor: "secondary.main" }}
src={userImage}
data-testid="avatar"
>
{userImage || userId[0].toUpperCase()}
</Avatar>
}
title={`${userId} ${nickname}`}
subheader={createdAt}
sx={{ p: 0 }}
/>
<CardContent sx={{ px: 0 }}>
<Typography variant="body1">{content}</Typography>
<PostHashTagList tags={tags} />
</CardContent>
{/* 이미지 */}
{image.length !== 0 && (
<CardMedia
data-testid="postImg"
component="img"
height="360px"
image={image[0]}
alt={`${userId}의 포스트`}
sx={{ borderRadius: 2, bgcolor: "background.default" }}
/>
)}
<CardActions sx={{ justifyContent: "end", gap: 2, px: 0 }}>
<ButtonBase data-testid="likeBtn" aria-label="add to favorites">
<Typography>좋아요</Typography>
</ButtonBase>
<ButtonBase data-testid="shareBtn" aria-label="share">
<Typography>공유하기</Typography>
</ButtonBase>
</CardActions>
<TextField label="댓글" fullWidth></TextField>
</Card>
);
};
export default PostDetail;
40 changes: 40 additions & 0 deletions client/src/components/post/PostHashtagList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SEARCH_BY_KEYWORD } from "@/const/clientPath";
import { PostInterface } from "@/types/post/PostInterface";
import { Box, BoxProps, Typography } from "@mui/material";
import Link from "next/link";

interface TagListInterface extends BoxProps {
tags: PostInterface["tags"];
}
const PostHashTagList = ({ tags, ...others }: TagListInterface) => {
return (
<>
{tags?.length > 0 && (
<Box
data-testid="tags"
sx={{
pt: 2,
display: "flex",
flexDirection: "row",
gap: "8px",
}}
{...others}
>
{tags.map((tag) => (
<Link href={SEARCH_BY_KEYWORD(tag)} key={tag}>
<Typography
component={"span"}
variant={"label"}
color="text.secondary"
>
{`#${tag}`}
</Typography>
</Link>
))}
</Box>
)}
</>
);
};

export default PostHashTagList;
17 changes: 14 additions & 3 deletions client/src/const/clientPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@ export const FORGOTPASSWORD = "/auth/forgot-password" as const;
/**
* 로그인 했을 경우만 접근 가능한 마이페이지
*/
export const MY_PROFILE = "/user" as const
export const MY_PROFILE = "/user" as const;
/**
* 술과사전 페이지 라우트
*/
export const WIKI = "/wiki" as const
export const WIKI = "/wiki" as const;
/**
* 검색 페이지 라우트
*/
export const SEARCH = "/search" as const;
/**
* 키워드를 인자로 받아 쿼리스트링이 추가된 검색페이지 라우트
* @param keyword
* @returns
*/
export const SEARCH_BY_KEYWORD = (keyword: string) =>
`${SEARCH}?keyword=${keyword}`;

/**
* 유저아이디와 게시글 아이디를 입력받아 /post/@userId/postId 형태의 path를 리턴
Expand All @@ -29,7 +40,7 @@ export const WIKI = "/wiki" as const
export const POST_DETAIL = (userId: string, postId: string) => {
const trimmedUserId = userId.trim();
const trimmedPostId = postId.trim();

return `/post/@${trimmedUserId}/${trimmedPostId}`;
};

Expand Down
Loading

0 comments on commit c852214

Please sign in to comment.