Skip to content

Commit

Permalink
feat-fe: Sidebar 컴포넌트 개선 (#773)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeongwoo Park <[email protected]>
  • Loading branch information
github-actions[bot] and lurgi authored Oct 10, 2024
1 parent c5fc9a0 commit b0b02e1
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 106 deletions.
1 change: 1 addition & 0 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const preview: Preview = {
loaders: [mswLoader],
decorators: [
(Story) => {
localStorage.setItem('clubId', '1');
return (
<ModalProvider>
<QueryClientProvider client={new QueryClient()}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useState } from 'react';

import { reactRouterParameters, withRouter } from 'storybook-addon-remix-react-router';
import type { Meta, StoryObj } from '@storybook/react';
import DashboardSidebar from '.';
Expand All @@ -23,47 +25,89 @@ const meta: Meta<typeof DashboardSidebar> = {
options: [
{
text: '첫번째 옵션',
isSelected: true,
isSelected: false,
dashboardId: '1',
applyFormId: '10',
status: {
isClosed: true,
isPending: false,
isOngoing: false,
status: 'Closed',
},
},
{
text: '두번째 옵션',
isSelected: false,
dashboardId: '2',
applyFormId: '11',
status: {
isClosed: true,
isPending: false,
isOngoing: false,
status: 'Closed',
},
},
{
text: '세번째 옵션',
isSelected: true,
dashboardId: '2',
applyFormId: '12',
status: {
isClosed: false,
isPending: false,
isOngoing: true,
status: 'Ongoing',
},
},
{
text: '네번째 옵션',
isSelected: false,
dashboardId: '2',
applyFormId: '13',
status: {
isClosed: false,
isPending: true,
isOngoing: false,
status: 'Pending',
},
},
],
},
tags: ['autodocs'],
decorators: [
withRouter,
(Child) => (
<div
style={{
height: '1000px',
width: '400px',
backgroundColor: 'gray',
display: 'flex',
justifyContent: 'center',
alignContent: 'center',
}}
>
<Child />
</div>
),
(Child, context) => {
const [isOpen, setIsOpen] = useState(true);

const handleToggle = () => {
if (isOpen) setIsOpen(false);
if (!isOpen) setIsOpen(true);
};

return (
<div
style={{
height: '1000px',
width: '400px',
backgroundColor: 'gray',
display: 'flex',
justifyContent: 'center',
alignContent: 'center',
}}
>
<Child
args={{
...context.args,
sidebarStyle: { isSidebarOpen: isOpen, onClickSidebarToggle: handleToggle },
}}
/>
</div>
);
},
],
};

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

export const Default: Story = {
args: {
options: [
{ text: '우아한테크코스 6기 프론트엔드', isSelected: true, dashboardId: '1', applyFormId: '10' },
{ text: '우아한테크코스 6기 백엔드', isSelected: false, dashboardId: '2', applyFormId: '11' },
{ text: '우아한테크코스 6기 안드로이드', isSelected: false, dashboardId: '3', applyFormId: '12' },
],
},
};
export const Default: Story = {};
154 changes: 128 additions & 26 deletions frontend/src/components/dashboard/DashboardSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,147 @@
import { useMemo } from 'react';

import Logo from '@assets/images/logo.svg';
import Accordion from '@components/_common/molecules/Accordion';
import { routes } from '@router/path';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';

import type { RecruitmentStatusObject } from '@utils/compareTime';

import { Fragment } from 'react/jsx-runtime';
import { HiChevronDoubleLeft, HiOutlineHome } from 'react-icons/hi2';
import { HiOutlineMenu } from 'react-icons/hi';
import { GrDocumentLocked, GrDocumentTime, GrDocumentUser } from 'react-icons/gr';
import type { IconType } from 'react-icons';

import IconButton from '@components/_common/atoms/IconButton';
import LogoutButton from './LogoutButton';

import S from './style';

interface Option {
text: string;
isSelected: boolean;
applyFormId: string;
dashboardId: string;
status: RecruitmentStatusObject;
}

interface SidebarStyle {
isSidebarOpen: boolean;
onClickSidebarToggle: () => void;
}

interface DashboardSidebarProps {
options: Option[];
sidebarStyle: SidebarStyle;
options?: Option[];
}

export default function DashboardSidebar({ options }: DashboardSidebarProps) {
export default function DashboardSidebar({ sidebarStyle, options }: DashboardSidebarProps) {
const pendingPosts = useMemo(() => options?.filter(({ status }) => status.isPending), [options]);
const onGoingPosts = useMemo(() => options?.filter(({ status }) => status.isOngoing), [options]);
const closedPosts = useMemo(() => options?.filter(({ status }) => status.isClosed), [options]);

const sidebarContentList = [
{ title: '모집 예정 공고 목록', posts: pendingPosts },
{ title: '진행 중 공고 목록', posts: onGoingPosts },
{ title: '마감 된 공고 목록', posts: closedPosts },
];

const location = useLocation();

const IconObj: Record<RecruitmentStatusObject['status'], IconType> = {
Pending: GrDocumentTime,
Ongoing: GrDocumentUser,
Closed: GrDocumentLocked,
};

return (
<S.Container>
<Link to={routes.dashboard.list()}>
<S.Logo
src={Logo}
alt="크루루 로고"
/>
</Link>

<S.Contents>
<Accordion title={<Link to={routes.dashboard.list()}>공고</Link>}>
{options.map(({ text, isSelected, applyFormId, dashboardId }, index) => (
// eslint-disable-next-line react/no-array-index-key
<Accordion.ListItem key={index}>
<S.LinkContainer isSelected={isSelected}>
<Link to={routes.dashboard.post({ dashboardId, applyFormId })}>{text}</Link>
</S.LinkContainer>
</Accordion.ListItem>
))}
</Accordion>
</S.Contents>

<LogoutButton />
<S.Container isSidebarOpen={sidebarStyle.isSidebarOpen}>
<S.SidebarHeader>
{sidebarStyle.isSidebarOpen && (
<Link to={routes.dashboard.list()}>
<S.Logo
src={Logo}
alt="크루루 로고"
/>
</Link>
)}

<IconButton
size="sm"
outline={false}
onClick={sidebarStyle.onClickSidebarToggle}
>
<S.SidebarToggleIcon>
{sidebarStyle.isSidebarOpen ? (
<HiChevronDoubleLeft
size={24}
strokeWidth={0.8}
/>
) : (
<HiOutlineMenu
size={24}
strokeWidth={2.4}
/>
)}
</S.SidebarToggleIcon>
</IconButton>
</S.SidebarHeader>

<nav>
<S.Contents>
<S.SidebarItem>
<Link to={routes.dashboard.list()}>
<S.SidebarItemLink
isSelected={location.pathname === routes.dashboard.list()}
isSidebarOpen={sidebarStyle.isSidebarOpen}
>
<S.IconContainer>
<HiOutlineHome
size={22}
strokeWidth={2}
/>
</S.IconContainer>
{sidebarStyle.isSidebarOpen && <S.SidebarItemTextHeader>모집 공고</S.SidebarItemTextHeader>}
</S.SidebarItemLink>
</Link>
</S.SidebarItem>

{!!options?.length && <S.Divider />}

{sidebarContentList.map(({ title, posts }) => {
if (posts?.length === 0) return null;
return (
<Fragment key={title}>
<S.ContentSubTitle>{sidebarStyle.isSidebarOpen ? title : <S.Circle />}</S.ContentSubTitle>
{posts?.map(({ text, isSelected, applyFormId, dashboardId, status }) => {
const Icon = IconObj[status.status];

return (
<S.SidebarItem key={applyFormId}>
<Link to={routes.dashboard.post({ dashboardId, applyFormId })}>
<S.SidebarItemLink
isSelected={isSelected}
isSidebarOpen={sidebarStyle.isSidebarOpen}
>
<S.IconContainer>
<Icon
size={16}
strokeWidth={4}
/>
</S.IconContainer>
{sidebarStyle.isSidebarOpen && <S.SidebarItemText>{text}</S.SidebarItemText>}
</S.SidebarItemLink>
</Link>
</S.SidebarItem>
);
})}
</Fragment>
);
})}
</S.Contents>
</nav>

{sidebarStyle.isSidebarOpen && <LogoutButton />}
</S.Container>
);
}
Loading

0 comments on commit b0b02e1

Please sign in to comment.