diff --git a/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx b/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx index 32a44deb225..355f0d1220c 100644 --- a/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx @@ -68,6 +68,8 @@ describe('PluginTypeSwitch', () => { await user.click(screen.getByRole('button', { name: 'category.models' })) await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - expect(onUrlUpdate.mock.calls.at(-1)?.[0].searchParams.get('category')).toBe('model') + const update = onUrlUpdate.mock.calls.at(-1)?.[0] + expect(update?.searchParams.get('category')).toBe('model') + expect(update?.options.scroll).toBe(false) }) }) diff --git a/web/app/components/plugins/marketplace/home/__tests__/home-catalog-navigation.spec.tsx b/web/app/components/plugins/marketplace/home/__tests__/home-catalog-navigation.spec.tsx index 82d25169b5c..b2d3c1c05a0 100644 --- a/web/app/components/plugins/marketplace/home/__tests__/home-catalog-navigation.spec.tsx +++ b/web/app/components/plugins/marketplace/home/__tests__/home-catalog-navigation.spec.tsx @@ -48,8 +48,9 @@ describe('HomeCatalogNavigation', () => { expect(navigationSection).toHaveClass(styles.catalogNavigation) expect(navigationSection.firstElementChild).toHaveClass('w-full') expect(navigationSection.firstElementChild).not.toHaveClass('mx-auto', 'max-w-[1200px]') - const activeTab = screen.getByText('plugin.marketplace.home.plugins') + const activeTab = screen.getByRole('link', { name: 'plugin.marketplace.home.plugins' }) expect(activeTab).toHaveAttribute('aria-current', 'page') + expect(activeTab).toHaveAttribute('href', '/plugins') expect(activeTab.querySelector('[aria-hidden="true"]')).toHaveClass( 'absolute', 'h-0.5', @@ -62,9 +63,26 @@ describe('HomeCatalogNavigation', () => { expect(screen.getByTestId('plugin-type-switch')).toHaveAttribute('data-variant', 'home') }) + it('keeps compact tabs clickable and hides the active indicator', () => { + render() + + const pluginsTab = screen.getByRole('link', { name: 'plugin.marketplace.home.plugins' }) + const templatesTab = screen.getByRole('link', { name: 'plugin.marketplace.home.templates' }) + + expect(pluginsTab).toHaveAttribute('href', '/plugins') + expect(pluginsTab).toHaveClass('cursor-pointer') + expect(pluginsTab.querySelector('[aria-hidden="true"]')).not.toBeInTheDocument() + expect(templatesTab).toHaveAttribute('href', '/templates') + expect(templatesTab).toHaveClass('cursor-pointer') + }) + it('links Dify users to the hosted Marketplace templates page', () => { renderNavigation(false) + expect(screen.getByRole('link', { name: 'plugin.marketplace.home.plugins' })).toHaveAttribute( + 'href', + 'https://marketplace.dify.ai/plugins?source=console', + ) expect( screen.getByRole('link', { name: /plugin\.marketplace\.home\.templates/ }), ).toHaveAttribute('href', 'https://marketplace.dify.ai/templates?source=console') @@ -128,4 +146,19 @@ describe('HomeCatalogNavigation', () => { scrollContainer.remove() }) + + it('prevents scroll anchoring from reversing the sticky threshold', () => { + const scrollContainer = document.createElement('div') + scrollContainer.id = 'marketplace-container' + document.body.appendChild(scrollContainer) + + const { unmount } = renderNavigation(true) + + expect(scrollContainer.style.overflowAnchor).toBe('none') + + unmount() + expect(scrollContainer.style.overflowAnchor).toBe('') + + scrollContainer.remove() + }) }) diff --git a/web/app/components/plugins/marketplace/home/home-catalog-navigation.tsx b/web/app/components/plugins/marketplace/home/home-catalog-navigation.tsx index 170d8da3527..608531cf827 100644 --- a/web/app/components/plugins/marketplace/home/home-catalog-navigation.tsx +++ b/web/app/components/plugins/marketplace/home/home-catalog-navigation.tsx @@ -25,6 +25,11 @@ function HomeCatalogNavigation({ catalogTabs }: HomeCatalogNavigationProps) { const scrollContainer = document.getElementById('marketplace-container') if (!scrollContainer) return + const previousOverflowAnchor = scrollContainer.style.overflowAnchor + // The sticky section becomes shorter when its tabs move into the header. + // Prevent browser scroll anchoring from moving it back across the pin threshold. + scrollContainer.style.overflowAnchor = 'none' + const updatePinnedState = () => { const pinTrigger = pinTriggerRef.current if (!pinTrigger) return @@ -41,6 +46,7 @@ function HomeCatalogNavigation({ catalogTabs }: HomeCatalogNavigationProps) { return () => { scrollContainer.removeEventListener('scroll', updatePinnedState) window.removeEventListener('resize', updatePinnedState) + scrollContainer.style.overflowAnchor = previousOverflowAnchor } }, [setIsPinned]) diff --git a/web/app/components/plugins/marketplace/home/home-catalog-tabs.tsx b/web/app/components/plugins/marketplace/home/home-catalog-tabs.tsx index 935c386c13b..68a211fa922 100644 --- a/web/app/components/plugins/marketplace/home/home-catalog-tabs.tsx +++ b/web/app/components/plugins/marketplace/home/home-catalog-tabs.tsx @@ -5,11 +5,17 @@ import { getMarketplaceUrl } from '@/utils/var' type HomeCatalogTabsProps = { className?: string + compact?: boolean isMarketplacePlatform: boolean } -const HomeCatalogTabs = ({ className, isMarketplacePlatform }: HomeCatalogTabsProps) => { +const HomeCatalogTabs = ({ + className, + compact = false, + isMarketplacePlatform, +}: HomeCatalogTabsProps) => { const { t } = useTranslation() + const pluginsHref = isMarketplacePlatform ? '/plugins' : getMarketplaceUrl('/plugins') const templatesHref = isMarketplacePlatform ? '/templates' : getMarketplaceUrl('/templates') return ( @@ -17,24 +23,35 @@ const HomeCatalogTabs = ({ className, isMarketplacePlatform }: HomeCatalogTabsPr aria-label={t(($) => $['mainNav.marketplace'], { ns: 'common' })} className={cn('flex h-8 items-center gap-1', className)} > - {t(($) => $['marketplace.home.plugins'], { ns: 'plugin' })} - - + {!compact && ( + + )} + {t(($) => $['marketplace.home.templates'], { ns: 'plugin' })} - - {t(($) => $['marketplace.home.new'], { ns: 'plugin' })} - + {!compact && ( + + {t(($) => $['marketplace.home.new'], { ns: 'plugin' })} + + )} ) diff --git a/web/app/components/plugins/marketplace/home/home-header.tsx b/web/app/components/plugins/marketplace/home/home-header.tsx index 32f753f732a..0d607851f49 100644 --- a/web/app/components/plugins/marketplace/home/home-header.tsx +++ b/web/app/components/plugins/marketplace/home/home-header.tsx @@ -18,7 +18,7 @@ function Guide() { const docLink = useDocLink() return ( - +