Skip to content

Commit b5cb846

Browse files
authored
feat(Dropdown): restyle dropdown menu with modern appearance (#82)
1 parent 83f71b1 commit b5cb846

File tree

2 files changed

+109
-80
lines changed

2 files changed

+109
-80
lines changed

src/Dropdown.stories.tsx

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { faFilter } from '@fortawesome/free-solid-svg-icons'
1+
import { faFilter, faList } from '@fortawesome/free-solid-svg-icons'
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
33
import React, { useState } from 'react'
44
import { twMerge } from 'tailwind-merge'
@@ -15,7 +15,29 @@ export const Default = () => {
1515
not displayed.
1616
</div>
1717
<Dropdown
18-
trigger="Test"
18+
trigger="Test Content"
19+
items={[
20+
{
21+
label: 'Element 1 long',
22+
onClick: () => alert('Element 1 clicked'),
23+
},
24+
{ label: 'Element 2', onClick: () => alert('Element 2 clicked') },
25+
{
26+
label: 'Element 3 short',
27+
onClick: () => alert('Element 3 clicked'),
28+
},
29+
{ label: 'Element 4', onClick: () => alert('Element 4 clicked') },
30+
]}
31+
/>
32+
</div>
33+
)
34+
}
35+
36+
export const CustomIcon = () => {
37+
return (
38+
<div>
39+
<Dropdown
40+
trigger="Test Content"
1941
items={[
2042
{
2143
label: 'Element 1 long',
@@ -28,6 +50,7 @@ export const Default = () => {
2850
},
2951
{ label: 'Element 4', onClick: () => alert('Element 4 clicked') },
3052
]}
53+
triggerIcon={faList}
3154
/>
3255
</div>
3356
)

src/Dropdown.tsx

+84-78
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
13
import * as RadixDropdown from '@radix-ui/react-dropdown-menu'
24
import React from 'react'
35
import { twMerge } from 'tailwind-merge'
@@ -20,12 +22,11 @@ interface DropdownProps {
2022
test?: string
2123
}
2224
trigger: string | React.ReactNode
25+
triggerIcon?: IconDefinition
2326
items?: Item[]
2427
activeItems?: string[]
2528
groups?: Item[][]
2629
className?: {
27-
triggerOverride?: string
28-
viewportOverride?: string
2930
trigger?: string
3031
triggerDisabled?: string
3132
viewport?: string
@@ -52,6 +53,7 @@ export interface DropdownWithGroupsProps extends DropdownProps {
5253
* @param id - The id of the dropdown.
5354
* @param data - The object of data attributes that can be used for testing (e.g. data-test or data-cy)
5455
* @param trigger - The content of the trigger button or a custom trigger component to replace the default button.
56+
* @param triggerIcon - The icon that is displayed next to the trigger content.
5557
* @param items - The items that are displayed in the dropdown menu. This attribute should not be set, if groups are used.
5658
* @param activeItems - List of labels that should be considered active. This attribute has a similar function as the "select" attribute on the item props and should not be used at the same time.
5759
* @param groups - The groups of items that are displayed in the dropdown menu. This attribute should not be set, if items are used.
@@ -63,77 +65,13 @@ export function Dropdown({
6365
id,
6466
data,
6567
trigger,
68+
triggerIcon,
6669
items,
6770
activeItems,
6871
groups,
6972
className,
7073
disabled = false,
7174
}: DropdownWithItemsProps | DropdownWithGroupsProps) {
72-
const DropdownItem = ({
73-
id,
74-
data,
75-
label,
76-
active = false,
77-
onClick,
78-
shorting,
79-
selected,
80-
className,
81-
}: {
82-
id?: string
83-
data?: {
84-
cy?: string
85-
test?: string
86-
}
87-
label: string | React.ReactNode
88-
active?: boolean
89-
onClick: () => void
90-
shorting?: string
91-
selected?: boolean
92-
className?: {
93-
override?: string
94-
root?: string
95-
active?: string
96-
}
97-
}) => {
98-
if (typeof label === 'string') {
99-
return (
100-
<RadixDropdown.Item
101-
id={id}
102-
data-cy={data?.cy}
103-
data-test={data?.test}
104-
className={twMerge(
105-
className?.override,
106-
`flex flex-row rounded px-2 py-0.5 hover:cursor-pointer hover:bg-primary-60 sm:hover:!text-white`,
107-
active && twMerge('font-bold', className?.active),
108-
className?.root
109-
)}
110-
onClick={onClick}
111-
>
112-
<div
113-
className={twMerge(
114-
'flex-1',
115-
selected && twMerge('font-bold', className?.active)
116-
)}
117-
>
118-
{label}
119-
</div>
120-
{shorting && <div className="ml-6">{shorting}</div>}
121-
</RadixDropdown.Item>
122-
)
123-
}
124-
return (
125-
<RadixDropdown.Item
126-
id={id}
127-
data-cy={data?.cy}
128-
data-test={data?.test}
129-
onClick={onClick}
130-
className={twMerge('rounded-md', className?.root)}
131-
>
132-
{label}
133-
</RadixDropdown.Item>
134-
)
135-
}
136-
13775
return (
13876
<RadixDropdown.Root>
13977
{typeof trigger === 'string' ? (
@@ -142,15 +80,18 @@ export function Dropdown({
14280
data-cy={data?.cy}
14381
data-test={data?.test}
14482
className={twMerge(
145-
className?.triggerOverride,
146-
`rounded-md border border-solid border-uzh-grey-60 px-2 py-1 hover:bg-primary-20`,
147-
disabled && 'cursor-not-allowed text-gray-500 hover:bg-white',
148-
className?.trigger,
149-
className?.triggerDisabled
83+
'inline-flex h-7 items-center justify-between gap-3 rounded-md border',
84+
'bg-white py-1.5 pl-2 pr-2 shadow-sm hover:bg-primary-20 sm:hover:text-primary',
85+
disabled &&
86+
'hover:bg-none, sm:hover:text-none cursor-not-allowed bg-uzh-grey-20 opacity-70 shadow-sm',
87+
className?.trigger
15088
)}
15189
disabled={disabled}
15290
>
153-
{trigger}
91+
<div>{trigger}</div>
92+
{triggerIcon && (
93+
<FontAwesomeIcon icon={triggerIcon} size="sm" className="mb-0.5" />
94+
)}
15495
</RadixDropdown.Trigger>
15596
) : (
15697
<RadixDropdown.Trigger
@@ -159,7 +100,6 @@ export function Dropdown({
159100
data-test={data?.test}
160101
disabled={disabled}
161102
className={twMerge(
162-
className?.triggerOverride,
163103
disabled && 'cursor-not-allowed text-gray-500 hover:bg-white',
164104
className?.trigger
165105
)}
@@ -170,17 +110,16 @@ export function Dropdown({
170110

171111
<RadixDropdown.Content
172112
className={twMerge(
173-
className?.viewportOverride,
174-
'rounded-md bg-primary-20 p-1.5',
113+
'rounded-lg border border-solid p-1 shadow-md',
175114
className?.viewport
176115
)}
177116
>
178117
<RadixDropdown.Arrow
179-
className={twMerge('fill-primary-80 opacity-25', className?.arrow)}
118+
className={twMerge('fill-gray-500 opacity-25', className?.arrow)}
180119
/>
181120

182121
{items && (
183-
<div className="border-b border-solid border-uzh-grey-100 pb-1 pt-1 first:pt-0 last:border-b-0 last:pb-0">
122+
<div className="border-b border-solid border-uzh-grey-100 pb-1 first:pt-0 last:border-b-0 last:pb-0">
184123
{items.map((item, index) => (
185124
<DropdownItem
186125
key={index}
@@ -228,4 +167,71 @@ export function Dropdown({
228167
)
229168
}
230169

170+
const DropdownItem = ({
171+
id,
172+
data,
173+
label,
174+
active = false,
175+
onClick,
176+
shorting,
177+
selected,
178+
className,
179+
}: {
180+
id?: string
181+
data?: {
182+
cy?: string
183+
test?: string
184+
}
185+
label: string | React.ReactNode
186+
active?: boolean
187+
onClick: () => void
188+
shorting?: string
189+
selected?: boolean
190+
className?: {
191+
override?: string
192+
root?: string
193+
active?: string
194+
}
195+
}) => {
196+
if (typeof label === 'string') {
197+
return (
198+
<RadixDropdown.Item
199+
id={id}
200+
data-cy={data?.cy}
201+
data-test={data?.test}
202+
className={twMerge(
203+
className?.override,
204+
`flex flex-row rounded px-2 py-0.5 hover:cursor-pointer hover:bg-primary-20 sm:hover:text-primary`,
205+
active && twMerge('font-bold', className?.active),
206+
className?.root
207+
)}
208+
onClick={onClick}
209+
>
210+
<div
211+
className={twMerge(
212+
'flex-1',
213+
selected && twMerge('font-bold', className?.active)
214+
)}
215+
>
216+
{label}
217+
</div>
218+
219+
{shorting && <div className="ml-6">{shorting}</div>}
220+
</RadixDropdown.Item>
221+
)
222+
}
223+
224+
return (
225+
<RadixDropdown.Item
226+
id={id}
227+
data-cy={data?.cy}
228+
data-test={data?.test}
229+
onClick={onClick}
230+
className={twMerge('rounded-md', className?.root)}
231+
>
232+
{label}
233+
</RadixDropdown.Item>
234+
)
235+
}
236+
231237
export default Dropdown

0 commit comments

Comments
 (0)