Skip to content

Commit a7677a3

Browse files
authored
[#103] ✨ shared 컴포넌트 Header 개발 (#148)
* [#103] 💄 remove default margin-x in container component * [#103] ♻️ update user interface since type renewed * [#103] ♻️ update imageUrl field with string * [#103] ✨ add Header component * [#103] ✅ add Header Stories * [#103] 💄 update header layout * [#103] 💄 add currentPage style in Header * [#103] 💄 remove outline reset css for assesibility * Merge dev into feat/shared-component-header * [#103] 🐛 solve build error by changing URL type to ImageURL * [#103] 💄 change hover effect from bg color to text color
1 parent 09c0585 commit a7677a3

File tree

8 files changed

+123
-25
lines changed

8 files changed

+123
-25
lines changed

src/components/common/containers/Container.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { twMerge } from 'tailwind-merge'
1+
import { twMergeEx } from '@/lib/twMerge'
22

33
type ContainerProps<T extends React.ElementType> = {
44
as?: T
@@ -14,8 +14,8 @@ export const Container = <T extends React.ElementType = 'div'>({
1414
}: ContainerProps<T>): JSX.Element => {
1515
const Component = as || 'div'
1616

17-
const baseStyle = 'mx-auto max-w-1200'
18-
const containerStyle = twMerge(baseStyle, className)
17+
const baseStyle = 'max-w-1200'
18+
const containerStyle = twMergeEx(baseStyle, className)
1919

2020
return (
2121
<Component className={containerStyle} {...props}>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import clsx from 'clsx'
2+
3+
import { Link } from '@/components/common/button'
4+
import { Container } from '@/components/common/containers'
5+
import { Logo } from '@/components/common/logo'
6+
7+
import { HeaderUserMenu } from './HeaderUserMenu'
8+
9+
const headerTabs = [
10+
{ label: '포트폴리오', link: '/portfolio' },
11+
{ label: '프로젝트', link: '/project' },
12+
{ label: '팀원찾기', link: '/team' },
13+
{ label: '커뮤니티', link: '/community' },
14+
]
15+
16+
type HeaderProps = {
17+
isAuthenticated: boolean
18+
user: User | null
19+
currentPage?: string
20+
}
21+
22+
const renderUserMenu = (isAuthenticated: boolean, user: User | null) => {
23+
return isAuthenticated && user ? (
24+
<HeaderUserMenu user={user} />
25+
) : (
26+
<Container className='flex items-center justify-between gap-12'>
27+
<Link href='/auth/sign-in' variant='outlined' size='lg'>
28+
로그인
29+
</Link>
30+
<Link href='/auth/sign-up' size='lg'>
31+
회원가입
32+
</Link>
33+
</Container>
34+
)
35+
}
36+
37+
export const Header = ({
38+
isAuthenticated,
39+
user = null,
40+
currentPage = '/',
41+
}: HeaderProps): JSX.Element => {
42+
return (
43+
<header className='flex w-full justify-center'>
44+
<Container className='flex h-72 w-full items-center justify-between gap-188'>
45+
<Logo />
46+
<Container className='flex flex-grow items-center justify-between'>
47+
<Container className='flex items-center justify-between gap-40'>
48+
{headerTabs.map(headerTab => (
49+
<Link
50+
href={headerTab.link}
51+
variant='text'
52+
size='lg'
53+
className={clsx('w-118 hover:text-primary-normal', {
54+
'text-primary-normal': currentPage === headerTab.link,
55+
})}
56+
key={headerTab.label}
57+
>
58+
{headerTab.label}
59+
</Link>
60+
))}
61+
</Container>
62+
{renderUserMenu(isAuthenticated, user)}
63+
</Container>
64+
</Container>
65+
</header>
66+
)
67+
}

src/components/shared/header/HeaderUserMenu.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@ import { Avatar } from '@/components/common/avatar'
66
import { Dropdown } from '@/components/common/dropdown'
77
import { Highlight, Text } from '@/components/common/text'
88

9-
type Image = string
10-
119
interface UserMenuProps {
12-
user: {
13-
id: Id
14-
name: Name
15-
image: Image
16-
}
10+
user: User
1711
}
1812

1913
type Option = {
@@ -25,25 +19,25 @@ type Option = {
2519
const linkOptions: Option[] = [
2620
{
2721
label: '포트폴리오 관리',
28-
link: '/portfolio',
22+
link: '/my-page/portfolio',
2923
icon: <IcWorkbag />,
3024
},
3125
{
3226
label: '나의 프로젝트',
33-
link: '/project',
27+
link: '/my-page/project',
3428
icon: <IcCodefile />,
3529
},
3630
{
3731
label: '설정',
38-
link: '/setting',
32+
link: '/my-page/setting',
3933
icon: <IcSetting />,
4034
},
4135
]
4236

4337
const itemClass = 'h-48 w-full gap-8'
4438

4539
export const HeaderUserMenu = ({
46-
user: { image, name },
40+
user: { imageUrl, name },
4741
}: UserMenuProps): JSX.Element => {
4842
const handleLogout = async () => {
4943
console.log('로그아웃')
@@ -52,10 +46,10 @@ export const HeaderUserMenu = ({
5246
return (
5347
<Dropdown>
5448
<Dropdown.Trigger>
55-
<UserMenuTrigger image={image} />
49+
<UserMenuTrigger image={imageUrl} />
5650
</Dropdown.Trigger>
5751
<Dropdown.Menu alignment={'right'} className={'justify-start p-0'}>
58-
<UserMenuHeader image={image} name={name} />
52+
<UserMenuHeader image={imageUrl} name={name} />
5953
<hr className='w-full text-gray-200' />
6054
<div className={'w-full p-16'}>
6155
{linkOptions.map(option => (
@@ -78,7 +72,7 @@ export const HeaderUserMenu = ({
7872
)
7973
}
8074

81-
const UserMenuTrigger = ({ image }: { image: Image }) => (
75+
const UserMenuTrigger = ({ image }: { image: ImageURL }) => (
8276
<div className={'flex items-center gap-12'}>
8377
<Text.Body variant={'body2'} className={'font-medium'}>
8478
환영합니다!
@@ -87,7 +81,7 @@ const UserMenuTrigger = ({ image }: { image: Image }) => (
8781
</div>
8882
)
8983

90-
const UserMenuHeader = ({ image, name }: { image: Image; name: Name }) => (
84+
const UserMenuHeader = ({ image, name }: { image: ImageURL; name: Name }) => (
9185
<div className={'flex items-center gap-12 p-16'}>
9286
<Avatar image={image} size={48} />
9387
<div>
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Header } from './Header'
12
import { HeaderUserMenu } from './HeaderUserMenu'
23

3-
export { HeaderUserMenu }
4+
export { Header, HeaderUserMenu }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Meta, StoryObj } from '@storybook/react'
2+
3+
import { Header } from '@/components/shared/header'
4+
5+
export default {
6+
title: 'Shared/Header/Header',
7+
component: Header,
8+
parameters: {
9+
layout: 'fullscreen',
10+
},
11+
} as Meta
12+
13+
export const Default: StoryObj = {
14+
args: {
15+
isAuthenticated: false,
16+
user: null,
17+
currentPage: '/project',
18+
},
19+
}
20+
export const WithUserMenu: StoryObj = {
21+
args: {
22+
isAuthenticated: true,
23+
user: {
24+
id: '2',
25+
name: 'Alexander',
26+
imageUrl: 'https://picsum.photos/250/250',
27+
},
28+
currentPage: '/portfolio',
29+
},
30+
}
31+
export const LoggedInWithoutUser: StoryObj = {
32+
args: {
33+
isAuthenticated: true,
34+
user: null,
35+
currentPage: '/team',
36+
},
37+
}

src/stories/shared/header/HeaderUserMenu.stories.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const Default: StoryObj = {
1515
user: {
1616
id: '1',
1717
name: 'John Doe',
18-
image: 'https://picsum.photos/250/250',
18+
imageUrl: 'https://picsum.photos/250/250',
1919
},
2020
},
2121
}
@@ -24,7 +24,7 @@ export const WithLongName: StoryObj = {
2424
user: {
2525
id: '2',
2626
name: 'Alexander the Great',
27-
image: 'https://picsum.photos/250/250',
27+
imageUrl: 'https://picsum.photos/250/250',
2828
},
2929
},
3030
}
@@ -33,7 +33,7 @@ export const WithoutImage: StoryObj = {
3333
user: {
3434
id: '3',
3535
name: 'Jane Doe',
36-
image: '',
36+
imageUrl: '',
3737
},
3838
},
3939
}

src/styles/globals.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ button,
1111
input,
1212
select {
1313
margin: 0;
14-
outline: 0;
1514
cursor: pointer;
1615
}
1716

src/types/api/Global.types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type Id = number
77
/**
88
URL
99
*/
10-
type URL = string
10+
type ImageURL = string
1111
/**
1212
JWT 또는 인증 토큰
1313
*/
@@ -64,7 +64,7 @@ type User = {
6464
email: Email
6565
name: Name
6666
nickname: Nickname
67-
imageUrl: URL
67+
imageUrl: ImageURL
6868
}
6969

7070
// MemberInfo: 일부 사용자 정보를 제외한 타입

0 commit comments

Comments
 (0)