fix(objects): render MySQL event create editor with fresh state

This commit is contained in:
skyler
2026-08-30 09:58:45 +08:00
committed by GitHub
parent 349342efb7
commit de2c4b38cd
4 changed files with 41 additions and 6 deletions
@@ -155,7 +155,7 @@ import { loadObjectMetadataFacet } from "@/lib/metadata/objectMetadataCache";
import { invalidateObjectMetadataCache } from "@/lib/metadata/objectMetadataCache";
import { invalidateObjectDdl } from "@/lib/metadata/objectDdlCache";
import { invalidateObjectBrowserRowsCache } from "@/lib/table/objectBrowserRowsCache";
import { resolveInitialEventEditorRequest } from "@/lib/table/eventEditorRequest";
import { eventEditorInstanceKey, resolveInitialEventEditorRequest } from "@/lib/table/eventEditorRequest";
type ObjectFilter = ObjectBrowserFilter;
type ObjectBrowserColumnKey = "select" | "name" | "type" | "estimatedRows" | "totalBytes" | "created_at" | "updated_at" | "comment";
@@ -214,6 +214,13 @@ const sidePanelRow = ref<ObjectBrowserRow | null>(null);
const openedInitialEvent = ref("");
const isEventEditor = computed(() => sidePanelMode.value === "event-editor");
const sidePanelMode = ref<"table-info" | "source" | "type-info" | "event-editor">("source");
const eventEditorKey = computed(() =>
eventEditorInstanceKey({
createRequestId: props.initialEventCreateRequestId,
openRequestId: props.initialEventOpenRequestId,
rowId: sidePanelRow.value?.id,
}),
);
// Table info panel state
const tableInfoTab = ref<TableInfoTab>("ddl");
const tableColumns = ref<ColumnInfo[]>([]);
@@ -3492,7 +3499,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
</div>
</div>
<!-- Right-side panel: table info or source -->
<div v-if="sidePanelRow" class="object-browser-side-panel relative flex min-h-0 shrink-0 flex-col border-l bg-background" :class="{ 'side-panel-resizing': isResizingSidePanel }" :style="{ width: `${sidePanelWidth}px` }">
<div v-if="sidePanelRow || isEventEditor" class="object-browser-side-panel relative flex min-h-0 shrink-0 flex-col border-l bg-background" :class="{ 'side-panel-resizing': isResizingSidePanel }" :style="{ width: `${sidePanelWidth}px` }">
<div class="absolute left-0 top-0 bottom-0 z-20 w-1.5 -translate-x-1/2 cursor-col-resize hover:bg-primary/30" @mousedown.prevent="onSidePanelResizeStart" />
<!-- Table info mode -->
<template v-if="sidePanelMode === 'table-info'">
@@ -3681,7 +3688,7 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
<CustomTypeInfoPanel ref="sidePanelRef" :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name || ''" :catalog="props.catalog" @close="closeSidePanel" />
</template>
<template v-else-if="sidePanelMode === 'event-editor'">
<MySqlEventEditor :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name" :read-only="props.initialEventReadOnly" @saved="onEventSaved" @close="closeSidePanel" />
<MySqlEventEditor :key="eventEditorKey" :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name" :read-only="props.initialEventReadOnly" @saved="onEventSaved" @close="closeSidePanel" />
</template>
<!-- Source mode (views, procedures, functions, sequences) -->
<template v-else>
@@ -42,8 +42,14 @@ describe("ObjectBrowser MySQL Event CREATE navigation", () => {
expect(openInitialEventIfNeeded).not.toContain("sidePanelRow.value = row;");
});
it("renders the editor with sidePanelRow name (empty in create mode -> CREATE SQL)", () => {
expect(objectBrowserSource).toContain('<MySqlEventEditor :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name"');
it("keeps the CREATE editor mounted when there is no EVENT row", () => {
expect(objectBrowserSource).toContain('v-if="sidePanelRow || isEventEditor"');
expect(objectBrowserSource).toContain('<MySqlEventEditor :key="eventEditorKey"');
});
it("renders the editor with an empty CREATE name and request-scoped instance", () => {
expect(objectBrowserSource).toContain('<MySqlEventEditor :key="eventEditorKey" :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name"');
expect(objectBrowserSource).toMatch(/const eventEditorKey = computed\(\(\) =>[\s\S]*?createRequestId: props\.initialEventCreateRequestId[\s\S]*?openRequestId: props\.initialEventOpenRequestId[\s\S]*?rowId: sidePanelRow\.value\?\.id/);
});
it("re-triggers a create request when the request id changes on a reused tab", () => {
+12 -1
View File
@@ -1,8 +1,19 @@
import { describe, expect, it } from "vitest";
import { resolveInitialEventEditorRequest } from "./eventEditorRequest";
import { eventEditorInstanceKey, resolveInitialEventEditorRequest } from "./eventEditorRequest";
const base = { openedRequestKey: "" };
describe("eventEditorInstanceKey", () => {
it("separates repeated CREATE requests", () => {
expect(eventEditorInstanceKey({ createRequestId: 1 })).not.toBe(eventEditorInstanceKey({ createRequestId: 2 }));
});
it("separates ALTER rows from CREATE mode", () => {
expect(eventEditorInstanceKey({ openRequestId: 1, rowId: "event:old_event" })).not.toBe(eventEditorInstanceKey({ createRequestId: 3 }));
expect(eventEditorInstanceKey({ openRequestId: 1, rowId: "event:old_event" })).not.toBe(eventEditorInstanceKey({ openRequestId: 1, rowId: "event:other_event" }));
});
});
describe("resolveInitialEventEditorRequest", () => {
describe("create request (New Event)", () => {
it("opens CREATE mode without requiring an existing EVENT row", () => {
@@ -30,6 +30,17 @@ export interface InitialEventEditorRequestInput {
loadingObjects?: boolean;
}
export interface EventEditorInstanceKeyInput {
createRequestId?: number;
openRequestId?: number;
rowId?: string;
}
export function eventEditorInstanceKey({ createRequestId, openRequestId, rowId }: EventEditorInstanceKeyInput): string {
const requestKey = createRequestId !== undefined ? `create:${createRequestId}` : `open:${openRequestId ?? 0}`;
return `${requestKey}:${rowId ?? "new"}`;
}
export function resolveInitialEventEditorRequest(input: InitialEventEditorRequestInput): InitialEventEditorDecision {
const { eventCreateRequestId, eventName, eventOpenRequestId, openedRequestKey, hasEventRow = false, loadingObjects = false } = input;