From 0db446b8ea31cdbf4e05a292f214ae78f7556fbf Mon Sep 17 00:00:00 2001 From: yessenia Date: Thu, 12 Feb 2026 01:41:58 +0800 Subject: [PATCH] feat: add template icon URL utility and integrate AppIcon component in template card and search dropdown for improved icon rendering --- .../marketplace/list/list-with-collection.tsx | 1 - .../marketplace/list/template-card.spec.tsx | 246 ++++++++++++++++++ .../marketplace/list/template-card.tsx | 74 +----- .../search-box/search-dropdown/index.tsx | 17 +- .../components/plugins/marketplace/utils.ts | 8 + 5 files changed, 275 insertions(+), 71 deletions(-) create mode 100644 web/app/components/plugins/marketplace/list/template-card.spec.tsx diff --git a/web/app/components/plugins/marketplace/list/list-with-collection.tsx b/web/app/components/plugins/marketplace/list/list-with-collection.tsx index 7f694559c9..49c13146e3 100644 --- a/web/app/components/plugins/marketplace/list/list-with-collection.tsx +++ b/web/app/components/plugins/marketplace/list/list-with-collection.tsx @@ -75,7 +75,6 @@ const ListWithCollection = (props: ListWithCollectionProps) => { itemKeyField="id" renderCard={renderTemplateCard} carouselCollectionNames={[CAROUSEL_COLLECTION_NAMES.featured]} - viewMoreSearchTab="templates" cardContainerClassName={cardContainerClassName} /> ) diff --git a/web/app/components/plugins/marketplace/list/template-card.spec.tsx b/web/app/components/plugins/marketplace/list/template-card.spec.tsx new file mode 100644 index 0000000000..05fa100949 --- /dev/null +++ b/web/app/components/plugins/marketplace/list/template-card.spec.tsx @@ -0,0 +1,246 @@ +import type { Template } from '../types' +import { render } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' +import TemplateCard from './template-card' + +// Mock AppIcon component to capture props for assertion +vi.mock('@/app/components/base/app-icon', () => ({ + default: ({ size, iconType, icon, imageUrl, background }: { + size?: string + iconType?: string + icon?: string + imageUrl?: string | null + background?: string | null + }) => ( + + ), +})) + +// Mock i18n +vi.mock('#i18n', () => ({ + useTranslation: () => ({ + t: (key: string, options?: Record) => { + if (key === 'marketplace.templateCard.by') + return `by ${options?.author || ''}` + if (key === 'usedCount') + return `${options?.num || 0} used` + return key + }, + }), + useLocale: () => 'en-US', +})) + +// Mock next/link +vi.mock('next/link', () => ({ + default: ({ children, href, ...props }: { children: React.ReactNode, href: string }) => ( + {children} + ), +})) + +// Mock next-themes +vi.mock('next-themes', () => ({ + useTheme: () => ({ theme: 'light' }), +})) + +// Mock marketplace utils +vi.mock('@/utils/get-icon', () => ({ + getIconFromMarketPlace: (id: string) => `https://marketplace.dify.ai/api/v1/plugins/${id}/icon`, +})) + +vi.mock('@/utils/template', () => ({ + formatUsedCount: (count: number) => String(count), +})) + +vi.mock('@/utils/var', () => ({ + getMarketplaceUrl: (path: string) => `https://marketplace.dify.ai${path}`, +})) + +vi.mock('@/app/components/plugins/card/base/corner-mark', () => ({ + default: ({ text }: { text: string }) => {text}, +})) + +// Mock marketplace utils (getTemplateIconUrl) +vi.mock('../utils', () => ({ + getTemplateIconUrl: (template: { id: string, icon?: string, icon_file_key?: string }): string => { + if (template.icon?.startsWith('http')) + return template.icon + if (template.icon_file_key) + return `https://marketplace.dify.ai/api/v1/templates/${template.id}/icon` + return '' + }, +})) + +// ================================ +// Test Data Factories +// ================================ + +const createMockTemplate = (overrides?: Partial