fix(sidebar): sync create database dialog state
This commit is contained in:
1 parent
08cd8903f4
commit
c88fad6bd8
5 files changed
+108
-9
No files matched your search
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, watch, onBeforeUnmount, inject, reactive, shallowRef } from "vue";
|
||||
import { createRoutedSidebarDialogController } from "./sidebarDialogControllerRouting";
|
||||
import { useSqlHighlighter } from "@/composables/useSqlHighlighter";
|
||||
import { useSidebarDataOpenRuntime } from "@/composables/useSidebarDataOpenRuntime";
|
||||
import { useSidebarConnectionMutationRuntime } from "@/composables/useSidebarConnectionMutationRuntime";
|
||||
@@ -404,14 +405,15 @@ function routeTreeItemDialogController() {
|
||||
const controller = getTreeItemDialogController();
|
||||
const target = createSidebarActionTarget(activeNode.value);
|
||||
sidebarFormTarget.value = target;
|
||||
const routedController = reactive<Record<string, any>>({ ...controller, node: target });
|
||||
for (const [key, value] of Object.entries(controller)) {
|
||||
if (typeof value !== "function") continue;
|
||||
routedController[key] = (...args: unknown[]) => {
|
||||
activateActionTarget(target);
|
||||
return value(...args);
|
||||
};
|
||||
}
|
||||
const routedController = createRoutedSidebarDialogController(controller, {
|
||||
node: target,
|
||||
wrapAction: (action) => {
|
||||
return (...args: unknown[]) => {
|
||||
activateActionTarget(target);
|
||||
return action(...args);
|
||||
};
|
||||
},
|
||||
});
|
||||
routedController.pasteTableDataCopySupported = pasteTableDataCopySupported.value;
|
||||
routedController.canSetCreateDatabaseCharset = canSetCreateDatabaseCharset.value;
|
||||
routedController.canEditDatabaseCharsetCollation = canEditDatabaseCharsetCollation.value;
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import { createRoutedSidebarDialogController } from "@/components/sidebar/sidebarDialogControllerRouting";
|
||||
|
||||
describe("createRoutedSidebarDialogController", () => {
|
||||
it("keeps dialog open flags linked to the shared module refs", () => {
|
||||
const showCreateDatabaseDialog = ref(false);
|
||||
const createDatabaseName = ref("");
|
||||
const controller = reactive({
|
||||
showCreateDatabaseDialog,
|
||||
createDatabaseName,
|
||||
confirmCreateDatabase: () => "ok",
|
||||
});
|
||||
|
||||
let wrappedCalls = 0;
|
||||
const routed = createRoutedSidebarDialogController(controller, {
|
||||
node: { id: "connection:1" },
|
||||
wrapAction: (action) => {
|
||||
return (...args) => {
|
||||
wrappedCalls += 1;
|
||||
return action(...args);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const { showCreateDatabaseDialog: dialogOpen, createDatabaseName: dialogName } = toRefs(routed);
|
||||
|
||||
showCreateDatabaseDialog.value = true;
|
||||
expect(dialogOpen.value).toBe(true);
|
||||
|
||||
dialogOpen.value = false;
|
||||
expect(showCreateDatabaseDialog.value).toBe(false);
|
||||
|
||||
dialogName.value = "analytics";
|
||||
expect(createDatabaseName.value).toBe("analytics");
|
||||
|
||||
expect(routed.confirmCreateDatabase()).toBe("ok");
|
||||
expect(wrappedCalls).toBe(1);
|
||||
expect(routed.node).toEqual({ id: "connection:1" });
|
||||
});
|
||||
|
||||
it("does not leave shared open flags stuck after a disconnected-style cancel", () => {
|
||||
const showCreateDatabaseDialog = ref(false);
|
||||
const controller = reactive({ showCreateDatabaseDialog });
|
||||
|
||||
// Reproduce the old bug: spreading a reactive proxy unwraps refs.
|
||||
const broken = reactive({ ...controller, node: { id: "connection:1" } });
|
||||
showCreateDatabaseDialog.value = true;
|
||||
expect(broken.showCreateDatabaseDialog).toBe(false);
|
||||
broken.showCreateDatabaseDialog = false;
|
||||
expect(showCreateDatabaseDialog.value).toBe(true);
|
||||
|
||||
// Fixed routing keeps cancel and open in sync.
|
||||
const routed = createRoutedSidebarDialogController(controller, {
|
||||
node: { id: "connection:1" },
|
||||
wrapAction: (action) => action,
|
||||
});
|
||||
showCreateDatabaseDialog.value = true;
|
||||
expect(routed.showCreateDatabaseDialog).toBe(true);
|
||||
routed.showCreateDatabaseDialog = false;
|
||||
expect(showCreateDatabaseDialog.value).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { reactive, toRaw } from "vue";
|
||||
|
||||
/**
|
||||
* Build a per-open dialog controller without unwrapping shared dialog refs.
|
||||
*
|
||||
* Spreading a reactive controller (`{ ...controller }`) unwraps nested refs into
|
||||
* plain booleans. The dialog UI then mutates a disconnected copy, while actions
|
||||
* still write the module-level refs — so cancel leaves `showCreateDatabaseDialog`
|
||||
* stuck `true`, and the next sidebar right-click remounts an already-open dialog.
|
||||
*/
|
||||
export function createRoutedSidebarDialogController(
|
||||
controller: Record<string, unknown>,
|
||||
options: {
|
||||
node: unknown;
|
||||
wrapAction: (action: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown;
|
||||
},
|
||||
): Record<string, any> {
|
||||
const routedController = reactive<Record<string, any>>({});
|
||||
for (const [key, value] of Object.entries(toRaw(controller))) {
|
||||
if (key === "node") continue;
|
||||
if (typeof value === "function") {
|
||||
routedController[key] = options.wrapAction(value as (...args: unknown[]) => unknown);
|
||||
continue;
|
||||
}
|
||||
routedController[key] = value;
|
||||
}
|
||||
routedController.node = options.node;
|
||||
return routedController;
|
||||
}
|
||||
Reference in new issue
Block a user