fix: 修正下载器卡片属性透传 (#714)

This commit is contained in:
InfinityPacer
2026-08-25 12:10:33 +08:00
committed by GitHub
parent 852c682278
commit fcddcda4b9
2 changed files with 123 additions and 2 deletions
+8 -2
View File
@@ -1,8 +1,11 @@
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
import api from '@/api'
import { formatFileSize } from '@/@core/utils/formatters'
import type { DownloaderConf, DownloaderInfo } from '@/api/types'
import { getLogoUrl } from '@/utils/imageUtils'
import { mergeProps } from 'vue'
import { useI18n } from 'vue-i18n'
import { downloaderDict } from '@/api/constants'
import { useBackground } from '@/composables/useBackground'
@@ -122,7 +125,7 @@ onUnmounted(() => {
<template>
<VHover v-slot="hover">
<VCard
v-bind="hover.props"
v-bind="mergeProps($attrs, hover.props)"
variant="tonal"
class="app-card-shell app-card-colorful"
:style="{ '--app-card-accent-rgb': accentRgb }"
@@ -146,7 +149,10 @@ onUnmounted(() => {
/>
<span class="app-card-summary__title text-h6">{{ downloader.name }}</span>
</div>
<div v-if="downloaderDict[downloader.type] && props.downloader.enabled" class="app-card-summary__meta text-sm">
<div
v-if="downloaderDict[downloader.type] && props.downloader.enabled"
class="app-card-summary__meta text-sm"
>
<span class="app-card-summary__meta-item">{{ `${formatFileSize(upload_rate, 1)}/s` }}</span>
<span class="app-card-summary__meta-item">{{ `${formatFileSize(download_rate, 1)}/s` }}</span>
</div>
@@ -0,0 +1,115 @@
import DownloaderCard from '@/components/cards/DownloaderCard.vue'
import { renderWithProviders } from '@tests/support/render'
import { defineComponent, h, ref } from 'vue'
import { describe, expect, it, vi } from 'vitest'
vi.mock('@/api', () => ({
default: {
get: vi.fn(),
},
}))
vi.mock('@/composables/useBackground', () => ({
useBackground: () => ({
useConditionalDataRefresh: () => ({ stop: vi.fn() }),
}),
}))
vi.mock('@/composables/useCardAccentColor', () => ({
useCardAccentColor: () => ({
accentRgb: ref('141, 81, 249'),
imageRef: ref(),
updateAccentColor: vi.fn(),
}),
}))
vi.mock('@/utils/imageUtils', () => ({
getLogoUrl: () => '/downloader.png',
}))
const passthroughStub = (name: string, tag = 'div') =>
defineComponent({
name,
inheritAttrs: false,
setup(_props, { attrs, slots }) {
return () => h(tag, attrs, slots.default?.())
},
})
const VHoverStub = defineComponent({
name: 'VHover',
inheritAttrs: false,
setup(_props, { slots }) {
return () => slots.default?.({ props: { onMouseenter: hoverMouseenter } })
},
})
const hoverMouseenter = vi.fn()
const VCardStub = passthroughStub('VCard', 'article')
const globalStubs = {
IconBtn: passthroughStub('IconBtn', 'button'),
VBadge: passthroughStub('VBadge', 'span'),
VCard: VCardStub,
VCardText: passthroughStub('VCardText'),
VDialogCloseBtn: passthroughStub('VDialogCloseBtn', 'button'),
VHover: VHoverStub,
VIcon: passthroughStub('VIcon', 'span'),
VImg: defineComponent({
name: 'VImg',
props: { src: String },
setup(props, { attrs }) {
return () => h('img', { ...attrs, src: props.src })
},
}),
}
const downloader = {
name: 'qb-main',
type: 'qbittorrent',
default: true,
enabled: false,
config: {},
}
describe('DownloaderCard', () => {
it('places draggable attributes on the card DOM instead of the hover wrapper', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const externalMouseenter = vi.fn()
hoverMouseenter.mockClear()
const Harness = defineComponent({
setup() {
return () =>
h(DownloaderCard, {
downloader,
downloaders: [downloader],
allowRefresh: false,
'data-draggable': 'true',
'aria-label': 'qb-main-card',
class: 'draggable-item',
onMouseenter: externalMouseenter,
})
},
})
const { container } = await renderWithProviders(Harness, {
global: { stubs: globalStubs },
})
const card = container.querySelector('article')
expect(card).toBeInTheDocument()
expect(card).toHaveAttribute('data-draggable', 'true')
expect(card).toHaveAttribute('aria-label', 'qb-main-card')
expect(card).toHaveClass('draggable-item', 'app-card-shell', 'app-card-colorful')
expect(container.querySelectorAll('[data-draggable]')).toHaveLength(1)
card?.dispatchEvent(new MouseEvent('mouseenter'))
expect(externalMouseenter).toHaveBeenCalledOnce()
expect(hoverMouseenter).toHaveBeenCalledOnce()
const attributeWarnings = warn.mock.calls.filter(([message]) =>
String(message).includes('Extraneous non-props attributes'),
)
expect(attributeWarnings).toHaveLength(0)
})
})