-
Notifications
You must be signed in to change notification settings - Fork 4
[Feat] BannerCard 컴포넌트 제작 #25
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
The head ref may contain hidden characters: "23-feat/BannerCard-\uCEF4\uD3EC\uB10C\uD2B8-\uAD6C\uD604"
Changes from all commits
2a7ffee
8474a9d
664c57b
4502ec2
75d2ed5
fc32a67
5d1c574
2e1d043
ac19cf4
7e5545b
e8be079
da4ce8d
aeae252
0421190
52a945d
aab5da3
19c7936
642230f
d2ae872
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 |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { default as GearIcon } from './gear.svg'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import { GearIcon } from '@shared/assets/icons/banner'; | ||
| import { Button } from '@shared/ui/Button/Button'; | ||
| import { bannerCardVariants } from './BannerCard.variants'; | ||
| import type { ComponentPropsWithoutRef, ReactNode } from 'react'; | ||
| import type { VariantProps } from 'tailwind-variants'; | ||
|
|
||
| const { base, frame, textWrapper, title, description, imageWrapper, image } = bannerCardVariants(); | ||
|
|
||
| export type BannerCardProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & | ||
| VariantProps<typeof bannerCardVariants> & { | ||
| title: ReactNode; | ||
| description: string; | ||
| buttonText?: string; | ||
| onClick?: () => void; | ||
| }; | ||
|
|
||
| export const BannerCard = ({ | ||
| title: titleContent = ( | ||
| <> | ||
| 중고 전자기기 | ||
| <br /> | ||
| 구매하기 | ||
| </> | ||
| ), | ||
| description: descriptionText = '설명 최대 길이 2줄', | ||
| buttonText = '바로가기', | ||
| onClick, | ||
| className, | ||
| ...props | ||
| }: BannerCardProps) => { | ||
| return ( | ||
| <div | ||
| data-testid="banner-card" | ||
| className={base({ className })} | ||
| role="button" | ||
| aria-label="banner-card" | ||
| tabIndex={0} | ||
| onClick={onClick} | ||
| onKeyDown={(e) => { | ||
| if (e.key === 'Enter' || e.key === ' ') { | ||
| e.preventDefault(); | ||
| onClick?.(); | ||
| } | ||
| }} | ||
| {...props} | ||
| > | ||
|
Comment on lines
+32
to
+46
Member
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. 버튼 클릭 시 stopPropagation()으로 부모 이벤트 멈추고, 다시 onClick() 메소드를 호출하고 있는 상황 같은데 바로가기를 눌렀을 때 이동할 것인지 카드 전체를 눌렀을 때 이동할 것인지 고려하고 한쪽은 onClick() 지워주시면 좋을 것 같아요!
Collaborator
Author
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. 뭔가 카드 클릭, 버튼 클릭하여 이동하는 것을 모두 허용하게 하려 해서 약간 꼬였던 것 같습니다! 감사합니다! |
||
| <div className={frame()}> | ||
| <div className={textWrapper()}> | ||
| <p className={title()}>{titleContent}</p> | ||
|
|
||
| <p className={description()}>{descriptionText}</p> | ||
|
|
||
| <Button size="auto" className="w-[104px]"> | ||
| {buttonText} | ||
| </Button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className={imageWrapper()}> | ||
| <img src={GearIcon} alt="" className={image()} /> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
Member
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. 정의된 디자인 토큰에 있는 값들은 디자인 토큰 값을 사용하면 좋을 것 같아요! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import { tv } from 'tailwind-variants'; | ||
|
|
||
| export const bannerCardVariants = tv({ | ||
| base: [ | ||
| 'group', | ||
| 'relative', | ||
| 'flex', | ||
| 'flex-col', | ||
| 'justify-end', | ||
| 'items-start', | ||
|
|
||
| 'w-[427px]', | ||
| 'h-[384px]', | ||
|
|
||
| 'px-[var(--padding-xl)]', | ||
| 'py-[27px]', | ||
|
|
||
| 'gap-[var(--spacing-xxs)]', | ||
|
|
||
| 'rounded-[var(--radius-xl)]', | ||
|
|
||
| 'bg-white', | ||
| 'bg-[linear-gradient(0deg,rgba(4,217,181,0)_0%,rgba(0,179,155,0.2)_71.11%)]', | ||
|
|
||
| 'cursor-pointer', | ||
| 'outline-none', | ||
| ], | ||
|
|
||
| slots: { | ||
| frame: [ | ||
| 'flex', | ||
| 'flex-col', | ||
| 'items-start', | ||
|
|
||
| 'gap-[var(--spacing-xs)]', | ||
|
|
||
| 'self-stretch', | ||
|
|
||
| 'h-[330px]', | ||
| ], | ||
|
|
||
| textWrapper: [ | ||
| 'relative', | ||
| 'z-10', | ||
| 'flex', | ||
| 'flex-col', | ||
| 'items-start', | ||
|
|
||
| 'gap-[var(--spacing-xs)]', | ||
|
|
||
| 'self-stretch', | ||
| ], | ||
|
|
||
| title: ['typo-title-3', 'text-black'], | ||
|
|
||
| description: ['typo-body-1', 'text-black', 'overflow-hidden', 'text-ellipsis', 'line-clamp-2'], | ||
|
|
||
| imageWrapper: ['absolute', 'right-0', 'bottom-0', 'z-0', 'pointer-events-none'], | ||
|
|
||
| image: [ | ||
| 'w-[213px]', | ||
| 'h-[213px]', | ||
| 'object-cover', | ||
|
|
||
| 'transition-all', | ||
| 'duration-300', | ||
|
|
||
| 'group-hover:w-[307px]', | ||
| 'group-hover:h-[307px]', | ||
| ], | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { BannerCard } from './BannerCard'; | ||
| export type { BannerCardProps } from './BannerCard'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { Profile } from './Profile'; | ||
| export type { ProfileProps } from './Profile'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { BannerCard } from '@shared/ui/BannerCard'; | ||
| import { render, screen } from '@testing-library/react'; | ||
| import { describe, it, expect, vi } from 'vitest'; | ||
|
|
||
| describe('BannerCard', () => { | ||
| it('배너 카드 렌더링', () => { | ||
| render(<BannerCard />); | ||
| expect(screen.getByTestId('banner-card')).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('타이틀 텍스트 렌더링', () => { | ||
| render(<BannerCard />); | ||
| expect(screen.getByText(/중고 전자기기/)).toBeInTheDocument(); | ||
| expect(screen.getByText(/구매하기/)).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('버튼 렌더링', () => { | ||
| render(<BannerCard />); | ||
| expect(screen.getByRole('button', { name: '바로가기' })).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('버튼 클릭 시 onClick 호출', () => { | ||
| const onClick = vi.fn(); | ||
| render(<BannerCard onClick={onClick} />); | ||
|
|
||
| screen.getByRole('button', { name: '바로가기' }).click(); | ||
| expect(onClick).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('className 전달', () => { | ||
| render(<BannerCard className="custom-class" />); | ||
| expect(screen.getByTestId('banner-card')).toHaveClass('custom-class'); | ||
| }); | ||
| }); |
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.
이미지를 자세히보니 워터마크가 보이는 것 같아요
다른 이미지를 사용해야 할 것 같습니다 피그마에 요청하였으니 추후에 수정 부탁드립니다!
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.
저도 보면서 뭐지 했는데 감사합니다.! ㅎ