mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 06:35:10 +00:00
fix(widgets): fall back to id when item name is empty string
Some remote assets (e.g. /proxy/seedance/assets) return name='' for items the user never titled. Trigger and list rows rendered blank because nullish coalescing (??) only catches null/undefined, not empty strings. Add displayName(item) helper in base/remote/itemSchema.ts using logical-or fallback (matches the FormDropdownInput pattern in PR #11310) and use it in Trigger.vue's selected-label computed and Item.vue's name span, img alt, and video aria-label so the accessibility names also fall back instead of going empty.
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
buildSearchText,
|
||||
displayName,
|
||||
extractItems,
|
||||
getByPath,
|
||||
mapToDropdownItem,
|
||||
@@ -341,3 +342,13 @@ describe('mapToDropdownItem preview_url normalization', () => {
|
||||
expect(item.preview_url).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('displayName', () => {
|
||||
it('returns name when present', () => {
|
||||
expect(displayName({ id: 'abc', name: 'Cool Asset' })).toBe('Cool Asset')
|
||||
})
|
||||
|
||||
it('falls back to id when name is empty string', () => {
|
||||
expect(displayName({ id: 'abc-123', name: '' })).toBe('abc-123')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,14 @@ export interface DropdownItemShape {
|
||||
preview_url?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* User-facing label for a dropdown item. Falls back to id when name
|
||||
* is missing or empty, so trigger/list rows never render blank.
|
||||
*/
|
||||
export function displayName(item: DropdownItemShape): string {
|
||||
return item.name || item.id
|
||||
}
|
||||
|
||||
export function getByPath(obj: unknown, path: string): unknown {
|
||||
return path.split('.').reduce((acc: unknown, key: string) => {
|
||||
if (acc == null) return undefined
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { cn } from '@comfyorg/tailwind-utils'
|
||||
|
||||
import { displayName } from '@/base/remote/itemSchema'
|
||||
import type { DropdownItemShape } from '@/base/remote/itemSchema'
|
||||
|
||||
import { itemVariants } from './remoteCombo.variants'
|
||||
@@ -27,6 +28,7 @@ const { t } = useI18n()
|
||||
|
||||
const isSelected = computed(() => ctx.selectedValue.value === props.item.id)
|
||||
const hasPreview = computed(() => !!props.item.preview_url)
|
||||
const label = computed(() => displayName(props.item))
|
||||
|
||||
const audioEl = useTemplateRef<HTMLAudioElement>('audioEl')
|
||||
const isPlaying = ref(false)
|
||||
@@ -60,7 +62,7 @@ function handleAudioEnded() {
|
||||
<template v-if="hasPreview && ctx.previewType.value === 'image'">
|
||||
<img
|
||||
:src="item.preview_url"
|
||||
:alt="item.name"
|
||||
:alt="label"
|
||||
class="size-10 shrink-0 rounded-sm object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@@ -69,11 +71,11 @@ function handleAudioEnded() {
|
||||
<template v-else-if="hasPreview && ctx.previewType.value === 'video'">
|
||||
<video
|
||||
:src="item.preview_url"
|
||||
:aria-label="label"
|
||||
class="size-10 shrink-0 rounded-sm object-cover"
|
||||
preload="metadata"
|
||||
muted
|
||||
playsinline
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="hasPreview && ctx.previewType.value === 'audio'">
|
||||
@@ -108,7 +110,7 @@ function handleAudioEnded() {
|
||||
</button>
|
||||
</template>
|
||||
<div class="flex flex-1 flex-col gap-0.5 overflow-hidden">
|
||||
<span class="truncate">{{ item.name }}</span>
|
||||
<span class="truncate">{{ label }}</span>
|
||||
<span
|
||||
v-if="item.description"
|
||||
class="truncate text-[10px] text-muted-foreground"
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { cn } from '@comfyorg/tailwind-utils'
|
||||
|
||||
import { displayName } from '@/base/remote/itemSchema'
|
||||
|
||||
import { triggerVariants } from './remoteCombo.variants'
|
||||
import type { TriggerVariants } from './remoteCombo.variants'
|
||||
import { RemoteComboKey } from './state'
|
||||
@@ -31,7 +33,7 @@ const displayLabel = computed(() => {
|
||||
const id = ctx.selectedValue.value
|
||||
if (!id) return props.placeholder ?? t('widgets.uploadSelect.placeholder')
|
||||
const item = ctx.items.value.find((i) => i.id === id)
|
||||
return item?.name ?? id
|
||||
return item ? displayName(item) : id
|
||||
})
|
||||
|
||||
const computedBorder = computed<TriggerVariants['border']>(() => {
|
||||
|
||||
Reference in New Issue
Block a user