Skip to content

Commit

Permalink
Merge pull request #54 from kkkkkSE/feat/open-profile
Browse files Browse the repository at this point in the history
[feat] 오픈 프로필(단일) 페이지 구현 및 테스트 코드 작성
  • Loading branch information
kkkkkSE authored Aug 21, 2023
2 parents 4d8ecb4 + c82c84e commit 2fd6fb5
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 8 deletions.
39 changes: 39 additions & 0 deletions src/components/profile/OpenProfile.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { fireEvent, screen } from '@testing-library/react';

import fixtures from '../../../fixtures';

import { render } from '../../test-helper';

import OpenProfile from './OpenProfile';

const context = describe;

const handleClickInquery = jest.fn();

describe('<OpenProfile />', () => {
const openProfile = fixtures.companies[0];

it('renders open profile', () => {
render(<OpenProfile
openProfile={openProfile}
onClickInquiry={handleClickInquery}
/>);

screen.getByText(/기업 설명1/);
});

context('when click inquery button', () => {
it('execute handleClickInquiry function', () => {
render(<OpenProfile
openProfile={openProfile}
onClickInquiry={handleClickInquery}
/>);

const button = screen.getByText(/문의하기/);

fireEvent.click(button);

expect(handleClickInquery).toHaveBeenCalled();
});
});
});
98 changes: 98 additions & 0 deletions src/components/profile/OpenProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import styled from 'styled-components';

import { Profile } from '../../types';

import ProfileBody from './ProfileBody';

import inquiryIcon from '../../assets/image/icon/inquiry-icon.png';

interface OpenProfileProps {
openProfile: Profile;
onClickInquiry: () => void;
}

export default function OpenProfile({
openProfile, onClickInquiry,
}: OpenProfileProps) {
return (
<Container>
<ProfileBody profile={openProfile} />

<ul>
<li>
<button
type="button"
onClick={onClickInquiry}
>
<div><img src={inquiryIcon} alt="" /></div>
<span>문의하기</span>
</button>
</li>
</ul>
</Container>
);
}

const Container = styled.div`
${(props) => props.theme.alignCenter.vertical}
justify-content: start;
overflow-y: scroll;
ul {
display: flex;
padding-top: 6rem;
li {
margin-inline: 2.4rem;
button {
${(props) => props.theme.alignCenter.vertical}
div {
${(props) => props.theme.alignCenter.horizontal}
width: 6rem;
height: 6rem;
background-color: ${(props) => props.theme.colors.gray2.default};
border-radius: 50%;
margin-bottom: 1.2rem;
img {
width: 3.2rem;
height: 3.2rem;
}
}
span {
${(props) => props.theme.texts.regular.medium}
}
}
}
}
@media screen and (${(props) => props.theme.breakPoint.mobile}) {
ul {
padding-top: 3.8rem;
li {
margin-inline: 2rem;
button {
div {
width: 4.8rem;
height: 4.8rem;
margin-bottom: .8rem;
img {
width: 2.4rem;
height: 2.4rem;
}
}
span {
${(props) => props.theme.texts.regular.small}
}
}
}
}
}
`;
9 changes: 7 additions & 2 deletions src/components/profile/OpenProfileList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { useNavigate } from 'react-router-dom';
import { DYNAMIC_ROUTES } from '../../constants/routes';

import useOpenProfileInfiniteQuery from '../../hooks/useOpenProfileInfiniteQuery';

import { Profile } from '../../types';

import OpenProfileListRow from './OpenProfileListRow';

export default function OpenProfileList() {
const navigate = useNavigate();

const {
ref, isLoading, data, error,
} = useOpenProfileInfiniteQuery();
Expand All @@ -25,8 +30,8 @@ export default function OpenProfileList() {
return <p>등록된 오픈 프로필이 없습니다.</p>;
}

const handleClickProfile = () => {
// ...
const handleClickProfile = (id: number) => {
navigate(DYNAMIC_ROUTES.OPEN_PROFILE(id));
};

return (
Expand Down
19 changes: 18 additions & 1 deletion src/components/profile/OpenProfileListRow.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { screen } from '@testing-library/react';
import { fireEvent, screen } from '@testing-library/react';

import fixtures from '../../../fixtures';

import { render } from '../../test-helper';

import OpenProfileListRow from './OpenProfileListRow';

const context = describe;

const handleClickProfile = jest.fn();

describe('<OpenProfileListRow />', () => {
Expand All @@ -19,4 +21,19 @@ describe('<OpenProfileListRow />', () => {

screen.getByText(/기업명1/);
});

context('when click open profile', () => {
it('execute handleClickProfile function', () => {
render(<OpenProfileListRow
openProfile={openProfile}
handleClickProfile={handleClickProfile}
/>);

const element = screen.getByText(/기업명1/);

fireEvent.click(element);

expect(handleClickProfile).toHaveBeenCalledWith(openProfile.id);
});
});
});
1 change: 1 addition & 0 deletions src/constants/reactQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export const QUERY_KEY = {
CHAT_LIST: ['chat-list'],
AUTO_REPLY_ADMIN_LIST: ['auto-reply-admin'],
OPEN_PROFILE_LIST: ['open-profile-list'],
OPEN_PROFILE: (id: number) => ['open-profile', id],
};
21 changes: 21 additions & 0 deletions src/hooks/useOpenProfileQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query';

import { QUERY_KEY } from '../constants/reactQuery';

import { apiService } from '../services/ApiService';

import { nullProfile } from '../types';

const useOpenProfileQuery = (id: number) => {
const fetcher = () => apiService.fetchOpenProfile({ id });

const { isLoading, data, error } = useQuery(QUERY_KEY.OPEN_PROFILE(id), fetcher);

return {
isLoading,
openProfile: data ?? nullProfile,
error,
};
};

export default useOpenProfileQuery;
7 changes: 5 additions & 2 deletions src/mocks/handlers/customer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,11 @@ const customerHandlers = [
);
}),
rest.get(`${BASE_URL}/companies/:id`, (req, res, ctx) => {
const id = Number(req.params.id);

const { companies } = fixtures;
const company = companies[0];

const index = companies.findIndex((item) => item.id === id);

const authorization = req.headers.get('Authorization');
const accessToken = authorization ? authorization.split(' ')[1] : '';
Expand All @@ -217,7 +220,7 @@ const customerHandlers = [

return res(
ctx.status(200),
ctx.json(company),
ctx.json(companies[index]),
);
}),
];
Expand Down
File renamed without changes.
52 changes: 52 additions & 0 deletions src/pages/OpenProfilePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useParams } from 'react-router-dom';

import useOpenProfileQuery from '../hooks/useOpenProfileQuery';

import ContentLayout from '../components/layout/ContentLayout';
import OpenProfile from '../components/profile/OpenProfile';

export default function OpenProfilePage() {
const params = useParams();
const id = Number(params.id);

// TODO : id 없을 때 not found 페이지 이동
if (!id) {
return (
<div>
<p>Not Found</p>
</div>
);
}

const { isLoading, openProfile, error } = useOpenProfileQuery(id);

// TODO : Error Page로 이동하기
if (error) {
return (
<div>
<p>데이터를 불러올 수 없습니다.</p>
</div>
);
}

const handleClickInquiry = () => {
// ...
};

return (
<ContentLayout
pageHeader={openProfile.name}
testId="open-profile"
enableBack
>
{isLoading ? (
<p>Loading...</p>
) : (
<OpenProfile
openProfile={openProfile}
onClickInquiry={handleClickInquiry}
/>
)}
</ContentLayout>
);
}
6 changes: 4 additions & 2 deletions src/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@ import ChatRoomPage from './ChatRoomPage';
import HomePage from './HomePage';
import LoginPage from './LoginPage';
import ProfileEditPage from './ProfileEditPage';
import ProfilePage from './ProfilePage';
import MyProfilePage from './MyProfilePage';
import SignUpPage from './SignUpPage';
import AutoReplyAdminPage from './AutoReplyAdminPage';
import AutoReplyNewPage from './AutoReplyNewPage';
import AutoReplyEditPage from './AutoReplyEditPage';
import OpenProfileListPage from './OpenProfileListPage';
import OpenProfilePage from './OpenProfilePage';

export default {
ChatListPage,
ChatRoomPage,
HomePage,
LoginPage,
ProfileEditPage,
ProfilePage,
MyProfilePage,
SignUpPage,
AutoReplyAdminPage,
AutoReplyNewPage,
AutoReplyEditPage,
OpenProfileListPage,
OpenProfilePage,
};
18 changes: 18 additions & 0 deletions src/routes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,23 @@ describe('routes', () => {
screen.getByTestId(/auto-reply-edit/);
});
});

context('when the current path is "/open-profiles"', () => {
it('renders <OpenProfileListPage />', () => {
setupRouterProvider(STATIC_ROUTES.OPEN_PROFILES);

screen.getByTestId(/open-profile-list/);
});
});

context('when the current path is "/open-profiles/:id"', () => {
it('renders <OpenProfilePage />', () => {
const id = 1;

setupRouterProvider(DYNAMIC_ROUTES.OPEN_PROFILE(id));

screen.getByTestId(/open-profile/);
});
});
});
});
3 changes: 2 additions & 1 deletion src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ const routes = [
children: [
{ path: STATIC_ROUTES.CHATROOMS, element: <pages.ChatListPage /> },
{ path: `${STATIC_ROUTES.CHATROOMS}/:id`, element: <pages.ChatRoomPage /> },
{ path: STATIC_ROUTES.MY_PROFILE, element: <pages.ProfilePage /> },
{ path: STATIC_ROUTES.MY_PROFILE, element: <pages.MyProfilePage /> },
{ path: STATIC_ROUTES.MY_PROFILE_EDIT, element: <pages.ProfileEditPage /> },
{ path: STATIC_ROUTES.AUTO_REPLIES, element: <pages.AutoReplyAdminPage /> },
{ path: STATIC_ROUTES.AUTO_REPLIES_NEW, element: <pages.AutoReplyNewPage /> },
{ path: `${STATIC_ROUTES.AUTO_REPLIES}/:id`, element: <pages.AutoReplyEditPage /> },
{ path: STATIC_ROUTES.OPEN_PROFILES, element: <pages.OpenProfileListPage /> },
{ path: `${STATIC_ROUTES.OPEN_PROFILES}/:id`, element: <pages.OpenProfilePage /> },
],
},
];
Expand Down
6 changes: 6 additions & 0 deletions src/services/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ export default class ApiService {
return data;
}

async fetchOpenProfile({ id } : { id : number}) {
const { data } = await this.instance.get(DYNAMIC_API_PATHS.OPEN_PROFILE(id));

return data;
}

async uploadImage({ formData }:{
formData: FormData;
}) {
Expand Down

0 comments on commit 2fd6fb5

Please sign in to comment.