mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 00:36:41 +08:00
fix(shortcuts): scope dynamic close listeners (#716)
This commit is contained in:
@@ -14,6 +14,8 @@ const props = withDefaults(
|
|||||||
icon?: string
|
icon?: string
|
||||||
maxWidth?: string
|
maxWidth?: string
|
||||||
modelValue?: boolean
|
modelValue?: boolean
|
||||||
|
/** 动态视图是否声明并可能触发 close 事件。 */
|
||||||
|
supportsClose?: boolean
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
title: string
|
title: string
|
||||||
view: Component
|
view: Component
|
||||||
@@ -25,6 +27,7 @@ const props = withDefaults(
|
|||||||
icon: 'mdi-cog',
|
icon: 'mdi-cog',
|
||||||
maxWidth: '35rem',
|
maxWidth: '35rem',
|
||||||
modelValue: true,
|
modelValue: true,
|
||||||
|
supportsClose: false,
|
||||||
viewProps: () => ({}),
|
viewProps: () => ({}),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -81,7 +84,7 @@ function closeDialog() {
|
|||||||
</VCardItem>
|
</VCardItem>
|
||||||
<VDivider />
|
<VDivider />
|
||||||
<VCardText :class="bodyClasses">
|
<VCardText :class="bodyClasses">
|
||||||
<Component :is="props.view" v-bind="props.viewProps" @close="closeDialog" />
|
<Component :is="props.view" v-bind="props.viewProps" v-on="props.supportsClose ? { close: closeDialog } : {}" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
|
|||||||
@@ -2,20 +2,30 @@ import ShortcutToolDialog from '@/components/dialog/ShortcutToolDialog.vue'
|
|||||||
import { screen } from '@testing-library/vue'
|
import { screen } from '@testing-library/vue'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import { renderWithProviders } from '@tests/support/render'
|
import { renderWithProviders } from '@tests/support/render'
|
||||||
import { defineComponent, markRaw } from 'vue'
|
import { defineComponent, h, markRaw } from 'vue'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
const ToolView = defineComponent({
|
const ToolView = defineComponent({
|
||||||
emits: ['close'],
|
emits: ['close'],
|
||||||
template: '<button type="button" @click="$emit(\'close\')">关闭工具</button>',
|
template: '<button type="button" @click="$emit(\'close\')">关闭工具</button>',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const FragmentToolView = defineComponent({
|
||||||
|
setup(_, { attrs }) {
|
||||||
|
return () => [
|
||||||
|
h('span', { 'data-testid': 'close-listener-state' }, attrs.onClose ? '已绑定' : '未绑定'),
|
||||||
|
h('span', '普通工具内容'),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
describe('ShortcutToolDialog', () => {
|
describe('ShortcutToolDialog', () => {
|
||||||
it('closes the outer dialog when the active tool requests it', async () => {
|
it('closes the outer dialog when the active tool requests it', async () => {
|
||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
const result = await renderWithProviders(ShortcutToolDialog, {
|
const result = await renderWithProviders(ShortcutToolDialog, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: true,
|
modelValue: true,
|
||||||
|
supportsClose: true,
|
||||||
title: '测试工具',
|
title: '测试工具',
|
||||||
view: markRaw(ToolView),
|
view: markRaw(ToolView),
|
||||||
},
|
},
|
||||||
@@ -31,4 +41,26 @@ describe('ShortcutToolDialog', () => {
|
|||||||
expect(result.emitted()['update:modelValue']).toEqual([[false]])
|
expect(result.emitted()['update:modelValue']).toEqual([[false]])
|
||||||
expect(result.emitted().close).toEqual([[]])
|
expect(result.emitted().close).toEqual([[]])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('does not pass a close listener to an ordinary fragment view', async () => {
|
||||||
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||||
|
|
||||||
|
await renderWithProviders(ShortcutToolDialog, {
|
||||||
|
props: {
|
||||||
|
modelValue: true,
|
||||||
|
title: '普通工具',
|
||||||
|
view: markRaw(FragmentToolView),
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
VDialogCloseBtn: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(screen.getByTestId('close-listener-state')).toHaveTextContent('未绑定')
|
||||||
|
expect(warn.mock.calls.some(([message]) => String(message).includes('Extraneous non-emits event listeners'))).toBe(
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ export type ShortcutToolItem = PermissionProtectedItem & {
|
|||||||
dialogSubtitle?: string
|
dialogSubtitle?: string
|
||||||
icon: string
|
icon: string
|
||||||
maxWidth?: string
|
maxWidth?: string
|
||||||
|
/** 动态视图是否声明并可能触发 close 事件。 */
|
||||||
|
supportsClose?: boolean
|
||||||
subtitle: string
|
subtitle: string
|
||||||
title: string
|
title: string
|
||||||
titleText?: string
|
titleText?: string
|
||||||
@@ -50,6 +52,7 @@ export function useShortcutTools() {
|
|||||||
dialog: 'nameTest',
|
dialog: 'nameTest',
|
||||||
component: NameTestView,
|
component: NameTestView,
|
||||||
maxWidth: '65rem',
|
maxWidth: '65rem',
|
||||||
|
supportsClose: true,
|
||||||
titleText: t('shortcut.recognition.title'),
|
titleText: t('shortcut.recognition.title'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -143,6 +146,7 @@ export function useShortcutTools() {
|
|||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
maxWidth: item.maxWidth ?? '35rem',
|
maxWidth: item.maxWidth ?? '35rem',
|
||||||
subtitle: item.dialogSubtitle,
|
subtitle: item.dialogSubtitle,
|
||||||
|
supportsClose: item.supportsClose,
|
||||||
title: item.titleText ?? item.title,
|
title: item.titleText ?? item.title,
|
||||||
view: item.component,
|
view: item.component,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user