Browse Source
* fix(@vben-core/popup-ui): rebind popup api after hmr * fix(@vben-core/popup-ui): preserve options after remountpull/8219/head
committed by
GitHub
4 changed files with 254 additions and 26 deletions
@ -0,0 +1,100 @@ |
|||||
|
import type { App, Ref } from 'vue'; |
||||
|
|
||||
|
import type { ExtendedDrawerApi } from '../drawer'; |
||||
|
|
||||
|
import { createApp, defineComponent, h, nextTick, ref } from 'vue'; |
||||
|
|
||||
|
import { afterEach, describe, expect, it, vi } from 'vitest'; |
||||
|
|
||||
|
import { useVbenDrawer } from '../use-drawer'; |
||||
|
|
||||
|
vi.mock('@vben-core/preferences', () => ({ |
||||
|
usePreferences: () => ({ |
||||
|
globalEscapeShortcutKey: { value: true }, |
||||
|
}), |
||||
|
})); |
||||
|
|
||||
|
vi.mock('../drawer.vue', () => ({ |
||||
|
default: { |
||||
|
name: 'VbenDrawerStub', |
||||
|
render: () => null, |
||||
|
}, |
||||
|
})); |
||||
|
|
||||
|
let activeApp: App | undefined; |
||||
|
|
||||
|
async function mountRebindingHarness() { |
||||
|
const consumerKey = ref(0); |
||||
|
let currentApi: ExtendedDrawerApi | undefined; |
||||
|
const onOpenChange = vi.fn(); |
||||
|
|
||||
|
const Consumer = defineComponent(() => { |
||||
|
const [Drawer, drawerApi] = useVbenDrawer(); |
||||
|
currentApi = drawerApi; |
||||
|
return () => h(Drawer); |
||||
|
}); |
||||
|
|
||||
|
const ConnectedDrawer = defineComponent(() => { |
||||
|
return () => h(Consumer, { key: consumerKey.value }); |
||||
|
}); |
||||
|
|
||||
|
const [ParentDrawer, parentApi] = useVbenDrawer({ |
||||
|
connectedComponent: ConnectedDrawer, |
||||
|
onOpenChange, |
||||
|
title: 'Parent drawer title', |
||||
|
}); |
||||
|
const host = document.createElement('div'); |
||||
|
document.body.append(host); |
||||
|
|
||||
|
activeApp = createApp(() => h(ParentDrawer)); |
||||
|
activeApp.mount(host); |
||||
|
await nextTick(); |
||||
|
|
||||
|
return { |
||||
|
consumerKey, |
||||
|
getCurrentApi: () => currentApi, |
||||
|
onOpenChange, |
||||
|
parentApi, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
async function remountConsumer(consumerKey: Ref<number>) { |
||||
|
consumerKey.value += 1; |
||||
|
await nextTick(); |
||||
|
} |
||||
|
|
||||
|
afterEach(() => { |
||||
|
activeApp?.unmount(); |
||||
|
activeApp = undefined; |
||||
|
document.body.innerHTML = ''; |
||||
|
vi.restoreAllMocks(); |
||||
|
}); |
||||
|
|
||||
|
describe('useVbenDrawer', () => { |
||||
|
it('rebinds the parent api when the consumer is recreated', async () => { |
||||
|
const { consumerKey, getCurrentApi, onOpenChange, parentApi } = |
||||
|
await mountRebindingHarness(); |
||||
|
const initialApi = getCurrentApi(); |
||||
|
|
||||
|
expect(initialApi).toBeDefined(); |
||||
|
if (!initialApi) return; |
||||
|
expect(parentApi.store).toBe(initialApi.store); |
||||
|
|
||||
|
await remountConsumer(consumerKey); |
||||
|
const recreatedApi = getCurrentApi(); |
||||
|
|
||||
|
expect(recreatedApi).toBeDefined(); |
||||
|
if (!recreatedApi) return; |
||||
|
expect(recreatedApi).not.toBe(initialApi); |
||||
|
expect(recreatedApi.store.state.title).toBe('Parent drawer title'); |
||||
|
expect(parentApi.store).toBe(recreatedApi.store); |
||||
|
|
||||
|
parentApi.open(); |
||||
|
expect(onOpenChange).toHaveBeenCalledWith(true); |
||||
|
expect(recreatedApi.store.state.isOpen).toBe(true); |
||||
|
expect(initialApi.store.state.isOpen).toBe(false); |
||||
|
|
||||
|
await parentApi.close(); |
||||
|
expect(recreatedApi.store.state.isOpen).toBe(false); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,100 @@ |
|||||
|
import type { App, Ref } from 'vue'; |
||||
|
|
||||
|
import type { ExtendedModalApi } from '../modal'; |
||||
|
|
||||
|
import { createApp, defineComponent, h, nextTick, ref, toRaw } from 'vue'; |
||||
|
|
||||
|
import { afterEach, describe, expect, it, vi } from 'vitest'; |
||||
|
|
||||
|
import { useVbenModal } from '../use-modal'; |
||||
|
|
||||
|
vi.mock('@vben-core/preferences', () => ({ |
||||
|
usePreferences: () => ({ |
||||
|
globalEscapeShortcutKey: { value: true }, |
||||
|
}), |
||||
|
})); |
||||
|
|
||||
|
vi.mock('../modal.vue', () => ({ |
||||
|
default: { |
||||
|
name: 'VbenModalStub', |
||||
|
render: () => null, |
||||
|
}, |
||||
|
})); |
||||
|
|
||||
|
let activeApp: App | undefined; |
||||
|
|
||||
|
async function mountRebindingHarness() { |
||||
|
const consumerKey = ref(0); |
||||
|
let currentApi: ExtendedModalApi | undefined; |
||||
|
const onOpenChange = vi.fn(); |
||||
|
|
||||
|
const Consumer = defineComponent(() => { |
||||
|
const [Modal, modalApi] = useVbenModal(); |
||||
|
currentApi = modalApi; |
||||
|
return () => h(Modal); |
||||
|
}); |
||||
|
|
||||
|
const ConnectedModal = defineComponent(() => { |
||||
|
return () => h(Consumer, { key: consumerKey.value }); |
||||
|
}); |
||||
|
|
||||
|
const [ParentModal, parentApi] = useVbenModal({ |
||||
|
connectedComponent: ConnectedModal, |
||||
|
onOpenChange, |
||||
|
title: 'Parent modal title', |
||||
|
}); |
||||
|
const host = document.createElement('div'); |
||||
|
document.body.append(host); |
||||
|
|
||||
|
activeApp = createApp(() => h(ParentModal)); |
||||
|
activeApp.mount(host); |
||||
|
await nextTick(); |
||||
|
|
||||
|
return { |
||||
|
consumerKey, |
||||
|
getCurrentApi: () => currentApi, |
||||
|
onOpenChange, |
||||
|
parentApi, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
async function remountConsumer(consumerKey: Ref<number>) { |
||||
|
consumerKey.value += 1; |
||||
|
await nextTick(); |
||||
|
} |
||||
|
|
||||
|
afterEach(() => { |
||||
|
activeApp?.unmount(); |
||||
|
activeApp = undefined; |
||||
|
document.body.innerHTML = ''; |
||||
|
vi.restoreAllMocks(); |
||||
|
}); |
||||
|
|
||||
|
describe('useVbenModal', () => { |
||||
|
it('rebinds the parent api when the consumer is recreated', async () => { |
||||
|
const { consumerKey, getCurrentApi, onOpenChange, parentApi } = |
||||
|
await mountRebindingHarness(); |
||||
|
const initialApi = getCurrentApi(); |
||||
|
|
||||
|
expect(initialApi).toBeDefined(); |
||||
|
if (!initialApi) return; |
||||
|
expect(toRaw(parentApi.store)).toBe(initialApi.store); |
||||
|
|
||||
|
await remountConsumer(consumerKey); |
||||
|
const recreatedApi = getCurrentApi(); |
||||
|
|
||||
|
expect(recreatedApi).toBeDefined(); |
||||
|
if (!recreatedApi) return; |
||||
|
expect(recreatedApi).not.toBe(initialApi); |
||||
|
expect(recreatedApi.store.state.title).toBe('Parent modal title'); |
||||
|
expect(toRaw(parentApi.store)).toBe(recreatedApi.store); |
||||
|
|
||||
|
parentApi.open(); |
||||
|
expect(onOpenChange).toHaveBeenCalledWith(true); |
||||
|
expect(recreatedApi.store.state.isOpen).toBe(true); |
||||
|
expect(initialApi.store.state.isOpen).toBe(false); |
||||
|
|
||||
|
await parentApi.close(); |
||||
|
expect(recreatedApi.store.state.isOpen).toBe(false); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue