diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index 475fb7753..0bf11a65e 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -2,6 +2,10 @@ import type { Preview } from '@storybook/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import GlobalStyles from '../src/style/GlobalStyles'; +import { AuthProvider, ToastProvider } from '../src/contexts'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +const queryClient = new QueryClient(); const preview: Preview = { parameters: { @@ -15,19 +19,16 @@ const preview: Preview = { }, decorators: [ (Story) => ( - <> - - -
-
- -
-
+ + + + + -
-
-
- + + + + ), ], }; diff --git a/frontend/jest.config.ts b/frontend/jest.config.ts index 1bf28965d..8ee304dd4 100644 --- a/frontend/jest.config.ts +++ b/frontend/jest.config.ts @@ -11,12 +11,13 @@ const config: Config = { moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], transform: { '^.+\\.tsx?$': 'ts-jest', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': + '/src/mocks/fileTransformer.js', }, clearMocks: true, moduleNameMapper: { '^@/(.*)$': '/src/$1', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)$': '/src/mocks/fileMock.js', 'react-syntax-highlighter/dist/esm': 'react-syntax-highlighter/dist/cjs', }, transformIgnorePatterns: ['/node_modules/(?!react-syntax-highlighter)'], diff --git a/frontend/package.json b/frontend/package.json index 36c5c9d97..01053a986 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "code-zap", - "version": "1.1.6", + "version": "1.1.7", "description": "", "main": "index.js", "scripts": { diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js index 15751fa19..ecabdda5d 100644 --- a/frontend/public/mockServiceWorker.js +++ b/frontend/public/mockServiceWorker.js @@ -8,8 +8,8 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.3.5' -const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' +const PACKAGE_VERSION = '2.5.2' +const INTEGRITY_CHECKSUM = '07a8241b182f8a246a7cd39894799a9e' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() @@ -62,7 +62,12 @@ self.addEventListener('message', async function (event) { sendToClient(client, { type: 'MOCKING_ENABLED', - payload: true, + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, }) break } @@ -155,6 +160,10 @@ async function handleRequest(event, requestId) { async function resolveMainClient(event) { const client = await self.clients.get(event.clientId) + if (activeClientIds.has(event.clientId)) { + return client + } + if (client?.frameType === 'top-level') { return client } diff --git a/frontend/src/api/ApiClient.ts b/frontend/src/api/ApiClient.ts index 9edcd031b..5a15edfdf 100644 --- a/frontend/src/api/ApiClient.ts +++ b/frontend/src/api/ApiClient.ts @@ -96,7 +96,7 @@ class ApiClient { } } -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; const httpHeader = { 'Content-Type': 'application/json', diff --git a/frontend/src/api/authentication.ts b/frontend/src/api/authentication.ts index 9250e63bc..a2397fac4 100644 --- a/frontend/src/api/authentication.ts +++ b/frontend/src/api/authentication.ts @@ -5,7 +5,7 @@ import { MemberInfo } from '@/types'; import { apiClient } from './ApiClient'; import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const SIGNUP_API_URL = `${API_URL}${END_POINTS.SIGNUP}`; export const LOGIN_API_URL = `${API_URL}${END_POINTS.LOGIN}`; diff --git a/frontend/src/api/categories.ts b/frontend/src/api/categories.ts index 83956dd0d..8294f3876 100644 --- a/frontend/src/api/categories.ts +++ b/frontend/src/api/categories.ts @@ -12,7 +12,7 @@ import type { import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const CATEGORY_API_URL = `${API_URL}${END_POINTS.CATEGORIES}`; diff --git a/frontend/src/api/like.ts b/frontend/src/api/like.ts index 95e1d0270..86ae1c466 100644 --- a/frontend/src/api/like.ts +++ b/frontend/src/api/like.ts @@ -5,7 +5,7 @@ import { LikeDeleteRequest, LikePostRequest } from '@/types'; import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const LIKE_API_URL = `${API_URL}${END_POINTS.LIKES}`; diff --git a/frontend/src/api/members.ts b/frontend/src/api/members.ts index 783f98dec..8330e4e19 100644 --- a/frontend/src/api/members.ts +++ b/frontend/src/api/members.ts @@ -3,7 +3,7 @@ import { GetMemberNameResponse } from '@/types'; import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const MEMBER_API_URL = `${API_URL}${END_POINTS.MEMBERS}`; diff --git a/frontend/src/api/tags.ts b/frontend/src/api/tags.ts index 28d9921f6..19ea86616 100644 --- a/frontend/src/api/tags.ts +++ b/frontend/src/api/tags.ts @@ -3,7 +3,7 @@ import { TagListResponse } from '@/types/api'; import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const TAG_API_URL = `${API_URL}${END_POINTS.TAGS}`; diff --git a/frontend/src/api/templates.ts b/frontend/src/api/templates.ts index 87f740ea8..a9c007fa4 100644 --- a/frontend/src/api/templates.ts +++ b/frontend/src/api/templates.ts @@ -12,7 +12,7 @@ import { SortingOption } from '@/types'; import { apiClient } from './ApiClient'; import { customFetch } from './customFetch'; -const API_URL = process.env.REACT_APP_API_URL ?? ''; +const API_URL = process.env.REACT_APP_API_URL || 'https://default-url.com'; export const TEMPLATE_API_URL = `${API_URL}${END_POINTS.TEMPLATES_EXPLORE}`; diff --git a/frontend/src/components/PagingButtons/PagingButtons.stories.tsx b/frontend/src/components/PagingButtons/PagingButtons.stories.tsx index 3001a6147..2e290738d 100644 --- a/frontend/src/components/PagingButtons/PagingButtons.stories.tsx +++ b/frontend/src/components/PagingButtons/PagingButtons.stories.tsx @@ -6,7 +6,7 @@ const meta: Meta = { title: 'PagingButtons', component: PagingButtons, args: { - totalPages: 6, + paginationSizes: 5, onPageChange: () => {}, }, }; diff --git a/frontend/src/components/PagingButtons/PagingButtons.tsx b/frontend/src/components/PagingButtons/PagingButtons.tsx index 7584a7ebb..6ef473830 100644 --- a/frontend/src/components/PagingButtons/PagingButtons.tsx +++ b/frontend/src/components/PagingButtons/PagingButtons.tsx @@ -6,28 +6,26 @@ import * as S from './PagingButtons.style'; interface Props { currentPage: number; - totalPages: number; + paginationSizes: number; onPageChange: (page: number) => void; } -const PagingButtons = ({ currentPage, totalPages, onPageChange }: Props) => { +const PagingButtons = ({ currentPage, paginationSizes, onPageChange }: Props) => { const getPageNumbers = () => { - const startPage = Math.max(1, Math.min(currentPage - 2, totalPages - 4)); - const endPage = Math.min(totalPages, startPage + 4); + const startPage = Math.max(1, Math.min(currentPage - 2, currentPage + paginationSizes - 5)); + const endPage = Math.min(currentPage + paginationSizes - 1, startPage + 4); return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i); }; const handlePagingClick = (page: number, label: string) => { - trackMyTemplatePaging({ page, totalPages, label }); + trackMyTemplatePaging({ page, label }); onPageChange(page); }; return ( - - {getPageNumbers().map((page) => ( { label={String(page)} /> ))} - - - + ); }; @@ -59,7 +50,9 @@ interface PagingButtonProps { const PagingButton = ({ page, isActive, disabled, onClick, label }: PagingButtonProps) => ( onClick(page ?? 1, label)}> - {label} + {!disabled && ( + {label} + )} ); diff --git a/frontend/src/components/SelectList/SelectList.style.ts b/frontend/src/components/SelectList/SelectList.style.ts index 5ce32eb8b..5cc4fb1f1 100644 --- a/frontend/src/components/SelectList/SelectList.style.ts +++ b/frontend/src/components/SelectList/SelectList.style.ts @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import { theme } from '@/style/theme'; + import type { OptionProps } from './SelectList'; export const SelectListContainer = styled.div` diff --git a/frontend/src/components/SelectList/SelectList.tsx b/frontend/src/components/SelectList/SelectList.tsx index 5c4c23568..19af20f64 100644 --- a/frontend/src/components/SelectList/SelectList.tsx +++ b/frontend/src/components/SelectList/SelectList.tsx @@ -1,7 +1,7 @@ -import { useTheme } from '@emotion/react'; import { PropsWithChildren } from 'react'; import { Text } from '@/components'; +import { theme } from '@/style/theme'; import * as S from './SelectList.style'; @@ -12,19 +12,15 @@ export interface OptionProps { const SelectListBase = ({ children }: PropsWithChildren) => {children}; -const SelectListOption = ({ children, isSelected, onClick }: PropsWithChildren) => { - const theme = useTheme(); - - return ( - - - - {children} - - - - ); -}; +const SelectListOption = ({ children, isSelected, onClick }: PropsWithChildren) => ( + + + + {children} + + + +); const SelectList = Object.assign(SelectListBase, { Option: SelectListOption, diff --git a/frontend/src/components/SourceCode/SourceCode.tsx b/frontend/src/components/SourceCode/SourceCode.tsx index b00b75fe4..b6d7b8ae4 100644 --- a/frontend/src/components/SourceCode/SourceCode.tsx +++ b/frontend/src/components/SourceCode/SourceCode.tsx @@ -1,7 +1,6 @@ -import { ViewUpdate } from '@codemirror/view'; import { type LanguageName, loadLanguage } from '@uiw/codemirror-extensions-langs'; import { quietlight } from '@uiw/codemirror-theme-quietlight'; -import ReactCodeMirror, { EditorView, ReactCodeMirrorRef } from '@uiw/react-codemirror'; +import ReactCodeMirror, { EditorView, ReactCodeMirrorRef, type ViewUpdate } from '@uiw/react-codemirror'; import { useRef } from 'react'; import { useWindowWidth } from '@/hooks'; diff --git a/frontend/src/components/SourceCodeEditor/SourceCodeEditor.tsx b/frontend/src/components/SourceCodeEditor/SourceCodeEditor.tsx index 0f0e61348..d192f325b 100644 --- a/frontend/src/components/SourceCodeEditor/SourceCodeEditor.tsx +++ b/frontend/src/components/SourceCodeEditor/SourceCodeEditor.tsx @@ -1,4 +1,4 @@ -import { ViewUpdate } from '@codemirror/view'; +import { type ViewUpdate } from '@uiw/react-codemirror'; import { useRef } from 'react'; import { TrashcanIcon } from '@/assets/images'; diff --git a/frontend/src/components/SourceCodeViewer/SourceCodeViewer.style.ts b/frontend/src/components/SourceCodeViewer/SourceCodeViewer.style.ts index ec3ecb365..a1d2a55c5 100644 --- a/frontend/src/components/SourceCodeViewer/SourceCodeViewer.style.ts +++ b/frontend/src/components/SourceCodeViewer/SourceCodeViewer.style.ts @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import { Button } from '@/components'; +import { theme } from '@/style/theme'; export const SourceCodeViewerContainer = styled.div` overflow: hidden; @@ -17,7 +18,7 @@ export const FilenameContainer = styled.div` height: 3rem; padding: 1rem 1.5rem; - background: ${({ theme }) => theme.color.light.tertiary_600}; + background: ${theme.color.light.tertiary_600}; `; export const ToggleButton = styled.button` diff --git a/frontend/src/components/Toast/Toast.style.ts b/frontend/src/components/Toast/Toast.style.ts index 9d8bef67c..3818a73ea 100644 --- a/frontend/src/components/Toast/Toast.style.ts +++ b/frontend/src/components/Toast/Toast.style.ts @@ -1,6 +1,8 @@ import { keyframes } from '@emotion/react'; import styled from '@emotion/styled'; +import { theme } from '@/style/theme'; + const slideIn = keyframes` from { transform: translateY(20px) translateX(-50%); @@ -37,7 +39,7 @@ export const BaseToast = styled.div<{ visible: boolean; type: 'success' | 'fail' font-size: 16px; color: white; - background-color: ${({ theme, type }) => { + background-color: ${({ type }) => { switch (type) { case 'success': return theme.color.light.complementary_300; diff --git a/frontend/src/hooks/template/index.ts b/frontend/src/hooks/template/index.ts index 56cdbcf6e..d071d71ba 100644 --- a/frontend/src/hooks/template/index.ts +++ b/frontend/src/hooks/template/index.ts @@ -1,3 +1,2 @@ export { useSourceCode } from './useSourceCode'; export { useTag } from './useTag'; -export { useSearchKeyword } from './useSearchKeyword'; diff --git a/frontend/src/hooks/template/useSearchKeyword.ts b/frontend/src/hooks/template/useSearchKeyword.ts deleted file mode 100644 index ff82815d2..000000000 --- a/frontend/src/hooks/template/useSearchKeyword.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useInput, useDebounce } from '..'; - -export const useSearchKeyword = (initKeyword: string = '') => { - const [keyword, handleKeywordChange] = useInput(initKeyword); - const debouncedKeyword = useDebounce(keyword, 300); - - return { keyword, debouncedKeyword, handleKeywordChange }; -}; diff --git a/frontend/src/mocks/fileTransformer.js b/frontend/src/mocks/fileTransformer.js new file mode 100644 index 000000000..3ea1ef262 --- /dev/null +++ b/frontend/src/mocks/fileTransformer.js @@ -0,0 +1,9 @@ +const path = require('path'); + +module.exports = { + process(sourceText, sourcePath, options) { + return { + code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`, + }; + }, +}; diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 32bfe2bf8..57e62978e 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -61,8 +61,7 @@ export const templateHandlers = [ break; } - const totalElements = filteredTemplates.length; - const totalPages = Math.ceil(totalElements / size); + const paginationSizes = Math.min(Math.ceil(filteredTemplates.length / size - page + 1), 5); const startIndex = (page - 1) * size; const endIndex = startIndex + size; const paginatedTemplates = filteredTemplates.slice(startIndex, endIndex); @@ -71,8 +70,7 @@ export const templateHandlers = [ return HttpResponse.json({ status: 200, templates: paginatedTemplates, - totalPages, - totalElements, + paginationSizes, numberOfElements, }); }), diff --git a/frontend/src/pages/LoginPage/LoginPage.tsx b/frontend/src/pages/LoginPage/LoginPage.tsx index 123faedb3..11c8321b4 100644 --- a/frontend/src/pages/LoginPage/LoginPage.tsx +++ b/frontend/src/pages/LoginPage/LoginPage.tsx @@ -8,6 +8,7 @@ import { useTrackPageViewed } from '@/service/amplitude'; import { useLoginForm } from './hooks'; import * as S from './LoginPage.style'; +import { theme } from '@/style/theme'; const LoginPage = () => { useTrackPageViewed({ eventName: '[Viewed] 로그인 페이지' }); @@ -21,7 +22,7 @@ const LoginPage = () => { - 환영하잽 + 환영하잽 { 로그인 - 계정이 없으신가요? + 계정이 없으신가요? - 이미 계정이 있으신가요? + 이미 계정이 있으신가요?