From 204a906de808ee3a66972b93434f55d3cfc06bf9 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Thu, 13 Aug 2026 07:04:15 +0000 Subject: [PATCH] refactor(web): migrate help menu icon trigger (#40650) --- web/app/components/detail-sidebar/index.tsx | 6 +-- .../main-nav/__tests__/index.spec.tsx | 20 +++++++- .../main-nav/components/help-menu.tsx | 40 ++++++++++----- .../main-nav/components/web-apps-section.tsx | 50 +++++++++++-------- 4 files changed, 77 insertions(+), 39 deletions(-) diff --git a/web/app/components/detail-sidebar/index.tsx b/web/app/components/detail-sidebar/index.tsx index 3739c742317..a9cbf208bef 100644 --- a/web/app/components/detail-sidebar/index.tsx +++ b/web/app/components/detail-sidebar/index.tsx @@ -31,10 +31,8 @@ function SecondarySidebarHelpMenu({ triggerClassName }: { triggerClassName?: str return ( ) } diff --git a/web/app/components/main-nav/__tests__/index.spec.tsx b/web/app/components/main-nav/__tests__/index.spec.tsx index 9bbc932eb25..590e1ba812b 100644 --- a/web/app/components/main-nav/__tests__/index.spec.tsx +++ b/web/app/components/main-nav/__tests__/index.spec.tsx @@ -1313,7 +1313,12 @@ describe('MainNav', () => { const top = typeof optionsOrX === 'object' ? optionsOrX.top : y scrollViewport.scrollTop = Number(top ?? 0) } - await user.click(await screen.findByRole('button', { name: 'common.operation.search' })) + const searchButton = await screen.findByRole('button', { name: 'common.operation.search' }) + expect(searchButton).toHaveAttribute('aria-expanded', 'false') + + await user.click(searchButton) + expect(searchButton).toHaveAttribute('aria-expanded', 'true') + const searchInput = screen.getByPlaceholderText('common.mainNav.webApps.searchPlaceholder') await user.type(searchInput, 'beta') @@ -1326,6 +1331,19 @@ describe('MainNav', () => { expect( screen.getByRole('link', { name: 'common.mainNav.webApps.openApp:{"name":"Beta Tool"}' }), ).toHaveAttribute('href', '/installed/installed-2') + + const webAppsButton = screen.getByRole('button', { name: 'explore.sidebar.webApps' }) + await user.click(webAppsButton) + expect(searchButton).toHaveAttribute('aria-expanded', 'false') + expect( + screen.queryByPlaceholderText('common.mainNav.webApps.searchPlaceholder'), + ).not.toBeInTheDocument() + + await user.click(webAppsButton) + expect(searchButton).toHaveAttribute('aria-expanded', 'true') + expect(screen.getByPlaceholderText('common.mainNav.webApps.searchPlaceholder')).toHaveValue( + 'beta', + ) }) it('hides the installed web apps section while installed apps are loading', () => { diff --git a/web/app/components/main-nav/components/help-menu.tsx b/web/app/components/main-nav/components/help-menu.tsx index 00373a633b8..d32fe803949 100644 --- a/web/app/components/main-nav/components/help-menu.tsx +++ b/web/app/components/main-nav/components/help-menu.tsx @@ -1,6 +1,7 @@ 'use client' -import type { ReactNode } from 'react' +import type { IconButtonProps } from '@langgenius/dify-ui/icon-button' +import type { ReactElement } from 'react' import { cn } from '@langgenius/dify-ui/cn' import { DropdownMenu, @@ -12,6 +13,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' +import { IconButton } from '@langgenius/dify-ui/icon-button' import { Switch } from '@langgenius/dify-ui/switch' import { skipToken, useQuery, useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue, useSetAtom } from 'jotai' @@ -52,8 +54,9 @@ import AccountAboutDialog from './help-menu/account-about-dialog' import SupportMenu from './support-menu' type HelpMenuProps = { - triggerIcon?: ReactNode + triggerIcon?: ReactElement triggerClassName?: string + triggerSize?: IconButtonProps['size'] } const defaultTriggerIcon = ( @@ -84,7 +87,7 @@ const MenuSwitchIndicator = ({ checked }: { checked: boolean }) => ( /> ) -const HelpMenu = ({ triggerIcon = defaultTriggerIcon, triggerClassName }: HelpMenuProps) => { +const HelpMenu = ({ triggerIcon, triggerClassName, triggerSize }: HelpMenuProps) => { const { t } = useTranslation() const docLink = useDocLink() const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions()) @@ -114,6 +117,7 @@ const HelpMenu = ({ triggerIcon = defaultTriggerIcon, triggerClassName }: HelpMe const disableStepByStepTour = useSetAtom(disableStepByStepTourForCurrentWorkspaceAtom) const setStepByStepTourShellMode = useSetStepByStepTourShellMode() const [aboutOpen, setAboutOpen] = useState(false) + const usesDefaultTrigger = !triggerIcon const shouldShowLearnDifySwitch = systemFeatures.enable_learn_app const shouldShowStepByStepTourSwitch = systemFeatures.enable_step_by_step_tour const canToggleStepByStepTour = @@ -148,17 +152,27 @@ const HelpMenu = ({ triggerIcon = defaultTriggerIcon, triggerClassName }: HelpMe <> $['mainNav.help.openMenu'], { ns: 'common' })} data-learn-dify-help-target - className={cn( - 'inline-flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-full border border-components-card-border bg-components-card-bg p-0 text-text-tertiary shadow-xs transition-colors hover:bg-components-card-bg-alt hover:text-saas-dify-blue-inverted focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden', - triggerClassName, - 'data-popup-open:bg-components-card-bg-alt data-popup-open:text-saas-dify-blue-inverted', - skipRecoveryVisible && styles.stepByStepTourRecoveryPulse, - )} - > - {triggerIcon} - + render={ + $['mainNav.help.openMenu'], { ns: 'common' })} + className={cn( + usesDefaultTrigger && [ + 'rounded-full border border-components-card-border bg-components-card-bg text-text-tertiary shadow-xs transition-colors hover:bg-components-card-bg-alt hover:text-saas-dify-blue-inverted', + !triggerSize && 'size-7 p-0', + 'data-popup-open:bg-components-card-bg-alt data-popup-open:text-saas-dify-blue-inverted', + ], + !usesDefaultTrigger && + 'data-popup-open:bg-state-base-hover data-popup-open:text-text-secondary', + triggerClassName, + skipRecoveryVisible && styles.stepByStepTourRecoveryPulse, + )} + > + {triggerIcon ?? defaultTriggerIcon} + + } + /> { setSearchText(value) } + const handleSearchVisibleChange = (visible: boolean) => { + setAppsExpanded(true) + if (!visible) handleSearchTextChange('') + setSearchVisible(visible) + } + const handleDelete = () => { if (!uninstallDialogAppId) return @@ -136,7 +144,11 @@ const WebAppsSectionContent = () => { /> ) return ( - + { /> - $['operation.search'], { ns: 'common' })} - className={cn( - 'flex h-6 w-6 items-center justify-center rounded-md p-0.5 text-text-tertiary outline-hidden hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid', - searchVisible && 'bg-state-base-hover text-text-secondary', - )} - onClick={() => { - setAppsExpanded(true) - if (searchVisible) handleSearchTextChange('') - setSearchVisible(!searchVisible) - }} - > - - - - + $['operation.search'], { ns: 'common' })} + className="rounded-md" + > + + + + + } + /> - {appsExpanded && searchVisible && ( + { autoFocus /> - )} + {appsExpanded && ( { - + ) }