Skip to content

Commit 133d8ae

Browse files
committed
fix: fix build error
1 parent d9fda85 commit 133d8ae

File tree

11 files changed

+658
-1
lines changed

11 files changed

+658
-1
lines changed

src/pages/reviews/Review.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { render, screen } from '@testing-library/react'
2+
import Reviews from '.';
3+
import { createWrapper } from 'tests/test-utils';
4+
5+
describe('Review 페이지', () => {
6+
it('Review 페이지가 렌더링 되어야한다.', () => {
7+
// Review 페이지가 렌더링 되는지 확인
8+
render(<Reviews />, { wrapper: createWrapper() });
9+
10+
expect(screen.getByText(/Reviews/i)).toBeInTheDocument();
11+
})
12+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { render, screen } from '@testing-library/react';
2+
import Card from './Card';
3+
4+
import { reviewsMockData } from 'mocks/__fixtures__/reviews';
5+
6+
describe('Card Components', () => {
7+
const cardData = reviewsMockData.list.response.reviews[0];
8+
9+
/**
10+
* TODO: 백엔드 API 확정시 Image는 확인
11+
* Card 컴포넌트가 렌더 되는지 확인하는 테스트를 작성합니다.
12+
*/
13+
describe('Card 컴포넌트가 렌더되어야한다.', () => {
14+
it('Card 모든 props가 렌더링 되어야한다.', () => {
15+
render(
16+
<Card {...cardData} />
17+
);
18+
19+
expect(screen.getByText(cardData.reviewDate)).toBeInTheDocument();
20+
expect(screen.getByText(cardData.content)).toBeInTheDocument();
21+
expect(screen.getByText(cardData.member.nickname)).toBeInTheDocument();
22+
expect(screen.getByText(cardData.menus.name)).toBeInTheDocument();
23+
expect(screen.getByText(cardData.content)).toBeInTheDocument();
24+
})
25+
});
26+
})

src/pages/reviews/components/Card.tsx

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/** @jsxImportSource @emotion/react */
2+
import { css } from "@emotion/react";
3+
4+
import colors from 'styles/color';
5+
import StarRating from 'pages/reviews/components/StarRating';
6+
7+
import { hexToRgba } from 'utils/hexToRgba';
8+
import { CardProps } from './types';
9+
import { getProfileImg } from 'utils/getProfileImg';
10+
11+
12+
const Card = ({ content, member, menus, reviewDate, grade }: CardProps) => {
13+
const { profileImage, nickname } = member;
14+
const { name } = menus;
15+
16+
return (
17+
<div css={_cardContainer}>
18+
<div css={_reviewContainer}>
19+
<div css={_profileContainer}>
20+
<div>{getProfileImg(profileImage)}</div>
21+
<div css={_contentsContainer}>
22+
<div css={_menuContainer}>
23+
<div css={_nickname}>{nickname}</div>
24+
<div css={_menuTag}>{name}</div>
25+
</div>
26+
<div css={_reviewDate}>{reviewDate}</div>
27+
<div css={_reviewContent}>
28+
<div>{content}</div>
29+
</div>
30+
</div>
31+
</div>
32+
33+
<div css={_gradeContainer}>
34+
<div css={_grade}>{grade.toFixed(1)}</div>
35+
<StarRating rating={grade} />
36+
</div>
37+
</div>
38+
</div>
39+
)
40+
}
41+
42+
export default Card;
43+
44+
45+
const _cardContainer = css`
46+
border: 1px solid ${colors.icy};
47+
padding: 16px;
48+
margin: 16px 0;
49+
border-radius: 8px;
50+
background-color: ${colors.white};
51+
max-width: 100%; // 하단 가로 스크롤 방지
52+
overflow: hidden;
53+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
54+
55+
@media (max-width: 768px) { // 테블릿 대응
56+
padding: 12px;
57+
}
58+
59+
@media (max-width: 480px) { // 모바일 대응
60+
padding: 8px;
61+
}
62+
`;
63+
64+
const _menuContainer = css`
65+
display: flex;
66+
gap: 8px;
67+
align-items: center;
68+
`;
69+
70+
const _reviewContainer = css`
71+
display: flex;
72+
justify-content: space-between;
73+
align-items: center;
74+
flex-wrap: wrap; // 반응형을 위해 요소들을 줄 바꿈
75+
gap: 16px; // 간격을 설정하여 반응형 레이아웃에서의 간격 유지
76+
77+
@media (max-width: 768px) {
78+
flex-direction: column; // 테블릿에서 세로 정렬
79+
align-items: flex-start;
80+
}
81+
`;
82+
83+
const _profileContainer = css`
84+
display: flex;
85+
gap: 16px;
86+
align-items: flex-start;
87+
width: 85%;
88+
89+
@media (max-width: 480px) { // 모바일 대응
90+
gap: 12px;
91+
}
92+
`;
93+
94+
const _contentsContainer = css`
95+
display: flex;
96+
flex-direction: column;
97+
gap: 8px;
98+
max-width: 100%;
99+
100+
@media (max-width: 480px) {
101+
gap: 6px;
102+
}
103+
`;
104+
105+
const _nickname = css`
106+
font-size: 1rem;
107+
font-weight: bold;
108+
color: #333;
109+
110+
@media (max-width: 480px) {
111+
font-size: 0.9rem;
112+
}
113+
`;
114+
115+
const _menuTag = css`
116+
background-color: ${hexToRgba(colors.brightRed, 0.1)};
117+
color: ${colors.brightRed};
118+
font-size: 0.875rem;
119+
padding: 4px 8px;
120+
border-radius: 12px;
121+
display: inline-block;
122+
margin-top: 4px;
123+
width: fit-content;
124+
125+
@media (max-width: 480px) {
126+
font-size: 0.75rem;
127+
padding: 3px 6px;
128+
}
129+
`;
130+
131+
const _reviewDate = css`
132+
font-size: 0.875rem;
133+
color: #888;
134+
135+
@media (max-width: 480px) {
136+
font-size: 0.75rem;
137+
}
138+
`;
139+
140+
const _reviewContent = css`
141+
font-size: 0.875rem;
142+
color: #555;
143+
margin-top: 8px;
144+
145+
@media (max-width: 480px) {
146+
font-size: 0.75rem;
147+
}
148+
`;
149+
150+
const _gradeContainer = css`
151+
display: flex;
152+
flex-direction: column;
153+
align-items: center;
154+
justify-content: center;
155+
width: 10%;
156+
157+
@media (max-width: 768px) {
158+
align-self: flex-end; // 테블릿에서 오른쪽 정렬
159+
}
160+
161+
@media (max-width: 480px) {
162+
align-self: flex-start; // 모바일에서는 세로로 정렬
163+
margin-top: 12px;
164+
}
165+
`;
166+
167+
const _grade = css`
168+
font-size: 1.5rem;
169+
font-weight: bold;
170+
color: #333;
171+
margin-bottom: 4px;
172+
173+
@media (max-width: 480px) {
174+
font-size: 1.2rem;
175+
}
176+
`;
177+
178+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { render, renderHook, screen, waitFor } from '@testing-library/react';
2+
import CardList from 'pages/reviews/components/CardList';
3+
import { createWrapper } from 'tests/test-utils';
4+
5+
import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery';
6+
import { reviewsMockData } from 'mocks/__fixtures__';
7+
8+
describe('CardList Components', () => {
9+
it('CardList 컴포넌트가 렌더되어야한다.', async () => {
10+
render(<CardList />, { wrapper: createWrapper() });
11+
12+
expect(screen.getByText(/ /i)).toBeInTheDocument();
13+
})
14+
15+
it('useGetStoresReview API를 가져온 후 Card가 렌더링 되어야한다.', async () => {
16+
render(<CardList />, { wrapper: createWrapper() });
17+
18+
const { result } = renderHook(() => useGetStoresReview(1), { wrapper: createWrapper() });
19+
20+
// useGetStoresReview 훅을 사용하여 API 호출 확인
21+
await waitFor(() => {
22+
expect(result.current.isSuccess).toBe(true);
23+
})
24+
25+
// 데이터가 로드되고, Card 컴포넌트들이 렌더링되는지 확인
26+
await waitFor(() => {
27+
reviewsMockData.list.response.reviews.forEach((review) => {
28+
// 각 리뷰 내용이 화면에 존재하는지 확인
29+
expect(screen.getByText(review.member.nickname)).toBeInTheDocument();
30+
expect(screen.getByText(review.content)).toBeInTheDocument();
31+
});
32+
});
33+
})
34+
})
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/** @jsxImportSource @emotion/react */
2+
import { css } from "@emotion/react";
3+
import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery';
4+
import Card from './Card';
5+
6+
import get from 'lodash/get';
7+
import isUndefined from 'lodash/isUndefined';
8+
import StarRating from 'pages/reviews/components/StarRating';
9+
10+
const CardList = () => {
11+
const { data: storeReviewData } = useGetStoresReview(1);
12+
13+
const reviews = get(storeReviewData, ['response', 'reviews']) ;
14+
const grade = get(storeReviewData, ['response', 'grade']);
15+
16+
return (
17+
<div css={_container}>
18+
<div css={_header}>
19+
<div css={_score}>내 음식점 리뷰 평균 평점</div>
20+
<StarRating rating={grade ?? 0} size={18} />
21+
</div>
22+
{isUndefined(reviews) ?
23+
<div css={__emptyReviewContainer}>
24+
<div css={_emptyReview}>리뷰가 없습니다.</div>
25+
</div> :
26+
<>
27+
{reviews?.map((review) => (
28+
<Card
29+
key={review.id}
30+
grade={review.grade}
31+
member={review.member}
32+
menus={review.menus}
33+
content={review.content}
34+
images={review.images}
35+
reviewDate={review.reviewDate}
36+
/>
37+
))}
38+
</>
39+
}
40+
</div>
41+
)
42+
}
43+
44+
export default CardList;
45+
46+
const _container = css`
47+
display: flex;
48+
flex-direction: column;
49+
margin-top: 20px;
50+
`;
51+
52+
const _header = css`
53+
display: flex;
54+
gap: 10px;
55+
`;
56+
57+
const _score = css`
58+
font-size: 1.2rem;
59+
font-weight: bold;
60+
color: #333;
61+
62+
@media (max-width: 480px) {
63+
font-size: 1.0rem;
64+
}
65+
`
66+
67+
const _emptyReview = css`
68+
font-size: 1.0rem;
69+
color: #888;
70+
margin-top: 20px;
71+
`
72+
73+
const __emptyReviewContainer = css`
74+
display: flex;
75+
justify-content: center;
76+
align-items: center;
77+
height: 100px;
78+
font-size: 1.0rem;
79+
color: #888;
80+
margin-top: 20px;
81+
`

0 commit comments

Comments
 (0)