Skip to content

Commit

Permalink
merge: [REFACTOR] Barrel Pattern 적용 (#446)
Browse files Browse the repository at this point in the history
  • Loading branch information
novice0840 authored Jan 14, 2025
2 parents 6859429 + 5ba8e50 commit f0d465f
Show file tree
Hide file tree
Showing 70 changed files with 400 additions and 238 deletions.
4 changes: 2 additions & 2 deletions frontend/eslint-plugin-ddangkong/component-folder-match.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ module.exports = {
const dirName = path.basename(path.dirname(filePath)); // 현재 파일이 속한 폴더 이름
const parentDir = path.dirname(path.dirname(filePath)); // 상위 디렉터리

// 부모 폴더가 "components"로 끝나는 폴더만 검사
if (parentDir.endsWith('components')) {
// 부모 폴더가 "components"로 끝나면서 대문자로 시작하는 폴더는 컴포넌트 폴더로 간주
if (parentDir.endsWith('components') && /^[A-Z]/.test(dirName)) {
const expectedFileName = `${dirName}.tsx`;
const expectedFilePath = path.join(parentDir, dirName, expectedFileName);

Expand Down
Binary file removed frontend/src/assets/images/angryDdangkong.png
Binary file not shown.
Binary file removed frontend/src/assets/images/ddangkong.png
Binary file not shown.
Binary file removed frontend/src/assets/images/ddangkongTimer.png
Binary file not shown.
Binary file removed frontend/src/assets/images/errorDdangkong.png
Binary file not shown.
Binary file removed frontend/src/assets/images/sadDdangkong.png
Binary file not shown.
Binary file removed frontend/src/assets/images/spinDdangkong.png
Binary file not shown.
39 changes: 39 additions & 0 deletions frontend/src/assets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import AngryDdangkong from './images/angryDdangkong.webp';
import ArrowDown from './images/arrowDown.svg';
import ArrowLeft from './images/arrowLeft.svg';
import ArrowUp from './images/arrowUp.svg';
import CloseIcon from './images/closeIcon.png';
import CopyIcon from './images/copyIcon.png';
import CrownIcon from './images/crownIcon.webp';
import Ddangkong from './images/ddangkong.webp';
import DdangkongTimer from './images/ddangkongTimer.webp';
import ErrorDdangkong from './images/errorDdangkong.webp';
import ExitIcon from './images/exitIcon.svg';
import HomeIcon from './images/homeIcon.svg';
import SadDdangkong from './images/sadDdangkong.webp';
import SettingIcon from './images/settingIcon.svg';
import SillyDdangkong from './images/sillyDdangkong.webp';
import SillyDdangkongMedium from './images/sillyDdangkongMedium.webp';
import SillyDdangkongSmall from './images/sillyDdangkongSmall.webp';
import SpinDdangkong from './images/spinDdangkong.webp';

export {
AngryDdangkong,
ArrowDown,
ArrowLeft,
ArrowUp,
CloseIcon,
CopyIcon,
CrownIcon,
Ddangkong,
DdangkongTimer,
ErrorDdangkong,
ExitIcon,
HomeIcon,
SadDdangkong,
SettingIcon,
SillyDdangkong,
SillyDdangkongMedium,
SillyDdangkongSmall,
SpinDdangkong,
};
6 changes: 3 additions & 3 deletions frontend/src/components/InviteModal/InviteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
} from './InviteModal.styled';
import Modal from '../common/Modal/Modal';

import CopyIcon from '@/assets/images/copyIcon.png';
import { INVITE_URL } from '@/constants/url';
import useGetUserInfo from '@/hooks/useGetUserInfo';
import useToast from '@/hooks/useToast';

import { CopyIcon } from '@/assets';
import { useGetUserInfo, useToast } from '@/hooks';

interface InviteModalProps {
isOpen: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import userEvent from '@testing-library/user-event';
import RoomSettingModal from './RoomSettingModal';

import { POLLING_DELAY } from '@/constants/config';
import { useGetRoomInfo } from '@/hooks/useGetRoomInfo';
import ROOM_INFO from '@/mocks/data/roomInfo.json';
import { customRender, wrapper } from '@/utils/test-utils';

import { useGetRoomInfo } from '@/hooks';

describe('RoomSettingModal 방 설정 모달 테스트', () => {
it('방 설정 모달에서 적용 버튼을 클릭하면 모달이 닫힌다.', async () => {
const user = userEvent.setup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useCategoryDropdown from './useCategoryDropdown';
import useTimerPerRound from './useTimerPerRound';
import useTotalRound from './useTotalRound';

import { useGetRoomInfo } from '@/hooks/useGetRoomInfo';
import { useGetRoomInfo } from '@/hooks';

interface UseRoomSettingProps {
onClose: () => void;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/TopicContainer/TopicContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useLocation, useParams } from 'react-router-dom';

import { categoryText, topicContainerLayout, topicText } from './TopicContainer.styled';
import A11yOnly from '../common/a11yOnly/A11yOnly';
import A11yOnly from '../common/A11yOnly/A11yOnly';

import { ROUTES } from '@/constants/routes';
import useBalanceContentQuery from '@/hooks/useBalanceContentQuery';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const A11yOnly = <T extends ElementType = 'span'>({
...props
}: PropsWithChildren<A11yOnlyProps<T>>) => {
const Component = as || 'span';

return (
<Component css={a11yOnlyLayout} {...props}>
{children}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/common/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
} from './Modal.styled';

import CloseIcon from '@/assets/images/closeIcon.png';
import useFocus from '@/hooks/useFocus';

import { useFocus } from '@/hooks';

export interface ModalProps {
isOpen: boolean;
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/components/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import A11yOnly from './A11yOnly/A11yOnly';
import Button from './Button/Button';
import DeferredComponent from './DeferredComponent/DeferredComponent';
import Dropdown from './Dropdown/Dropdown';
import AsyncErrorBoundary from './ErrorBoundary/AsyncErrorBoundary';
import RootErrorBoundary from './ErrorBoundary/RootErrorBoundary';
import AsyncErrorFallback from './ErrorFallback/AsyncErrorFallback/AsyncErrorFallback';
import RootErrorFallback from './ErrorFallback/RootErrorFallback/RootErrorFallback';
import RouterErrorFallback from './ErrorFallback/RouterErrorFallback/RouterErrorFallback';
import SpinnerErrorFallback from './ErrorFallback/SpinnerErrorFallback/SpinnerErrorFallback';
import Modal from './Modal/Modal';
import GameSkeleton from './Skeleton/GameSkeleton/GameSkeleton';
import ReadySkeleton from './Skeleton/ReadySkeleton/ReadySkeleton';
import Spinner from './Spinner/Spinner';
import Toast from './Toast/Toast';

export {
A11yOnly,
Button,
DeferredComponent,
Dropdown,
AsyncErrorBoundary,
RootErrorBoundary,
RouterErrorFallback,
AsyncErrorFallback,
RootErrorFallback,
SpinnerErrorFallback,
Modal,
GameSkeleton,
ReadySkeleton,
Spinner,
Toast,
};
6 changes: 6 additions & 0 deletions frontend/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import AlertModal from './AlertModal/AlertModal';
import InviteModal from './InviteModal/InviteModal';
import RoomSettingModal from './RoomSettingModal/RoomSettingModal';
import TopicContainer from './TopicContainer/TopicContainer';

export { AlertModal, InviteModal, RoomSettingModal, TopicContainer };
34 changes: 34 additions & 0 deletions frontend/src/components/layout/Header/GameHeader/GameHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
import { useParams } from 'react-router-dom';

import { gameTitle, headerLayout, roundText } from '../Header.styled';

import A11yOnly from '@/components/common/A11yOnly/A11yOnly';
import { convertMsecToSecond } from '@/pages/GamePage/components/SelectContainer/Timer/Timer.util';

import { useBalanceContentQuery, useFocus } from '@/hooks';

// 게임 화면
const GameHeader = () => {
const { roomId } = useParams();
const { balanceContent } = useBalanceContentQuery(Number(roomId));

const { totalRound, currentRound, timeLimit } = balanceContent;
const screenReaderHeader = `${totalRound}라운드.중.${currentRound}라운드. 밸런스 게임 페이지. 제한 시간 ${convertMsecToSecond(timeLimit)}초.`;
const focusRef = useFocus<HTMLElement>();

return (
<header css={headerLayout()} tabIndex={0} ref={focusRef}>
<A11yOnly>{screenReaderHeader}</A11yOnly>
<span css={roundText} aria-hidden>
{currentRound}/{totalRound}
</span>
<h1 css={gameTitle} aria-hidden>
밸런스 게임
</h1>
<span css={roundText} aria-hidden></span>
</header>
);
};

export default GameHeader;
8 changes: 3 additions & 5 deletions frontend/src/components/layout/Header/Header.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { StoryObj, Meta } from '@storybook/react';

import Header, { BackHeader, RoomSettingHeader, RoundResultHeader, TitleHeader } from './Header';
import Header from './Header';

import { RoomSettingHeader, RoundResultHeader, TitleHeader } from '.';

const meta = {
title: 'Header',
Expand All @@ -22,7 +24,3 @@ export const 방_설정_헤더: Story = {
export const 라운드_헤더: Story = {
render: () => <RoundResultHeader />,
};

export const 투표_현황_헤더: Story = {
render: () => <BackHeader title="투표 현황" />,
};
5 changes: 3 additions & 2 deletions frontend/src/components/layout/Header/Header.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';

import { RoomSettingHeader } from './Header';
import { RoomSettingHeader } from '.';

import useIsMaster from '@/hooks/useIsMaster';
import { customRender } from '@/utils/test-utils';

import { useIsMaster } from '@/hooks';

jest.mock('@/hooks/useIsMaster');

describe('Header 테스트', () => {
Expand Down
151 changes: 2 additions & 149 deletions frontend/src/components/layout/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,6 @@
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
import { useRef } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useBlockRefresh, useRoutePath } from './hooks';

import {
buttonWrapper,
gameTitle,
headerLayout,
roundText,
iconImage,
matchingResultTitle,
matchingResultCaption,
MatchingResultHeaderContainer,
} from './Header.styled';
import { useBlockRefresh } from './hooks/useBlockRefresh';
import { useExit } from './hooks/useExit';
import useRoutePath from './hooks/useRoutePath';

import ArrowLeft from '@/assets/images/arrowLeft.svg';
import ExitIcon from '@/assets/images/exitIcon.svg';
import SettingIcon from '@/assets/images/settingIcon.svg';
import AlertModal from '@/components/AlertModal/AlertModal';
import A11yOnly from '@/components/common/a11yOnly/A11yOnly';
import RoomSettingModal from '@/components/RoomSettingModal/RoomSettingModal';
import useBalanceContentQuery from '@/hooks/useBalanceContentQuery';
import useFocus from '@/hooks/useFocus';
import useIsMaster from '@/hooks/useIsMaster';
import useModal from '@/hooks/useModal';
import { convertMsecToSecond } from '@/pages/GamePage/components/SelectContainer/Timer/Timer.util';

interface HeaderProps {
title: string;
}
import { TitleHeader, RoomSettingHeader, RoundResultHeader, MatchingResultHeader } from '.';

const Header = () => {
const { isNicknamePage, isReadyPage, isRoundResultPage, isMatchingResultPage } = useRoutePath();
Expand All @@ -43,121 +13,4 @@ const Header = () => {
if (isMatchingResultPage) return <MatchingResultHeader title="매칭 결과" />;
};

// 1. 가운데 제목과 설명이 있는 헤더 : 최종 게임 결과 화면
export const MatchingResultHeader = ({ title }: HeaderProps) => {
const focusRef = useFocus<HTMLElement>();
return (
<header css={headerLayout(true)} tabIndex={0} ref={focusRef}>
<div css={MatchingResultHeaderContainer}>
<h1 css={matchingResultTitle}>{title}</h1>
<h2 css={matchingResultCaption}>매칭도를 통해 당신과 가장 잘 맞는 사람을 알아보세요😊</h2>
</div>
</header>
);
};

// 2. 가운데 제목만 차지하는 헤더 : 닉네임 설정 화면
export const TitleHeader = ({ title }: HeaderProps) => (
<header css={headerLayout(true)}>
<h1 css={gameTitle}>{title}</h1>
</header>
);

// 3. 가운데 제목, 우측 상단 차지하는 헤더 : 게임 대기 화면
export const RoomSettingHeader = ({ title }: HeaderProps) => {
const isMaster = useIsMaster();
const { showModal } = useModal();

const { handleExit } = useExit();
const returnFocusRef = useRef(null);
const focusRef = useFocus<HTMLElement>();

const handleClickRoomSetting = () => {
showModal(RoomSettingModal, { returnFocusRef });
};

const handleClickExit = () => {
showModal(AlertModal, { message: '정말로 나가시겠습니까?', onConfirm: handleExit });
};

return (
<header css={headerLayout()} tabIndex={0} ref={focusRef}>
<button onClick={handleClickExit} css={buttonWrapper}>
<img src={ExitIcon} alt="방 나가기" css={iconImage} />
</button>
<h1 css={gameTitle}>{title}</h1>
{isMaster ? (
<button ref={returnFocusRef} onClick={handleClickRoomSetting} css={buttonWrapper}>
<img src={SettingIcon} alt="방 설정" css={iconImage} />
</button>
) : (
<span css={roundText}></span>
)}
</header>
);
};

// 4. 좌측 상단 라운드, 가운데 제목 차지하는 헤더 (API 호출 O) : 라운드 통계 화면
export const RoundResultHeader = () => {
const { roomId } = useParams();
const { balanceContent } = useBalanceContentQuery(Number(roomId));
const screenReaderRoundResult = `${balanceContent.totalRound}라운드 중. ${balanceContent.currentRound}라운드. 투표 결과 페이지`;
const focusRef = useFocus<HTMLElement>();

return (
<header css={headerLayout()} tabIndex={0} ref={focusRef}>
<A11yOnly>{screenReaderRoundResult}</A11yOnly>
<span css={roundText} aria-hidden>
{balanceContent.currentRound}/{balanceContent.totalRound}
</span>
<h1 css={gameTitle} aria-hidden>
투표 결과
</h1>
<span css={roundText} aria-hidden></span>
</header>
);
};

// 게임 화면
export const GameHeader = () => {
const { roomId } = useParams();
const { balanceContent } = useBalanceContentQuery(Number(roomId));

const { totalRound, currentRound, timeLimit } = balanceContent;
const screenReaderHeader = `${totalRound}라운드.중.${currentRound}라운드. 밸런스 게임 페이지. 제한 시간 ${convertMsecToSecond(timeLimit)}초.`;
const focusRef = useFocus<HTMLElement>();

return (
<header css={headerLayout()} tabIndex={0} ref={focusRef}>
<A11yOnly>{screenReaderHeader}</A11yOnly>
<span css={roundText} aria-hidden>
{currentRound}/{totalRound}
</span>
<h1 css={gameTitle} aria-hidden>
밸런스 게임
</h1>
<span css={roundText} aria-hidden></span>
</header>
);
};

// 5. 좌측 상단 뒤로가기, 가운데 제목 차지하는 헤더 (API 호출 X) : 라운드 투표 현황
export const BackHeader = ({ title }: HeaderProps) => {
const navigate = useNavigate();
const focusRef = useFocus<HTMLElement>();
const goToBack = () => {
navigate(-1);
};

return (
<header css={headerLayout()} tabIndex={0} ref={focusRef}>
<button onClick={goToBack} css={buttonWrapper}>
<img src={ArrowLeft} alt="뒤로 가기" />
</button>
<h1 css={gameTitle}>{title}</h1>
<span css={roundText}></span>
</header>
);
};

export default Header;
Loading

0 comments on commit f0d465f

Please sign in to comment.