Skip to content

Commit

Permalink
Merge pull request #13971 from ethereum/shadcn-button-dropdown
Browse files Browse the repository at this point in the history
Shadcn migration - button dropdown
  • Loading branch information
wackerow authored Sep 30, 2024
2 parents e5dc289 + bb7e72e commit 6b5ad1c
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 190 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-compose-refs": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-portal": "^1.1.1",
Expand Down
128 changes: 54 additions & 74 deletions src/components/ButtonDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import React, { MouseEvent, useState } from "react"
import { useTranslation } from "next-i18next"
import React, { useState } from "react"
import { MdMenu } from "react-icons/md"
import {
Button,
ButtonProps,
Menu,
MenuButton,
MenuItem,
MenuList,
} from "@chakra-ui/react"

// Utils
import { cn } from "@/lib/utils/cn"
import { trackCustomEvent } from "@/lib/utils/matomo"

// Components
import { BaseLink } from "./Link"
import { Button } from "./ui/buttons/Button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "./ui/dropdown-menu"
import { BaseLink } from "./ui/Link"

export interface ListItem {
text: string
Expand All @@ -33,91 +30,74 @@ export interface List {
items: Array<ListItem>
}

export type ButtonDropdownProps = ButtonProps & {
export type ButtonDropdownProps = {
list: List
className?: string
}

const ButtonDropdown = ({ list, ...rest }: ButtonDropdownProps) => {
const { t } = useTranslation("common")
const ButtonDropdown = ({ list, className }: ButtonDropdownProps) => {
const [selectedItem, setSelectedItem] = useState(list.text)
const handleClick = (
e: MouseEvent<HTMLElement>,
item: ListItem,
idx: number
) => {
const handleClick = (item: ListItem, idx: number) => {
const { matomo, callback } = item

if (matomo) {
trackCustomEvent(matomo)
}

if (callback) {
e.preventDefault()
callback(idx)
}
setSelectedItem(item.text)
}

return (
<Menu matchWidth>
<MenuButton
as={Button}
leftIcon={<MdMenu />}
variant="outline"
_active={{ bg: "transparent" }}
{...rest}
>
{t(selectedItem)}
</MenuButton>
<MenuList
py={2}
borderRadius="base"
border="1px"
borderColor="text"
bg="dropdownBackground"
zIndex="popover"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className={cn("flex justify-between", className)}
>
<MdMenu />
<span className="flex-1 text-center">{selectedItem}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[var(--radix-dropdown-menu-trigger-width)]"
sideOffset={8}
>
{list.items.map((item, idx) => {
const { text, href } = item

return href ? (
<BaseLink
key={idx}
href={href!}
isPartiallyActive={false}
textDecor="none"
color="text"
fontWeight="normal"
_hover={{ textDecor: "none", color: "primary.base" }}
_focus={{ textDecor: "none", color: "primary.base" }}
>
<MenuItem
as="span"
onClick={(e) => handleClick(e, item, idx)}
p={2}
textAlign="center"
justifyContent="center"
bg="dropdownBackground"
_hover={{
color: "primary.base",
bg: "dropdownBackgroundHover",
}}
_focus={{
color: "primary.base",
bg: "dropdownBackgroundHover",
}}
if (href) {
return (
<DropdownMenuItem
key={item.text}
className="justify-center"
onClick={() => handleClick(item, idx)}
asChild
>
{t(text)}
</MenuItem>
</BaseLink>
) : (
<MenuItem key={idx} onClick={(e) => handleClick(e, item, idx)}>
{t(text)}
</MenuItem>
<BaseLink
href={item.href!}
className="text-body no-underline focus-visible:outline-0"
>
<span>{text}</span>
</BaseLink>
</DropdownMenuItem>
)
}

return (
<DropdownMenuItem
key={item.text}
className="justify-center"
onClick={() => handleClick(item, idx)}
>
<span>{text}</span>
</DropdownMenuItem>
)
})}
</MenuList>
</Menu>
</DropdownMenuContent>
</DropdownMenu>
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/FeedbackWidget/FixedDot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const FixedDot = forwardRef<HTMLButtonElement, FixedDotProps>(
data-testid="feedback-widget-button"
aria-label={t("feedback-widget")}
className={cn(
"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",
"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",
"transition-all duration-200 hover:shadow-none hover:transition-transform hover:duration-200",
!suppressScale && "hover:scale-110",
offsetBottom && "bottom-31 lg:bottom-4",
Expand Down
5 changes: 4 additions & 1 deletion src/components/LeftNavBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ const LeftNavBar = ({
>
{dropdownLinks && (
<div className="relative mb-8 flex items-end justify-end">
<ButtonDropdown list={dropdownLinks} w="full" minW="240px" />
<ButtonDropdown
list={dropdownLinks}
className="w-full min-w-[240px]"
/>
</div>
)}
<h2 className="mb-8 text-3xl leading-xs">
Expand Down
59 changes: 0 additions & 59 deletions src/components/MdComponents/MdComponents.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import pickBy from "lodash/pickBy"
import type { Meta, StoryObj } from "@storybook/react/*"

import type { List as ButtonDropdownList } from "@/components/ButtonDropdown"

import { viewportModes } from "../../../.storybook/modes"

import MdComponentSet from "."
Expand Down Expand Up @@ -34,8 +32,6 @@ const {
hr: HR,
pre: Pre,
Page,
MobileButton,
MobileButtonDropdown,
} = MdComponentSet

const Para = () => (
Expand All @@ -53,9 +49,6 @@ export const MdComponents: StoryObj = {
<div className="mx-auto max-w-screen-lg">
<Page>
<ContentContainer>
<MobileButton>
<MobileButtonDropdown list={roadmapDropdownLinks} />
</MobileButton>
<Heading1>Heading1</Heading1>
<Para />
<Heading2>Heading2</Heading2>
Expand All @@ -75,55 +68,3 @@ export const MdComponents: StoryObj = {
</div>
),
}

const roadmapDropdownLinks: ButtonDropdownList = {
text: "nav-roadmap-options",
ariaLabel: "nav-roadmap-options-alt",
items: [
{
text: "nav-roadmap-home",
href: "/roadmap/",
matomo: {
eventCategory: `Roadmap dropdown`,
eventAction: `Clicked`,
eventName: "clicked roadmap home",
},
},
{
text: "nav-roadmap-security",
href: "/roadmap/security",
matomo: {
eventCategory: `Roadmap security dropdown`,
eventAction: `Clicked`,
eventName: "clicked roadmap security",
},
},
{
text: "nav-roadmap-scaling",
href: "/roadmap/scaling",
matomo: {
eventCategory: `Roadmap scaling dropdown`,
eventAction: `Clicked`,
eventName: "clicked roadmap scaling home",
},
},
{
text: "nav-roadmap-user-experience",
href: "/roadmap/user-experience/",
matomo: {
eventCategory: `Roadmap user experience dropdown`,
eventAction: `Clicked`,
eventName: "clicked roadmap user experience home",
},
},
{
text: "nav-roadmap-future-proofing",
href: "/roadmap/future-proofing",
matomo: {
eventCategory: `Roadmap future-proofing dropdown`,
eventAction: `Clicked`,
eventName: "clicked roadmap future-proofing home",
},
},
],
}
34 changes: 1 addition & 33 deletions src/components/MdComponents/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { ComponentProps, type HTMLAttributes } from "react"
import { type HTMLAttributes } from "react"
import { Badge, Box, type BoxProps } from "@chakra-ui/react"

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

import ButtonDropdown, {
type ButtonDropdownProps,
} from "@/components/ButtonDropdown"
import Contributors from "@/components/Contributors"
import MarkdownImage from "@/components/MarkdownImage"
import TooltipLink from "@/components/TooltipLink"
Expand Down Expand Up @@ -155,32 +152,6 @@ export const ContentContainer = (props: Pick<BoxProps, "id" | "children">) => {
)
}

export const MobileButton = (props: ChildOnlyProp) => {
return (
<div
className="sticky bottom-0 z-sticky w-full bg-background p-8 shadow-md lg:hidden"
{...props}
/>
)
}

export const StyledButtonDropdown = ({
list,
className,
...rest
}: HTMLAttributes<HTMLDivElement> & Pick<ButtonDropdownProps, "list">) => (
<Flex className={cn("mb-8 items-end justify-end", className)} {...rest}>
<ButtonDropdown list={list} w={{ base: "full", lg: "auto" }} minW="240px" />
</Flex>
)

export const MobileButtonDropdown = ({
className,
...props
}: ComponentProps<typeof StyledButtonDropdown>) => (
<StyledButtonDropdown className={cn("mb-0", className)} {...props} />
)

// All custom React components
export const reactComponents = {
Badge,
Expand All @@ -196,11 +167,8 @@ export const reactComponents = {
FeaturedText,
GlossaryTooltip,
InfoBanner,
MobileButton,
MobileButtonDropdown,
Page,
QuizWidget: StandaloneQuizWidget,
StyledButtonDropdown,
IssuesList,
Title,
YouTube,
Expand Down
11 changes: 11 additions & 0 deletions src/components/MobileButtonDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ButtonDropdown, { type ButtonDropdownProps } from "./ButtonDropdown"

const MobileButtonDropdown = (props: ButtonDropdownProps) => {
return (
<div className="sticky bottom-0 z-sticky w-full bg-background p-8 shadow-md lg:hidden">
<ButtonDropdown {...props} className="w-full lg:w-auto" />
</div>
)
}

export default MobileButtonDropdown
2 changes: 1 addition & 1 deletion src/components/Staking/StakingConsiderations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const StakingConsiderations = ({ page }: StakingConsiderationsProps) => {

return (
<Flex flexDir={{ base: "column", md: "row" }}>
<ButtonDropdown list={dropdownLinks} hideFrom={mdBp} />
<ButtonDropdown list={dropdownLinks} className="mb-4 md:hidden" />
{/* TODO: Improve a11y */}
<Box flex={1} hideBelow={mdBp}>
{!!pageData && (
Expand Down
Loading

0 comments on commit 6b5ad1c

Please sign in to comment.