Skip to content

Commit 6b5ad1c

Browse files
authored
Merge pull request #13971 from ethereum/shadcn-button-dropdown
Shadcn migration - button dropdown
2 parents e5dc289 + bb7e72e commit 6b5ad1c

File tree

12 files changed

+320
-190
lines changed

12 files changed

+320
-190
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@radix-ui/react-checkbox": "^1.1.1",
4040
"@radix-ui/react-compose-refs": "^1.1.0",
4141
"@radix-ui/react-dialog": "^1.1.1",
42+
"@radix-ui/react-dropdown-menu": "^2.1.1",
4243
"@radix-ui/react-navigation-menu": "^1.2.0",
4344
"@radix-ui/react-popover": "^1.1.1",
4445
"@radix-ui/react-portal": "^1.1.1",

src/components/ButtonDropdown.tsx

Lines changed: 54 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
import React, { MouseEvent, useState } from "react"
2-
import { useTranslation } from "next-i18next"
1+
import React, { useState } from "react"
32
import { MdMenu } from "react-icons/md"
4-
import {
5-
Button,
6-
ButtonProps,
7-
Menu,
8-
MenuButton,
9-
MenuItem,
10-
MenuList,
11-
} from "@chakra-ui/react"
123

13-
// Utils
4+
import { cn } from "@/lib/utils/cn"
145
import { trackCustomEvent } from "@/lib/utils/matomo"
156

16-
// Components
17-
import { BaseLink } from "./Link"
7+
import { Button } from "./ui/buttons/Button"
8+
import {
9+
DropdownMenu,
10+
DropdownMenuContent,
11+
DropdownMenuItem,
12+
DropdownMenuTrigger,
13+
} from "./ui/dropdown-menu"
14+
import { BaseLink } from "./ui/Link"
1815

1916
export interface ListItem {
2017
text: string
@@ -33,91 +30,74 @@ export interface List {
3330
items: Array<ListItem>
3431
}
3532

36-
export type ButtonDropdownProps = ButtonProps & {
33+
export type ButtonDropdownProps = {
3734
list: List
35+
className?: string
3836
}
3937

40-
const ButtonDropdown = ({ list, ...rest }: ButtonDropdownProps) => {
41-
const { t } = useTranslation("common")
38+
const ButtonDropdown = ({ list, className }: ButtonDropdownProps) => {
4239
const [selectedItem, setSelectedItem] = useState(list.text)
43-
const handleClick = (
44-
e: MouseEvent<HTMLElement>,
45-
item: ListItem,
46-
idx: number
47-
) => {
40+
const handleClick = (item: ListItem, idx: number) => {
4841
const { matomo, callback } = item
4942

5043
if (matomo) {
5144
trackCustomEvent(matomo)
5245
}
5346

5447
if (callback) {
55-
e.preventDefault()
5648
callback(idx)
5749
}
5850
setSelectedItem(item.text)
5951
}
6052

6153
return (
62-
<Menu matchWidth>
63-
<MenuButton
64-
as={Button}
65-
leftIcon={<MdMenu />}
66-
variant="outline"
67-
_active={{ bg: "transparent" }}
68-
{...rest}
69-
>
70-
{t(selectedItem)}
71-
</MenuButton>
72-
<MenuList
73-
py={2}
74-
borderRadius="base"
75-
border="1px"
76-
borderColor="text"
77-
bg="dropdownBackground"
78-
zIndex="popover"
54+
<DropdownMenu>
55+
<DropdownMenuTrigger asChild>
56+
<Button
57+
variant="outline"
58+
className={cn("flex justify-between", className)}
59+
>
60+
<MdMenu />
61+
<span className="flex-1 text-center">{selectedItem}</span>
62+
</Button>
63+
</DropdownMenuTrigger>
64+
<DropdownMenuContent
65+
className="w-[var(--radix-dropdown-menu-trigger-width)]"
66+
sideOffset={8}
7967
>
8068
{list.items.map((item, idx) => {
8169
const { text, href } = item
8270

83-
return href ? (
84-
<BaseLink
85-
key={idx}
86-
href={href!}
87-
isPartiallyActive={false}
88-
textDecor="none"
89-
color="text"
90-
fontWeight="normal"
91-
_hover={{ textDecor: "none", color: "primary.base" }}
92-
_focus={{ textDecor: "none", color: "primary.base" }}
93-
>
94-
<MenuItem
95-
as="span"
96-
onClick={(e) => handleClick(e, item, idx)}
97-
p={2}
98-
textAlign="center"
99-
justifyContent="center"
100-
bg="dropdownBackground"
101-
_hover={{
102-
color: "primary.base",
103-
bg: "dropdownBackgroundHover",
104-
}}
105-
_focus={{
106-
color: "primary.base",
107-
bg: "dropdownBackgroundHover",
108-
}}
71+
if (href) {
72+
return (
73+
<DropdownMenuItem
74+
key={item.text}
75+
className="justify-center"
76+
onClick={() => handleClick(item, idx)}
77+
asChild
10978
>
110-
{t(text)}
111-
</MenuItem>
112-
</BaseLink>
113-
) : (
114-
<MenuItem key={idx} onClick={(e) => handleClick(e, item, idx)}>
115-
{t(text)}
116-
</MenuItem>
79+
<BaseLink
80+
href={item.href!}
81+
className="text-body no-underline focus-visible:outline-0"
82+
>
83+
<span>{text}</span>
84+
</BaseLink>
85+
</DropdownMenuItem>
86+
)
87+
}
88+
89+
return (
90+
<DropdownMenuItem
91+
key={item.text}
92+
className="justify-center"
93+
onClick={() => handleClick(item, idx)}
94+
>
95+
<span>{text}</span>
96+
</DropdownMenuItem>
11797
)
11898
})}
119-
</MenuList>
120-
</Menu>
99+
</DropdownMenuContent>
100+
</DropdownMenu>
121101
)
122102
}
123103

src/components/FeedbackWidget/FixedDot.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const FixedDot = forwardRef<HTMLButtonElement, FixedDotProps>(
2323
data-testid="feedback-widget-button"
2424
aria-label={t("feedback-widget")}
2525
className={cn(
26-
"lg:mt-inherit sticky bottom-4 z-20 me-4 ms-auto flex size-12 items-center gap-0 rounded-full text-white shadow-table-item-box",
26+
"lg:mt-inherit sticky bottom-4 z-overlay me-4 ms-auto flex size-12 items-center gap-0 rounded-full text-white shadow-table-item-box",
2727
"transition-all duration-200 hover:shadow-none hover:transition-transform hover:duration-200",
2828
!suppressScale && "hover:scale-110",
2929
offsetBottom && "bottom-31 lg:bottom-4",

src/components/LeftNavBar/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ const LeftNavBar = ({
3232
>
3333
{dropdownLinks && (
3434
<div className="relative mb-8 flex items-end justify-end">
35-
<ButtonDropdown list={dropdownLinks} w="full" minW="240px" />
35+
<ButtonDropdown
36+
list={dropdownLinks}
37+
className="w-full min-w-[240px]"
38+
/>
3639
</div>
3740
)}
3841
<h2 className="mb-8 text-3xl leading-xs">
Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import pickBy from "lodash/pickBy"
22
import type { Meta, StoryObj } from "@storybook/react/*"
33

4-
import type { List as ButtonDropdownList } from "@/components/ButtonDropdown"
5-
64
import { viewportModes } from "../../../.storybook/modes"
75

86
import MdComponentSet from "."
@@ -34,8 +32,6 @@ const {
3432
hr: HR,
3533
pre: Pre,
3634
Page,
37-
MobileButton,
38-
MobileButtonDropdown,
3935
} = MdComponentSet
4036

4137
const Para = () => (
@@ -53,9 +49,6 @@ export const MdComponents: StoryObj = {
5349
<div className="mx-auto max-w-screen-lg">
5450
<Page>
5551
<ContentContainer>
56-
<MobileButton>
57-
<MobileButtonDropdown list={roadmapDropdownLinks} />
58-
</MobileButton>
5952
<Heading1>Heading1</Heading1>
6053
<Para />
6154
<Heading2>Heading2</Heading2>
@@ -75,55 +68,3 @@ export const MdComponents: StoryObj = {
7568
</div>
7669
),
7770
}
78-
79-
const roadmapDropdownLinks: ButtonDropdownList = {
80-
text: "nav-roadmap-options",
81-
ariaLabel: "nav-roadmap-options-alt",
82-
items: [
83-
{
84-
text: "nav-roadmap-home",
85-
href: "/roadmap/",
86-
matomo: {
87-
eventCategory: `Roadmap dropdown`,
88-
eventAction: `Clicked`,
89-
eventName: "clicked roadmap home",
90-
},
91-
},
92-
{
93-
text: "nav-roadmap-security",
94-
href: "/roadmap/security",
95-
matomo: {
96-
eventCategory: `Roadmap security dropdown`,
97-
eventAction: `Clicked`,
98-
eventName: "clicked roadmap security",
99-
},
100-
},
101-
{
102-
text: "nav-roadmap-scaling",
103-
href: "/roadmap/scaling",
104-
matomo: {
105-
eventCategory: `Roadmap scaling dropdown`,
106-
eventAction: `Clicked`,
107-
eventName: "clicked roadmap scaling home",
108-
},
109-
},
110-
{
111-
text: "nav-roadmap-user-experience",
112-
href: "/roadmap/user-experience/",
113-
matomo: {
114-
eventCategory: `Roadmap user experience dropdown`,
115-
eventAction: `Clicked`,
116-
eventName: "clicked roadmap user experience home",
117-
},
118-
},
119-
{
120-
text: "nav-roadmap-future-proofing",
121-
href: "/roadmap/future-proofing",
122-
matomo: {
123-
eventCategory: `Roadmap future-proofing dropdown`,
124-
eventAction: `Clicked`,
125-
eventName: "clicked roadmap future-proofing home",
126-
},
127-
},
128-
],
129-
}

src/components/MdComponents/index.tsx

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { ComponentProps, type HTMLAttributes } from "react"
1+
import { type HTMLAttributes } from "react"
22
import { Badge, Box, type BoxProps } from "@chakra-ui/react"
33

44
import type { ChildOnlyProp } from "@/lib/types"
55

6-
import ButtonDropdown, {
7-
type ButtonDropdownProps,
8-
} from "@/components/ButtonDropdown"
96
import Contributors from "@/components/Contributors"
107
import MarkdownImage from "@/components/MarkdownImage"
118
import TooltipLink from "@/components/TooltipLink"
@@ -155,32 +152,6 @@ export const ContentContainer = (props: Pick<BoxProps, "id" | "children">) => {
155152
)
156153
}
157154

158-
export const MobileButton = (props: ChildOnlyProp) => {
159-
return (
160-
<div
161-
className="sticky bottom-0 z-sticky w-full bg-background p-8 shadow-md lg:hidden"
162-
{...props}
163-
/>
164-
)
165-
}
166-
167-
export const StyledButtonDropdown = ({
168-
list,
169-
className,
170-
...rest
171-
}: HTMLAttributes<HTMLDivElement> & Pick<ButtonDropdownProps, "list">) => (
172-
<Flex className={cn("mb-8 items-end justify-end", className)} {...rest}>
173-
<ButtonDropdown list={list} w={{ base: "full", lg: "auto" }} minW="240px" />
174-
</Flex>
175-
)
176-
177-
export const MobileButtonDropdown = ({
178-
className,
179-
...props
180-
}: ComponentProps<typeof StyledButtonDropdown>) => (
181-
<StyledButtonDropdown className={cn("mb-0", className)} {...props} />
182-
)
183-
184155
// All custom React components
185156
export const reactComponents = {
186157
Badge,
@@ -196,11 +167,8 @@ export const reactComponents = {
196167
FeaturedText,
197168
GlossaryTooltip,
198169
InfoBanner,
199-
MobileButton,
200-
MobileButtonDropdown,
201170
Page,
202171
QuizWidget: StandaloneQuizWidget,
203-
StyledButtonDropdown,
204172
IssuesList,
205173
Title,
206174
YouTube,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import ButtonDropdown, { type ButtonDropdownProps } from "./ButtonDropdown"
2+
3+
const MobileButtonDropdown = (props: ButtonDropdownProps) => {
4+
return (
5+
<div className="sticky bottom-0 z-sticky w-full bg-background p-8 shadow-md lg:hidden">
6+
<ButtonDropdown {...props} className="w-full lg:w-auto" />
7+
</div>
8+
)
9+
}
10+
11+
export default MobileButtonDropdown

src/components/Staking/StakingConsiderations/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const StakingConsiderations = ({ page }: StakingConsiderationsProps) => {
7676

7777
return (
7878
<Flex flexDir={{ base: "column", md: "row" }}>
79-
<ButtonDropdown list={dropdownLinks} hideFrom={mdBp} />
79+
<ButtonDropdown list={dropdownLinks} className="mb-4 md:hidden" />
8080
{/* TODO: Improve a11y */}
8181
<Box flex={1} hideBelow={mdBp}>
8282
{!!pageData && (

0 commit comments

Comments
 (0)