-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/#492 가수 검색 기능을 구현한다. #503
Changes from 55 commits
d485312
efc4114
76ee527
a5ea87c
b3342aa
e9e9282
4cfe61c
78c6286
d03635b
0e186eb
378e250
d05a5d5
1448498
d570d64
b5c897f
5151d5a
a6441d5
0e91005
0991a57
a401565
20c6842
71d35a0
973b3e3
b007896
9a4c61c
9660d05
0739545
8f1f066
e6cb8c4
36ba66c
ef42015
e592ff5
d4e39cc
04bff52
7d238a0
bc49b86
f295f68
eb0cff6
b040457
6c1186a
4f1ce89
3a45d8f
8597f53
ff2b349
134a10a
a50055f
ec2a977
dcf0b82
9b835fc
0320767
706f34f
b54f41f
1eb1384
d104057
5f80ba0
58ecc44
99cc599
36033b3
3a23ef5
fdf0e29
b2b6734
1bc5f89
80079d4
0b08c70
8ca98d4
2152aa3
7365915
19c9bea
2f418a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,101 @@ | ||
import { initialize, mswLoader } from 'msw-storybook-addon'; | ||
import type { Preview } from '@storybook/react'; | ||
import GlobalStyles from '../src/shared/styles/GlobalStyles'; | ||
import { ThemeProvider } from 'styled-components'; | ||
import theme from '../src/shared/styles/theme'; | ||
import { BrowserRouter } from 'react-router-dom'; | ||
import AuthProvider from '@/features/auth/components/AuthProvider'; | ||
import LoginPopupProvider from '@/features/auth/hooks/LoginPopUpContext'; | ||
import handlers from '@/mocks/handlers'; | ||
|
||
const customViewport = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 프로젝트 내부 breakpoint 스팩대로 viewport 추가하였습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍👍 와우 커스텀 뷰포트 추가하는거 좋네요! |
||
xxl: { | ||
name: 'xxl', | ||
styles: { | ||
width: '1440px', | ||
height: '1080px', | ||
}, | ||
}, | ||
|
||
xl: { | ||
name: 'xl', | ||
styles: { | ||
width: '1280px', | ||
height: '720px', | ||
}, | ||
}, | ||
|
||
lg: { | ||
name: 'lg', | ||
styles: { | ||
width: '1024px', | ||
height: '720px', | ||
}, | ||
}, | ||
|
||
md: { | ||
name: 'md', | ||
styles: { | ||
width: '768px', | ||
height: '1024px', | ||
}, | ||
}, | ||
|
||
sm: { | ||
name: 'sm', | ||
styles: { | ||
width: '640px', | ||
height: '768px', | ||
}, | ||
}, | ||
|
||
xs: { | ||
name: 'xs', | ||
styles: { | ||
width: '420px', | ||
height: '768px', | ||
}, | ||
}, | ||
|
||
xxs: { | ||
name: 'xxs', | ||
styles: { | ||
width: '380px', | ||
height: '768px', | ||
}, | ||
}, | ||
}; | ||
|
||
initialize(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. msw 실행 메서드에요 |
||
|
||
const preview: Preview = { | ||
parameters: { | ||
actions: { argTypesRegex: '^on[A-Z].*' }, | ||
viewport: { viewports: customViewport, defaultViewport: 'xs' }, | ||
controls: { | ||
matchers: { | ||
color: /(background|color)$/i, | ||
date: /Date$/, | ||
}, | ||
}, | ||
msw: { | ||
handlers: [...handlers], | ||
}, | ||
}, | ||
loaders: [mswLoader], | ||
|
||
decorators: [ | ||
(Story) => ( | ||
<ThemeProvider theme={theme}> | ||
<GlobalStyles /> | ||
<Story /> | ||
</ThemeProvider> | ||
<AuthProvider> | ||
<LoginPopupProvider> | ||
<ThemeProvider theme={theme}> | ||
<BrowserRouter> | ||
<GlobalStyles /> | ||
<Story /> | ||
</BrowserRouter> | ||
</ThemeProvider> | ||
</LoginPopupProvider> | ||
</AuthProvider> | ||
), | ||
], | ||
}; | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,10 +59,12 @@ | |
"jest": "^29.6.1", | ||
"jest-environment-jsdom": "^29.6.1", | ||
"msw": "^1.2.3", | ||
"msw-storybook-addon": "^1.9.0", | ||
"postcss-styled-syntax": "^0.4.0", | ||
"prettier": "^3.0.0", | ||
"react-refresh": "^0.14.0", | ||
"storybook": "^7.0.27", | ||
"storybook-addon-react-router-v6": "^2.0.7", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 storybook에서 react router가 필요한 이유가 있나요? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞아요. '넘나들기 위해서' 라기 보다는 컴포넌트를 보여주기 위해서 사용했습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확인이 끝나고 추후에 삭제...를 해야할까요 ? ㅋㅋㅋㅋㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 일단 제 작업에서 스토리북 수정하면서 useNavigate를 사용한다고 에러가 떴었는데, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하.. 알겠습니당 |
||
"stylelint": "^15.10.2", | ||
"stylelint-config-clean-order": "^5.0.1", | ||
"ts-jest": "^29.1.1", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import { createContext, useContext, useMemo, useState } from 'react'; | ||
import accessTokenStorage from '@/shared/utils/accessTokenStorage'; | ||
import parseJWT from '../utils/parseJWT'; | ||
import type { PropsWithChildren } from 'react'; | ||
|
||
interface User { | ||
memberId: number; | ||
|
@@ -23,7 +24,7 @@ export const useAuthContext = () => { | |
|
||
const AuthContext = createContext<AuthContextProps | null>(null); | ||
|
||
const AuthProvider = ({ children }: { children: React.ReactElement[] }) => { | ||
const AuthProvider = ({ children }: PropsWithChildren) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. provider 의 type이 💬 혹시 type으로 단일 children을 제한한 이유가 있을까요? // type error!
<AuthProvider>
<One>
</AuthProvider> |
||
const [accessToken, setAccessToken] = useState(accessTokenStorage.getToken() || ''); | ||
|
||
const user: User | null = useMemo(() => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ const LoginPopUpContext = createContext<LoginPopUpContextProps | null>(null); | |
export const useLoginPopup = () => { | ||
const contextValue = useContext(LoginPopUpContext); | ||
|
||
if (contextValue === null) throw new Error('AuthContext가 null입니다.'); | ||
if (contextValue === null) throw new Error('LoginPopUpContext에 값이 제공되지 않았습니다.'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 동일한 형식의 코드를 복붙할 때는 꼭 확인 부탁드립니다~! |
||
|
||
return contextValue; | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { useNavigate } from 'react-router-dom'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 파일 이름도 Search라는 단어가 들어가면 더 파악하기 쉬울 것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오우 감사합니다. |
||
import styled from 'styled-components'; | ||
import Thumbnail from '@/features/songs/components/Thumbnail'; | ||
import Flex from '@/shared/components/Flex/Flex'; | ||
import ROUTE_PATH from '@/shared/constants/path'; | ||
import type { SingerSearchPreview } from '../types/search'; | ||
|
||
interface ResultSheetProps { | ||
result: SingerSearchPreview[]; | ||
} | ||
|
||
const ResultSheet = ({ result }: ResultSheetProps) => { | ||
const navigate = useNavigate(); | ||
|
||
const hasResult = result.length > 0; | ||
|
||
return ( | ||
<SheetContainer aria-label="아티스트 검색 결과 미리보기"> | ||
<SheetTitle $align="center" aria-hidden> | ||
아티스트 | ||
</SheetTitle> | ||
<PreviewItemList as="ul" $direction="column" $gap={16}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Flex 공용컴포넌트를 사용하면서부터 flex props들이 JSX 표기로 이동된 김에 suffix로 "Flex"붙이는 거 어떠신가요? JSX단 읽을 때 더 쉽게 해당 element가 flex임을 알 수 있을 것 같네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 생각이네요! 안그래도 flex 관련 props를 맨앞으로 빼서 식별하게 해줘야하나 생각하고 있었어요 |
||
{result.map(({ id, singer, profileImageUrl }) => ( | ||
<PreviewItem key={id} as="li"> | ||
<GoToDetail | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근성을 위해 li 하위에 버튼을 두었습니다. |
||
as="button" | ||
type="button" | ||
onMouseDown={() => navigate(`${ROUTE_PATH.SINGER_DETAIL}/${id}`)} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. onClick 이벤트로 구현하다가 onMouseDown 이벤트로 수정했어요. 상위 input의 blur 이벤트가 우선순위가 높아 먼저 적용 되어 onClick 이벤트가 실행되지 않았기에 가수 상세 페이지로 이동이 불가능 했었습니다. PC 환경에서는 마우스를 드래그하여 취소하는 행동이 불가능 해졌지만, 이에대해 고민을 해보았고, input blur로 얻을 수 있는 사용성의 이점이 더 크다고 판단해서 onMouseDown으로 타협했습니다. 💬 나름의 트레이드 오프라고 생각했는데, 다른 의견이 있다면 말씀해 주세요 |
||
aria-label={`${singer} 상세 페이지 바로가기`} | ||
$gap={16} | ||
$align="center" | ||
> | ||
<Thumbnail src={profileImageUrl} alt="" size="sm" /> | ||
<Singer aria-hidden>{singer}</Singer> | ||
</GoToDetail> | ||
</PreviewItem> | ||
))} | ||
</PreviewItemList> | ||
{!hasResult && <DefaultMessage>검색 결과가 없습니다</DefaultMessage>} | ||
</SheetContainer> | ||
); | ||
}; | ||
|
||
export default ResultSheet; | ||
|
||
const SheetContainer = styled.section` | ||
position: fixed; | ||
z-index: 2000; | ||
top: 70px; | ||
right: 12.33%; | ||
|
||
overflow-y: scroll; | ||
|
||
width: 340px; | ||
height: auto; | ||
max-height: 320px; | ||
padding: 0 16px 16px 16px; | ||
|
||
color: white; | ||
|
||
background-color: #121212; | ||
border-radius: 8px; | ||
box-shadow: 0px 0px 10px #ffffff49; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 'px' 빼주세용~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 추가로 빛 번지듯이 흰새긍로 shadow 생기는 게 다소 어색하다고 느껴져요. 도밥은 어떻게 생각하시나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
@media (max-width: ${({ theme }) => theme.breakPoints.xxl}) { | ||
right: 8.33%; | ||
} | ||
|
||
@media (max-width: ${({ theme }) => theme.breakPoints.md}) { | ||
right: 0%; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 '%' 빼주세요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 반영 완! |
||
width: 100%; | ||
min-height: 100%; | ||
} | ||
|
||
@media (max-width: ${({ theme }) => theme.breakPoints.xs}) { | ||
top: 60px; | ||
} | ||
|
||
@media (max-width: ${({ theme }) => theme.breakPoints.xxs}) { | ||
top: 50px; | ||
} | ||
`; | ||
|
||
const SheetTitle = styled(Flex)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 검색결과가 많을 경우에도 상단 타이틀은 고정되는 사용성을 위해 추가했습니다. |
||
position: sticky; | ||
Creative-Lee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
top: 0; | ||
left: 0; | ||
|
||
height: 50px; | ||
|
||
font-weight: 700; | ||
letter-spacing: 2px; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. letter-spacing 보다는 weight와 size로만 소제목 구성하는 게 좋을 것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제목이 너무 다닥다닥 붙어있는것 같아서 letter-spacing을 넣었는데, 지금보니깐 또 괜찮은것도 같네요 ㅋㅋㅋ |
||
|
||
background-color: #121212; | ||
`; | ||
|
||
const PreviewItemList = styled(Flex)``; | ||
|
||
const PreviewItem = styled(Flex)` | ||
cursor: pointer; | ||
|
||
width: 100%; | ||
height: 66px; | ||
padding: 8px; | ||
|
||
background-color: ${({ theme: { color } }) => color.black400}; | ||
border-radius: 4px; | ||
|
||
&:hover { | ||
background-color: ${({ theme: { color } }) => color.secondary}; | ||
} | ||
`; | ||
|
||
const Singer = styled.p` | ||
font-weight: 700; | ||
letter-spacing: 1px; | ||
`; | ||
const DefaultMessage = styled.p``; | ||
|
||
const GoToDetail = styled(Flex)` | ||
width: 100%; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Header from '@/shared/components/Layout/Header'; | ||
import SearchBar from './SearchBar'; | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
const meta = { | ||
component: SearchBar, | ||
title: 'SearchBar', | ||
} satisfies Meta<typeof SearchBar>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof SearchBar>; | ||
|
||
export const Default: Story = { | ||
render: () => <Header />, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msw worker.js 파일 경로 설정입니다.