fix: not support jump to detail in marketplace (#39347)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Joel 2026-07-21 15:01:00 +08:00 committed by GitHub
parent 8d406aec31
commit 4f8477427e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 80 additions and 7 deletions

View File

@ -122,6 +122,22 @@ describe('getPluginLinkInMarketplace', () => {
})
})
describe('getPluginDetailLinkInMarketplace', () => {
it('should return the local detail link for a regular plugin', async () => {
const { getPluginDetailLinkInMarketplace } = await import('../utils')
const plugin = createMockPlugin({ org: 'test-org', name: 'test-plugin', type: 'plugin' })
expect(getPluginDetailLinkInMarketplace(plugin)).toBe('/plugin/test-org/test-plugin')
})
it('should return the local detail link for a bundle', async () => {
const { getPluginDetailLinkInMarketplace } = await import('../utils')
const bundle = createMockPlugin({ org: 'test-org', name: 'test-bundle', type: 'bundle' })
expect(getPluginDetailLinkInMarketplace(bundle)).toBe('/bundles/test-org/test-bundle')
})
})
describe('getMarketplaceListCondition', () => {
it('should return category condition for tool', async () => {
const { getMarketplaceListCondition } = await import('../utils')

View File

@ -8,6 +8,7 @@ import StickySearchAndSwitchWrapper from './sticky-search-and-switch-wrapper'
type MarketplaceProps = {
showInstallButton?: boolean
linkToMarketplaceDetail?: boolean
pluginTypeSwitchClassName?: string
isMarketplacePlatform?: boolean
marketplaceNav?: React.ReactNode
@ -19,6 +20,7 @@ type MarketplaceProps = {
const Marketplace = async ({
showInstallButton = false,
linkToMarketplaceDetail = false,
pluginTypeSwitchClassName,
isMarketplacePlatform = false,
marketplaceNav,
@ -35,7 +37,10 @@ const Marketplace = async ({
{!isMarketplacePlatform && (
<StickySearchAndSwitchWrapper pluginTypeSwitchClassName={pluginTypeSwitchClassName} />
)}
<ListWrapper showInstallButton={showInstallButton} />
<ListWrapper
showInstallButton={showInstallButton}
linkToMarketplaceDetail={linkToMarketplaceDetail}
/>
</PluginInstallPermissionProviderGuard>
</HydrateQueryClient>
</TanstackQueryInitializer>

View File

@ -87,13 +87,19 @@ describe('CardWrapper', () => {
</ThemeProvider>,
)
it('renders a non-navigating card when install button is hidden', () => {
it('renders a non-navigating card by default when install button is hidden', () => {
renderCardWrapper()
expect(screen.queryByRole('link')).not.toBeInTheDocument()
expect(screen.getByTestId('card-more-info')).toHaveTextContent('42:tag:search|tag:agent')
})
it('links the card to its marketplace detail when explicitly enabled', () => {
renderCardWrapper({ linkToMarketplaceDetail: true })
expect(screen.getByRole('link')).toHaveAttribute('href', '/detail/dify/plugin-a')
})
it('renders install and marketplace detail actions when install button is shown', () => {
renderCardWrapper({ showInstallButton: true })

View File

@ -11,17 +11,20 @@ import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
import { useTags } from '@/app/components/plugins/hooks'
import { useOptionalPluginInstallPermission } from '@/app/components/plugins/install-plugin/hooks/use-plugin-install-permission'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import { getPluginLinkInMarketplace } from '../utils'
import Link from '@/next/link'
import { getPluginDetailLinkInMarketplace, getPluginLinkInMarketplace } from '../utils'
type CardWrapperProps = {
plugin: Plugin
showInstallButton?: boolean
isInstalled?: boolean
linkToMarketplaceDetail?: boolean
}
const CardWrapperComponent = ({
plugin,
showInstallButton,
isInstalled = false,
linkToMarketplaceDetail = false,
}: CardWrapperProps) => {
const { t } = useTranslation()
const { theme } = useTheme()
@ -102,7 +105,7 @@ const CardWrapperComponent = ({
)
}
return (
const card = (
<div className="group relative rounded-xl">
<Card
key={plugin.name}
@ -118,6 +121,17 @@ const CardWrapperComponent = ({
/>
</div>
)
if (!linkToMarketplaceDetail) return card
return (
<Link
href={getPluginDetailLinkInMarketplace(plugin)}
className="block rounded-xl focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden"
>
{card}
</Link>
)
}
// Memoize the component to prevent unnecessary re-renders when props haven't changed

View File

@ -15,6 +15,7 @@ type ListProps = {
marketplaceCollectionPluginsMap: Record<string, Plugin[]>
plugins?: Plugin[]
showInstallButton?: boolean
linkToMarketplaceDetail?: boolean
cardContainerClassName?: string
cardRender?: (plugin: Plugin) => React.JSX.Element | null
emptyClassName?: string
@ -25,6 +26,7 @@ const List = ({
marketplaceCollectionPluginsMap,
plugins,
showInstallButton,
linkToMarketplaceDetail,
cardContainerClassName,
cardRender,
emptyClassName,
@ -62,6 +64,7 @@ const List = ({
marketplaceCollections={marketplaceCollections}
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap}
showInstallButton={showInstallButton}
linkToMarketplaceDetail={linkToMarketplaceDetail}
cardContainerClassName={cardContainerClassName}
cardRender={cardRender}
onCollectionMoreClick={onCollectionMoreClick}
@ -79,6 +82,7 @@ const List = ({
plugin={plugin}
showInstallButton={showInstallButton}
isInstalled={installedPluginIds.has(plugin.plugin_id)}
linkToMarketplaceDetail={linkToMarketplaceDetail}
/>
)
})}

View File

@ -35,6 +35,7 @@ type ListWithCollectionProps = {
marketplaceCollections: MarketplaceCollection[]
marketplaceCollectionPluginsMap: Record<string, Plugin[]>
showInstallButton?: boolean
linkToMarketplaceDetail?: boolean
cardContainerClassName?: string
cardRender?: (plugin: Plugin) => React.JSX.Element | null
onCollectionMoreClick?: (searchParams?: SearchParamsFromCollection) => void
@ -46,13 +47,25 @@ type PluginCardProps = {
showInstallButton?: boolean
cardRender?: (plugin: Plugin) => React.JSX.Element | null
isInstalled?: boolean
linkToMarketplaceDetail?: boolean
}
const PluginCard = ({ plugin, showInstallButton, cardRender, isInstalled }: PluginCardProps) => {
const PluginCard = ({
plugin,
showInstallButton,
cardRender,
isInstalled,
linkToMarketplaceDetail,
}: PluginCardProps) => {
if (cardRender) return cardRender(plugin)
return (
<CardWrapper plugin={plugin} showInstallButton={showInstallButton} isInstalled={isInstalled} />
<CardWrapper
plugin={plugin}
showInstallButton={showInstallButton}
isInstalled={isInstalled}
linkToMarketplaceDetail={linkToMarketplaceDetail}
/>
)
}
@ -60,6 +73,7 @@ const ListWithCollection = ({
marketplaceCollections,
marketplaceCollectionPluginsMap,
showInstallButton,
linkToMarketplaceDetail,
cardContainerClassName,
cardRender,
onCollectionMoreClick,
@ -147,6 +161,7 @@ const ListWithCollection = ({
<PluginCard
plugin={plugin}
showInstallButton={showInstallButton}
linkToMarketplaceDetail={linkToMarketplaceDetail}
cardRender={cardRender}
isInstalled={installedPluginIds?.has(plugin.plugin_id)}
/>
@ -163,6 +178,7 @@ const ListWithCollection = ({
key={plugin.plugin_id}
plugin={plugin}
showInstallButton={showInstallButton}
linkToMarketplaceDetail={linkToMarketplaceDetail}
cardRender={cardRender}
isInstalled={installedPluginIds?.has(plugin.plugin_id)}
/>

View File

@ -7,8 +7,9 @@ import List from './index'
type ListWrapperProps = {
showInstallButton?: boolean
linkToMarketplaceDetail?: boolean
}
const ListWrapper = ({ showInstallButton }: ListWrapperProps) => {
const ListWrapper = ({ showInstallButton, linkToMarketplaceDetail }: ListWrapperProps) => {
const { t } = useTranslation()
const {
@ -45,6 +46,7 @@ const ListWrapper = ({ showInstallButton }: ListWrapperProps) => {
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap || {}}
plugins={plugins}
showInstallButton={showInstallButton}
linkToMarketplaceDetail={linkToMarketplaceDetail}
/>
)}
</div>

View File

@ -60,6 +60,16 @@ export const getPluginLinkInMarketplace = (
return getMarketplaceUrl(`/plugins/${plugin.org}/${plugin.name}`, params)
}
export const getPluginDetailLinkInMarketplace = (
plugin: Pick<MarketplacePluginPayload, 'name' | 'org' | 'type'>,
) => {
const org = encodeURIComponent(plugin.org)
const name = encodeURIComponent(plugin.name)
if (plugin.type === 'bundle') return `/bundles/${org}/${name}`
return `/plugin/${org}/${name}`
}
export const getMarketplaceCategoryUrl = (
category?: string,
params?: Record<string, string | undefined>,