From 1a0ba58e2dcfd720c3a825d2f3e16f96c8c75c6b Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:24:25 +0900 Subject: [PATCH 01/52] style : error code modify --- tailwind.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tailwind.config.ts b/tailwind.config.ts index 1623cb42..29a05668 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -8,7 +8,7 @@ const config = { "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}", "./src/stories/**/*.{js,ts,jsx,tsx}", - "./src/**/**/**/*.{js,ts,jsx,tsx}" + "./src/**/**/**/*.{js,ts,jsx,tsx}", ], prefix: "", theme: { @@ -55,7 +55,7 @@ const config = { foreground: "hsl(var(--card-foreground))", }, success: "#0166B3", - error: "##B00020", + error: "#B00020", main: "#264932", background1: "#F5F5F5", text: { From 1e931955ae4dc693ffa6e13fcbe335fb083e10c6 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:24:55 +0900 Subject: [PATCH 02/52] docs : remove constans file --- src/quiz/constants/endpoint.ts | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/quiz/constants/endpoint.ts diff --git a/src/quiz/constants/endpoint.ts b/src/quiz/constants/endpoint.ts deleted file mode 100644 index 35dfd2e8..00000000 --- a/src/quiz/constants/endpoint.ts +++ /dev/null @@ -1 +0,0 @@ -export const LOGOUT_URL = "/api/v1/auth/logout"; From 94c96c2d2ec11f27013d611fa87b476ae8e7d5a5 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:25:47 +0900 Subject: [PATCH 03/52] fix : modify the api path to match the msw --- src/shared/constants/apiRoutes.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shared/constants/apiRoutes.ts b/src/shared/constants/apiRoutes.ts index 731f7362..2c43e8c7 100644 --- a/src/shared/constants/apiRoutes.ts +++ b/src/shared/constants/apiRoutes.ts @@ -1,4 +1,5 @@ export const apiRoutes = { - quiz: "/quiz", - tags: "/tags", + quiz: "/api/quiz", + tags: "/api/tags", + quizAnswer: "/api/quizAnswer", }; From 8e4e8fc7a1ed45254aedce746d4da268bac28000 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:27:44 +0900 Subject: [PATCH 04/52] refactor : Isolated by common type and improved for use with quiz, buttonInfo --- src/common/types/constKeyObject.ts | 3 +++ src/quiz/types/answerButtonInfo.ts | 9 ++++++--- src/quiz/types/quizTitle.ts | 12 ++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/common/types/constKeyObject.ts create mode 100644 src/quiz/types/quizTitle.ts diff --git a/src/common/types/constKeyObject.ts b/src/common/types/constKeyObject.ts new file mode 100644 index 00000000..b8bbfd46 --- /dev/null +++ b/src/common/types/constKeyObject.ts @@ -0,0 +1,3 @@ +export type ConstKeyObject = { + [key in Key]: Value; +}; diff --git a/src/quiz/types/answerButtonInfo.ts b/src/quiz/types/answerButtonInfo.ts index b211bac0..c785012b 100644 --- a/src/quiz/types/answerButtonInfo.ts +++ b/src/quiz/types/answerButtonInfo.ts @@ -1,5 +1,8 @@ import { BUTTON_STATE } from "@quiz/constants/answerButtonInfo"; -export type ButtonInfo = { - [key in (typeof BUTTON_STATE)[number]]: React.ButtonHTMLAttributes; -}; +import { ConstKeyObject } from "@common/types/constKeyObject"; + +export type ButtonInfo = ConstKeyObject< + (typeof BUTTON_STATE)[number], + React.ButtonHTMLAttributes +>; diff --git a/src/quiz/types/quizTitle.ts b/src/quiz/types/quizTitle.ts new file mode 100644 index 00000000..1e3bad0d --- /dev/null +++ b/src/quiz/types/quizTitle.ts @@ -0,0 +1,12 @@ +import { QUIZ_ANSWER_TYPE } from "@quiz/constants/quizTitle"; + +import { ConstKeyObject } from "@common/types/constKeyObject"; + +export type QuizTitleInfo = ConstKeyObject< + (typeof QUIZ_ANSWER_TYPE)[number], + React.HTMLAttributes +>; + +export type QuizAnswer = { + answer: string; +}; From 8c6e3ce2612b283bac9fb507b5c198c448862948 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:28:20 +0900 Subject: [PATCH 05/52] fix : modify to return in case of error --- src/quiz/components/TagList/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quiz/components/TagList/index.tsx b/src/quiz/components/TagList/index.tsx index 3181ff28..1111626d 100644 --- a/src/quiz/components/TagList/index.tsx +++ b/src/quiz/components/TagList/index.tsx @@ -10,8 +10,7 @@ import Tag from "@common/components/Tag"; export default function TagList() { const { data: tagInfo, isError } = useQuery({ ...getTagQueryOptions() }); - - if (isError)
error
; + if (isError) return
error
; return (
From da28497692a695b3b79a331211f3d81d72f8ee0c Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:29:00 +0900 Subject: [PATCH 06/52] feat : feat dev mode quizAnswer data --- src/mocks/response/quizAnswer.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/mocks/response/quizAnswer.json diff --git a/src/mocks/response/quizAnswer.json b/src/mocks/response/quizAnswer.json new file mode 100644 index 00000000..b180979b --- /dev/null +++ b/src/mocks/response/quizAnswer.json @@ -0,0 +1,3 @@ +{ + "answer": "높은 운용 비용" +} From 78338df6eac1bc3325c9a4949eeade221aefdbea Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:29:25 +0900 Subject: [PATCH 07/52] feat : implementation of quiz answer api routes --- src/app/api/quizAnswer/route.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/app/api/quizAnswer/route.ts diff --git a/src/app/api/quizAnswer/route.ts b/src/app/api/quizAnswer/route.ts new file mode 100644 index 00000000..bade6dba --- /dev/null +++ b/src/app/api/quizAnswer/route.ts @@ -0,0 +1,13 @@ +import { NextResponse } from "next/server"; + +import { apiRoutes } from "@shared/constants/apiRoutes"; + +import response from "@mocks/response"; + +export async function GET(request: Request) { + try { + return NextResponse.json(response[apiRoutes.quizAnswer], { status: 200 }); + } catch (error) { + return NextResponse.json({ error }, { status: 500 }); + } +} From 2d7bdbdb929b7ce62aae63e39f740450d685acdb Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:30:05 +0900 Subject: [PATCH 08/52] feat : implementation of quiz answer api response value --- src/mocks/response/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mocks/response/index.ts b/src/mocks/response/index.ts index aff83337..1bd417a9 100644 --- a/src/mocks/response/index.ts +++ b/src/mocks/response/index.ts @@ -1,10 +1,12 @@ import { apiRoutes } from "@shared/constants/apiRoutes"; import quiz from "./quiz.json"; +import quizAnswer from "./quizAnswer.json"; import tags from "./tags.json"; // eslint-disable-next-line import/no-anonymous-default-export export default { [apiRoutes.quiz]: quiz, [apiRoutes.tags]: tags, + [apiRoutes.quizAnswer]: quizAnswer, }; From 5e2576401d252628d6b46e36c070327418d28f74 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:30:52 +0900 Subject: [PATCH 09/52] feat : implementation of quiz answer api response value --- src/quiz/constants/quizTitle.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/quiz/constants/quizTitle.ts diff --git a/src/quiz/constants/quizTitle.ts b/src/quiz/constants/quizTitle.ts new file mode 100644 index 00000000..51f52aaa --- /dev/null +++ b/src/quiz/constants/quizTitle.ts @@ -0,0 +1,22 @@ +import { QuizTitleInfo } from "@quiz/types/quizTitle"; + +export const QUIZ_ANSWER_TYPE = [ + "NO_ANSWER", + "ANSWER_CORRECT", + "ANSWER_FAIL", +] as const; + +export const QUIZ_TITLE_INFO: QuizTitleInfo = { + NO_ANSWER: { + title: "Q.", + className: "text-main sub1-bold", + }, + ANSWER_CORRECT: { + title: "정답이에요!", + className: "text-success sub1-semibold", + }, + ANSWER_FAIL: { + title: "아쉽지만 정답이 아니에요.", + className: "text-error sub1-semibold", + }, +}; From ff1fc09d83b7dfd96bf994916bcce8c1b6b29330 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:31:29 +0900 Subject: [PATCH 10/52] feat : implementation of the quizzitle component - Q, Quarterly processing of correct and incorrect answers --- src/app/quiz/page.tsx | 4 ++- src/quiz/components/QuizTitle/index.tsx | 35 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/quiz/components/QuizTitle/index.tsx diff --git a/src/app/quiz/page.tsx b/src/app/quiz/page.tsx index 897fcf01..f98dcc00 100644 --- a/src/app/quiz/page.tsx +++ b/src/app/quiz/page.tsx @@ -1,6 +1,7 @@ import React from "react"; import AnswerContextButton from "@quiz/components/AnswerContextButton"; +import QuizTitle from "@quiz/components/QuizTitle"; import TagList from "@quiz/components/TagList"; import { QuizProvider } from "@quiz/context/quizContext"; @@ -8,8 +9,9 @@ export default function QuizPage() { return ( <> -
+
+
diff --git a/src/quiz/components/QuizTitle/index.tsx b/src/quiz/components/QuizTitle/index.tsx new file mode 100644 index 00000000..d6a53f09 --- /dev/null +++ b/src/quiz/components/QuizTitle/index.tsx @@ -0,0 +1,35 @@ +"use client"; + +import React, { useContext } from "react"; + +import { useQuery } from "@tanstack/react-query"; + +import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; +import QuizContext from "@quiz/context/quizContext"; +import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; + +export default function QuizTitle() { + const { + states: { answer, isSubmit }, + } = useContext(QuizContext); + + const { data: quizAnswer, isSuccess } = useQuery({ + ...getQuizAnswerQueryOptions(), + }); + + if (!isSuccess && !quizAnswer) return
; + + const subTitleInfo = + (!isSubmit && QUIZ_TITLE_INFO.NO_ANSWER) || + (isSubmit && + answer && + answer === quizAnswer?.answer && + QUIZ_TITLE_INFO.ANSWER_CORRECT) || + QUIZ_TITLE_INFO.ANSWER_FAIL; + + return ( +
+

{subTitleInfo.title}

+
+ ); +} From 7830255be611e337cbf4a3aa37e11150c4e2676b Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:33:44 +0900 Subject: [PATCH 11/52] test : Q, Test implementation for correct answer guidance and incorrect answer guidance branch processing --- .../components/QuizTitle/QuizTitle.test.tsx | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/quiz/components/QuizTitle/QuizTitle.test.tsx diff --git a/src/quiz/components/QuizTitle/QuizTitle.test.tsx b/src/quiz/components/QuizTitle/QuizTitle.test.tsx new file mode 100644 index 00000000..104b9f26 --- /dev/null +++ b/src/quiz/components/QuizTitle/QuizTitle.test.tsx @@ -0,0 +1,114 @@ +import { + QueryClient, + QueryClientProvider, + useQuery, +} from "@tanstack/react-query"; + +import { describe, expect, it } from "vitest"; + +import QueryClientProviders from "@shared/components/queryClientProvider"; + +import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; +import QuizContext from "@quiz/context/quizContext"; +import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; +import { QuizContextInfo } from "@quiz/types/quizContextInfo"; + +import QuizTitle from "."; +import { render, renderHook, screen, waitFor } from "@testing-library/react"; + +const queryClient = new QueryClient(); +const wrapper = ({ children }: React.PropsWithChildren) => ( + {children} +); + +const defaultState = { + states: { + answer: null, + isSubmit: false, + }, +}; +const defaultActions = { + actions: { + updateAnswer: () => {}, + updateSubmit: () => {}, + }, +}; +describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { + const renderWithContext = (contextValue: QuizContextInfo) => { + return render( + + + + + , + ); + }; + it("정답 제출 전 상태", async () => { + renderWithContext({ + ...defaultActions, + states: { ...defaultState.states, isSubmit: false }, + }); + + await waitFor(() => { + const heading = screen.getByRole("heading", { level: 3 }); + if (QUIZ_TITLE_INFO.NO_ANSWER.title) + expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.NO_ANSWER.title); + if (QUIZ_TITLE_INFO.NO_ANSWER.className) + expect(heading).toHaveClass(QUIZ_TITLE_INFO.NO_ANSWER.className); + }); + }); + + it("정답 제출 후 정답 상태", async () => { + const contextValue = { + ...defaultActions, + states: { answer: "높은 운용 비용", isSubmit: true }, + }; + renderWithContext(contextValue); + const { result } = renderHook( + () => useQuery({ ...getQuizAnswerQueryOptions() }), + { + wrapper, + }, + ); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + if (result.current.data) + expect(result.current.data.answer === contextValue.states.answer).toBe( + true, + ); + + const heading = screen.getByRole("heading", { level: 3 }); + if (QUIZ_TITLE_INFO.ANSWER_CORRECT.title) + expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.ANSWER_CORRECT.title); + + if (QUIZ_TITLE_INFO.ANSWER_CORRECT.className) + expect(heading).toHaveClass(QUIZ_TITLE_INFO.ANSWER_CORRECT.className); + }); + it("정답 제출 후 오답 상태", async () => { + const contextValue = { + ...defaultActions, + states: { answer: "유동성", isSubmit: true }, + }; + renderWithContext(contextValue); + + const { result } = renderHook( + () => useQuery({ ...getQuizAnswerQueryOptions() }), + { + wrapper, + }, + ); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + if (result.current.data) + expect(result.current.data.answer === contextValue.states.answer).toBe( + false, + ); + + const heading = screen.getByRole("heading", { level: 3 }); + if (QUIZ_TITLE_INFO.ANSWER_FAIL.title) + expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.ANSWER_FAIL.title); + if (QUIZ_TITLE_INFO.ANSWER_FAIL.className) + expect(heading).toHaveClass(QUIZ_TITLE_INFO.ANSWER_FAIL.className); + }); +}); From 5bac20f388de1d67503ae3ea0aefdcf5804a9994 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:34:26 +0900 Subject: [PATCH 12/52] feat : feat quiz answer react query api --- src/quiz/remotes/getQuizAnswerOption.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/quiz/remotes/getQuizAnswerOption.ts diff --git a/src/quiz/remotes/getQuizAnswerOption.ts b/src/quiz/remotes/getQuizAnswerOption.ts new file mode 100644 index 00000000..272cb93d --- /dev/null +++ b/src/quiz/remotes/getQuizAnswerOption.ts @@ -0,0 +1,21 @@ +import { UseQueryOptions } from "@tanstack/react-query"; + +import { axiosRequest } from "@api/api-config"; + +import { apiRoutes } from "@shared/constants/apiRoutes"; + +import { QuizAnswer } from "@quiz/types/quizTitle"; + +export const getQuizAnswer = (): Promise => { + return axiosRequest("get", apiRoutes.quizAnswer); +}; +export const getQuizAnswerQueryOptions = (): UseQueryOptions< + unknown, + Error, + QuizAnswer +> => { + return { + queryKey: ["get-answer"], + queryFn: () => getQuizAnswer(), + }; +}; From 0d283d2f3abdbec9b113055df0cd7ea76c661f83 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 12:59:40 +0900 Subject: [PATCH 13/52] feat : feat common module alias path --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index 14e2bf27..b2bfc90f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ "@quiz": resolve("/src/quiz"), "@article": resolve("src/article"), "@main": resolve("src/main"), + "@common": resolve("src/common"), }, }, }); From 8d9bf2db14fdb8ed7cf141384932572f2d4dfbb0 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 13:00:10 +0900 Subject: [PATCH 14/52] fix : modify api response to ApiResponse --- src/quiz/remotes/getQuizAnswerOption.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quiz/remotes/getQuizAnswerOption.ts b/src/quiz/remotes/getQuizAnswerOption.ts index 272cb93d..c65412cc 100644 --- a/src/quiz/remotes/getQuizAnswerOption.ts +++ b/src/quiz/remotes/getQuizAnswerOption.ts @@ -1,12 +1,12 @@ import { UseQueryOptions } from "@tanstack/react-query"; -import { axiosRequest } from "@api/api-config"; +import { ApiResponse, axiosRequest } from "@api/api-config"; import { apiRoutes } from "@shared/constants/apiRoutes"; import { QuizAnswer } from "@quiz/types/quizTitle"; -export const getQuizAnswer = (): Promise => { +export const getQuizAnswer = (): Promise> => { return axiosRequest("get", apiRoutes.quizAnswer); }; export const getQuizAnswerQueryOptions = (): UseQueryOptions< From 068492bc5806cfa56c3537cbd87924689e67c511 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 13:00:38 +0900 Subject: [PATCH 15/52] feat : feat common query wrapper --- src/common/remotes/testQueryClient.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/common/remotes/testQueryClient.tsx diff --git a/src/common/remotes/testQueryClient.tsx b/src/common/remotes/testQueryClient.tsx new file mode 100644 index 00000000..9e6144ff --- /dev/null +++ b/src/common/remotes/testQueryClient.tsx @@ -0,0 +1,8 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const queryClient = new QueryClient(); +export const testQueryClientWrapper = ({ + children, +}: React.PropsWithChildren) => ( + {children} +); From 20415753f417900f0d578d540ade5a1d45fb46c4 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 13:01:16 +0900 Subject: [PATCH 16/52] feat : quiz info fetch to data in page --- src/quiz/components/QuizTitle/index.tsx | 5 +++++ src/quiz/remotes/getQuizInfoQueryOptions.ts | 21 +++++++++++++++++++++ src/quiz/types/quizTitle.ts | 5 +++++ 3 files changed, 31 insertions(+) create mode 100644 src/quiz/remotes/getQuizInfoQueryOptions.ts diff --git a/src/quiz/components/QuizTitle/index.tsx b/src/quiz/components/QuizTitle/index.tsx index d6a53f09..23765db7 100644 --- a/src/quiz/components/QuizTitle/index.tsx +++ b/src/quiz/components/QuizTitle/index.tsx @@ -7,6 +7,7 @@ import { useQuery } from "@tanstack/react-query"; import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; import QuizContext from "@quiz/context/quizContext"; import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; +import { getQuizInfoQueryOptions } from "@quiz/remotes/getQuizInfoQueryOptions"; export default function QuizTitle() { const { @@ -16,6 +17,9 @@ export default function QuizTitle() { const { data: quizAnswer, isSuccess } = useQuery({ ...getQuizAnswerQueryOptions(), }); + const { data: quizInfo } = useQuery({ + ...getQuizInfoQueryOptions(), + }); if (!isSuccess && !quizAnswer) return
; @@ -30,6 +34,7 @@ export default function QuizTitle() { return (

{subTitleInfo.title}

+

{quizInfo?.question}

); } diff --git a/src/quiz/remotes/getQuizInfoQueryOptions.ts b/src/quiz/remotes/getQuizInfoQueryOptions.ts new file mode 100644 index 00000000..13d540d0 --- /dev/null +++ b/src/quiz/remotes/getQuizInfoQueryOptions.ts @@ -0,0 +1,21 @@ +import { UseQueryOptions } from "@tanstack/react-query"; + +import { ApiResponse, axiosRequest } from "@api/api-config"; + +import { apiRoutes } from "@shared/constants/apiRoutes"; + +import { QuizInfo } from "@quiz/types/quizTitle"; + +export const getQuizInfo = (): Promise> => { + return axiosRequest("get", apiRoutes.quiz); +}; +export const getQuizInfoQueryOptions = (): UseQueryOptions< + unknown, + Error, + QuizInfo +> => { + return { + queryKey: ["get-quiz-info"], + queryFn: () => getQuizInfo(), + }; +}; diff --git a/src/quiz/types/quizTitle.ts b/src/quiz/types/quizTitle.ts index 1e3bad0d..3da989d6 100644 --- a/src/quiz/types/quizTitle.ts +++ b/src/quiz/types/quizTitle.ts @@ -10,3 +10,8 @@ export type QuizTitleInfo = ConstKeyObject< export type QuizAnswer = { answer: string; }; + +export type QuizInfo = { + question: string; + answer: string[]; +}; From a1cf337c4cbd692dd7388456cf73e4a834172321 Mon Sep 17 00:00:00 2001 From: happhee Date: Sun, 9 Jun 2024 13:01:42 +0900 Subject: [PATCH 17/52] test : test to fetch quiz info in QuizTitle component --- .../components/QuizTitle/QuizTitle.test.tsx | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/quiz/components/QuizTitle/QuizTitle.test.tsx b/src/quiz/components/QuizTitle/QuizTitle.test.tsx index 104b9f26..242a9f9b 100644 --- a/src/quiz/components/QuizTitle/QuizTitle.test.tsx +++ b/src/quiz/components/QuizTitle/QuizTitle.test.tsx @@ -1,8 +1,4 @@ -import { - QueryClient, - QueryClientProvider, - useQuery, -} from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import { describe, expect, it } from "vitest"; @@ -11,16 +7,13 @@ import QueryClientProviders from "@shared/components/queryClientProvider"; import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; import QuizContext from "@quiz/context/quizContext"; import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; +import { getQuizInfoQueryOptions } from "@quiz/remotes/getQuizInfoQueryOptions"; import { QuizContextInfo } from "@quiz/types/quizContextInfo"; import QuizTitle from "."; +import { testQueryClientWrapper } from "@common/remotes/testQueryClient"; import { render, renderHook, screen, waitFor } from "@testing-library/react"; -const queryClient = new QueryClient(); -const wrapper = ({ children }: React.PropsWithChildren) => ( - {children} -); - const defaultState = { states: { answer: null, @@ -33,17 +26,18 @@ const defaultActions = { updateSubmit: () => {}, }, }; -describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { - const renderWithContext = (contextValue: QuizContextInfo) => { - return render( - - - - - , - ); - }; - it("정답 제출 전 상태", async () => { +const renderWithContext = (contextValue: QuizContextInfo) => { + return render( + + + + + , + ); +}; + +describe("퀴즈 정보 불러오기 테스트", () => { + it("퀴즈 불러와서 페이지에 보이는지 확인", async () => { renderWithContext({ ...defaultActions, states: { ...defaultState.states, isSubmit: false }, @@ -57,6 +51,28 @@ describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { expect(heading).toHaveClass(QUIZ_TITLE_INFO.NO_ANSWER.className); }); }); +}); +describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { + it("정답 제출 전 상태", async () => { + renderWithContext({ + ...defaultActions, + states: { ...defaultState.states, isSubmit: false }, + }); + const { result } = renderHook( + () => useQuery({ ...getQuizInfoQueryOptions() }), + { + wrapper: testQueryClientWrapper, + }, + ); + const heading = screen.getByRole("heading", { level: 2 }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + if (result.current.data) + expect(heading).toHaveTextContent(result.current.data?.question); + }); it("정답 제출 후 정답 상태", async () => { const contextValue = { @@ -67,7 +83,7 @@ describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { const { result } = renderHook( () => useQuery({ ...getQuizAnswerQueryOptions() }), { - wrapper, + wrapper: testQueryClientWrapper, }, ); await waitFor(() => expect(result.current.isSuccess).toBe(true)); @@ -94,7 +110,7 @@ describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { const { result } = renderHook( () => useQuery({ ...getQuizAnswerQueryOptions() }), { - wrapper, + wrapper: testQueryClientWrapper, }, ); From 2ba5122a9cd0e203fd365c46219de9fd9df69bd4 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:09:33 +0900 Subject: [PATCH 18/52] docs : rename quiz to problem --- src/app/{quiz => problem}/layout.tsx | 0 src/app/problem/page.tsx | 22 +++ src/app/quiz/page.tsx | 20 --- .../components/TagList/TagList.stories.tsx | 20 +++ src/problem/components/TagList/index.tsx | 19 +++ src/problem/constants/answerButtonInfo.ts | 22 +++ src/problem/context/problemContext/index.tsx | 33 +++++ src/problem/hooks/useIsMounted.tsx | 13 ++ src/problem/remotes/getTagQueryOptions.ts | 21 +++ src/problem/types/answerButtonInfo.ts | 7 + src/problem/types/problemContextInfo.ts | 10 ++ src/problem/types/problemInfo.ts | 32 +++++ src/problem/types/tagInfo.ts | 3 + .../AnswerContextButton.stories.ts | 31 ----- .../AnswerContextButton.test.tsx | 82 ----------- .../components/AnswerContextButton/index.tsx | 24 ---- .../components/QuizTitle/QuizTitle.test.tsx | 130 ------------------ src/quiz/components/QuizTitle/index.tsx | 40 ------ .../articlePopup/articlePopup.test.tsx | 8 -- src/quiz/components/articlePopup/index.tsx | 5 - tsconfig.json | 4 +- vitest.config.ts | 2 +- 22 files changed, 205 insertions(+), 343 deletions(-) rename src/app/{quiz => problem}/layout.tsx (100%) create mode 100644 src/app/problem/page.tsx delete mode 100644 src/app/quiz/page.tsx create mode 100644 src/problem/components/TagList/TagList.stories.tsx create mode 100644 src/problem/components/TagList/index.tsx create mode 100644 src/problem/constants/answerButtonInfo.ts create mode 100644 src/problem/context/problemContext/index.tsx create mode 100644 src/problem/hooks/useIsMounted.tsx create mode 100644 src/problem/remotes/getTagQueryOptions.ts create mode 100644 src/problem/types/answerButtonInfo.ts create mode 100644 src/problem/types/problemContextInfo.ts create mode 100644 src/problem/types/problemInfo.ts create mode 100644 src/problem/types/tagInfo.ts delete mode 100644 src/quiz/components/AnswerContextButton/AnswerContextButton.stories.ts delete mode 100644 src/quiz/components/AnswerContextButton/AnswerContextButton.test.tsx delete mode 100644 src/quiz/components/AnswerContextButton/index.tsx delete mode 100644 src/quiz/components/QuizTitle/QuizTitle.test.tsx delete mode 100644 src/quiz/components/QuizTitle/index.tsx delete mode 100644 src/quiz/components/articlePopup/articlePopup.test.tsx delete mode 100644 src/quiz/components/articlePopup/index.tsx diff --git a/src/app/quiz/layout.tsx b/src/app/problem/layout.tsx similarity index 100% rename from src/app/quiz/layout.tsx rename to src/app/problem/layout.tsx diff --git a/src/app/problem/page.tsx b/src/app/problem/page.tsx new file mode 100644 index 00000000..668d7810 --- /dev/null +++ b/src/app/problem/page.tsx @@ -0,0 +1,22 @@ +import React from "react"; + +import AnswerChoiceList from "@problem/components/AnswerChoiceList"; +import AnswerContextButton from "@problem/components/AnswerContextButton"; +import ProblemTitle from "@problem/components/ProblemTitle"; +import TagList from "@problem/components/TagList"; +import { ProblemProvider } from "@problem/context/problemContext"; + +export default function QuizPage() { + return ( + + <> +
+ + + +
+ + +
+ ); +} diff --git a/src/app/quiz/page.tsx b/src/app/quiz/page.tsx deleted file mode 100644 index f98dcc00..00000000 --- a/src/app/quiz/page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; - -import AnswerContextButton from "@quiz/components/AnswerContextButton"; -import QuizTitle from "@quiz/components/QuizTitle"; -import TagList from "@quiz/components/TagList"; -import { QuizProvider } from "@quiz/context/quizContext"; - -export default function QuizPage() { - return ( - - <> -
- - -
- - -
- ); -} diff --git a/src/problem/components/TagList/TagList.stories.tsx b/src/problem/components/TagList/TagList.stories.tsx new file mode 100644 index 00000000..2e35041c --- /dev/null +++ b/src/problem/components/TagList/TagList.stories.tsx @@ -0,0 +1,20 @@ +import TagList from "."; +import { tagsHandler } from "@mocks/worker"; +import { Meta, StoryObj } from "@storybook/react"; + +const meta = { + component: TagList, +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +export const TagComponent = { + parameters: { + msw: { + handlers: { + quiz: tagsHandler, + }, + }, + }, +} satisfies Story; diff --git a/src/problem/components/TagList/index.tsx b/src/problem/components/TagList/index.tsx new file mode 100644 index 00000000..e38d52f4 --- /dev/null +++ b/src/problem/components/TagList/index.tsx @@ -0,0 +1,19 @@ +"use client"; + +import React from "react"; + +import { useQuery } from "@tanstack/react-query"; + +import Tag from "@common/components/Tag"; +import { getTagQueryOptions } from "@problem/remotes/getTagQueryOptions"; + +export default function TagList() { + const { data: tagInfo, isError } = useQuery({ ...getTagQueryOptions() }); + if (isError) return
error
; + + return ( +
+ {tagInfo?.tags.map((tag) => )} +
+ ); +} diff --git a/src/problem/constants/answerButtonInfo.ts b/src/problem/constants/answerButtonInfo.ts new file mode 100644 index 00000000..cdfe8cbf --- /dev/null +++ b/src/problem/constants/answerButtonInfo.ts @@ -0,0 +1,22 @@ +import { ButtonInfo } from "@problem/types/answerButtonInfo"; + +export const BUTTON_STATE = [ + "PRE_ANSWER_SELECT", + "POST_ANSWER_PRE_SUBMIT", + "POST_SUBMIT", +] as const; + +export const BUTTON_INFO: ButtonInfo = { + PRE_ANSWER_SELECT: { + title: "정답 제출하기", + className: "bg-text-gray3 text-text-gray2", + }, + POST_ANSWER_PRE_SUBMIT: { + title: "정답 제출하기", + className: "bg-main", + }, + POST_SUBMIT: { + title: "다음 문제 풀기", + className: "bg-text-black", + }, +}; diff --git a/src/problem/context/problemContext/index.tsx b/src/problem/context/problemContext/index.tsx new file mode 100644 index 00000000..52d0affd --- /dev/null +++ b/src/problem/context/problemContext/index.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { createContext, useState } from "react"; + +import { ProblemContextInfo } from "@problem/types/problemContextInfo"; + +const ProblemContext = createContext({ + states: { answer: "", isSubmit: false }, + actions: { + updateAnswer: () => {}, + updateSubmit: () => {}, + }, +}); + +function ProblemProvider({ children }: { children: React.ReactElement }) { + const [answer, setAnswer] = useState(""); + const [isSubmit, setIsSubmit] = useState(false); + + const value = { + states: { answer, isSubmit }, + actions: { + updateAnswer: (answer: string) => setAnswer(answer), + updateSubmit: (isSubmit: boolean) => setIsSubmit(isSubmit), + }, + }; + return ( + {children} + ); +} + +const ProblemConsumer = ProblemContext.Consumer; +export { ProblemConsumer, ProblemProvider }; +export default ProblemContext; diff --git a/src/problem/hooks/useIsMounted.tsx b/src/problem/hooks/useIsMounted.tsx new file mode 100644 index 00000000..ef6d0b45 --- /dev/null +++ b/src/problem/hooks/useIsMounted.tsx @@ -0,0 +1,13 @@ +import { useEffect, useState } from "react"; + +const useIsMounted = () => { + const [mounted, setMounted] = useState(false); + + useEffect(function setMouteState() { + setMounted(true); + }, []); + + return mounted; +}; + +export default useIsMounted; diff --git a/src/problem/remotes/getTagQueryOptions.ts b/src/problem/remotes/getTagQueryOptions.ts new file mode 100644 index 00000000..ee55c0e9 --- /dev/null +++ b/src/problem/remotes/getTagQueryOptions.ts @@ -0,0 +1,21 @@ +import { UseQueryOptions } from "@tanstack/react-query"; + +import { ApiResponse, axiosRequest } from "@api/api-config"; + +import { apiRoutes } from "@shared/constants/apiRoutes"; + +import { TagInfo } from "@problem/types/tagInfo"; + +const getTags = (): Promise> => { + return axiosRequest("get", apiRoutes.tags); +}; +export const getTagQueryOptions = (): UseQueryOptions< + unknown, + unknown, + TagInfo +> => { + return { + queryKey: ["get-Tags"], + queryFn: () => getTags(), + }; +}; diff --git a/src/problem/types/answerButtonInfo.ts b/src/problem/types/answerButtonInfo.ts new file mode 100644 index 00000000..5585fea8 --- /dev/null +++ b/src/problem/types/answerButtonInfo.ts @@ -0,0 +1,7 @@ +import { ConstKeyObject } from "@common/types/constKeyObject"; +import { BUTTON_STATE } from "@problem/constants/answerButtonInfo"; + +export type ButtonInfo = ConstKeyObject< + (typeof BUTTON_STATE)[number], + React.ButtonHTMLAttributes +>; diff --git a/src/problem/types/problemContextInfo.ts b/src/problem/types/problemContextInfo.ts new file mode 100644 index 00000000..c4c871ba --- /dev/null +++ b/src/problem/types/problemContextInfo.ts @@ -0,0 +1,10 @@ +export type ProblemContextInfo = { + states: { + answer: string | null; + isSubmit: boolean; + }; + actions: { + updateAnswer: (answer: string) => void; + updateSubmit: (isSubmit: boolean) => void; + }; +}; diff --git a/src/problem/types/problemInfo.ts b/src/problem/types/problemInfo.ts new file mode 100644 index 00000000..014af3b1 --- /dev/null +++ b/src/problem/types/problemInfo.ts @@ -0,0 +1,32 @@ +import { ConstKeyObject } from "@common/types/constKeyObject"; +import { + ANSWER_CHOICHE_TYPE, + PROBLEM_ANSWER_TYPE, +} from "@problem/constants/problemInfo"; + +export type ProblemTitleInfo = ConstKeyObject< + (typeof PROBLEM_ANSWER_TYPE)[number], + React.HTMLAttributes +>; + +export type ProblemAnswer = { + answer: string; +}; +export type PromblemInfo = { + id: number; + title: string; + contents: AnswerChoiceInfo[]; +}; +export type ProblemsInfo = { + day: string; + problems: PromblemInfo[]; +}; +export type AnswerChoiceInfo = { + number: number; + content: string; +}; + +export type AnswerChoiceButtonInfo = ConstKeyObject< + (typeof ANSWER_CHOICHE_TYPE)[number], + React.HTMLAttributes +>; diff --git a/src/problem/types/tagInfo.ts b/src/problem/types/tagInfo.ts new file mode 100644 index 00000000..9e582863 --- /dev/null +++ b/src/problem/types/tagInfo.ts @@ -0,0 +1,3 @@ +export type TagInfo = { + tags: string[]; +}; diff --git a/src/quiz/components/AnswerContextButton/AnswerContextButton.stories.ts b/src/quiz/components/AnswerContextButton/AnswerContextButton.stories.ts deleted file mode 100644 index 209c292a..00000000 --- a/src/quiz/components/AnswerContextButton/AnswerContextButton.stories.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Button } from "@shared/components/ui/button"; - -import { Meta, StoryObj } from "@storybook/react"; - -const meta = { - component: Button, -} satisfies Meta; -export default meta; - -type Story = StoryObj; - -export const WithChoiceAnswer = { - args: { - className: "bg-main", - children: "정답 제출하기", - }, -} satisfies Story; - -export const WithoutChoiceAnswer = { - args: { - className: "bg-text-gray3", - children: "정답 제출하기", - }, -} satisfies Story; - -export const SubmitAnswer = { - args: { - className: "bg-text-black", - children: "다음 문제 풀기", - }, -} satisfies Story; diff --git a/src/quiz/components/AnswerContextButton/AnswerContextButton.test.tsx b/src/quiz/components/AnswerContextButton/AnswerContextButton.test.tsx deleted file mode 100644 index 3e5669bb..00000000 --- a/src/quiz/components/AnswerContextButton/AnswerContextButton.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { ReactElement } from "react"; - -import { describe, expect, it, vi } from "vitest"; - -import { BUTTON_INFO } from "@quiz/constants/answerButtonInfo"; -import QuizContext from "@quiz/context/quizContext"; -import { QuizContextInfo } from "@quiz/types/quizContextInfo"; - -import AnswerContextButton from "."; -import { render, screen } from "@testing-library/react"; - -vi.mock("@shared/components/ui/button", () => ({ - Button: ({ - className, - children, - }: { - className: string; - children: ReactElement; - }) => , -})); -const defaultActions = { - actions: { - updateAnswer: () => {}, - updateSubmit: () => {}, - }, -}; -describe("퀴즈 푸는 하단 버튼, 정답 선택에 따른 테스트", () => { - const renderWithContext = (contextValue: QuizContextInfo) => { - return render( - - - , - ); - }; - - it("정답 선택 전 상태", () => { - renderWithContext({ - states: { answer: null, isSubmit: false }, - ...defaultActions, - }); - if (BUTTON_INFO.PRE_ANSWER_SELECT.title) - expect( - screen.getByText(BUTTON_INFO.PRE_ANSWER_SELECT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.PRE_ANSWER_SELECT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.PRE_ANSWER_SELECT.className, - ); - }); - it("정답 선택 후 상태", () => { - renderWithContext({ - states: { answer: "정답!!", isSubmit: false }, - ...defaultActions, - }); - if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title) - expect( - screen.getByText(BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className, - ); - }); - - it("정답 선택 후 상태", () => { - renderWithContext({ - states: { answer: "정답!!", isSubmit: true }, - ...defaultActions, - }); - if (BUTTON_INFO.POST_SUBMIT.title) - expect( - screen.getByText(BUTTON_INFO.POST_SUBMIT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.POST_SUBMIT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.POST_SUBMIT.className, - ); - }); -}); diff --git a/src/quiz/components/AnswerContextButton/index.tsx b/src/quiz/components/AnswerContextButton/index.tsx deleted file mode 100644 index d052befe..00000000 --- a/src/quiz/components/AnswerContextButton/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client"; - -import React, { useContext } from "react"; - -import { Button } from "@shared/components/ui/button"; - -import { BUTTON_INFO } from "@quiz/constants/answerButtonInfo"; -import QuizContext from "@quiz/context/quizContext"; - -export default function AnswerContextButton() { - const { - states: { answer, isSubmit }, - } = useContext(QuizContext); - - const result = isSubmit - ? BUTTON_INFO.POST_SUBMIT.title - : BUTTON_INFO.PRE_ANSWER_SELECT.title; - const style = - (!answer && !isSubmit && BUTTON_INFO.PRE_ANSWER_SELECT.className) || - (answer && !isSubmit && BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) || - BUTTON_INFO.POST_SUBMIT.className; - - return ; -} diff --git a/src/quiz/components/QuizTitle/QuizTitle.test.tsx b/src/quiz/components/QuizTitle/QuizTitle.test.tsx deleted file mode 100644 index 242a9f9b..00000000 --- a/src/quiz/components/QuizTitle/QuizTitle.test.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -import { describe, expect, it } from "vitest"; - -import QueryClientProviders from "@shared/components/queryClientProvider"; - -import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; -import QuizContext from "@quiz/context/quizContext"; -import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; -import { getQuizInfoQueryOptions } from "@quiz/remotes/getQuizInfoQueryOptions"; -import { QuizContextInfo } from "@quiz/types/quizContextInfo"; - -import QuizTitle from "."; -import { testQueryClientWrapper } from "@common/remotes/testQueryClient"; -import { render, renderHook, screen, waitFor } from "@testing-library/react"; - -const defaultState = { - states: { - answer: null, - isSubmit: false, - }, -}; -const defaultActions = { - actions: { - updateAnswer: () => {}, - updateSubmit: () => {}, - }, -}; -const renderWithContext = (contextValue: QuizContextInfo) => { - return render( - - - - - , - ); -}; - -describe("퀴즈 정보 불러오기 테스트", () => { - it("퀴즈 불러와서 페이지에 보이는지 확인", async () => { - renderWithContext({ - ...defaultActions, - states: { ...defaultState.states, isSubmit: false }, - }); - - await waitFor(() => { - const heading = screen.getByRole("heading", { level: 3 }); - if (QUIZ_TITLE_INFO.NO_ANSWER.title) - expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.NO_ANSWER.title); - if (QUIZ_TITLE_INFO.NO_ANSWER.className) - expect(heading).toHaveClass(QUIZ_TITLE_INFO.NO_ANSWER.className); - }); - }); -}); -describe("퀴즈 결과에 따른, 문제 타이틀 변경 테스트", () => { - it("정답 제출 전 상태", async () => { - renderWithContext({ - ...defaultActions, - states: { ...defaultState.states, isSubmit: false }, - }); - const { result } = renderHook( - () => useQuery({ ...getQuizInfoQueryOptions() }), - { - wrapper: testQueryClientWrapper, - }, - ); - const heading = screen.getByRole("heading", { level: 2 }); - - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - }); - - if (result.current.data) - expect(heading).toHaveTextContent(result.current.data?.question); - }); - - it("정답 제출 후 정답 상태", async () => { - const contextValue = { - ...defaultActions, - states: { answer: "높은 운용 비용", isSubmit: true }, - }; - renderWithContext(contextValue); - const { result } = renderHook( - () => useQuery({ ...getQuizAnswerQueryOptions() }), - { - wrapper: testQueryClientWrapper, - }, - ); - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - - if (result.current.data) - expect(result.current.data.answer === contextValue.states.answer).toBe( - true, - ); - - const heading = screen.getByRole("heading", { level: 3 }); - if (QUIZ_TITLE_INFO.ANSWER_CORRECT.title) - expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.ANSWER_CORRECT.title); - - if (QUIZ_TITLE_INFO.ANSWER_CORRECT.className) - expect(heading).toHaveClass(QUIZ_TITLE_INFO.ANSWER_CORRECT.className); - }); - it("정답 제출 후 오답 상태", async () => { - const contextValue = { - ...defaultActions, - states: { answer: "유동성", isSubmit: true }, - }; - renderWithContext(contextValue); - - const { result } = renderHook( - () => useQuery({ ...getQuizAnswerQueryOptions() }), - { - wrapper: testQueryClientWrapper, - }, - ); - - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - - if (result.current.data) - expect(result.current.data.answer === contextValue.states.answer).toBe( - false, - ); - - const heading = screen.getByRole("heading", { level: 3 }); - if (QUIZ_TITLE_INFO.ANSWER_FAIL.title) - expect(heading).toHaveTextContent(QUIZ_TITLE_INFO.ANSWER_FAIL.title); - if (QUIZ_TITLE_INFO.ANSWER_FAIL.className) - expect(heading).toHaveClass(QUIZ_TITLE_INFO.ANSWER_FAIL.className); - }); -}); diff --git a/src/quiz/components/QuizTitle/index.tsx b/src/quiz/components/QuizTitle/index.tsx deleted file mode 100644 index 23765db7..00000000 --- a/src/quiz/components/QuizTitle/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client"; - -import React, { useContext } from "react"; - -import { useQuery } from "@tanstack/react-query"; - -import { QUIZ_TITLE_INFO } from "@quiz/constants/quizTitle"; -import QuizContext from "@quiz/context/quizContext"; -import { getQuizAnswerQueryOptions } from "@quiz/remotes/getQuizAnswerOption"; -import { getQuizInfoQueryOptions } from "@quiz/remotes/getQuizInfoQueryOptions"; - -export default function QuizTitle() { - const { - states: { answer, isSubmit }, - } = useContext(QuizContext); - - const { data: quizAnswer, isSuccess } = useQuery({ - ...getQuizAnswerQueryOptions(), - }); - const { data: quizInfo } = useQuery({ - ...getQuizInfoQueryOptions(), - }); - - if (!isSuccess && !quizAnswer) return
; - - const subTitleInfo = - (!isSubmit && QUIZ_TITLE_INFO.NO_ANSWER) || - (isSubmit && - answer && - answer === quizAnswer?.answer && - QUIZ_TITLE_INFO.ANSWER_CORRECT) || - QUIZ_TITLE_INFO.ANSWER_FAIL; - - return ( -
-

{subTitleInfo.title}

-

{quizInfo?.question}

-
- ); -} diff --git a/src/quiz/components/articlePopup/articlePopup.test.tsx b/src/quiz/components/articlePopup/articlePopup.test.tsx deleted file mode 100644 index 46bc1162..00000000 --- a/src/quiz/components/articlePopup/articlePopup.test.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import ArticlePopup from "."; -import { render } from "@testing-library/react"; - -describe("Time formatter utils", () => { - test("test counter format to minute and seconds", () => { - const { container } = render(); - }); -}); diff --git a/src/quiz/components/articlePopup/index.tsx b/src/quiz/components/articlePopup/index.tsx deleted file mode 100644 index af6a0f0d..00000000 --- a/src/quiz/components/articlePopup/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export default function ArticlePopup() { - return
ㅎㅎ
; -} diff --git a/tsconfig.json b/tsconfig.json index d4977d2e..d972257b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,10 +23,10 @@ "@shared/*": ["src/shared/*"], "@workbook/*": ["src/workbook/*"], "@article/*": ["src/article/*"], - "@quiz/*": ["src/quiz/*"], "@main/*": ["src/main/*"], "@common/*": ["src/common/*"], - "@mocks/*": ["src/mocks/*"] + "@mocks/*": ["src/mocks/*"], + "@problem/*": ["src/problem/*"] } }, "include": [ diff --git a/vitest.config.ts b/vitest.config.ts index d873cdcb..d7a3b20c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -19,11 +19,11 @@ export default defineConfig({ "@api": resolve("src/api"), "@shared": resolve("src/shared"), "@workbook": resolve("src/workbook"), - "@quiz": resolve("src/quiz"), "@article": resolve("src/article"), "@main": resolve("src/main"), "@mocks": resolve("src/mocks"), "@common": resolve("src/common"), + "@problem": resolve("src/problem"), }, }, }); From aa9f53682d3a1607747a01afc4b72daf4fe0b6f4 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:10:07 +0900 Subject: [PATCH 19/52] docs : rename quiz to problem --- .../components/TagList/TagList.stories.tsx | 20 ------------ src/quiz/components/TagList/index.tsx | 20 ------------ src/quiz/constants/answerButtonInfo.ts | 22 ------------- src/quiz/constants/quizTitle.ts | 22 ------------- src/quiz/context/quizContext/index.tsx | 31 ------------------- src/quiz/hooks/useIsMounted.tsx | 13 -------- src/quiz/remotes/getQuizAnswerOption.ts | 21 ------------- src/quiz/remotes/getQuizInfoQueryOptions.ts | 21 ------------- src/quiz/remotes/getTagQueryOptions.ts | 21 ------------- src/quiz/types/answerButtonInfo.ts | 8 ----- src/quiz/types/quizContextInfo.ts | 10 ------ src/quiz/types/quizTitle.ts | 17 ---------- src/quiz/types/tagInfo.ts | 3 -- src/quiz/utils/index.ts | 1 - 14 files changed, 230 deletions(-) delete mode 100644 src/quiz/components/TagList/TagList.stories.tsx delete mode 100644 src/quiz/components/TagList/index.tsx delete mode 100644 src/quiz/constants/answerButtonInfo.ts delete mode 100644 src/quiz/constants/quizTitle.ts delete mode 100644 src/quiz/context/quizContext/index.tsx delete mode 100644 src/quiz/hooks/useIsMounted.tsx delete mode 100644 src/quiz/remotes/getQuizAnswerOption.ts delete mode 100644 src/quiz/remotes/getQuizInfoQueryOptions.ts delete mode 100644 src/quiz/remotes/getTagQueryOptions.ts delete mode 100644 src/quiz/types/answerButtonInfo.ts delete mode 100644 src/quiz/types/quizContextInfo.ts delete mode 100644 src/quiz/types/quizTitle.ts delete mode 100644 src/quiz/types/tagInfo.ts delete mode 100644 src/quiz/utils/index.ts diff --git a/src/quiz/components/TagList/TagList.stories.tsx b/src/quiz/components/TagList/TagList.stories.tsx deleted file mode 100644 index 2e35041c..00000000 --- a/src/quiz/components/TagList/TagList.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import TagList from "."; -import { tagsHandler } from "@mocks/worker"; -import { Meta, StoryObj } from "@storybook/react"; - -const meta = { - component: TagList, -} satisfies Meta; -export default meta; - -type Story = StoryObj; - -export const TagComponent = { - parameters: { - msw: { - handlers: { - quiz: tagsHandler, - }, - }, - }, -} satisfies Story; diff --git a/src/quiz/components/TagList/index.tsx b/src/quiz/components/TagList/index.tsx deleted file mode 100644 index 1111626d..00000000 --- a/src/quiz/components/TagList/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import React from "react"; - -import { useQuery } from "@tanstack/react-query"; - -import { getTagQueryOptions } from "@quiz/remotes/getTagQueryOptions"; - -import Tag from "@common/components/Tag"; - -export default function TagList() { - const { data: tagInfo, isError } = useQuery({ ...getTagQueryOptions() }); - if (isError) return
error
; - - return ( -
- {tagInfo?.tags.map((tag) => )} -
- ); -} diff --git a/src/quiz/constants/answerButtonInfo.ts b/src/quiz/constants/answerButtonInfo.ts deleted file mode 100644 index 115f7625..00000000 --- a/src/quiz/constants/answerButtonInfo.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ButtonInfo } from "@quiz/types/answerButtonInfo"; - -export const BUTTON_STATE = [ - "PRE_ANSWER_SELECT", - "POST_ANSWER_PRE_SUBMIT", - "POST_SUBMIT", -] as const; - -export const BUTTON_INFO: ButtonInfo = { - PRE_ANSWER_SELECT: { - title: "정답 제출하기", - className: "bg-text-gray3 text-text-gray2", - }, - POST_ANSWER_PRE_SUBMIT: { - title: "정답 제출하기", - className: "bg-main", - }, - POST_SUBMIT: { - title: "다음 문제 풀기", - className: "bg-text-black", - }, -}; diff --git a/src/quiz/constants/quizTitle.ts b/src/quiz/constants/quizTitle.ts deleted file mode 100644 index 51f52aaa..00000000 --- a/src/quiz/constants/quizTitle.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { QuizTitleInfo } from "@quiz/types/quizTitle"; - -export const QUIZ_ANSWER_TYPE = [ - "NO_ANSWER", - "ANSWER_CORRECT", - "ANSWER_FAIL", -] as const; - -export const QUIZ_TITLE_INFO: QuizTitleInfo = { - NO_ANSWER: { - title: "Q.", - className: "text-main sub1-bold", - }, - ANSWER_CORRECT: { - title: "정답이에요!", - className: "text-success sub1-semibold", - }, - ANSWER_FAIL: { - title: "아쉽지만 정답이 아니에요.", - className: "text-error sub1-semibold", - }, -}; diff --git a/src/quiz/context/quizContext/index.tsx b/src/quiz/context/quizContext/index.tsx deleted file mode 100644 index 23cea43c..00000000 --- a/src/quiz/context/quizContext/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client"; - -import { createContext, useState } from "react"; - -import { QuizContextInfo } from "@quiz/types/quizContextInfo"; - -const QuizContext = createContext({ - states: { answer: "", isSubmit: false }, - actions: { - updateAnswer: () => {}, - updateSubmit: () => {}, - }, -}); - -function QuizProvider({ children }: { children: React.ReactElement }) { - const [answer, setAnswer] = useState(""); - const [isSubmit, setIsSubmit] = useState(false); - - const value = { - states: { answer, isSubmit }, - actions: { - updateAnswer: (answer: string) => setAnswer(answer), - updateSubmit: (isSubmit: boolean) => setIsSubmit(isSubmit), - }, - }; - return {children}; -} - -const QuizConsumer = QuizContext.Consumer; -export { QuizConsumer, QuizProvider }; -export default QuizContext; diff --git a/src/quiz/hooks/useIsMounted.tsx b/src/quiz/hooks/useIsMounted.tsx deleted file mode 100644 index ef6d0b45..00000000 --- a/src/quiz/hooks/useIsMounted.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useEffect, useState } from "react"; - -const useIsMounted = () => { - const [mounted, setMounted] = useState(false); - - useEffect(function setMouteState() { - setMounted(true); - }, []); - - return mounted; -}; - -export default useIsMounted; diff --git a/src/quiz/remotes/getQuizAnswerOption.ts b/src/quiz/remotes/getQuizAnswerOption.ts deleted file mode 100644 index c65412cc..00000000 --- a/src/quiz/remotes/getQuizAnswerOption.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UseQueryOptions } from "@tanstack/react-query"; - -import { ApiResponse, axiosRequest } from "@api/api-config"; - -import { apiRoutes } from "@shared/constants/apiRoutes"; - -import { QuizAnswer } from "@quiz/types/quizTitle"; - -export const getQuizAnswer = (): Promise> => { - return axiosRequest("get", apiRoutes.quizAnswer); -}; -export const getQuizAnswerQueryOptions = (): UseQueryOptions< - unknown, - Error, - QuizAnswer -> => { - return { - queryKey: ["get-answer"], - queryFn: () => getQuizAnswer(), - }; -}; diff --git a/src/quiz/remotes/getQuizInfoQueryOptions.ts b/src/quiz/remotes/getQuizInfoQueryOptions.ts deleted file mode 100644 index 13d540d0..00000000 --- a/src/quiz/remotes/getQuizInfoQueryOptions.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UseQueryOptions } from "@tanstack/react-query"; - -import { ApiResponse, axiosRequest } from "@api/api-config"; - -import { apiRoutes } from "@shared/constants/apiRoutes"; - -import { QuizInfo } from "@quiz/types/quizTitle"; - -export const getQuizInfo = (): Promise> => { - return axiosRequest("get", apiRoutes.quiz); -}; -export const getQuizInfoQueryOptions = (): UseQueryOptions< - unknown, - Error, - QuizInfo -> => { - return { - queryKey: ["get-quiz-info"], - queryFn: () => getQuizInfo(), - }; -}; diff --git a/src/quiz/remotes/getTagQueryOptions.ts b/src/quiz/remotes/getTagQueryOptions.ts deleted file mode 100644 index babde360..00000000 --- a/src/quiz/remotes/getTagQueryOptions.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UseQueryOptions } from "@tanstack/react-query"; - -import { ApiResponse, axiosRequest } from "@api/api-config"; - -import { apiRoutes } from "@shared/constants/apiRoutes"; - -import { TagInfo } from "@quiz/types/tagInfo"; - -const getTags = (): Promise> => { - return axiosRequest("get", apiRoutes.tags); -}; -export const getTagQueryOptions = (): UseQueryOptions< - unknown, - unknown, - TagInfo -> => { - return { - queryKey: ["get-Tags"], - queryFn: () => getTags(), - }; -}; diff --git a/src/quiz/types/answerButtonInfo.ts b/src/quiz/types/answerButtonInfo.ts deleted file mode 100644 index c785012b..00000000 --- a/src/quiz/types/answerButtonInfo.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { BUTTON_STATE } from "@quiz/constants/answerButtonInfo"; - -import { ConstKeyObject } from "@common/types/constKeyObject"; - -export type ButtonInfo = ConstKeyObject< - (typeof BUTTON_STATE)[number], - React.ButtonHTMLAttributes ->; diff --git a/src/quiz/types/quizContextInfo.ts b/src/quiz/types/quizContextInfo.ts deleted file mode 100644 index e301a2a3..00000000 --- a/src/quiz/types/quizContextInfo.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type QuizContextInfo = { - states: { - answer: string | null; - isSubmit: boolean; - }; - actions: { - updateAnswer: (answer: string) => void; - updateSubmit: (isSubmit: boolean) => void; - }; -}; diff --git a/src/quiz/types/quizTitle.ts b/src/quiz/types/quizTitle.ts deleted file mode 100644 index 3da989d6..00000000 --- a/src/quiz/types/quizTitle.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { QUIZ_ANSWER_TYPE } from "@quiz/constants/quizTitle"; - -import { ConstKeyObject } from "@common/types/constKeyObject"; - -export type QuizTitleInfo = ConstKeyObject< - (typeof QUIZ_ANSWER_TYPE)[number], - React.HTMLAttributes ->; - -export type QuizAnswer = { - answer: string; -}; - -export type QuizInfo = { - question: string; - answer: string[]; -}; diff --git a/src/quiz/types/tagInfo.ts b/src/quiz/types/tagInfo.ts deleted file mode 100644 index 9e582863..00000000 --- a/src/quiz/types/tagInfo.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type TagInfo = { - tags: string[]; -}; diff --git a/src/quiz/utils/index.ts b/src/quiz/utils/index.ts deleted file mode 100644 index 42caf9b9..00000000 --- a/src/quiz/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -//utils From 7e67b7048e7df906fc855a0a5c5465b941ace5c4 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:10:32 +0900 Subject: [PATCH 20/52] feat : set remote api route paths --- src/problem/remotes/api.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/problem/remotes/api.ts diff --git a/src/problem/remotes/api.ts b/src/problem/remotes/api.ts new file mode 100644 index 00000000..5083accf --- /dev/null +++ b/src/problem/remotes/api.ts @@ -0,0 +1,4 @@ +export const PROBLEM_API_ROUTES = { + problems: (articleId: number) => `/articles/${articleId}/problems`, + submitAnswer: (problemId: number) => `/problems/${problemId}`, +}; From 02ef07205738bbd70554fa304007589700e6bd7e Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:11:58 +0900 Subject: [PATCH 21/52] docs : download problem answer choice round svg --- public/assets/icon16/answer_choice_16.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 public/assets/icon16/answer_choice_16.svg diff --git a/public/assets/icon16/answer_choice_16.svg b/public/assets/icon16/answer_choice_16.svg new file mode 100644 index 00000000..6d445e60 --- /dev/null +++ b/public/assets/icon16/answer_choice_16.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From 4255b4629073f4075fb9957fe916283938b93351 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:24:16 +0900 Subject: [PATCH 22/52] feat : divide problem type constant --- src/problem/constants/problemInfo.ts | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/problem/constants/problemInfo.ts diff --git a/src/problem/constants/problemInfo.ts b/src/problem/constants/problemInfo.ts new file mode 100644 index 00000000..f0ce4320 --- /dev/null +++ b/src/problem/constants/problemInfo.ts @@ -0,0 +1,47 @@ +import { + AnswerChoiceButtonInfo, + ProblemTitleInfo, +} from "@problem/types/problemInfo"; + +export const PROBLEM_ANSWER_TYPE = [ + "NO_ANSWER", + "ANSWER_CORRECT", + "ANSWER_FAIL", +] as const; + +export const PROBLEM_TITLE_INFO: ProblemTitleInfo = { + NO_ANSWER: { + title: "Q.", + className: "text-main sub1-bold", + }, + ANSWER_CORRECT: { + title: "정답이에요!", + className: "text-success sub1-semibold", + }, + ANSWER_FAIL: { + title: "아쉽지만 정답이 아니에요.", + className: "text-error sub1-semibold", + }, +}; + +export const ANSWER_CHOICHE_TYPE = [ + "INIT_CHOICE_ANSWER", + "CURRENT_CHOICE_ANSWER", + "CHOICE_ANSWER_CORRECT", + "CHOICE_ANSWER_FAIL", +] as const; +export const ANSWER_CHOICHE_BUTTON_INFO: AnswerChoiceButtonInfo = { + INIT_CHOICE_ANSWER: { + className: "bg-transparent text-black", + }, + CURRENT_CHOICE_ANSWER: { + className: "text-white bg-main hover:bg-main", + }, + CHOICE_ANSWER_CORRECT: { + className: + "bg-transparent text-success hover:bg-transparent border-success", + }, + CHOICE_ANSWER_FAIL: { + className: "bg-transparent text-error hover:bg-transparent border-error", + }, +}; From fab6078a4362619d9ee8419ece8c359ea138a4b0 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:24:33 +0900 Subject: [PATCH 23/52] feat : post problem Answer mutation options --- .../remotes/postProblemAnswerOption.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/problem/remotes/postProblemAnswerOption.ts diff --git a/src/problem/remotes/postProblemAnswerOption.ts b/src/problem/remotes/postProblemAnswerOption.ts new file mode 100644 index 00000000..0b3c1fd1 --- /dev/null +++ b/src/problem/remotes/postProblemAnswerOption.ts @@ -0,0 +1,33 @@ +import { UseMutationOptions } from "@tanstack/react-query"; + +import { ApiResponse, axiosRequest } from "@api/api-config"; + +import { PROBLEM_API_ROUTES } from "./api"; +import { ProblemAnswer } from "@problem/types/problemInfo"; + +export const postProblemAnswer = ( + params: ProblemAnswerParams, + body: ProblemAnswerBody, +): Promise> => { + return axiosRequest( + "post", + PROBLEM_API_ROUTES.submitAnswer(params.problemId), + body, + ); +}; +export const postProblemAnswerMutationOptions = ( + params: ProblemAnswerParams, +): UseMutationOptions, Error, ProblemAnswerBody> => { + return { + mutationKey: ["get-answer"], + mutationFn: (body) => postProblemAnswer(params, body), + }; +}; + +type ProblemAnswerParams = { + problemId: number; +}; + +type ProblemAnswerBody = { + choiceAns: number; +}; From c499fc49ff3877657b8fb5bc693b5e8a793273d2 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:24:48 +0900 Subject: [PATCH 24/52] feat : get problems query options --- .../remotes/getProblemsQueryOptions.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/problem/remotes/getProblemsQueryOptions.ts diff --git a/src/problem/remotes/getProblemsQueryOptions.ts b/src/problem/remotes/getProblemsQueryOptions.ts new file mode 100644 index 00000000..b5bb96e4 --- /dev/null +++ b/src/problem/remotes/getProblemsQueryOptions.ts @@ -0,0 +1,24 @@ +import { UseQueryOptions } from "@tanstack/react-query"; + +import { ApiResponse, axiosRequest } from "@api/api-config"; + +import { PROBLEM_API_ROUTES } from "./api"; +import { ProblemsInfo } from "@problem/types/problemInfo"; + +export const getProblemsInfo = ({ + articleId, +}: ProblemsInfoParams): Promise> => { + return axiosRequest("get", PROBLEM_API_ROUTES.problems(articleId)); +}; +export const getProblemsQueryOptions = ({ + articleId, +}: ProblemsInfoParams): UseQueryOptions => { + return { + queryKey: ["get-Problems-info"], + queryFn: () => getProblemsInfo({ articleId }), + }; +}; + +type ProblemsInfoParams = { + articleId: number; +}; From a86732f4d2760e072eee6692af049ea20b789914 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Mon, 10 Jun 2024 22:25:39 +0900 Subject: [PATCH 25/52] docs : rename quiz to problem --- .../AnswerContextButton.stories.ts | 31 +++++++ .../AnswerContextButton.test.tsx | 81 +++++++++++++++++++ .../components/AnswerContextButton/index.tsx | 24 ++++++ 3 files changed, 136 insertions(+) create mode 100644 src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts create mode 100644 src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx create mode 100644 src/problem/components/AnswerContextButton/index.tsx diff --git a/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts b/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts new file mode 100644 index 00000000..209c292a --- /dev/null +++ b/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts @@ -0,0 +1,31 @@ +import { Button } from "@shared/components/ui/button"; + +import { Meta, StoryObj } from "@storybook/react"; + +const meta = { + component: Button, +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +export const WithChoiceAnswer = { + args: { + className: "bg-main", + children: "정답 제출하기", + }, +} satisfies Story; + +export const WithoutChoiceAnswer = { + args: { + className: "bg-text-gray3", + children: "정답 제출하기", + }, +} satisfies Story; + +export const SubmitAnswer = { + args: { + className: "bg-text-black", + children: "다음 문제 풀기", + }, +} satisfies Story; diff --git a/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx b/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx new file mode 100644 index 00000000..99357bfa --- /dev/null +++ b/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx @@ -0,0 +1,81 @@ +import { ReactElement } from "react"; + +import { describe, expect, it, vi } from "vitest"; + +import AnswerContextButton from "."; +import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; +import QuizContext from "@problem/context/problemContext"; +import { ProblemContextInfo } from "@problem/types/problemContextInfo"; +import { render, screen } from "@testing-library/react"; + +vi.mock("@shared/components/ui/button", () => ({ + Button: ({ + className, + children, + }: { + className: string; + children: ReactElement; + }) => , +})); +const defaultActions = { + actions: { + updateAnswer: () => {}, + updateSubmit: () => {}, + }, +}; +describe("퀴즈 푸는 하단 버튼, 정답 선택에 따른 테스트", () => { + const renderWithContext = (contextValue: ProblemContextInfo) => { + return render( + + + , + ); + }; + + it("정답 선택 전 상태", () => { + renderWithContext({ + states: { answer: null, isSubmit: false }, + ...defaultActions, + }); + if (BUTTON_INFO.PRE_ANSWER_SELECT.title) + expect( + screen.getByText(BUTTON_INFO.PRE_ANSWER_SELECT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.PRE_ANSWER_SELECT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.PRE_ANSWER_SELECT.className, + ); + }); + it("정답 선택 후 상태", () => { + renderWithContext({ + states: { answer: "정답!!", isSubmit: false }, + ...defaultActions, + }); + if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title) + expect( + screen.getByText(BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className, + ); + }); + + it("정답 선택 후 상태", () => { + renderWithContext({ + states: { answer: "정답!!", isSubmit: true }, + ...defaultActions, + }); + if (BUTTON_INFO.POST_SUBMIT.title) + expect( + screen.getByText(BUTTON_INFO.POST_SUBMIT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.POST_SUBMIT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.POST_SUBMIT.className, + ); + }); +}); diff --git a/src/problem/components/AnswerContextButton/index.tsx b/src/problem/components/AnswerContextButton/index.tsx new file mode 100644 index 00000000..142c66ae --- /dev/null +++ b/src/problem/components/AnswerContextButton/index.tsx @@ -0,0 +1,24 @@ +"use client"; + +import React, { useContext } from "react"; + +import { Button } from "@shared/components/ui/button"; + +import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; +import QuizContext from "@problem/context/problemContext"; + +export default function AnswerContextButton() { + const { + states: { answer, isSubmit }, + } = useContext(QuizContext); + + const result = isSubmit + ? BUTTON_INFO.POST_SUBMIT.title + : BUTTON_INFO.PRE_ANSWER_SELECT.title; + const style = + (!answer && !isSubmit && BUTTON_INFO.PRE_ANSWER_SELECT.className) || + (answer && !isSubmit && BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) || + BUTTON_INFO.POST_SUBMIT.className; + + return ; +} From 4936a9e1b90b62c365c4b3807935bb6d3ad88bcb Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:53:40 +0900 Subject: [PATCH 26/52] fix : rename query option --- src/problem/remotes/getProblemQueryOptions.ts | 29 +++++++++++++++++++ .../remotes/getProblemsQueryOptions.ts | 24 --------------- 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 src/problem/remotes/getProblemQueryOptions.ts delete mode 100644 src/problem/remotes/getProblemsQueryOptions.ts diff --git a/src/problem/remotes/getProblemQueryOptions.ts b/src/problem/remotes/getProblemQueryOptions.ts new file mode 100644 index 00000000..4fd5e698 --- /dev/null +++ b/src/problem/remotes/getProblemQueryOptions.ts @@ -0,0 +1,29 @@ +import { UseQueryOptions } from "@tanstack/react-query"; + +import { ApiResponse, axiosRequest } from "@api/api-config"; + +import { PROBLEM_API_ROUTES } from "./api"; +import { PromblemInfo } from "@problem/types/problemInfo"; + +export const getProblemInfo = ({ + problemId, +}: ProblemInfoParams): Promise> => { + return axiosRequest("get", PROBLEM_API_ROUTES.problems(problemId)); +}; +export const getProblemQueryOptions = ({ + problemId, +}: ProblemInfoParams): UseQueryOptions< + ApiResponse, + Error, + PromblemInfo +> => { + return { + queryKey: ["get-Problems-info"], + queryFn: () => getProblemInfo({ problemId }), + select: (data) => data.data, + }; +}; + +type ProblemInfoParams = { + problemId: number; +}; diff --git a/src/problem/remotes/getProblemsQueryOptions.ts b/src/problem/remotes/getProblemsQueryOptions.ts deleted file mode 100644 index b5bb96e4..00000000 --- a/src/problem/remotes/getProblemsQueryOptions.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { UseQueryOptions } from "@tanstack/react-query"; - -import { ApiResponse, axiosRequest } from "@api/api-config"; - -import { PROBLEM_API_ROUTES } from "./api"; -import { ProblemsInfo } from "@problem/types/problemInfo"; - -export const getProblemsInfo = ({ - articleId, -}: ProblemsInfoParams): Promise> => { - return axiosRequest("get", PROBLEM_API_ROUTES.problems(articleId)); -}; -export const getProblemsQueryOptions = ({ - articleId, -}: ProblemsInfoParams): UseQueryOptions => { - return { - queryKey: ["get-Problems-info"], - queryFn: () => getProblemsInfo({ articleId }), - }; -}; - -type ProblemsInfoParams = { - articleId: number; -}; From 39b18b2453e05688995e9e636f739cd36952ae07 Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:54:11 +0900 Subject: [PATCH 27/52] fix : submit answer api route path --- src/shared/constants/apiRoutes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/constants/apiRoutes.ts b/src/shared/constants/apiRoutes.ts index cc7bd4cc..7ad068fc 100644 --- a/src/shared/constants/apiRoutes.ts +++ b/src/shared/constants/apiRoutes.ts @@ -1,6 +1,6 @@ export const apiRoutes = { - quizAnswer: "/api/quizAnswer", quiz: "/quiz", tags: "/tags", problems: "/articles/:articleId/problems", + submitAnswer: "/problems/:problemId", }; From ca21e0895302855a95f6e3cd6c2704c9c5c3e8e9 Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:54:31 +0900 Subject: [PATCH 28/52] docs : remove unuse file --- public/assets/icon16/answer_choice_16.svg | 3 --- src/app/api/quizAnswer/route.ts | 13 ------------- src/mocks/response/quizAnswer.json | 3 --- 3 files changed, 19 deletions(-) delete mode 100644 public/assets/icon16/answer_choice_16.svg delete mode 100644 src/app/api/quizAnswer/route.ts delete mode 100644 src/mocks/response/quizAnswer.json diff --git a/public/assets/icon16/answer_choice_16.svg b/public/assets/icon16/answer_choice_16.svg deleted file mode 100644 index 6d445e60..00000000 --- a/public/assets/icon16/answer_choice_16.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/app/api/quizAnswer/route.ts b/src/app/api/quizAnswer/route.ts deleted file mode 100644 index bade6dba..00000000 --- a/src/app/api/quizAnswer/route.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NextResponse } from "next/server"; - -import { apiRoutes } from "@shared/constants/apiRoutes"; - -import response from "@mocks/response"; - -export async function GET(request: Request) { - try { - return NextResponse.json(response[apiRoutes.quizAnswer], { status: 200 }); - } catch (error) { - return NextResponse.json({ error }, { status: 500 }); - } -} diff --git a/src/mocks/response/quizAnswer.json b/src/mocks/response/quizAnswer.json deleted file mode 100644 index b180979b..00000000 --- a/src/mocks/response/quizAnswer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "answer": "높은 운용 비용" -} From ba8406fcd539006f82a7d3794c87148490690d1a Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:55:15 +0900 Subject: [PATCH 29/52] feat : react query devetool --- src/app/layout.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 63c7f2fa..c2e54ee8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,8 @@ import localFont from "next/font/local"; import { Suspense } from "react"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + import QueryClientProviders from "@shared/components/queryClientProvider"; import { cn } from "@shared/utils/cn"; @@ -63,6 +65,8 @@ export default function RootLayout({ {children} + + From 9d5f4dd6245c644c0d7f0695ead1d5ef2631c35c Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:56:11 +0900 Subject: [PATCH 30/52] feat : submit answer handler and response --- src/mocks/handlers.ts | 25 +++++++++++++++++++++---- src/mocks/response/index.ts | 4 ++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index 1aea89f0..1e28b21d 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -11,13 +11,30 @@ export const tagsHandler = http.get(apiRoutes.tags, () => HttpResponse.json(response[apiRoutes.tags]), ); -export const problemsHandler = http.get(apiRoutes.problems, ({ request }) => { - const url = new URL(request.url); - const articleId = url.searchParams.get("articleId"); +export const problemsHandler = http.get(apiRoutes.problems, ({ params }) => { + const articleId = params?.articleId; if (!articleId) { return new HttpResponse(null, { status: 404 }); } return HttpResponse.json(response[apiRoutes.problems]); }); -export const handlers = [quizHandler, tagsHandler, problemsHandler]; +export const submitAnswerHandler = http.post( + apiRoutes.submitAnswer, + async ({ request, params }) => { + const problemId = params?.problemId; + const result: any = await request.json(); + const choiceAns = result?.choiceAns; + + if (!choiceAns && problemId) { + return new HttpResponse(null, { status: 404 }); + } + return HttpResponse.json(response[apiRoutes.submitAnswer]); + }, +); +export const handlers = [ + quizHandler, + tagsHandler, + problemsHandler, + submitAnswerHandler, +]; diff --git a/src/mocks/response/index.ts b/src/mocks/response/index.ts index 8d9170bd..c4bba710 100644 --- a/src/mocks/response/index.ts +++ b/src/mocks/response/index.ts @@ -2,13 +2,13 @@ import { apiRoutes } from "@shared/constants/apiRoutes"; import problems from "./problems.json"; import quiz from "./quiz.json"; -import quizAnswer from "./quizAnswer.json"; +import submitAnswer from "./submitAnswer.json"; import tags from "./tags.json"; // eslint-disable-next-line import/no-anonymous-default-export export default { [apiRoutes.quiz]: quiz, [apiRoutes.tags]: tags, - [apiRoutes.quizAnswer]: quizAnswer, [apiRoutes.problems]: problems, + [apiRoutes.submitAnswer]: submitAnswer, }; From b590b7610e0f8c8c25686e2743b3b75ba696c32b Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:56:43 +0900 Subject: [PATCH 31/52] docs : modify problem,submitanswer mock data --- src/mocks/response/problems.json | 42 +++++++++++++--------------- src/mocks/response/submitAnswer.json | 5 ++++ 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 src/mocks/response/submitAnswer.json diff --git a/src/mocks/response/problems.json b/src/mocks/response/problems.json index 0cd6af2d..136ad866 100644 --- a/src/mocks/response/problems.json +++ b/src/mocks/response/problems.json @@ -1,30 +1,26 @@ { "message": "String", "data": { - "problems": [ + "id": 1, + "title": "ETF(상장지수펀드)의 특징이 아닌것은?", + "contents": [ { - "id": 1, - "title": "ETF(상장지수펀드)의 특징이 아닌것은?", - "contents": [ - { - "number": 1, - "content": "분산투자" - }, - { - "number": 2, - "content": "높은 운용 비용" - }, - { - "number": 3, - "content": "유동성" - }, - { - "number": 4, - "content": "투명성" - } - ], - "creatorId": 1 + "number": 1, + "content": "분산투자" + }, + { + "number": 2, + "content": "높은 운용 비용" + }, + { + "number": 3, + "content": "유동성" + }, + { + "number": 4, + "content": "투명성" } - ] + ], + "creatorId": 1 } } diff --git a/src/mocks/response/submitAnswer.json b/src/mocks/response/submitAnswer.json new file mode 100644 index 00000000..dc674dc1 --- /dev/null +++ b/src/mocks/response/submitAnswer.json @@ -0,0 +1,5 @@ +{ + "explanation": "ETF는 일반적으로 낮은 운용 비용을 특징으로 합니다.이는 ETF가 보통 지수 추종(passive management) 방식으로 운용되기 때문입니다. 지수를 추종하는 전략은 액티브 매니지먼트(active management)에 비해 관리가 덜 복잡하고, 따라서 비용이 낮습니다.", + "isSolved": true, + "answer": "높은 운용 비용" +} From 97bdbb4e25b5ceb71502345eee8bda999818441a Mon Sep 17 00:00:00 2001 From: happhee Date: Thu, 13 Jun 2024 14:58:18 +0900 Subject: [PATCH 32/52] fix : server error handlers --- vitest.setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vitest.setup.ts b/vitest.setup.ts index ecaa8ca2..6bdc6abf 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -4,7 +4,7 @@ import "@testing-library/jest-dom"; import { server } from "@mocks/server"; import { cleanup } from "@testing-library/react"; -beforeAll(() => server.listen()); +beforeAll(() => server.listen({ onUnhandledRequest: "error" })); afterEach(() => { cleanup(); From 8e55ea0a663f13632faff7f26f79873da8974f1b Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Thu, 13 Jun 2024 23:56:30 +0900 Subject: [PATCH 33/52] fix : proble api response data type --- src/problem/types/problemInfo.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/problem/types/problemInfo.ts b/src/problem/types/problemInfo.ts index 014af3b1..b71e7c40 100644 --- a/src/problem/types/problemInfo.ts +++ b/src/problem/types/problemInfo.ts @@ -9,18 +9,11 @@ export type ProblemTitleInfo = ConstKeyObject< React.HTMLAttributes >; -export type ProblemAnswer = { - answer: string; -}; export type PromblemInfo = { id: number; title: string; contents: AnswerChoiceInfo[]; }; -export type ProblemsInfo = { - day: string; - problems: PromblemInfo[]; -}; export type AnswerChoiceInfo = { number: number; content: string; @@ -30,3 +23,9 @@ export type AnswerChoiceButtonInfo = ConstKeyObject< (typeof ANSWER_CHOICHE_TYPE)[number], React.HTMLAttributes >; + +export type AnswerCheckInfo = { + explanation: string; + isSolved: boolean; + answer: string; +}; From cd7aa422367b3db335ae724ef174c1b7b5928e4d Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Thu, 13 Jun 2024 23:57:12 +0900 Subject: [PATCH 34/52] fix : hover bg transparent --- src/problem/constants/problemInfo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/problem/constants/problemInfo.ts b/src/problem/constants/problemInfo.ts index f0ce4320..193b45c9 100644 --- a/src/problem/constants/problemInfo.ts +++ b/src/problem/constants/problemInfo.ts @@ -32,7 +32,7 @@ export const ANSWER_CHOICHE_TYPE = [ ] as const; export const ANSWER_CHOICHE_BUTTON_INFO: AnswerChoiceButtonInfo = { INIT_CHOICE_ANSWER: { - className: "bg-transparent text-black", + className: "bg-transparent text-black hover:bg-transparent", }, CURRENT_CHOICE_ANSWER: { className: "text-white bg-main hover:bg-main", From 89d24323759cdea7e4bca426f1b0b09dff256284 Mon Sep 17 00:00:00 2001 From: seohee0112 Date: Thu, 13 Jun 2024 23:58:21 +0900 Subject: [PATCH 35/52] docs : page move to params folder --- src/app/problem/{ => [problemId]}/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/app/problem/{ => [problemId]}/page.tsx (93%) diff --git a/src/app/problem/page.tsx b/src/app/problem/[problemId]/page.tsx similarity index 93% rename from src/app/problem/page.tsx rename to src/app/problem/[problemId]/page.tsx index 668d7810..f376bb84 100644 --- a/src/app/problem/page.tsx +++ b/src/app/problem/[problemId]/page.tsx @@ -6,7 +6,7 @@ import ProblemTitle from "@problem/components/ProblemTitle"; import TagList from "@problem/components/TagList"; import { ProblemProvider } from "@problem/context/problemContext"; -export default function QuizPage() { +export default function ProblemPage() { return ( <> From 151dfd3439928910a0dc696fb492fa7328134671 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:29:16 +0900 Subject: [PATCH 36/52] fix : problem data field type --- src/mocks/response/problems.json | 1 + src/problem/types/problemInfo.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/mocks/response/problems.json b/src/mocks/response/problems.json index 136ad866..97a47915 100644 --- a/src/mocks/response/problems.json +++ b/src/mocks/response/problems.json @@ -3,6 +3,7 @@ "data": { "id": 1, "title": "ETF(상장지수펀드)의 특징이 아닌것은?", + "day": "Day1", "contents": [ { "number": 1, diff --git a/src/problem/types/problemInfo.ts b/src/problem/types/problemInfo.ts index b71e7c40..e5559bac 100644 --- a/src/problem/types/problemInfo.ts +++ b/src/problem/types/problemInfo.ts @@ -12,6 +12,7 @@ export type ProblemTitleInfo = ConstKeyObject< export type PromblemInfo = { id: number; title: string; + day: string; contents: AnswerChoiceInfo[]; }; export type AnswerChoiceInfo = { From 93cfa85c55230582fb584771a375a32bb1db5c34 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:30:51 +0900 Subject: [PATCH 37/52] fix : remove problem context data --- src/problem/context/problemContext/index.tsx | 26 ++++++++++++++------ src/problem/types/problemContextInfo.ts | 4 +-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/problem/context/problemContext/index.tsx b/src/problem/context/problemContext/index.tsx index 52d0affd..99bf9077 100644 --- a/src/problem/context/problemContext/index.tsx +++ b/src/problem/context/problemContext/index.tsx @@ -4,23 +4,33 @@ import { createContext, useState } from "react"; import { ProblemContextInfo } from "@problem/types/problemContextInfo"; -const ProblemContext = createContext({ - states: { answer: "", isSubmit: false }, +const defaultStates = { + states: { + choiceAnswer: null, + answer: null, + }, +}; +const defaultActions = { actions: { + updateChoiceAnswer: () => {}, updateAnswer: () => {}, - updateSubmit: () => {}, }, +}; +const ProblemContext = createContext({ + ...defaultStates, + ...defaultActions, }); function ProblemProvider({ children }: { children: React.ReactElement }) { - const [answer, setAnswer] = useState(""); - const [isSubmit, setIsSubmit] = useState(false); + const [choiceAnswer, setChoiceAnswer] = useState(null); + const [answer, setAnswer] = useState(null); const value = { - states: { answer, isSubmit }, + states: { choiceAnswer, answer }, actions: { + updateChoiceAnswer: (choiceAnswer: string) => + setChoiceAnswer(choiceAnswer), updateAnswer: (answer: string) => setAnswer(answer), - updateSubmit: (isSubmit: boolean) => setIsSubmit(isSubmit), }, }; return ( @@ -29,5 +39,5 @@ function ProblemProvider({ children }: { children: React.ReactElement }) { } const ProblemConsumer = ProblemContext.Consumer; -export { ProblemConsumer, ProblemProvider }; +export { defaultActions, defaultStates, ProblemConsumer, ProblemProvider }; export default ProblemContext; diff --git a/src/problem/types/problemContextInfo.ts b/src/problem/types/problemContextInfo.ts index c4c871ba..79af8703 100644 --- a/src/problem/types/problemContextInfo.ts +++ b/src/problem/types/problemContextInfo.ts @@ -1,10 +1,10 @@ export type ProblemContextInfo = { states: { + choiceAnswer: string | null; answer: string | null; - isSubmit: boolean; }; actions: { + updateChoiceAnswer: (choiceAnswer: string) => void; updateAnswer: (answer: string) => void; - updateSubmit: (isSubmit: boolean) => void; }; }; From bd477e1602d51929d68c39aac9c1457670455d82 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:31:46 +0900 Subject: [PATCH 38/52] fix : initwork a bug that calls twice --- src/mocks/MSWProviders.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/mocks/MSWProviders.tsx b/src/mocks/MSWProviders.tsx index c1904e1f..33b05d1a 100644 --- a/src/mocks/MSWProviders.tsx +++ b/src/mocks/MSWProviders.tsx @@ -1,16 +1,18 @@ "use client"; -import { PropsWithChildren, useEffect, useState } from "react"; +import { PropsWithChildren, useEffect, useRef, useState } from "react"; const isMockingMode = process.env.NEXT_PUBLIC_API_MOCKING === "enable"; export default function MSWProviders({ children }: PropsWithChildren) { const [mswReady, setMSWReady] = useState(() => !isMockingMode); + const isMockingModeRef = useRef(!isMockingMode); useEffect( function setMswMocks() { const init = async () => { - if (isMockingMode) { + if (isMockingMode && !isMockingModeRef.current) { + isMockingModeRef.current = true; const initMocks = await import("@mocks/index").then( (res) => res.initMocks, ); @@ -18,7 +20,9 @@ export default function MSWProviders({ children }: PropsWithChildren) { setMSWReady(true); } }; - if (!mswReady) init(); + if (!mswReady) { + init(); + } }, [mswReady], ); From 99ea5329517211445e75a11b479de7d1f9b8a64a Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:32:25 +0900 Subject: [PATCH 39/52] fix : post problem answer query key and type --- src/problem/remotes/postProblemAnswerOption.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/problem/remotes/postProblemAnswerOption.ts b/src/problem/remotes/postProblemAnswerOption.ts index 0b3c1fd1..feca00ef 100644 --- a/src/problem/remotes/postProblemAnswerOption.ts +++ b/src/problem/remotes/postProblemAnswerOption.ts @@ -3,12 +3,12 @@ import { UseMutationOptions } from "@tanstack/react-query"; import { ApiResponse, axiosRequest } from "@api/api-config"; import { PROBLEM_API_ROUTES } from "./api"; -import { ProblemAnswer } from "@problem/types/problemInfo"; +import { AnswerCheckInfo } from "@problem/types/problemInfo"; export const postProblemAnswer = ( params: ProblemAnswerParams, body: ProblemAnswerBody, -): Promise> => { +): Promise> => { return axiosRequest( "post", PROBLEM_API_ROUTES.submitAnswer(params.problemId), @@ -17,9 +17,13 @@ export const postProblemAnswer = ( }; export const postProblemAnswerMutationOptions = ( params: ProblemAnswerParams, -): UseMutationOptions, Error, ProblemAnswerBody> => { +): UseMutationOptions< + ApiResponse, + Error, + ProblemAnswerBody +> => { return { - mutationKey: ["get-answer"], + mutationKey: ["get-problem-answer", params.problemId], mutationFn: (body) => postProblemAnswer(params, body), }; }; @@ -29,5 +33,5 @@ type ProblemAnswerParams = { }; type ProblemAnswerBody = { - choiceAns: number; + choiceAns: string; }; From d9f3ca725ae287c9aa2279db1416b66341b4d39b Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:32:58 +0900 Subject: [PATCH 40/52] feat : problem title component --- src/problem/components/ProblemTitle/index.tsx | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/problem/components/ProblemTitle/index.tsx diff --git a/src/problem/components/ProblemTitle/index.tsx b/src/problem/components/ProblemTitle/index.tsx new file mode 100644 index 00000000..6f3fa42b --- /dev/null +++ b/src/problem/components/ProblemTitle/index.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useParams } from "next/navigation"; + +import React from "react"; + +import { useMutationState, useQuery } from "@tanstack/react-query"; + +import { PROBLEM_TITLE_INFO } from "@problem/constants/problemInfo"; +import { getProblemQueryOptions } from "@problem/remotes/getProblemQueryOptions"; +import { AnswerCheckInfo } from "@problem/types/problemInfo"; + +export default function ProblemTitle() { + const { problemId } = useParams<{ problemId: string }>(); + const problemIdNumber = Number(problemId); + const { data: problemInfo } = useQuery({ + ...getProblemQueryOptions({ problemId: problemIdNumber }), + }); + const problemAnswerInfo = useMutationState({ + filters: { + mutationKey: ["get-problem-answer", problemIdNumber], + }, + select: (mutation) => mutation.state.data as AnswerCheckInfo, + }); + + if (!problemInfo) return
error
; + + if (!problemAnswerInfo) return
정답제출 실패
; + + const problemAnswerData = problemAnswerInfo[0]; + const subTitleInfo = + (problemAnswerData && + (problemAnswerData.isSolved + ? PROBLEM_TITLE_INFO.ANSWER_CORRECT + : PROBLEM_TITLE_INFO.ANSWER_FAIL)) || + PROBLEM_TITLE_INFO.NO_ANSWER; + + return ( +
+

{subTitleInfo.title}

+

{problemInfo.title}

+
+ ); +} From 48f7ff7485e7e75fab41e1813a6dc0f1896fa037 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:33:09 +0900 Subject: [PATCH 41/52] test : problem title component --- .../ProblemTitle/ProblemTitle.test.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/problem/components/ProblemTitle/ProblemTitle.test.tsx diff --git a/src/problem/components/ProblemTitle/ProblemTitle.test.tsx b/src/problem/components/ProblemTitle/ProblemTitle.test.tsx new file mode 100644 index 00000000..974dd6de --- /dev/null +++ b/src/problem/components/ProblemTitle/ProblemTitle.test.tsx @@ -0,0 +1,49 @@ +import { describe, expect, it, vi } from "vitest"; + +import QueryClientProviders from "@shared/components/queryClientProvider"; + +import ProblemTitle from "."; +import { PROBLEM_TITLE_INFO } from "@problem/constants/problemInfo"; +import { render, screen, waitFor } from "@testing-library/react"; +vi.mock("next/navigation", () => { + return { + __esModule: true, + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + }), + useParams: () => ({ + get: () => {}, + query: { + problemId: "1", + }, + }), + }; +}); +const renderWithQueryClient = () => { + return render( + + + , + ); +}; + +describe("퀴즈 정보 불러오기 테스트", () => { + it("퀴즈 불러와서 페이지에 보이는지 확인", async () => { + await waitFor(async () => renderWithQueryClient()); + + await waitFor(() => { + const heading = screen.getByRole("heading", { level: 3 }); + if (PROBLEM_TITLE_INFO.NO_ANSWER.title) + expect(heading).toHaveTextContent(PROBLEM_TITLE_INFO.NO_ANSWER.title); + if (PROBLEM_TITLE_INFO.NO_ANSWER.className) + expect(heading).toHaveClass(PROBLEM_TITLE_INFO.NO_ANSWER.className); + + const subHeading = screen.getByRole("heading", { level: 2 }); + expect(subHeading).toHaveTextContent( + "ETF(상장지수펀드)의 특징이 아닌것은?", + ); + }); + }); +}); From b7025796f50f6bb3eb507f3cddcfc9c38e3ae2b9 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:33:38 +0900 Subject: [PATCH 42/52] feat : answer choice button component --- .../components/AnswerChoiceButton/index.tsx | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/problem/components/AnswerChoiceButton/index.tsx diff --git a/src/problem/components/AnswerChoiceButton/index.tsx b/src/problem/components/AnswerChoiceButton/index.tsx new file mode 100644 index 00000000..fbae4c9a --- /dev/null +++ b/src/problem/components/AnswerChoiceButton/index.tsx @@ -0,0 +1,98 @@ +import React, { useContext, useEffect, useState } from "react"; + +import { useMutationState } from "@tanstack/react-query"; + +import { Button } from "@shared/components/ui/button"; +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 { AnswerCheckInfo, AnswerChoiceInfo } from "@problem/types/problemInfo"; + +interface AnswerChoiceButtonProps extends Pick {} + +export default function AnswerChoiceButton({ + content, +}: AnswerChoiceButtonProps) { + const [className, setClassName] = useState( + ANSWER_CHOICHE_BUTTON_INFO.INIT_CHOICE_ANSWER.className, + ); + const { + states: { choiceAnswer }, + actions: { updateChoiceAnswer }, + } = useContext(ProblemContext); + + const problemAnswerInfo = useMutationState({ + filters: { + mutationKey: ["get-problem-answer"], + }, + select: (mutation) => mutation.state.data as AnswerCheckInfo, + }); + + const onClickAnswerChoice = () => { + if (problemAnswerInfo.length === 0) updateChoiceAnswer(content); + }; + + useEffect( + function setButtonClassName() { + if (!problemAnswerInfo.length) { + if (choiceAnswer === content) + setClassName( + ANSWER_CHOICHE_BUTTON_INFO.CURRENT_CHOICE_ANSWER.className, + ); + + if (choiceAnswer !== content) + setClassName(ANSWER_CHOICHE_BUTTON_INFO.INIT_CHOICE_ANSWER.className); + } + + if (problemAnswerInfo.length) { + const problemAnswerData = problemAnswerInfo[0]; + if (problemAnswerData) { + if (problemAnswerData.answer === content) + setClassName( + ANSWER_CHOICHE_BUTTON_INFO.CHOICE_ANSWER_CORRECT.className, + ); + if ( + problemAnswerData.isSolved === false && + choiceAnswer === content + ) { + setClassName( + ANSWER_CHOICHE_BUTTON_INFO.CHOICE_ANSWER_FAIL.className, + ); + } + } + } + }, + [choiceAnswer, content, problemAnswerInfo], + ); + + if (!problemAnswerInfo) return
정답제출 실패
; + const problemAnswerData = problemAnswerInfo[0]; + + return ( + + ); +} From 3fb682e87b562609edbd5d5f0913974495b65b98 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:54:07 +0900 Subject: [PATCH 43/52] test : answer list api mocking --- .../AnswerChoiceList.test.tsx | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/problem/components/AnswerChoiceList/AnswerChoiceList.test.tsx diff --git a/src/problem/components/AnswerChoiceList/AnswerChoiceList.test.tsx b/src/problem/components/AnswerChoiceList/AnswerChoiceList.test.tsx new file mode 100644 index 00000000..e04dd592 --- /dev/null +++ b/src/problem/components/AnswerChoiceList/AnswerChoiceList.test.tsx @@ -0,0 +1,55 @@ +import { useQuery } from "@tanstack/react-query"; + +import { describe, expect, it, vi } from "vitest"; + +import QueryClientProviders from "@shared/components/queryClientProvider"; + +import AnswerChoiceList from "."; +import { getProblemQueryOptions } from "@problem/remotes/getProblemQueryOptions"; +import { render, renderHook, screen, waitFor } from "@testing-library/react"; + +vi.mock("next/navigation", () => { + return { + __esModule: true, + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + }), + useParams: () => ({ + get: () => {}, + query: { + problemId: "1", + }, + }), + }; +}); +const wrapper = ({ children }: React.PropsWithChildren) => { + return {children}; +}; +const renderWithQueryClient = () => { + return render( + + + , + ); +}; + +describe("선택지 불러오고, 버튼 클릭으로 버튼 ui 변경", () => { + it("contents 불러오는지 확인", async () => { + await waitFor(() => renderWithQueryClient()); + const { result } = renderHook( + () => + useQuery({ + ...getProblemQueryOptions({ problemId: 1 }), + }), + { wrapper }, + ); + await waitFor(() => result.current.isSuccess); + + expect(screen.getByText("유동성")); + expect(screen.getByText("분산투자")); + expect(screen.getByText("높은 운용 비용")); + expect(screen.getByText("투명성")); + }); +}); From 534668dffecb95b96cf95b13fe93d27eb34c8018 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:54:21 +0900 Subject: [PATCH 44/52] feat : answerchoicelist component --- .../components/AnswerChoiceList/index.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/problem/components/AnswerChoiceList/index.tsx diff --git a/src/problem/components/AnswerChoiceList/index.tsx b/src/problem/components/AnswerChoiceList/index.tsx new file mode 100644 index 00000000..e137532d --- /dev/null +++ b/src/problem/components/AnswerChoiceList/index.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { useParams } from "next/navigation"; + +import React from "react"; + +import { useQuery } from "@tanstack/react-query"; + +import AnswerChoiceButton from "../AnswerChoiceButton"; +import { getProblemQueryOptions } from "@problem/remotes/getProblemQueryOptions"; + +export default function AnswerChoiceList() { + const { problemId } = useParams<{ problemId: string }>(); + const problemIdNumber = Number(problemId); + const { data: problemInfo } = useQuery({ + ...getProblemQueryOptions({ problemId: problemIdNumber }), + }); + + if (!problemInfo) return
error
; + + return ( +
+ {problemInfo.contents.map((answerValue) => ( + + ))} +
+ ); +} From acbcaa1e7fa0c827e87017a2b3187324ee47b531 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:54:43 +0900 Subject: [PATCH 45/52] feat : answer choice button right icon --- .../components/ChoiceFillCircleSvg/index.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/problem/components/ChoiceFillCircleSvg/index.tsx diff --git a/src/problem/components/ChoiceFillCircleSvg/index.tsx b/src/problem/components/ChoiceFillCircleSvg/index.tsx new file mode 100644 index 00000000..59177501 --- /dev/null +++ b/src/problem/components/ChoiceFillCircleSvg/index.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +interface Props { + fill: string; +} + +export default function ChoiceFillCircleSvg({ fill }: Props) { + return ( + + + + + ); +} From 4c3d92c7f0267607d9e3b1d00333d325d524664d Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:56:35 +0900 Subject: [PATCH 46/52] docs : rename answerContextButton to answerSubmitButton --- .../AnswerContextButton.stories.ts | 31 ------- .../AnswerContextButton.test.tsx | 81 ------------------- .../components/AnswerContextButton/index.tsx | 24 ------ 3 files changed, 136 deletions(-) delete mode 100644 src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts delete mode 100644 src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx delete mode 100644 src/problem/components/AnswerContextButton/index.tsx diff --git a/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts b/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts deleted file mode 100644 index 209c292a..00000000 --- a/src/problem/components/AnswerContextButton/AnswerContextButton.stories.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Button } from "@shared/components/ui/button"; - -import { Meta, StoryObj } from "@storybook/react"; - -const meta = { - component: Button, -} satisfies Meta; -export default meta; - -type Story = StoryObj; - -export const WithChoiceAnswer = { - args: { - className: "bg-main", - children: "정답 제출하기", - }, -} satisfies Story; - -export const WithoutChoiceAnswer = { - args: { - className: "bg-text-gray3", - children: "정답 제출하기", - }, -} satisfies Story; - -export const SubmitAnswer = { - args: { - className: "bg-text-black", - children: "다음 문제 풀기", - }, -} satisfies Story; diff --git a/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx b/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx deleted file mode 100644 index 99357bfa..00000000 --- a/src/problem/components/AnswerContextButton/AnswerContextButton.test.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { ReactElement } from "react"; - -import { describe, expect, it, vi } from "vitest"; - -import AnswerContextButton from "."; -import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; -import QuizContext from "@problem/context/problemContext"; -import { ProblemContextInfo } from "@problem/types/problemContextInfo"; -import { render, screen } from "@testing-library/react"; - -vi.mock("@shared/components/ui/button", () => ({ - Button: ({ - className, - children, - }: { - className: string; - children: ReactElement; - }) => , -})); -const defaultActions = { - actions: { - updateAnswer: () => {}, - updateSubmit: () => {}, - }, -}; -describe("퀴즈 푸는 하단 버튼, 정답 선택에 따른 테스트", () => { - const renderWithContext = (contextValue: ProblemContextInfo) => { - return render( - - - , - ); - }; - - it("정답 선택 전 상태", () => { - renderWithContext({ - states: { answer: null, isSubmit: false }, - ...defaultActions, - }); - if (BUTTON_INFO.PRE_ANSWER_SELECT.title) - expect( - screen.getByText(BUTTON_INFO.PRE_ANSWER_SELECT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.PRE_ANSWER_SELECT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.PRE_ANSWER_SELECT.className, - ); - }); - it("정답 선택 후 상태", () => { - renderWithContext({ - states: { answer: "정답!!", isSubmit: false }, - ...defaultActions, - }); - if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title) - expect( - screen.getByText(BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className, - ); - }); - - it("정답 선택 후 상태", () => { - renderWithContext({ - states: { answer: "정답!!", isSubmit: true }, - ...defaultActions, - }); - if (BUTTON_INFO.POST_SUBMIT.title) - expect( - screen.getByText(BUTTON_INFO.POST_SUBMIT.title), - ).toBeInTheDocument(); - - if (BUTTON_INFO.POST_SUBMIT.className) - expect(screen.getByRole("button")).toHaveClass( - BUTTON_INFO.POST_SUBMIT.className, - ); - }); -}); diff --git a/src/problem/components/AnswerContextButton/index.tsx b/src/problem/components/AnswerContextButton/index.tsx deleted file mode 100644 index 142c66ae..00000000 --- a/src/problem/components/AnswerContextButton/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client"; - -import React, { useContext } from "react"; - -import { Button } from "@shared/components/ui/button"; - -import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; -import QuizContext from "@problem/context/problemContext"; - -export default function AnswerContextButton() { - const { - states: { answer, isSubmit }, - } = useContext(QuizContext); - - const result = isSubmit - ? BUTTON_INFO.POST_SUBMIT.title - : BUTTON_INFO.PRE_ANSWER_SELECT.title; - const style = - (!answer && !isSubmit && BUTTON_INFO.PRE_ANSWER_SELECT.className) || - (answer && !isSubmit && BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) || - BUTTON_INFO.POST_SUBMIT.className; - - return ; -} From 6b94c2c23fb3d3972a4641171f1c4efd83ed0aad Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:56:58 +0900 Subject: [PATCH 47/52] feat : AnswerSubmitButton component --- .../components/AnswerSubmitButton/index.tsx | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/problem/components/AnswerSubmitButton/index.tsx diff --git a/src/problem/components/AnswerSubmitButton/index.tsx b/src/problem/components/AnswerSubmitButton/index.tsx new file mode 100644 index 00000000..2f2aebef --- /dev/null +++ b/src/problem/components/AnswerSubmitButton/index.tsx @@ -0,0 +1,47 @@ +"use client"; +import { useParams } from "next/navigation"; + +import React, { useContext } from "react"; + +import { useMutation } from "@tanstack/react-query"; + +import { Button } from "@shared/components/ui/button"; + +import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; +import QuizContext from "@problem/context/problemContext"; +import { postProblemAnswerMutationOptions } from "@problem/remotes/postProblemAnswerOption"; + +export default function AnswerSubmitButton() { + const { problemId } = useParams<{ problemId: string }>(); + const problemIdNumber = Number(problemId); + const { + states: { choiceAnswer }, + } = useContext(QuizContext); + + const { mutate: postProblemAnswer, isSuccess: isPostAnswerSuccess } = + useMutation({ + ...postProblemAnswerMutationOptions({ problemId: problemIdNumber }), + }); + + const onPostProblemAnswer = () => { + if (choiceAnswer) postProblemAnswer({ choiceAns: choiceAnswer }); + }; + const result = isPostAnswerSuccess + ? BUTTON_INFO.POST_SUBMIT.title + : BUTTON_INFO.PRE_ANSWER_SELECT.title; + + const style = + (!choiceAnswer && + !isPostAnswerSuccess && + BUTTON_INFO.PRE_ANSWER_SELECT.className) || + (choiceAnswer && + !isPostAnswerSuccess && + BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) || + (choiceAnswer && isPostAnswerSuccess && BUTTON_INFO.POST_SUBMIT.className); + + return ( + + ); +} From c893cbb95e2071831aa8e133172873f6f2d4cb45 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:57:08 +0900 Subject: [PATCH 48/52] test : answer submit button --- .../AnswerSubmitButton.test.tsx | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/problem/components/AnswerSubmitButton/AnswerSubmitButton.test.tsx diff --git a/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.test.tsx b/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.test.tsx new file mode 100644 index 00000000..efc8271b --- /dev/null +++ b/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.test.tsx @@ -0,0 +1,100 @@ +import { describe, expect, it, vi } from "vitest"; + +import QueryClientProviders from "@shared/components/queryClientProvider"; + +import AnswerSubmitButton from "."; +import { BUTTON_INFO } from "@problem/constants/answerButtonInfo"; +import ProblemContext, { + defaultActions, + defaultStates, +} from "@problem/context/problemContext"; +import { ProblemContextInfo } from "@problem/types/problemContextInfo"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; + +vi.mock("next/navigation", () => { + return { + __esModule: true, + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + }), + useParams: () => ({ + get: () => {}, + query: { + problemId: "1", + }, + }), + }; +}); + +const renderWithContext = (contextValue: ProblemContextInfo) => { + return render( + + + + + , + ); +}; +describe("퀴즈 푸는 하단 버튼, 정답 선택에 따른 테스트", () => { + it("정답 선택 전 상태", () => { + renderWithContext({ + ...defaultStates, + ...defaultActions, + }); + if (BUTTON_INFO.PRE_ANSWER_SELECT.title) + expect( + screen.getByText(BUTTON_INFO.PRE_ANSWER_SELECT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.PRE_ANSWER_SELECT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.PRE_ANSWER_SELECT.className, + ); + }); + it("정답 선택 후 제출 전 상태", async () => { + renderWithContext({ + states: { + ...defaultStates.states, + choiceAnswer: "높은 운용 비용", + }, + ...defaultActions, + }); + + if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title) + expect( + screen.getByText(BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.POST_ANSWER_PRE_SUBMIT.className, + ); + }); + + it("정답 선택 후 제출 완료 상태", async () => { + renderWithContext({ + states: { + ...defaultStates.states, + choiceAnswer: "높은 운용 비용", + }, + ...defaultActions, + }); + const button = screen.getByRole("button"); + await userEvent.click(button); + + await waitFor(() => { + if (BUTTON_INFO.POST_SUBMIT.title) + expect( + screen.getByText(BUTTON_INFO.POST_SUBMIT.title), + ).toBeInTheDocument(); + + if (BUTTON_INFO.POST_SUBMIT.className) + expect(screen.getByRole("button")).toHaveClass( + BUTTON_INFO.POST_SUBMIT.className, + ); + }); + }); +}); From 8143375e2db757d5d154e79faeb8214eb9798178 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:57:24 +0900 Subject: [PATCH 49/52] feat : answer submit button story --- .../AnswerSubmitButton.stories.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/problem/components/AnswerSubmitButton/AnswerSubmitButton.stories.ts diff --git a/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.stories.ts b/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.stories.ts new file mode 100644 index 00000000..209c292a --- /dev/null +++ b/src/problem/components/AnswerSubmitButton/AnswerSubmitButton.stories.ts @@ -0,0 +1,31 @@ +import { Button } from "@shared/components/ui/button"; + +import { Meta, StoryObj } from "@storybook/react"; + +const meta = { + component: Button, +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +export const WithChoiceAnswer = { + args: { + className: "bg-main", + children: "정답 제출하기", + }, +} satisfies Story; + +export const WithoutChoiceAnswer = { + args: { + className: "bg-text-gray3", + children: "정답 제출하기", + }, +} satisfies Story; + +export const SubmitAnswer = { + args: { + className: "bg-text-black", + children: "다음 문제 풀기", + }, +} satisfies Story; From 9ac0ae0f2726a2e742acb20cf077ebdc0c349ad4 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:57:36 +0900 Subject: [PATCH 50/52] fix : import component name --- src/app/problem/[problemId]/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/problem/[problemId]/page.tsx b/src/app/problem/[problemId]/page.tsx index f376bb84..1a4d18fb 100644 --- a/src/app/problem/[problemId]/page.tsx +++ b/src/app/problem/[problemId]/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import AnswerChoiceList from "@problem/components/AnswerChoiceList"; -import AnswerContextButton from "@problem/components/AnswerContextButton"; +import AnswerSubmitButton from "@problem/components/AnswerSubmitButton"; import ProblemTitle from "@problem/components/ProblemTitle"; import TagList from "@problem/components/TagList"; import { ProblemProvider } from "@problem/context/problemContext"; @@ -15,7 +15,7 @@ export default function ProblemPage() {
- + ); From eef916f343af70913bb682f688605b539c960080 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:57:53 +0900 Subject: [PATCH 51/52] fix : tag list from api data --- src/problem/components/TagList/index.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/problem/components/TagList/index.tsx b/src/problem/components/TagList/index.tsx index e38d52f4..bc4fe543 100644 --- a/src/problem/components/TagList/index.tsx +++ b/src/problem/components/TagList/index.tsx @@ -2,18 +2,27 @@ import React from "react"; -import { useQuery } from "@tanstack/react-query"; +import { ApiResponse } from "@api/api-config"; +import queryClient from "@api/query-client"; import Tag from "@common/components/Tag"; -import { getTagQueryOptions } from "@problem/remotes/getTagQueryOptions"; +import { PromblemInfo } from "@problem/types/problemInfo"; export default function TagList() { - const { data: tagInfo, isError } = useQuery({ ...getTagQueryOptions() }); - if (isError) return
error
; + const problemInfoData = queryClient.getQueryData(["get-Problems-info"]) as + | ApiResponse + | undefined; + + if (!problemInfoData) return
error
; + + const { day } = problemInfoData.data; + const tagList = [day]; return (
- {tagInfo?.tags.map((tag) => )} + {tagList.map((tag) => ( + + ))}
); } From e8f9711269973da53f1eed729e226dc654007b81 Mon Sep 17 00:00:00 2001 From: happhee Date: Fri, 14 Jun 2024 13:58:33 +0900 Subject: [PATCH 52/52] init : answer choice button story --- .../AnswerChoiceButton.stories.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/problem/components/AnswerChoiceButton/AnswerChoiceButton.stories.ts diff --git a/src/problem/components/AnswerChoiceButton/AnswerChoiceButton.stories.ts b/src/problem/components/AnswerChoiceButton/AnswerChoiceButton.stories.ts new file mode 100644 index 00000000..a7a9c34f --- /dev/null +++ b/src/problem/components/AnswerChoiceButton/AnswerChoiceButton.stories.ts @@ -0,0 +1,38 @@ +import AnswerChoiceButton from "."; +import { Meta, StoryObj } from "@storybook/react"; + +const meta = { + component: AnswerChoiceButton, +} satisfies Meta; +export default meta; + +type Story = StoryObj; + +// TODO : msw 사용해서 다르게 보여주는 story 작성 필요 +export const InitChoiceAnswer = { + args: { + // className: ANSWER_CHOICHE_BUTTON_INFO.INIT_CHOICE_ANSWER.className, + content: "유동성", + }, +} satisfies Story; + +export const CurrentChoiceAnswer = { + args: { + // className: ANSWER_CHOICHE_BUTTON_INFO.CURRENT_CHOICE_ANSWER.className, + content: "유동성", + }, +} satisfies Story; + +export const ChoiceAnswerCorrect = { + args: { + // className: ANSWER_CHOICHE_BUTTON_INFO.CHOICE_ANSWER_CORRECT.className, + content: "유동성", + }, +} satisfies Story; + +export const ChoiceAnswerFail = { + args: { + // className: ANSWER_CHOICHE_BUTTON_INFO.CHOICE_ANSWER_FAIL.className, + content: "유동성", + }, +} satisfies Story;