Merge branch 'fork/dream-weave/fix/popup-route-reentry'
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
|
||||||
|
import { createApp, defineComponent, h, nextTick } from 'vue';
|
||||||
|
|
||||||
|
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
||||||
|
|
||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { useVbenDrawer } from '../use-drawer';
|
||||||
|
|
||||||
|
vi.mock('@vben-core/preferences', () => ({
|
||||||
|
usePreferences: () => ({
|
||||||
|
globalEscapeShortcutKey: { value: true },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let activeApp: App | undefined;
|
||||||
|
|
||||||
|
async function mountPreopenedDrawer() {
|
||||||
|
const mainContent = document.createElement('main');
|
||||||
|
mainContent.id = ELEMENT_ID_MAIN_CONTENT;
|
||||||
|
mainContent.innerHTML = '<div><div></div></div>';
|
||||||
|
document.body.append(mainContent);
|
||||||
|
|
||||||
|
const Consumer = defineComponent(() => {
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({ appendToMain: true });
|
||||||
|
drawerApi.open();
|
||||||
|
return () => h(Drawer);
|
||||||
|
});
|
||||||
|
const host = document.createElement('div');
|
||||||
|
document.body.append(host);
|
||||||
|
|
||||||
|
activeApp = createApp(() => h(Consumer));
|
||||||
|
activeApp.mount(host);
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
return mainContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
activeApp?.unmount();
|
||||||
|
activeApp = undefined;
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('vben drawer', () => {
|
||||||
|
it('mounts an initially open drawer directly in the main content', async () => {
|
||||||
|
const mainContent = await mountPreopenedDrawer();
|
||||||
|
const dialog = document.querySelector('[role="dialog"]');
|
||||||
|
|
||||||
|
expect(dialog).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(dialog instanceof HTMLElement)) return;
|
||||||
|
expect(dialog.parentElement).toBe(mainContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows a drawer that is opened before mounting', async () => {
|
||||||
|
await mountPreopenedDrawer();
|
||||||
|
const dialog = document.querySelector('[role="dialog"]');
|
||||||
|
|
||||||
|
expect(dialog).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(dialog instanceof HTMLElement)) return;
|
||||||
|
expect(dialog.classList.contains('hidden')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -148,9 +148,7 @@ function handleFocusOutside(e: Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAppendTo = computed(() => {
|
const getAppendTo = computed(() => {
|
||||||
return appendToMain.value
|
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
|
||||||
? `#${ELEMENT_ID_MAIN_CONTENT}>div:not(.absolute)>div`
|
|
||||||
: undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,6 +165,7 @@ watch(
|
|||||||
hasOpened.value = true;
|
hasOpened.value = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
function handleClosed() {
|
function handleClosed() {
|
||||||
isClosed.value = true;
|
isClosed.value = true;
|
||||||
|
|||||||
102
packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal.test.ts
Normal file
102
packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal.test.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
|
||||||
|
import { createApp, defineComponent, h, nextTick, onMounted } from 'vue';
|
||||||
|
|
||||||
|
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
||||||
|
|
||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { useVbenModal } from '../use-modal';
|
||||||
|
|
||||||
|
vi.mock('@vben-core/preferences', () => ({
|
||||||
|
usePreferences: () => ({
|
||||||
|
globalEscapeShortcutKey: { value: true },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let activeApp: App | undefined;
|
||||||
|
|
||||||
|
async function mountModal() {
|
||||||
|
const mainContent = document.createElement('main');
|
||||||
|
mainContent.id = ELEMENT_ID_MAIN_CONTENT;
|
||||||
|
mainContent.innerHTML = '<div><div></div></div>';
|
||||||
|
document.body.append(mainContent);
|
||||||
|
|
||||||
|
const Consumer = defineComponent(() => {
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
appendToMain: true,
|
||||||
|
draggable: true,
|
||||||
|
title: 'Draggable modal',
|
||||||
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
modalApi.open();
|
||||||
|
});
|
||||||
|
return () => h(Modal);
|
||||||
|
});
|
||||||
|
const host = document.createElement('div');
|
||||||
|
document.body.append(host);
|
||||||
|
|
||||||
|
activeApp = createApp(() => h(Consumer));
|
||||||
|
activeApp.mount(host);
|
||||||
|
await nextTick();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
return mainContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
activeApp?.unmount();
|
||||||
|
activeApp = undefined;
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('vben modal', () => {
|
||||||
|
it('mounts an open modal directly in the main content', async () => {
|
||||||
|
const mainContent = await mountModal();
|
||||||
|
const dialog = document.querySelector('[role="dialog"]');
|
||||||
|
const overlay = document.querySelector('[data-dismissable-modal]');
|
||||||
|
|
||||||
|
expect(dialog).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(dialog instanceof HTMLElement)) return;
|
||||||
|
expect(overlay).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(overlay instanceof HTMLElement)) return;
|
||||||
|
expect(dialog.parentElement).toBe(mainContent);
|
||||||
|
expect(overlay.parentElement).toBe(mainContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('constrains dragging to the main content', async () => {
|
||||||
|
const mainContent = await mountModal();
|
||||||
|
const dialog = document.querySelector('[role="dialog"]');
|
||||||
|
const header = document.querySelector('.cursor-move');
|
||||||
|
|
||||||
|
expect(dialog).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(dialog instanceof HTMLElement)) return;
|
||||||
|
expect(header).toBeInstanceOf(HTMLElement);
|
||||||
|
if (!(header instanceof HTMLElement)) return;
|
||||||
|
|
||||||
|
vi.spyOn(mainContent, 'getBoundingClientRect').mockReturnValue(
|
||||||
|
new DOMRect(100, 100, 800, 600),
|
||||||
|
);
|
||||||
|
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue(
|
||||||
|
new DOMRect(300, 200, 400, 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
header.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', {
|
||||||
|
bubbles: true,
|
||||||
|
clientX: 400,
|
||||||
|
clientY: 300,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
document.dispatchEvent(
|
||||||
|
new MouseEvent('mousemove', {
|
||||||
|
clientX: 1400,
|
||||||
|
clientY: 1300,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dialog.style.transform).toBe('translate(200px, 200px)');
|
||||||
|
document.dispatchEvent(new MouseEvent('mouseup'));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -107,9 +107,7 @@ const shouldCentered = computed(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const getAppendTo = computed(() => {
|
const getAppendTo = computed(() => {
|
||||||
return appendToMain.value
|
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
|
||||||
? `#${ELEMENT_ID_MAIN_CONTENT}>div:not(.absolute)>div`
|
|
||||||
: undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dragging, transform } = useModalDraggable(
|
const { dragging, transform } = useModalDraggable(
|
||||||
|
|||||||
Reference in New Issue
Block a user