Skip to content

Commit

Permalink
Merge pull request #50 from YAPP-Github/refactor/convention-48
Browse files Browse the repository at this point in the history
[ Refactor/convention 48 ] 쿼리 컨벤션 및 shared 폴더로 컴포넌트 이동
  • Loading branch information
Happhee authored Jun 15, 2024
2 parents a9247ce + d4a01f5 commit 4af143c
Show file tree
Hide file tree
Showing 19 changed files with 172 additions and 162 deletions.
4 changes: 2 additions & 2 deletions src/app/problem/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, { ReactNode } from "react";

import TopBar from "@common/components/TopBar";

interface Props {
interface QuizLayoutProps {
children: ReactNode;
}
export default function QuizLayout({ children }: Props) {
export default function QuizLayout({ children }: QuizLayoutProps) {
return (
<section className="mx-[20px] mb-[10px] flex h-auto w-full flex-col justify-between">
<TopBar />
Expand Down
71 changes: 39 additions & 32 deletions src/app/workbook/workbookpage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,76 @@
/* eslint-disable react/display-name */
import { ClassAttributes, ImgHTMLAttributes, JSX, ReactNode } from 'react';
import { ClassAttributes, ImgHTMLAttributes, JSX } from "react";

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

import {describe, expect, it, vi } from 'vitest';
import { describe, expect, it, vi } from "vitest";

import { useWorkbook } from '@workbook/remotes/getWorkbookQueryOptions';
import { createQueryProviderWrapper } from "@shared/constants/createQueryProvider";

import WorkbookPage from './[id]/page';
import { render, renderHook,screen, waitFor } from '@testing-library/react';
import { useWorkbook } from "@workbook/remotes/getWorkbookQueryOptions";

export function createQueryProviderWrapper () {
const queryClient = new QueryClient();
return ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
};
import WorkbookPage from "./[id]/page";
import { render, renderHook, screen, waitFor } from "@testing-library/react";

// TBD: 필요하면 vitest.setup.ts 에 빼놓기
vi.mock('next/navigation', () => ({
vi.mock("next/navigation", () => ({
useSearchParams: () => ({
get: (key: string) => {
if (key === 'workbookId') return '1';
if (key === "workbookId") return "1";
return null;
},
}),
usePathname: () => '/workbooks/1',
usePathname: () => "/workbooks/1",
}));

vi.mock('next/image', () => ({
vi.mock("next/image", () => ({
__esModule: true,
default: (props: JSX.IntrinsicAttributes & ClassAttributes<HTMLImageElement> & ImgHTMLAttributes<HTMLImageElement>) => {
default: (
props: JSX.IntrinsicAttributes &
ClassAttributes<HTMLImageElement> &
ImgHTMLAttributes<HTMLImageElement>,
) => {
return <img {...props} alt={props.alt} />;
},
}));


describe('워크북 페이지 테스트', () => {
it('workbook page 랜딩 시 react-query 테스트', async () => {
describe("워크북 페이지 테스트", () => {
it("workbook page 랜딩 시 react-query 테스트", async () => {
const { result } = renderHook(() => useWorkbook(1), {
wrapper: createQueryProviderWrapper(),
});

await waitFor(() => {
expect(result.current.data?.title).toBe('재태크, 투자 필수 용어 모음집');
expect(result.current.data?.mainImageUrl).toBe('/main_img.png');
expect(result.current.data?.description).toBe('사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.');
expect(result.current.data?.category).toBe('경제');
})
expect(result.current.data?.title).toBe("재태크, 투자 필수 용어 모음집");
expect(result.current.data?.mainImageUrl).toBe("/main_img.png");
expect(result.current.data?.description).toBe(
"사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.",
);
expect(result.current.data?.category).toBe("경제");
});
});

it('데이터와 함께 워크북 페이지를 로딩한다', async () => {
it("데이터와 함께 워크북 페이지를 로딩한다", async () => {
const queryClient = new QueryClient();
render(
<QueryClientProvider client={queryClient}>
<WorkbookPage />
</QueryClientProvider>
</QueryClientProvider>,
);

await waitFor(() => {
expect(screen.getByText('재태크, 투자 필수 용어 모음집')).toBeInTheDocument();
expect(screen.getByAltText('Workbook landing image')).toHaveAttribute('src', '/main_img.png');
expect(screen.getByText('사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.')).toBeInTheDocument();
expect(
screen.getByText("재태크, 투자 필수 용어 모음집"),
).toBeInTheDocument();
expect(screen.getByAltText("Workbook landing image")).toHaveAttribute(
"src",
"/main_img.png",
);
expect(
screen.getByText(
"사회 초년생부터, 직장인, 은퇴자까지 모두가 알아야 할 기본적인 재태크, 투자 필수 용어 모음집 입니다.",
),
).toBeInTheDocument();
});
});

Expand Down
45 changes: 0 additions & 45 deletions src/common/components/ExternalControlOpenDialog/index.tsx

This file was deleted.

3 changes: 2 additions & 1 deletion src/problem/components/AnswerChoiceButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { cn } from "@shared/utils/cn";
import ChoiceFillCircleSvg from "../ChoiceFillCircleSvg";
import { ANSWER_CHOICHE_BUTTON_INFO } from "@problem/constants/problemInfo";
import ProblemContext from "@problem/context/problemContext";
import { QUERY_KEY } from "@problem/remotes/api";
import { AnswerCheckInfo, AnswerChoiceInfo } from "@problem/types/problemInfo";

interface AnswerChoiceButtonProps extends Pick<AnswerChoiceInfo, "content"> {}
Expand All @@ -25,7 +26,7 @@ export default function AnswerChoiceButton({

const problemAnswerInfo = useMutationState({
filters: {
mutationKey: ["get-problem-answer"],
mutationKey: [QUERY_KEY.POST_PROBLEM_ANSWER],
},
select: (mutation) => mutation.state.data as AnswerCheckInfo,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useQuery } from "@tanstack/react-query";
import { describe, expect, it, vi } from "vitest";

import QueryClientProviders from "@shared/components/queryClientProvider";
import { createQueryProviderWrapper } from "@shared/constants/createQueryProvider";

import AnswerChoiceList from ".";
import { getProblemQueryOptions } from "@problem/remotes/getProblemQueryOptions";
Expand All @@ -24,9 +25,7 @@ vi.mock("next/navigation", () => {
}),
};
});
const wrapper = ({ children }: React.PropsWithChildren) => {
return <QueryClientProviders>{children}</QueryClientProviders>;
};

const renderWithQueryClient = () => {
return render(
<QueryClientProviders>
Expand All @@ -43,7 +42,7 @@ describe("선택지 불러오고, 버튼 클릭으로 버튼 ui 변경", () => {
useQuery({
...getProblemQueryOptions({ problemId: 1 }),
}),
{ wrapper },
{ wrapper: createQueryProviderWrapper() },
);
await waitFor(() => result.current.isSuccess);

Expand Down
6 changes: 4 additions & 2 deletions src/problem/components/ChoiceFillCircleSvg/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from "react";

interface Props {
interface ChoiceFillCircleSvgProps {
fill: string;
}

export default function ChoiceFillCircleSvg({ fill }: Props) {
export default function ChoiceFillCircleSvg({
fill,
}: ChoiceFillCircleSvgProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
3 changes: 2 additions & 1 deletion src/problem/components/ProblemTitle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from "react";
import { useMutationState, useQuery } from "@tanstack/react-query";

import { PROBLEM_TITLE_INFO } from "@problem/constants/problemInfo";
import { QUERY_KEY } from "@problem/remotes/api";
import { getProblemQueryOptions } from "@problem/remotes/getProblemQueryOptions";
import { AnswerCheckInfo } from "@problem/types/problemInfo";

Expand All @@ -18,7 +19,7 @@ export default function ProblemTitle() {
});
const problemAnswerInfo = useMutationState({
filters: {
mutationKey: ["get-problem-answer", problemIdNumber],
mutationKey: [QUERY_KEY.POST_PROBLEM_ANSWER, problemIdNumber],
},
select: (mutation) => mutation.state.data as AnswerCheckInfo,
});
Expand Down
3 changes: 2 additions & 1 deletion src/problem/components/TagList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { ApiResponse } from "@api/api-config";
import queryClient from "@api/query-client";

import Tag from "@common/components/Tag";
import { QUERY_KEY } from "@problem/remotes/api";
import { PromblemInfo } from "@problem/types/problemInfo";

export default function TagList() {
const problemInfoData = queryClient.getQueryData(["get-Problems-info"]) as
const problemInfoData = queryClient.getQueryData([QUERY_KEY.GET_PROBLEM]) as
| ApiResponse<PromblemInfo>
| undefined;

Expand Down
11 changes: 8 additions & 3 deletions src/problem/remotes/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export const PROBLEM_API_ROUTES = {
problems: (articleId: number) => `/articles/${articleId}/problems`,
submitAnswer: (problemId: number) => `/problems/${problemId}`,
export const API_ROUTE = {
PROBLEM: (articleId: number) => `/articles/${articleId}/problems`,
SUBMIT_ANSWER: (problemId: number) => `/problems/${problemId}`,
};

export const QUERY_KEY = {
GET_PROBLEM: "get-problem-info",
POST_PROBLEM_ANSWER: "post-problem-answer",
};
6 changes: 3 additions & 3 deletions src/problem/remotes/getProblemQueryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { UseQueryOptions } from "@tanstack/react-query";

import { ApiResponse, axiosRequest } from "@api/api-config";

import { PROBLEM_API_ROUTES } from "./api";
import { API_ROUTE, QUERY_KEY } from "./api";
import { PromblemInfo } from "@problem/types/problemInfo";

export const getProblemInfo = ({
problemId,
}: ProblemInfoParams): Promise<ApiResponse<PromblemInfo>> => {
return axiosRequest("get", PROBLEM_API_ROUTES.problems(problemId));
return axiosRequest("get", API_ROUTE.PROBLEM(problemId));
};
export const getProblemQueryOptions = ({
problemId,
Expand All @@ -18,7 +18,7 @@ export const getProblemQueryOptions = ({
PromblemInfo
> => {
return {
queryKey: ["get-Problems-info"],
queryKey: [QUERY_KEY.GET_PROBLEM, problemId],
queryFn: () => getProblemInfo({ problemId }),
select: (data) => data.data,
};
Expand Down
21 changes: 0 additions & 21 deletions src/problem/remotes/getTagQueryOptions.ts

This file was deleted.

10 changes: 3 additions & 7 deletions src/problem/remotes/postProblemAnswerOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import { UseMutationOptions } from "@tanstack/react-query";

import { ApiResponse, axiosRequest } from "@api/api-config";

import { PROBLEM_API_ROUTES } from "./api";
import { API_ROUTE, QUERY_KEY } from "./api";
import { AnswerCheckInfo } from "@problem/types/problemInfo";

export const postProblemAnswer = (
params: ProblemAnswerParams,
body: ProblemAnswerBody,
): Promise<ApiResponse<AnswerCheckInfo>> => {
return axiosRequest(
"post",
PROBLEM_API_ROUTES.submitAnswer(params.problemId),
body,
);
return axiosRequest("post", API_ROUTE.SUBMIT_ANSWER(params.problemId), body);
};
export const postProblemAnswerMutationOptions = (
params: ProblemAnswerParams,
Expand All @@ -23,7 +19,7 @@ export const postProblemAnswerMutationOptions = (
ProblemAnswerBody
> => {
return {
mutationKey: ["get-problem-answer", params.problemId],
mutationKey: [QUERY_KEY.POST_PROBLEM_ANSWER, params.problemId],
mutationFn: (body) => postProblemAnswer(params, body),
};
};
Expand Down
44 changes: 44 additions & 0 deletions src/shared/components/ExternalControlOpenDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";

import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@shared/components/ui/dialog";
export interface ExternalControlOpenDialogProps {
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
title: React.ReactNode;
content: React.ReactNode;
footer?: React.ReactNode;
description?: string;
}

export default function ExternalControlOpenDialog({
isOpen,
setIsOpen,
title,
content,
footer,
description,
}: ExternalControlOpenDialogProps) {
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="z-50 w-full max-w-[380px] rounded">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
{description && <DialogDescription>{description}</DialogDescription>}
</DialogHeader>
{content}
{footer && (
<DialogFooter className="mt-[20px] sm:justify-start">
{footer}
</DialogFooter>
)}
</DialogContent>
</Dialog>
);
}
Loading

0 comments on commit 4af143c

Please sign in to comment.