-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Jeongwoo Park <[email protected]>
- Loading branch information
1 parent
f462f50
commit c3b226b
Showing
24 changed files
with
843 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,3 @@ export const Default: Story = { | |
applicantId: 1, | ||
}, | ||
}; | ||
|
||
Default.parameters = { | ||
docs: {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 8 additions & 7 deletions
15
frontend/src/components/_common/atoms/DropdownItem/DropdownItem.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
frontend/src/components/_common/atoms/DropdownSubTrigger/DropdownSubTrigger.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { DropdownProvider } from '@contexts/DropdownContext'; | ||
|
||
import DropdownItemRenderer from '@components/_common/molecules/DropdownItemRenderer'; | ||
import type { DropdownItemType } from '@components/_common/molecules/DropdownItemRenderer'; | ||
import DropdownSubTrigger from '.'; | ||
|
||
const meta = { | ||
title: 'Common/Atoms/DropdownSubTrigger', | ||
component: DropdownSubTrigger, | ||
parameters: { | ||
layout: 'centered', | ||
docs: { | ||
description: { | ||
component: | ||
'DropdownSubTrigger 컴포넌트는 하위 메뉴를 가진 드롭다운 아이템을 표시합니다. 내부적으로 DropdownItemRenderer을 사용하여 중첩된 메뉴 구조를 지원합니다.', | ||
}, | ||
}, | ||
}, | ||
tags: ['autodocs'], | ||
argTypes: { | ||
item: { | ||
description: '드롭다운 아이템의 텍스트', | ||
control: 'text', | ||
}, | ||
size: { | ||
description: '드롭다운 아이템의 크기', | ||
control: { type: 'radio' }, | ||
options: ['sm', 'md'], | ||
}, | ||
isHighlight: { | ||
description: '아이템 강조 여부', | ||
control: 'boolean', | ||
}, | ||
hasSeparate: { | ||
description: '구분선 표시 여부', | ||
control: 'boolean', | ||
}, | ||
placement: { | ||
description: '하위 메뉴의 위치', | ||
control: { type: 'radio' }, | ||
options: ['left', 'right'], | ||
}, | ||
children: { | ||
description: '하위 메뉴 아이템들 (DropdownItemRenderer으로 구성)', | ||
control: 'object', | ||
}, | ||
}, | ||
decorators: [ | ||
(Story) => ( | ||
<div style={{ width: '150px' }}> | ||
<DropdownProvider> | ||
<Story /> | ||
</DropdownProvider> | ||
</div> | ||
), | ||
], | ||
} satisfies Meta<typeof DropdownSubTrigger>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
const sampleSubItems: DropdownItemType[] = [ | ||
{ | ||
id: 1, | ||
name: 'Subitem 1', | ||
type: 'clickable', | ||
onClick: ({ targetProcessId }) => alert(`Clicked Subitem 1, processId: ${targetProcessId}`), | ||
}, | ||
{ | ||
id: 2, | ||
name: 'Subitem 2', | ||
type: 'clickable', | ||
onClick: ({ targetProcessId }) => alert(`Clicked Subitem 2, processId: ${targetProcessId}`), | ||
}, | ||
{ | ||
id: 3, | ||
name: 'Nested Submenu', | ||
type: 'subTrigger', | ||
items: [ | ||
{ | ||
id: 4, | ||
name: 'Nested Subitem', | ||
type: 'clickable', | ||
onClick: ({ targetProcessId }) => alert(`Clicked Nested Subitem, processId: ${targetProcessId}`), | ||
}, | ||
], | ||
}, | ||
]; | ||
|
||
export const Default: Story = { | ||
args: { | ||
item: 'Main Item', | ||
size: 'md', | ||
isHighlight: false, | ||
hasSeparate: false, | ||
placement: 'right', | ||
children: <DropdownItemRenderer items={sampleSubItems} />, | ||
}, | ||
}; | ||
|
||
export const SmallSize: Story = { | ||
args: { | ||
...Default.args, | ||
size: 'sm', | ||
}, | ||
}; | ||
|
||
export const Highlighted: Story = { | ||
args: { | ||
...Default.args, | ||
isHighlight: true, | ||
}, | ||
}; | ||
|
||
export const WithSeparator: Story = { | ||
args: { | ||
...Default.args, | ||
hasSeparate: true, | ||
}, | ||
}; | ||
|
||
export const LeftPlacement: Story = { | ||
args: { | ||
...Default.args, | ||
placement: 'left', | ||
}, | ||
}; | ||
|
||
export const ComplexNestedStructure: Story = { | ||
args: { | ||
...Default.args, | ||
children: ( | ||
<DropdownItemRenderer | ||
items={[ | ||
...sampleSubItems, | ||
{ | ||
id: 5, | ||
name: 'Another Submenu', | ||
type: 'subTrigger', | ||
items: [ | ||
{ | ||
id: 6, | ||
name: 'Deep Nested Item 1', | ||
type: 'clickable', | ||
onClick: ({ targetProcessId }) => alert(`Clicked Deep Nested Item 1, processId: ${targetProcessId}`), | ||
}, | ||
{ | ||
id: 7, | ||
name: 'Deep Nested Item 2', | ||
type: 'clickable', | ||
onClick: ({ targetProcessId }) => alert(`Clicked Deep Nested Item 2, processId: ${targetProcessId}`), | ||
isHighlight: true, | ||
}, | ||
], | ||
}, | ||
]} | ||
/> | ||
), | ||
}, | ||
}; |
59 changes: 59 additions & 0 deletions
59
frontend/src/components/_common/atoms/DropdownSubTrigger/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { PropsWithChildren, useRef, useState } from 'react'; | ||
import { HiChevronRight } from 'react-icons/hi'; | ||
import S from './style'; | ||
|
||
export interface DropdownSubTriggerProps extends PropsWithChildren { | ||
item: string; | ||
size: 'sm' | 'md'; | ||
isHighlight?: boolean; | ||
placement?: 'left' | 'right'; | ||
hasSeparate?: boolean; | ||
} | ||
|
||
export default function DropdownSubTrigger({ | ||
item, | ||
size, | ||
isHighlight = false, | ||
hasSeparate = false, | ||
placement = 'right', | ||
children, | ||
}: DropdownSubTriggerProps) { | ||
const [isSubOpen, setIsSubOpen] = useState(false); | ||
const ref = useRef<HTMLDivElement | null>(null); | ||
|
||
const handleMouseOver = (e: React.MouseEvent<HTMLDivElement>) => { | ||
e.stopPropagation(); | ||
setIsSubOpen(true); | ||
}; | ||
|
||
const handleMouseLeave = () => { | ||
setIsSubOpen(false); | ||
}; | ||
|
||
return ( | ||
<S.Item | ||
ref={ref} | ||
size={size} | ||
onMouseOver={handleMouseOver} | ||
onMouseLeave={handleMouseLeave} | ||
isHighlight={isHighlight} | ||
hasSeparate={hasSeparate} | ||
> | ||
{item} | ||
<HiChevronRight size={size === 'sm' ? 16 : 18} /> | ||
{isSubOpen && ( | ||
<S.SubItemBoundary | ||
placement={placement} | ||
width={ref.current?.offsetWidth || 0} | ||
> | ||
<S.SubItemContainer | ||
size={size} | ||
width={ref.current?.offsetWidth || 0} | ||
> | ||
{children} | ||
</S.SubItemContainer> | ||
</S.SubItemBoundary> | ||
)} | ||
</S.Item> | ||
); | ||
} |
Oops, something went wrong.