From 4633992f81e14c718eec3dad096285a99d3288e6 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 8 Jun 2026 21:24:39 +0800 Subject: [PATCH] Fix inconsistent padding in www mobile menu (#46704) ## Context As per PR title image Also resolves FE-3565 Closes mobile menu when a link is clicked ## Summary by CodeRabbit * **Improvements** * Mobile menu now closes reliably when selecting more navigation items (Modules, various Developers/Solutions links and footer links). * Enhanced link focus and interaction behavior in the mobile menu for clearer keyboard/touch feedback. * **New Features** * Navigation links can now accept optional click handlers, enabling custom onClick behavior. * **Refactor** * Internal reorganization of mobile menu styling and link composition to simplify maintenance. --- apps/www/components/Nav/MobileMenu.tsx | 81 ++++++++++++--------- apps/www/components/Nav/index.tsx | 2 +- apps/www/data/nav.tsx | 15 ++-- packages/ui-patterns/src/TextLink/index.tsx | 1 + 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/apps/www/components/Nav/MobileMenu.tsx b/apps/www/components/Nav/MobileMenu.tsx index abd64d40633..daa43631e75 100644 --- a/apps/www/components/Nav/MobileMenu.tsx +++ b/apps/www/components/Nav/MobileMenu.tsx @@ -1,12 +1,13 @@ 'use client' +import { type Menu } from '~/data/nav' import { useIsLoggedIn, useIsUserLoading } from 'common' import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion' import { ChevronRight } from 'lucide-react' import Link from 'next/link' import { useEffect } from 'react' import type { Dispatch, SetStateAction } from 'react' -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button } from 'ui' +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, cn } from 'ui' import { TextLink } from 'ui-patterns/TextLink' import MenuItem from './MenuItem' @@ -19,10 +20,10 @@ import { useSendTelemetryEvent } from '@/lib/telemetry' interface Props { open: boolean setOpen: Dispatch> - menu: any + menu: Menu } -const MobileMenu = ({ open, setOpen, menu }: Props) => { +export const MobileMenu = ({ open, setOpen, menu }: Props) => { const isLoggedIn = useIsLoggedIn() const isUserLoading = useIsUserLoading() const sendTelemetryEvent = useSendTelemetryEvent() @@ -50,17 +51,18 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { return () => window.removeEventListener('keydown', onKeyDown) }, [setOpen]) - const AccordionMenuItem = ({ menuItem }: any) => ( + const AccordionMenuItem = ({ menuItem }: { menuItem: Menu['primaryNav'][number] }) => ( {menuItem.title === 'Product' ? ( <> - {Object.values(menuItem.subMenu)?.map((component: any) => ( + {Object.values(menuItem.subMenu)?.map((component) => ( setOpen(false)} /> ))}
@@ -75,6 +77,7 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { href={productModule.url} description={productModule.description_short} icon={productModule.icon} + onClick={() => setOpen(false)} /> ))} @@ -90,6 +93,7 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { focus-visible:text-foreground focus-visible:ring-2 focus-visible:outline-hidden focus-visible:rounded-sm focus-visible:ring-foreground-lighter " + onClick={() => setOpen(false)} >
Features @@ -105,14 +109,14 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { ) : menuItem.title === 'Developers' ? (
- {menuItem.subMenu['navigation'].map((column: any) => ( + {menuItem.subMenu['navigation'].map((column) => (
{column.label !== 'Developers' && ( )} - {column.links.map((link: any) => ( + {column.links.map((link) => ( { label={link.text} counter={link.text === 'Careers' && jobsCount > 0 ? jobsCount : undefined} className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay mt-0!" + onClick={() => setOpen(false)} /> ))}
@@ -134,31 +139,34 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { url={menuItem.subMenu['footer']['support'].url} label={menuItem.subMenu['footer']['support'].text} className="focus-visible:ring-offset-4 focus-visible:ring-offset-background-overlay" + onClick={() => setOpen(false)} /> setOpen(false)} />
) : menuItem.title === 'Solutions' ? (
- {menuItem.subMenu['navigation'].map((column: any) => ( + {menuItem.subMenu['navigation'].map((column) => (
{column.label !== 'Solutions' && ( )} - {column.links.map((link: any) => ( + {column.links.map((link) => ( setOpen(false)} /> ))}
@@ -168,29 +176,38 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { ) - const Menu = () => ( - - {menu.primaryNav.map((menuItem: any) => ( - - {menuItem.hasDropdown ? ( - - + const Menu = () => { + const className = 'py-2 pl-2 pr-4 text-base font-medium text-foreground hover:bg-surface-200' + return ( + + {menu.primaryNav.map((menuItem) => ( + + {menuItem.hasDropdown ? ( + + {menuItem.title} + + + ) : ( + setOpen(false)} + > {menuItem.title} - - - - ) : ( - - {menuItem.title} - - )} - - ))} - - ) + + )} + + ))} + + ) + } return ( @@ -306,5 +323,3 @@ const MobileMenu = ({ open, setOpen, menu }: Props) => { ) } - -export default MobileMenu diff --git a/apps/www/components/Nav/index.tsx b/apps/www/components/Nav/index.tsx index f822f6bc72a..43f700548cc 100644 --- a/apps/www/components/Nav/index.tsx +++ b/apps/www/components/Nav/index.tsx @@ -23,7 +23,7 @@ import { import GitHubButton from './GitHubButton' import HamburgerButton from './HamburgerMenu' import MenuItem from './MenuItem' -import MobileMenu from './MobileMenu' +import { MobileMenu } from './MobileMenu' import RightClickBrandLogo from './RightClickBrandLogo' import useDropdownMenu from './useDropdownMenu' diff --git a/apps/www/data/nav.tsx b/apps/www/data/nav.tsx index 6c016f47cb9..c6170b4d837 100644 --- a/apps/www/data/nav.tsx +++ b/apps/www/data/nav.tsx @@ -1,44 +1,45 @@ import { DevelopersDropdown } from 'components/Nav/DevelopersDropdown' import { ProductDropdown } from 'components/Nav/ProductDropdown' import { SolutionsDropdown } from 'components/Nav/SolutionsDropdown' - import { data as DevelopersData } from 'data/Developers' import MainProductsData from 'data/MainProducts' import { navData as SolutionsData } from 'data/Solutions' +export type Menu = ReturnType + export const getMenu = () => ({ primaryNav: [ { - title: 'Product', + title: 'Product' as const, hasDropdown: true, dropdown: , dropdownContainerClassName: 'rounded-xl', subMenu: MainProductsData, }, { - title: 'Developers', + title: 'Developers' as const, hasDropdown: true, dropdown: , dropdownContainerClassName: 'rounded-xl', subMenu: DevelopersData, }, { - title: 'Solutions', + title: 'Solutions' as const, hasDropdown: true, dropdown: , dropdownContainerClassName: 'rounded-xl', subMenu: SolutionsData, }, { - title: 'Pricing', + title: 'Pricing' as const, url: '/pricing', }, { - title: 'Docs', + title: 'Docs' as const, url: '/docs', }, { - title: 'Blog', + title: 'Blog' as const, url: '/blog', }, ], diff --git a/packages/ui-patterns/src/TextLink/index.tsx b/packages/ui-patterns/src/TextLink/index.tsx index ac64fa5393c..5b7db8a1c49 100644 --- a/packages/ui-patterns/src/TextLink/index.tsx +++ b/packages/ui-patterns/src/TextLink/index.tsx @@ -12,6 +12,7 @@ interface Props { hasChevron?: boolean chevronAnimation?: 'translate' | 'fadeIn' target?: '_blank' | '_self' + onClick?: () => void } export function TextLink({