Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

공지사항 목록 컴포넌트 / 공지사항 상세 컴포넌트 UI 구현 / 공지사항 ROUTER 설정 #749

Merged
merged 21 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7a9a4a4
feat: (#741) 모바일 버전 공지사항 목록 구현
Gilpop8663 Oct 13, 2023
7f841f3
feat: (#741) PC 버전 공지사항 리스트 스타일 구현
Gilpop8663 Oct 13, 2023
efc71a3
feat: (#741) 공지사항 세부 내용 컴포넌트 구현
Gilpop8663 Oct 15, 2023
b69cf4b
feat: 공지사항 리스트 페이지 구현
Gilpop8663 Oct 15, 2023
de57115
feat: (#741) 공지사항 세부 페이지 구현
Gilpop8663 Oct 15, 2023
962d1ae
feat: (#741) 공지사항 목록, 상세 페이지 router 설정
Gilpop8663 Oct 15, 2023
5b1f7f9
feat: (#741) 공지사항 웹 접근성 개선
Gilpop8663 Oct 15, 2023
6118863
style: (#741) 공지사항 목록에서는 채널톡 안보이도록 설정 및 padding 속성 설정
Gilpop8663 Oct 15, 2023
94de948
style: (#741) 공지사항 목록 ...이 짧게 설정되어서 width 값 재설정
Gilpop8663 Oct 15, 2023
3c60fa1
chore: (#741) 디자인 변경으로 인한 공지사항 보러가기 삭제
Gilpop8663 Oct 16, 2023
4795a40
Merge branch 'dev' of https://github.com/woowacourse-teams/2023-votog…
Gilpop8663 Oct 17, 2023
98375d0
refactor: (#741) 홈과 공지사항 목록 url을 PATH로 변경
Gilpop8663 Oct 17, 2023
4000618
refactor :(#741) 공지사항 목록을 감싸는 부모를 div에서 main으로 변경
Gilpop8663 Oct 17, 2023
bb9881b
refactor: (#741) 날짜를 string에서 StringDate로 타입 변경
Gilpop8663 Oct 17, 2023
0f730f9
style: (#741) 데스크탑일때 홈으로 버튼 안보이도록 설정
Gilpop8663 Oct 17, 2023
9ce18cd
chore: (#741) 읽은 사람에게 혼동을 주는 주석 제거
Gilpop8663 Oct 17, 2023
41aec0a
chore: (#741) router의 :id를 :noticeId로 이름 변경
Gilpop8663 Oct 17, 2023
284b2e1
refactor: (#741) StringDateOnly 타입 추가 및 api에서 생성 날짜를 변환시켜 날짜만 존재하도록 수정
Gilpop8663 Oct 17, 2023
60464df
refactor: (#741) Response를 Omit하여 만든 타입의 가독성을 높이기 위해 Response 타입을 이동
Gilpop8663 Oct 17, 2023
fd69f50
refactor: (#741) StringDateOnly Type 이름 변경
Gilpop8663 Oct 18, 2023
4d83d1a
chore: (#741) dev 브런치와 충돌 해결
Gilpop8663 Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions frontend/__test__/api/notice.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NoticeRequest } from '@type/notice';

import {
createNotice,
deleteNotice,
Expand All @@ -12,7 +14,7 @@ import { MOCK_NOTICE_TEST } from '@mocks/notice';

describe('서버와 통신하여 공지사항 관련된 api를 통신할 수 있어야 한다. ', () => {
test('공지사항을 생성한다.', async () => {
const data = {
const data: NoticeRequest = {
title: '갤럭시',
content: '공지사항입니다',
deadline: '2023-10-12 15:13',
Expand Down Expand Up @@ -45,8 +47,15 @@ describe('서버와 통신하여 공지사항 관련된 api를 통신할 수 있
expect(result).toEqual(MOCK_TRANSFORM_NOTICE);
});

test('공지 사항의 createdAt을 날짜만 받도록 변환시킨다.', async () => {
const createdAt = '2023-08-19 08:23';
const result = '2023-08-19';

expect(createdAt.split(' ')[0]).toBe(result);
});

test('공지 사항을 수정한다.', async () => {
const data = {
const data: NoticeRequest = {
title: '아이폰입니다',
content: '공지사항입니다',
deadline: '2023-10-12 15:13',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { ReactNode } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';

import { NoticeRequest } from '@type/notice';

import { useCreateNotice } from '@hooks';

import { MOCK_NOTICE_TEST } from '@mocks/notice';
Expand All @@ -15,7 +17,7 @@ const wrapper = ({ children }: { children: ReactNode }) => (

describe('공지 사항을 생성하는 지 확인한다.', () => {
test('공지 사항을 생성한다.', async () => {
const data = {
const data: NoticeRequest = {
title: '갤럭시',
content: '공지사항입니다',
deadline: '2023-10-12 15:13',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { ReactNode } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';

import { NoticeRequest } from '@type/notice';

import { useModifyNotice } from '@hooks';

import { MOCK_NOTICE_TEST } from '@mocks/notice';
Expand All @@ -15,7 +17,7 @@ const wrapper = ({ children }: { children: ReactNode }) => (

describe('요청을 통해 공지 사항을 수정하는 지 확인한다.', () => {
test('공지 사항을 생성한다.', async () => {
const data = {
const data: NoticeRequest = {
title: '아이폰입니다',
content: '공지사항입니다',
deadline: '2023-10-12 15:13',
Expand Down
25 changes: 12 additions & 13 deletions frontend/src/api/notice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Notice, NoticeList } from '@type/notice';
import {
Notice,
NoticeList,
NoticeListResponse,
NoticeRequest,
NoticeResponse,
} from '@type/notice';
import { StringDateOnly } from '@type/time';

import { deleteFetch, getFetch, patchFetch, postFetch } from '@utils/fetch';

Expand All @@ -10,34 +17,26 @@ export const transformNotice = ({
deadline,
bannerTitle,
bannerSubtitle,
}: Notice): Notice => {
}: NoticeResponse): Notice => {
return {
id,
title,
content,
createdAt,
createdAt: createdAt.split(' ')[0] as StringDateOnly,
deadline,
bannerTitle,
bannerSubtitle,
};
};

export interface NoticeListResponse {
totalPageNumber: number;
currentPageNumber: number;
notices: Notice[];
}

export type NoticeRequest = Omit<Notice, 'createdAt' | 'id'>;

const BASE_URL = process.env.VOTOGETHER_BASE_URL ?? '';

export const createNotice = async (notice: NoticeRequest) => {
await postFetch(`${BASE_URL}/notices`, notice);
};

export const getBannerNotice = async () => {
const bannerNotice = await getFetch<Notice>(`${BASE_URL}/notices/progress`);
const bannerNotice = await getFetch<NoticeResponse>(`${BASE_URL}/notices/progress`);

return transformNotice(bannerNotice);
};
Expand All @@ -53,7 +52,7 @@ export const getNoticeList = async (page: number): Promise<NoticeList> => {
};

export const getNoticeDetail = async (noticeId: number) => {
const noticeDetail = await getFetch<Notice>(`${BASE_URL}/notices/${noticeId}`);
const noticeDetail = await getFetch<NoticeResponse>(`${BASE_URL}/notices/${noticeId}`);

return transformNotice(noticeDetail);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Meta, StoryObj } from '@storybook/react';

import { MOCK_TRANSFORM_NOTICE } from '@mocks/mockData/notice';

import NoticeDetail from '.';

const meta: Meta<typeof NoticeDetail> = {
component: NoticeDetail,
};

export default meta;
type Story = StoryObj<typeof NoticeDetail>;

export const Default: Story = {
render: () => <NoticeDetail notice={MOCK_TRANSFORM_NOTICE} />,
};
48 changes: 48 additions & 0 deletions frontend/src/components/notice/NoticeDetail/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useNavigate } from 'react-router-dom';

import { Notice } from '@type/notice';

import SquareButton from '@components/common/SquareButton';

import { PATH } from '@constants/path';

import * as S from './style';

interface NoticeDetailProps {
notice: Notice;
}

export default function NoticeDetail({ notice: { title, content, createdAt } }: NoticeDetailProps) {
const navigate = useNavigate();

return (
<S.Container>
<S.Category tabIndex={0}>VoTogether 공지사항</S.Category>
<S.Title tabIndex={0}>{title}</S.Title>
<S.CreatedAt tabIndex={0}>작성일 : {createdAt}</S.CreatedAt>
<S.Content tabIndex={0}>{content}</S.Content>
<S.ButtonContainer>
<S.ButtonWrapper>
<SquareButton
theme="fill"
onClick={() => {
navigate(PATH.HOME);
}}
>
홈으로 가기
</SquareButton>
</S.ButtonWrapper>
<S.ButtonWrapper>
<SquareButton
theme="blank"
onClick={() => {
navigate(PATH.NOTICES);
}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PATH.NOTICE 어떠신가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 제안 감사합니다

>
{`공지사항\n목록으로 가기`}
</SquareButton>
</S.ButtonWrapper>
</S.ButtonContainer>
</S.Container>
);
}
77 changes: 77 additions & 0 deletions frontend/src/components/notice/NoticeDetail/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { styled } from 'styled-components';

import { theme } from '@styles/theme';

export const Container = styled.div`
display: flex;
flex-direction: column;

width: 100%;
max-width: 600px;
padding-top: 48px;

@media (min-width: ${theme.breakpoint.sm}) {
padding-top: 30px;
}
`;

export const Category = styled.span`
font: var(--text-body);
`;

export const Title = styled.h1`
margin-top: 20px;

font: var(--text-title);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 조금 작은거 같은데 제가 알림 PR에다가 이거보다 큰 --text-page-title인가,, 만들었는데 나중에 그걸로 바꿔도 좋을 것 같아요.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋아요!

`;

export const CreatedAt = styled.span`
margin: 20px 0;

font: var(--text-body);
font-size: 1.4rem;
text-align: right;

color: var(--text-dark-gray);
`;

export const Content = styled.p`
font: var(--text-body);

white-space: pre-wrap;
`;

export const ButtonContainer = styled.div`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데스크탑에서는 하단에 홈으로 가기 버튼이 없어도 괜찮을 것 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래와 같이 수정해보았습니다~

image

image

display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 20px;

margin-top: 50px;

@media (min-width: ${theme.breakpoint.sm}) {
flex-direction: row;
justify-content: center;
gap: 80px;

padding: 0 100px;
}
`;

export const ButtonWrapper = styled.div`
display: flex;
width: 100%;
height: 40px;

@media (min-width: ${theme.breakpoint.sm}) {
width: 140px;
height: 60px;

white-space: pre-wrap;

&:first-child {
display: none;
}
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';

import { MOCK_TRANSFORM_NOTICE } from '@mocks/mockData/notice';

import NoticeItem from '.';

const meta: Meta<typeof NoticeItem> = {
component: NoticeItem,
decorators: [storyFn => <div style={{ width: '576px' }}>{storyFn()}</div>],
};

export default meta;
type Story = StoryObj<typeof NoticeItem>;

export const Default: Story = {
render: () => <NoticeItem notice={MOCK_TRANSFORM_NOTICE} />,
};
19 changes: 19 additions & 0 deletions frontend/src/components/notice/NoticeList/NoticeItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Notice } from '@type/notice';

import { PATH } from '@constants/path';

import * as S from './style';

interface NoticeItemProps {
notice: Notice;
}
export default function NoticeItem({ notice: { id, title, createdAt } }: NoticeItemProps) {
return (
<S.Container>
<S.DetailLink to={`${PATH.NOTICES}/${id}`}>
<S.Title>{title}</S.Title>
<S.CreatedAt>{createdAt}</S.CreatedAt>
</S.DetailLink>
</S.Container>
);
}
56 changes: 56 additions & 0 deletions frontend/src/components/notice/NoticeList/NoticeItem/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Link } from 'react-router-dom';

import { styled } from 'styled-components';

import { theme } from '@styles/theme';

export const Container = styled.li``;

export const DetailLink = styled(Link)`
display: flex;
flex-direction: column;
width: 100%;

padding: 10px 15px;

@media (min-width: ${theme.breakpoint.sm}) {
flex-direction: row;
justify-content: space-between;

padding: 16px 20px;
}
`;

export const Title = styled.span`
width: 100%;
display: -webkit-box;

text-overflow: ellipsis;
word-break: break-word;

overflow: hidden;

-webkit-line-clamp: 1;
-webkit-box-orient: vertical;

font: var(--text-body);

transition: color 0.2s ease-in-out;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옹 hover를 위한 애니메이션인가요???

  &:hover {
    color: rgba(51, 122, 183, 1);
  }

여기 안에 넣지 않으신 이유가 있나요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hover안에 transition을 두면 마우스를 치웠을 때는 transition 속성이 작동하지 않아서 hover 밖에 위치시켜주었어요


&:hover {
color: rgba(51, 122, 183, 1);
}
`;

export const CreatedAt = styled.span`
font: var(--text-body);
font-size: 1.4rem;

text-align: right;

color: var(--text-dark-gray);

@media (min-width: ${theme.breakpoint.sm}) {
width: 90px;
}
`;
21 changes: 21 additions & 0 deletions frontend/src/components/notice/NoticeList/NoticeList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react';

import { MOCK_TRANSFORM_NOTICE_LIST } from '@mocks/mockData/notice';

import NoticeList from '.';

const meta: Meta<typeof NoticeList> = {
component: NoticeList,
decorators: [storyFn => <div style={{ width: '576px' }}>{storyFn()}</div>],
};

export default meta;
type Story = StoryObj<typeof NoticeList>;

export const Default: Story = {
render: () => (
<div style={{ width: '100vw', padding: '15px' }}>
<NoticeList noticeList={MOCK_TRANSFORM_NOTICE_LIST.noticeList} />
</div>
),
};
Loading
Loading