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

Style/#273: 공지사항 리스트 UI 변경 #274

Merged
merged 32 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9d85a80
Style: 공지사항 리스트 디자인 변경
pp449 Oct 13, 2023
311b520
Design: 공지사항 리스트 UI 변경
pp449 Oct 15, 2023
5f293fc
Test: 테스트코드에 전역 상태값을 사용하기 위해 프로바이더 추가
pp449 Oct 15, 2023
2ccd386
Merge branch 'dev' of https://github.com/GDSC-PKNU-21-22/pknu-notice …
hwinkr Nov 3, 2023
3bfca87
Delete: 사용하지 않는 .gitkeep 파일 삭제
hwinkr Nov 4, 2023
875de9e
Config: 공지사항 관련 타입 추가
hwinkr Nov 4, 2023
c4b960d
Refactor: 링크 이동 함수 변경, 스타일 컴포넌트 이름 수정
hwinkr Nov 4, 2023
ee30cb0
Refactor: url에 따라 다른 공지사항을 렌더링 하도록 수정
hwinkr Nov 4, 2023
c4d5a2a
Test: 모킹하는 useMajor 훅에 graduationLink 추가
hwinkr Nov 4, 2023
dc2f6ae
Style: InformCard 컴포넌트 스타일 수정
hwinkr Nov 4, 2023
90fbb37
Chore: css selector에서 띄어쓰기로 인한 린트 에러를 해결
hwinkr Nov 4, 2023
9864252
Test: 모킹하는 useMajor 훅에 graduationLink 추가
hwinkr Nov 4, 2023
fc159c6
Refactor: MajorProvider가 graduationLink도 책임 지도록 수정
hwinkr Nov 4, 2023
fc8d607
Style: ConfirmModal 네, 아니오 위치 변경
hwinkr Nov 4, 2023
0737cb0
Config: 공지사항 관련 상수 설정
hwinkr Nov 4, 2023
8a2739e
Config: 경로들을 상수로 설정
hwinkr Nov 4, 2023
5b987fe
Chore: placeholder에 공지사항 검색 추가
hwinkr Nov 4, 2023
ac95f84
Chore: 토스트 메세지에 공지사항 검색 키워드가 없을 경우의 메세지 추가
hwinkr Nov 4, 2023
394655b
Refactor: MajorContext에 graduationLink 추가
hwinkr Nov 4, 2023
c4ee695
Refactor: Announcement 컴포넌트는 라우팅 책임만 지도록 변경
hwinkr Nov 4, 2023
d774d20
Feat: 공지사항 페이지 컴포턴트 구현
hwinkr Nov 4, 2023
2611491
Feat: 공지사항 검색 컴포넌트 구현
hwinkr Nov 4, 2023
f95add6
Feat: 공지사항 검색 결과 렌더링 컴포넌트 구현
hwinkr Nov 4, 2023
adb21ae
Feat: 공지 & 일반 구분 버튼 컴포넌트 구현
hwinkr Nov 4, 2023
6d33105
Chore: 공지사항 페이지를 구성하는 컴포넌트 한번에 export
hwinkr Nov 4, 2023
a4ef789
Test: 홈페이지에 렌더링 되는 텍스트 변경
hwinkr Nov 4, 2023
bec98e3
Refactor: 홈페이지 컴포넌트 구성 변경
hwinkr Nov 4, 2023
b26cbc1
Feat: 공지사항 관련 카드 컴포넌트를 묶은 컴포넌트를 구현
hwinkr Nov 4, 2023
dc62ac5
Test: 모킹하는 useMajor 훅에 graduationLink 추가
hwinkr Nov 4, 2023
af319f7
Feat: 링크를 이동 시키는 유틸함수 구현
hwinkr Nov 4, 2023
31b547c
Feat: 공지사항 검색 결과가 없을 경우 보여 줄 컴포넌트 분리
hwinkr Nov 16, 2023
47b3c3b
Refactor: 공지사항 검색 결과를 판단하는 함수 로직 수정
hwinkr Nov 16, 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
11 changes: 11 additions & 0 deletions src/@types/announcement.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
ANNOUNCEMENT_CATEGORY,
ANNOUNCEMENT_TYPE,
} from '@constants/announcement';

type AnnounceItemType = '고정' | '일반';

export interface AnnounceItem {
Expand All @@ -11,3 +16,9 @@ export interface AnnounceItem {
export type AnnounceItemList = {
[key in AnnounceItemType]: AnnounceItem[];
};

export type AnnouncementCategory =
(typeof ANNOUNCEMENT_CATEGORY)[keyof typeof ANNOUNCEMENT_CATEGORY];

export type AnnouncementType =
(typeof ANNOUNCEMENT_TYPE)[keyof typeof ANNOUNCEMENT_TYPE];
49 changes: 34 additions & 15 deletions src/components/Card/AnnounceCard/AnnounceList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { AnnounceItemList } from '@type/announcement';
import { ANNOUNCEMENT_TYPE } from '@constants/announcement';
import styled from '@emotion/styled';
import { AnnounceSearchList } from '@pages/Announcement/components';
import { THEME } from '@styles/ThemeProvider/theme';
import { AnnounceItemList, AnnouncementType } from '@type/announcement';
import { AxiosError, AxiosResponse } from 'axios';
import { Fragment } from 'react';

import AnnounceCard from '..';

Expand All @@ -13,30 +18,44 @@ interface AnnounceListProps {
resource: {
read: () => Resource;
};
type: AnnouncementType;
}

const AnnounceList = ({ resource }: AnnounceListProps) => {
const AnnounceList = ({ resource, type }: AnnounceListProps) => {
const announceList: Resource = resource.read();

if (announceList === null || announceList instanceof Error) {
return null;
return <></>;
}
const { 고정: pinned, 일반: normal } = announceList as AnnounceItemList;

const { 고정: pinnedAnnouncement, 일반: normalAnnouncemnet } =
announceList as AnnounceItemList;

return (
<>
{pinned.map((announce, idx) => (
<div key={idx}>
<AnnounceCard {...announce} pinned={true} />
</div>
))}
{normal.map((announce, idx) => (
<div key={idx}>
<AnnounceCard {...announce} />
</div>
))}
<BoundaryLine />
{type === ANNOUNCEMENT_TYPE.NORMAL &&
normalAnnouncemnet.map((announce, idx) => (
<Fragment key={idx}>
<AnnounceCard {...announce} />
</Fragment>
))}
{type === ANNOUNCEMENT_TYPE.PINNED &&
pinnedAnnouncement.map((announce, idx) => (
<Fragment key={idx}>
<AnnounceCard {...announce} />
</Fragment>
))}
{type === ANNOUNCEMENT_TYPE.SEARCH && (
<AnnounceSearchList
announceList={[...pinnedAnnouncement, ...normalAnnouncemnet]}
/>
)}
</>
);
};

export default AnnounceList;

const BoundaryLine = styled.div`
border-bottom: 1px solid ${THEME.TEXT.BLACK};
`;
8 changes: 7 additions & 1 deletion src/components/Card/AnnounceCard/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import http from '@apis/http';
import MajorProvider from '@components/MajorProvider';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AnnounceItemList } from '@type/announcement';
Expand Down Expand Up @@ -34,7 +35,12 @@ describe('공지사항 카드 컴포넌트 테스트', () => {
const { 고정, 일반 } = announceList;

일반.forEach(async (annouce) => {
render(<AnnounceCard {...annouce} />, { wrapper: MemoryRouter });
render(
<MajorProvider>
<AnnounceCard {...annouce} />
</MajorProvider>,
{ wrapper: MemoryRouter },
);
});

const annouceCards = screen.getAllByTestId('card');
Expand Down
93 changes: 48 additions & 45 deletions src/components/Card/AnnounceCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,93 +1,96 @@
import Icon from '@components/Icon';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import useMajor from '@hooks/useMajor';
import { THEME } from '@styles/ThemeProvider/theme';
import { AnnounceItem } from '@type/announcement';
import openLink from '@utils/router/openLink';

interface AnnounceCardProps extends AnnounceItem {
pinned?: boolean;
author?: string;
}

const AnnounceCard = ({
title,
link,
uploadDate,
pinned = false,
author,
}: AnnounceCardProps) => {
const onClick = () => {
window.open(link, '_blank');
};
const { major } = useMajor();

uploadDate = uploadDate.slice(2);

return (
<Card onClick={onClick} data-testid="card">
<Card onClick={() => openLink(link)} data-testid="card">
<ContentContainer>
{pinned && <Icon kind="speaker" color={THEME.PRIMARY} />}
<AnnounceTitle pinned={pinned}>{title}</AnnounceTitle>
<VertialSeparator
css={css`
border-left: 1px solid gray;
height: 12px;
margin: 0 5px;
`}
/>
<AnnounceDate>{uploadDate}</AnnounceDate>
<AnnounceTitle>{title}</AnnounceTitle>
<SubContent>
<AnnounceDate>20{uploadDate}</AnnounceDate>
<VertialBoundaryLine />
<Source>{author ? author : major}</Source>
</SubContent>
</ContentContainer>
<HorizonBoundaryLine />
</Card>
);
};

export default AnnounceCard;

const Card = styled.div`
height: 28px;
padding: 10px;
min-height: 50px;
display: flex;
flex-direction: column;
justify-content: center;

color: ${THEME.TEXT.BLACK};

transition: 0.3s;
&:active {
transform: scale(0.95);
opacity: 0.6;
}
`;

const ContentContainer = styled.div`
padding: 20px 0 20px 0;
display: flex;
align-items: center;
line-height: 1.5;
flex-direction: column;

gap: 10px;
`;

const AnnounceTitle = styled.span<{ pinned: boolean }>`
const AnnounceTitle = styled.span`
display: flex;
align-items: center;
flex: 9;
font-size: 15px;
font-weight: ${({ pinned }) => (pinned ? 'bold' : 500)};
margin-left: ${({ pinned }) => (pinned ? '' : '28px')};

white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

&: hover {
cursor: pointer;
}

transition: 0.3s;
&:active {
transform: scale(0.95);
opacity: 0.6;
}
font-size: 16px;
font-weight: 500;
`;

const VertialSeparator = styled.div`
const VertialBoundaryLine = styled.div`
border-left: 1px solid gray;
height: 12px;
margin: 0 5px;
`;

const AnnounceDate = styled.span`
flex: 1;
font-size: 10px;
font-weight: bold;
text-align: end;
font-size: 13px;
white-space: nowrap;

color: ${THEME.TEXT.GRAY};
padding-right: 5px;
`;

const HorizonBoundaryLine = styled.div`
border-bottom: 1px solid ${THEME.BACKGROUND};
`;

const SubContent = styled.div`
display: flex;
align-items: center;
`;

const Source = styled.div`
font-size: 13px;
color: gray;
padding-left: 5px;
`;
3 changes: 2 additions & 1 deletion src/components/Card/InformCard/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ const setMajorMock = (isRender: boolean) => {

jest.mock('react', () => ({
...jest.requireActual('react'),
useState: () => [mockMajor, mockSetMajor],
useState: () => [mockMajor, mockSetMajor, graduationLink],
}));

return {
major: mockMajor,
setMajor: mockSetMajor,
graduationLink,
};
};

Expand Down
93 changes: 36 additions & 57 deletions src/components/Card/InformCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ const InformCard = ({
}: InformCardProps) => {
const { major } = useMajor();
const { routerTo } = useRouter();
const routerToMajorDecision = () => routerTo('/major-decision');
const { openModal, closeModal } = useModals();

const routeToMajorDecisionPage = () => routerTo('/major-decision');

const handleMajorModal = () => {
if (!majorRequired || major) {
onClick();
Expand All @@ -38,48 +39,21 @@ const InformCard = ({
onClose: () => closeModal(modals.alert),
routerTo: () => {
closeModal(modals.alert);
routerToMajorDecision();
routeToMajorDecisionPage();
},
});
};

return (
<>
<Card data-testid="card" onClick={handleMajorModal}>
<Wrapper>
<div
css={css`
display: flex;
border-radius: 50%;
background-color: ${THEME.PRIMARY};
height: 45px;
width: 45px;
justify-content: center;
align-items: center;
`}
>
<Icon kind={icon} color={THEME.TEXT.WHITE} />
</div>
</Wrapper>
<Wrapper>
<span
css={css`
font-size: 13px;
`}
>
{title}
</span>
<span
css={css`
font-size: 15px;
margin: auto 0;
font-weight: bold;
color: ${THEME.TEXT.BLACK};
`}
>
{title} 보러가기
</span>
</Wrapper>
<IconContainer>
<Icon kind={icon} color={THEME.TEXT.WHITE} />
</IconContainer>
<TextContainer>
<span>{title}</span>
<span>{title} 보러가기</span>
</TextContainer>
</Card>
</>
);
Expand All @@ -89,33 +63,38 @@ export default InformCard;

const Card = styled.div`
display: flex;
flex-direction: row;
align-items: center;
padding: 3% 1% 2% 0;
color: ${THEME.TEXT.GRAY};
height: 70px;

& > svg {
margin: 10px 0;
}
cursor: pointer;
height: 4rem;

transition: all 0.2s ease-in-out;

&:active {
transform: scale(0.95);
opacity: 0.6;
span:nth-of-type(1) {
font-size: 12px;
color: ${THEME.TEXT.GRAY};
}
`;

const Wrapper = styled.div`
&:first-of-type {
display: flex;
align-items: center;
span:nth-of-type(2) {
font-size: 16px;
font-weight: bold;
color: ${THEME.TEXT.BLACK};
}
`;

&:nth-of-type(2) {
display: flex;
flex-direction: column;
padding: 4% 0 3% 3%;
}
const TextContainer = styled.div`
display: flex;
flex-direction: column;
gap: 5px;
`;

const IconContainer = styled.div`
height: 45px;
width: 45px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 10px;

border-radius: 50%;
background-color: ${THEME.PRIMARY};
`;
Loading