1 parent
5767a0fd59
commit
90a18a7b23
2 files changed
+105
-1
No files matched your search
@@ -1853,6 +1853,10 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function currentContextMenuItems(): ContextMenuItem[] {
|
||||||
|
return contextMenuItems.value;
|
||||||
|
}
|
||||||
|
|
||||||
function handleSqlIntentionActions(currentView: EditorViewType): boolean {
|
function handleSqlIntentionActions(currentView: EditorViewType): boolean {
|
||||||
if (props.readOnly) return false;
|
if (props.readOnly) return false;
|
||||||
try {
|
try {
|
||||||
@@ -5838,7 +5842,7 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full w-full overflow-hidden relative" @gesturestart="onEditorGestureStart" @gesturechange="onEditorGestureChange" @gestureend="onEditorGestureEnd">
|
<div class="h-full w-full overflow-hidden relative" @gesturestart="onEditorGestureStart" @gesturechange="onEditorGestureChange" @gestureend="onEditorGestureEnd">
|
||||||
<CustomContextMenu :items="contextMenuItems" @close="contextMenuOpen = false" v-slot="{ onContextMenu }">
|
<CustomContextMenu :items="currentContextMenuItems" @close="contextMenuOpen = false" v-slot="{ onContextMenu }">
|
||||||
<div
|
<div
|
||||||
ref="editorRef"
|
ref="editorRef"
|
||||||
data-query-editor-root
|
data-query-editor-root
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// @vitest-environment happy-dom
|
||||||
|
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { computed, createApp, defineComponent, h, nextTick, ref } from "vue";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||||
|
|
||||||
|
const source = readFileSync(path.resolve(process.cwd(), "apps/desktop/src/components/editor/QueryEditor.vue"), "utf8");
|
||||||
|
|
||||||
|
describe("QueryEditor context menu lifecycle", () => {
|
||||||
|
it("resolves menu items after synchronizing the right-click target", () => {
|
||||||
|
const syncStart = source.indexOf("function syncContextMenuStateAtEvent");
|
||||||
|
const syncEnd = source.indexOf("\n}", syncStart);
|
||||||
|
const syncSource = source.slice(syncStart, syncEnd);
|
||||||
|
const syncIndex = source.indexOf("syncContextMenuStateAtEvent(view, e);");
|
||||||
|
const openIndex = source.indexOf("onContextMenu(e);", syncIndex);
|
||||||
|
const getterStart = source.indexOf("function currentContextMenuItems()");
|
||||||
|
const getterEnd = source.indexOf("\n}", getterStart);
|
||||||
|
const getterSource = source.slice(getterStart, getterEnd);
|
||||||
|
|
||||||
|
expect(syncStart).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(syncEnd).toBeGreaterThan(syncStart);
|
||||||
|
expect(syncSource).toContain("if (pos == null)");
|
||||||
|
expect(syncSource).toContain("contextObjectTarget.value = null;");
|
||||||
|
expect(syncIndex).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(openIndex).toBeGreaterThan(syncIndex);
|
||||||
|
expect(getterStart).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(getterEnd).toBeGreaterThan(getterStart);
|
||||||
|
expect(getterSource).toContain("return contextMenuItems.value;");
|
||||||
|
expect(getterSource).not.toContain("nextTick");
|
||||||
|
expect(getterSource).not.toContain("setTimeout");
|
||||||
|
expect(source).toContain(':items="currentContextMenuItems"');
|
||||||
|
expect(source).not.toContain('<CustomContextMenu :items="contextMenuItems"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the target synchronized in the current context-menu event", async () => {
|
||||||
|
const target = ref<string | null>(null);
|
||||||
|
const openedTargets: string[] = [];
|
||||||
|
const items = computed<ContextMenuItem[]>(() => [
|
||||||
|
{
|
||||||
|
label: target.value ? `Inspect ${target.value}` : "Inspect",
|
||||||
|
disabled: !target.value,
|
||||||
|
action: () => {
|
||||||
|
if (target.value) openedTargets.push(target.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const root = defineComponent({
|
||||||
|
setup() {
|
||||||
|
const currentItems = () => items.value;
|
||||||
|
const contextTarget = (id: string, value: string | null) =>
|
||||||
|
h(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
onContextmenu: (event: MouseEvent) => {
|
||||||
|
target.value = value;
|
||||||
|
onContextMenu(event);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
let onContextMenu = (_event: MouseEvent) => {};
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
CustomContextMenu,
|
||||||
|
{ items: currentItems },
|
||||||
|
{
|
||||||
|
default: (slot: { onContextMenu: (event: MouseEvent) => void }) => {
|
||||||
|
onContextMenu = slot.onContextMenu;
|
||||||
|
return [contextTarget("table-a", "table_a"), contextTarget("table-b", "table_b"), contextTarget("empty", null)];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const container = document.createElement("div");
|
||||||
|
document.body.append(container);
|
||||||
|
const app = createApp(root);
|
||||||
|
app.mount(container);
|
||||||
|
|
||||||
|
const open = async (id: string) => {
|
||||||
|
container.querySelector(`#${id}`)?.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, cancelable: true }));
|
||||||
|
await nextTick();
|
||||||
|
return document.querySelector<HTMLButtonElement>("[data-dbx-context-menu] button");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(await open("table-a")).toMatchObject({ disabled: false, textContent: "Inspect table_a" });
|
||||||
|
expect(await open("table-a")).toMatchObject({ disabled: false, textContent: "Inspect table_a" });
|
||||||
|
const tableBItem = await open("table-b");
|
||||||
|
expect(tableBItem).toMatchObject({ disabled: false, textContent: "Inspect table_b" });
|
||||||
|
tableBItem?.click();
|
||||||
|
expect(openedTargets).toEqual(["table_b"]);
|
||||||
|
expect(await open("empty")).toMatchObject({ disabled: true, textContent: "Inspect" });
|
||||||
|
|
||||||
|
app.unmount();
|
||||||
|
container.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user