Skip to content

Commit

Permalink
Merge branch 'develop/fe' into fix/fe/#180
Browse files Browse the repository at this point in the history
  • Loading branch information
jinyoung234 authored Aug 6, 2024
2 parents 4cb5c30 + db5c393 commit 7ca7897
Show file tree
Hide file tree
Showing 65 changed files with 1,616 additions and 234 deletions.
1 change: 1 addition & 0 deletions frontend/.prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"^@type/(.*)$",
"^@apis/(.*)$",
"^@mocks/(.*)$",
"^@queries/(.*)$",
"^@components/(.*)$",
"^@hooks/(.*)$",
"^@constants/(.*)$",
Expand Down
45 changes: 45 additions & 0 deletions frontend/__tests__/mainPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { waitFor } from "@testing-library/react";

import { createInfiniteTravelogueHook } from "./utils/createInfiniteTravelogueHook";

describe("메인 페이지", () => {
describe("여행기 무한 스크롤 확인", () => {
it("메인 페이지로 진입했을때 보여지는 여행기는 총 5개이다.", async () => {
const { result } = createInfiniteTravelogueHook();
await waitFor(() => {
expect(result.current.status).toMatch("success");
expect(result.current.travelogues.length).toBe(5);
});
});

it("다음 페이지로 이동할 경우 보여지는 여행기는 총 10개이다.", async () => {
const { result } = createInfiniteTravelogueHook();

await waitFor(() => {
result.current.fetchNextPage();
});

await waitFor(() => {
expect(result.current.status).toMatch("success");
expect(result.current.travelogues.length).toBe(10);
});
});

it("마지막 페이지로 이동할 경우 더 이상 콘텐츠를 확인할 수 없다.", async () => {
const { result } = createInfiniteTravelogueHook();

await waitFor(() => {
result.current.fetchNextPage();
});

await waitFor(() => {
result.current.fetchNextPage();
});

await waitFor(() => {
expect(result.current.status).toMatch("success");
expect(result.current.hasNextPage).toBeFalsy();
});
});
});
});
22 changes: 22 additions & 0 deletions frontend/__tests__/utils/createInfiniteTravelogueHook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { renderHook } from "@testing-library/react";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

import useInfiniteTravelogues from "@queries/useInfiniteTravelogues";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 0,
},
},
});

export const wrapper = ({ children }: React.PropsWithChildren) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);

export const createInfiniteTravelogueHook = () =>
renderHook(() => useInfiniteTravelogues(), {
wrapper,
});
2 changes: 2 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module.exports = {
(https://mswjs.io/docs/migrations/1.x-to-2.x/#frequent-issues)
*/
setupFiles: ["./jest.polyfills.js"],
setupFilesAfterEnv: ["./jest-setup.ts"],

testEnvironmentOptions: {
customExportConditions: [""],
},
Expand Down
18 changes: 6 additions & 12 deletions frontend/src/apis/ApiError.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from "axios";

const HTTP_STATUS = {
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500,
} as const;
import { HTTP_STATUS_CODE_MAP } from "@constants/httpStatusCode";

class ApiError<T = unknown> extends Error implements AxiosError<T> {
config: InternalAxiosRequestConfig;
Expand All @@ -23,23 +17,23 @@ class ApiError<T = unknown> extends Error implements AxiosError<T> {
const errorStatus = error.response?.status || 0;
let name = "ApiError";

if (errorStatus === HTTP_STATUS.BAD_REQUEST) {
if (errorStatus === HTTP_STATUS_CODE_MAP.BAD_REQUEST) {
name = "ApiBadRequestError";
}

if (errorStatus === HTTP_STATUS.UNAUTHORIZED) {
if (errorStatus === HTTP_STATUS_CODE_MAP.UNAUTHORIZED) {
name = "ApiUnauthorizedError";
}

if (errorStatus === HTTP_STATUS.FORBIDDEN) {
if (errorStatus === HTTP_STATUS_CODE_MAP.FORBIDDEN) {
name = "ApiForbiddenError";
}

if (errorStatus === HTTP_STATUS.NOT_FOUND) {
if (errorStatus === HTTP_STATUS_CODE_MAP.NOT_FOUND) {
name = "ApiNotFoundError";
}

if (errorStatus === HTTP_STATUS.INTERNAL_SERVER_ERROR) {
if (errorStatus === HTTP_STATUS_CODE_MAP.INTERNAL_SERVER_ERROR) {
name = "ApiInternalServerError";
}

Expand Down
12 changes: 7 additions & 5 deletions frontend/src/apis/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import * as Sentry from "@sentry/react";
import { AxiosError, InternalAxiosRequestConfig } from "axios";

import type { ErrorResponse } from "@type/api/errorResponse";
import type { User } from "@type/domain/user";
import type { UserResponse } from "@type/domain/user";

import ApiError from "@apis/ApiError";

import { ROUTE_PATHS } from "@constants/route";
import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";
import { ROUTE_PATHS_MAP } from "@constants/route";
import { STORAGE_KEYS_MAP } from "@constants/storage";

export const checkAccessToken = (
config: InternalAxiosRequestConfig,
accessToken: string | null,
) => {
if (!accessToken) {
alert("로그인이 필요합니다.");
window.location.href = ROUTE_PATHS.login;
alert(ERROR_MESSAGE_MAP.api.login);
window.location.href = ROUTE_PATHS_MAP.login;
}

return config;
Expand All @@ -32,7 +34,7 @@ export const setAuthorizationHeader = (
};

export const handlePreviousRequest = (config: InternalAxiosRequestConfig) => {
const user: User | null = JSON.parse(localStorage.getItem("tourootUser") ?? "{}");
const user: UserResponse | null = JSON.parse(localStorage.getItem(STORAGE_KEYS_MAP.user) ?? "{}");
let newConfig = { ...config };

newConfig = checkAccessToken(config, user?.accessToken ?? null);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export { default as ImageUpload } from "./image-upload.svg";
export { default as KaKao } from "./kakao.svg";
export { default as KoreanLogo } from "./korean-logo.svg";
export { default as Plus } from "./plus.svg";
export { default as tturiUrl } from "./tturi.svg?url";
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createContext, useContext } from "react";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";

export const AccordionItemContext = createContext<string>("");

export const useAccordionItemContext = () => {
const context = useContext(AccordionItemContext);
if (!context) {
throw new Error("Provider 바깥에 존재합니다!");
throw new Error(ERROR_MESSAGE_MAP.provider);
}

return context;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createContext, useContext } from "react";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";

interface AccordionContextConfig {
value: Set<string>;
handleToggleAccordion: (item: string) => void;
Expand All @@ -10,7 +12,7 @@ export const AccordionContext = createContext<AccordionContextConfig | null>(nul
export const useAccordionContext = () => {
const context = useContext(AccordionContext);
if (!context) {
throw new Error("Provider 바깥에 존재합니다!");
throw new Error(ERROR_MESSAGE_MAP.provider);
}

return context;
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/common/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { createContext, useContext, useState } from "react";

import { ERROR_MESSAGE_MAP } from "@constants/errorMessage";

import * as S from "./Drawer.styled";

interface DrawerContextType {
Expand All @@ -14,7 +16,7 @@ const DrawerContext = createContext<DrawerContextType | undefined>(undefined);
const useDrawerContext = () => {
const context = useContext(DrawerContext);
if (!context) {
throw new Error("Drawer Provider가 없습니다.");
throw new Error(ERROR_MESSAGE_MAP.provider);
}
return context;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
// import { UserContext } from "@contexts/UserProvider";
import Icon from "@components/common/Icon/Icon";

import { ROUTE_PATHS } from "@constants/route";
import { ROUTE_PATHS_MAP } from "@constants/route";

import { PRIMITIVE_COLORS } from "@styles/tokens";

Expand All @@ -26,7 +26,7 @@ const FloatingButton = () => {
// alert("여행기 작성 전 로그인을 먼저 해주세요!");
// return;
// }
navigate(ROUTE_PATHS.travelogueRegister);
navigate(ROUTE_PATHS_MAP.travelogueRegister);
};

const handleClickShareButton = () => {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { Global, css } from "@emotion/react";

import { Autocomplete } from "@react-google-maps/api";

import type { Place } from "@type/domain/travelogue";
import type { TravelTransformPlace } from "@type/domain/travelTransform";

import { Button } from "@components/common";

import * as S from "./GoogleSearchPopup.styled";

interface GoogleSearchPopupProps {
onClosePopup: () => void;
onSearchPlaceInfo: (placeInfo: Pick<Place, "placeName" | "position">) => void;
onSearchPlaceInfo: (placeInfo: Pick<TravelTransformPlace, "placeName" | "position">) => void;
}

const GoogleSearchPopup = ({ onClosePopup, onSearchPlaceInfo }: GoogleSearchPopupProps) => {
Expand All @@ -35,7 +35,7 @@ const GoogleSearchPopup = ({ onClosePopup, onSearchPlaceInfo }: GoogleSearchPopu
lng: place.geometry.location.lng(),
};

const placeInfo: Pick<Place, "placeName" | "position"> = {
const placeInfo: Pick<TravelTransformPlace, "placeName" | "position"> = {
placeName: place.name || "",
position: newCenter,
};
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/components/common/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import IconButton from "@components/common/IconButton/IconButton";

import useUser from "@hooks/useUser";

import { ROUTE_PATHS } from "@constants/route";
import { ROUTE_PATHS_MAP } from "@constants/route";

import theme from "@styles/theme";
import { PRIMITIVE_COLORS } from "@styles/tokens";
Expand All @@ -21,21 +21,21 @@ const Header = () => {
const navigate = useNavigate();

const handleClickButton =
pathName === ROUTE_PATHS.root || pathName === ROUTE_PATHS.login
? () => navigate(ROUTE_PATHS.root)
: () => navigate(-1);
pathName === ROUTE_PATHS_MAP.root || pathName === ROUTE_PATHS_MAP.login
? () => navigate(ROUTE_PATHS_MAP.root)
: () => navigate(ROUTE_PATHS_MAP.back);

const { user } = useUser();

return (
<Drawer>
<S.HeaderLayout>
<IconButton
color={pathName === ROUTE_PATHS.root ? theme.colors.primary : PRIMITIVE_COLORS.black}
color={pathName === ROUTE_PATHS_MAP.root ? theme.colors.primary : PRIMITIVE_COLORS.black}
onClick={handleClickButton}
iconType={pathName === ROUTE_PATHS.root ? "korean-logo" : "back-icon"}
iconType={pathName === ROUTE_PATHS_MAP.root ? "korean-logo" : "back-icon"}
/>
{pathName === ROUTE_PATHS.login ? (
{pathName === ROUTE_PATHS_MAP.login ? (
<>
<S.HeaderTitle>로그인</S.HeaderTitle>
<S.HiddenDiv />
Expand Down Expand Up @@ -67,7 +67,7 @@ const Header = () => {
) : (
<S.MenuItem
onClick={() => {
navigate(ROUTE_PATHS.login);
navigate(ROUTE_PATHS_MAP.login);
}}
>
로그인
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/common/Icon/svg-icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@
"width": 11,
"height": 11,
"path": "M1.1002 11L0 9.9L4.40079 5.5L0 1.1L1.1002 0L5.50098 4.4L9.90177 0L11.002 1.1L6.60118 5.5L11.002 9.9L9.90177 11L5.50098 6.6L1.1002 11Z",
"stroke": "currentColor",
"strokeWidth": "1",
"strokeLinecap": "butt",
"strokeLinejoin": "miter"
"stroke": "",
"strokeWidth": "0",
"strokeLinecap": "",
"strokeLinejoin": ""
}
}
}
6 changes: 6 additions & 0 deletions frontend/src/components/common/Input/Input.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export const Input = styled.input`
outline: none;
}
&:disabled {
background-color: ${({ theme }) => theme.colors.background.disabled};
color: ${({ theme }) => theme.colors.text.secondary};
}
&::placeholder {
color: ${({ theme }) => theme.colors.text.secondary};
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/common/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as S from "./Input.styled";
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
count?: number;
maxCount?: number;
label: string;
label?: string;
}

const Input = ({ label, count, maxCount, ...props }: InputProps) => {
Expand Down
Loading

0 comments on commit 7ca7897

Please sign in to comment.