Skip to content

Commit

Permalink
Merge pull request #44 from YAPP-Github/feature/workbook-41
Browse files Browse the repository at this point in the history
[ Feature/workbook 41 ] Skeleton 화면 구현
  • Loading branch information
soomin9106 authored Jun 15, 2024
2 parents d0efa5a + 4723d10 commit a9247ce
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 28 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
"no-unused-vars": "off",
"import/no-duplicates": "error",
"unused-imports/no-unused-imports": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn",
"simple-import-sort/exports": "warn",
"react/display-name": "off",
"@next/next/no-img-element": "off",
"@typescript-eslint/no-explicit-any": "warn",
"simple-import-sort/imports": [
"warn",
{
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# dependencies
.env
.env.production
/node_modules
/.pnp
.pnp.js
Expand Down
22 changes: 22 additions & 0 deletions generate-runtime-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import fs from 'fs';
import path from 'path';

// 환경 변수를 읽어들임
const runtime = process.env.NODE_ENV === 'production' ? 'edge' : 'nodejs';

const configContent = `export const runtime = '${runtime}';\n`;

const configDir = path.join(process.cwd(), 'src/config');
const configPath = path.join(configDir, 'runtime.js');

// 디렉토리가 존재하지 않으면 생성
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}

// runtime.js 파일 생성
fs.writeFileSync(configPath, configContent, 'utf8');

console.log(`Runtime configuration written to ${configPath}`);


1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "next dev",
"prebuild": "node generate-runtime-config.mjs",
"build": "next build",
"start": "next start",
"lint": "next lint",
Expand Down
4 changes: 4 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import { cn } from "@shared/utils/cn";

import "./globals.css";
import MSWProviders from "@mocks/MSWProviders";
import { Toaster } from "@shared/components/ui/toaster";

export const metadata: Metadata = {
title: "FEW",
description: "매일 아침마다 경제 아티클과 문제를 보내드려요!",
};

// export const runtime = 'edge'; // TBD: 개발환경과 분리

const pretendard = localFont({
src: [
{
Expand Down Expand Up @@ -64,6 +67,7 @@ export default function RootLayout({
>
<MSWProviders>
<Suspense>{children}</Suspense>
<Toaster />
</MSWProviders>

<ReactQueryDevtools />
Expand Down
6 changes: 4 additions & 2 deletions src/app/workbook/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use client";

import Image from "next/image";
import { usePathname } from "next/navigation";

Expand All @@ -11,6 +12,7 @@ import { useToast } from "@shared/components/ui/use-toast";
import CurriculumSection from "@workbook/components/CurriculumSection";
import OverviewSection from "@workbook/components/OverviewSection";
import TitleSection from "@workbook/components/TitleSection";
import WorkbookSkeleton from "@workbook/components/WorkbookSkeleton";
import { getWorkbookQueryOptions } from "@workbook/remotes/getWorkbookQueryOptions";
import { getWorkbookId } from "@workbook/utils";

Expand Down Expand Up @@ -51,7 +53,7 @@ export default function WorkbookPage() {
setIsClient(true);
}, []);

if (isLoading) return <div>Loading...</div>;
if (isLoading) return <WorkbookSkeleton />;
if (isError) return <div>Error loading workbook</div>;

return (
Expand All @@ -66,7 +68,7 @@ export default function WorkbookPage() {
width={0}
height={0}
sizes="100vw"
style={{ width: "100%", height: "auto" }}
style={{ width: "100%", height: "338px" }}
/>
</figure>
<TitleSection
Expand Down
21 changes: 11 additions & 10 deletions src/app/workbook/workbookpage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { render, screen, waitFor, renderHook } from '@testing-library/react';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { HttpResponse, http } from 'msw';
import { apiRoutes } from '@shared/constants/apiRoutes';
import { expect, vi, describe, it, beforeAll, afterEach, afterAll, beforeEach } from 'vitest';
import { server } from '@mocks/server';
/* eslint-disable react/display-name */
import { ClassAttributes, ImgHTMLAttributes, JSX, ReactNode } from 'react';

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

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

import { useWorkbook } from '@workbook/remotes/getWorkbookQueryOptions';

import WorkbookPage from './[id]/page';
import { JSX, ClassAttributes, ImgHTMLAttributes, ReactNode } from 'react';
import response from '@mocks/response';
import { getWorkbookQueryOptions, useWorkbook } from '@workbook/remotes/getWorkbookQueryOptions';
import { render, renderHook,screen, waitFor } from '@testing-library/react';

export const createQueryProviderWrapper = () => {
export function createQueryProviderWrapper () {
const queryClient = new QueryClient();
return ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={queryClient}>
Expand Down
10 changes: 10 additions & 0 deletions src/common/components/ContentSkeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Skeleton } from "@shared/components/ui/skeleton";

export default function ContentSkeleton ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<Skeleton className={className} {...props} />
)
}
1 change: 1 addition & 0 deletions src/config/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const runtime = 'nodejs';
28 changes: 16 additions & 12 deletions src/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { http, HttpResponse } from "msw";

import { apiRoutes } from "@shared/constants/apiRoutes";

import { getWorkbookId } from "@workbook/utils";
import { _3_SECOND, delay } from "@shared/utils/delay";

import response from "./response";

Expand Down Expand Up @@ -31,19 +30,24 @@ export const submitAnswerHandler = http.post(
return HttpResponse.json(response[apiRoutes.submitAnswer]);
},
);
export const workbookHandler = http.get(apiRoutes.workbook, ({ request, params }) => {
export const workbookHandler = http.get(apiRoutes.workbook, async ({ request, params }) => {
const workbookId = params

if (!workbookId) {
return new HttpResponse(null, { status: 404 });
}
if (!workbookId) {
return new HttpResponse(null, { status: 404 });
}

console.log(
HttpResponse.json(response[apiRoutes.workbook]),
apiRoutes.workbook,
);
return HttpResponse.json(response[apiRoutes.workbook]);
});
console.log(
HttpResponse.json(response[apiRoutes.workbook]),
apiRoutes.workbook,
);

// 딜레이 적용
await delay(_3_SECOND);

return HttpResponse.json(response[apiRoutes.workbook]);
},
);

export const handlers = [
tagsHandler,
Expand Down
2 changes: 1 addition & 1 deletion src/problem/components/TagList/TagList.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import TagList from ".";
import { tagsHandler } from "@mocks/worker";
import { tagsHandler } from "@mocks/handlers";
import { Meta, StoryObj } from "@storybook/react";

const meta = {
Expand Down
15 changes: 15 additions & 0 deletions src/shared/components/ui/skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { cn } from "@shared/utils/cn"

function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
}

export { Skeleton }
5 changes: 5 additions & 0 deletions src/shared/utils/delay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const _1_SECOND = 1_000;
export const _3_SECOND = 3_000;


export const delay = (second = _3_SECOND) => new Promise((resolve) => setTimeout(resolve, second));
1 change: 1 addition & 0 deletions src/workbook/components/CurriculumSection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ICurriculumItem } from "@workbook/types";

import CurriculumItem from "../CurriculumItem";

interface CurriculumSectionProps {
Expand Down
3 changes: 2 additions & 1 deletion src/workbook/components/TitleSection/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";

import { Writer } from "@workbook/types";

import ShareIcon from "public/assets/icon36/share_36.svg";
import React from "react";

interface TitleSectionProps {
category: string;
Expand Down
29 changes: 29 additions & 0 deletions src/workbook/components/WorkbookSkeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Skeleton } from "@shared/components/ui/skeleton";

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

export default function WorkbookSkeleton() {
const skeletonItems = new Array(6).fill(null);

return (
<div className="flex h-[100vh] w-full flex-col items-center overflow-x-hidden">
<Skeleton className="h-[338px] w-full" />

{/* Content Skeleton */}
<div className="mt-[70px] w-full">
<ContentSkeleton className="h-[30px]" style={{ width: "60%" }} />
<ContentSkeleton
className="h-[30px]"
style={{ width: "80%", marginTop: "9px" }}
/>
{skeletonItems.map((_, index) => (
<ContentSkeleton
key={index}
className="h-[30px] w-full"
style={{ marginTop: "9px" }}
/>
))}
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion src/workbook/remotes/getWorkbookQueryOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UseQueryOptions, useQuery } from "@tanstack/react-query";
import { useQuery,UseQueryOptions } from "@tanstack/react-query";

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

Expand Down

0 comments on commit a9247ce

Please sign in to comment.