feat(objects): open routines in source tab on double click
This commit is contained in:
1 parent
41ec53442c
commit
55d4b0b613
4 files changed
+276
-10
No files matched your search
@@ -80,8 +80,27 @@ describe("doubleClickRowAction", () => {
|
||||
expect(doubleClickRowAction(row(type, "v_orders"))).toBe("open-table");
|
||||
});
|
||||
|
||||
it("returns open-source for PROCEDURE", () => {
|
||||
expect(doubleClickRowAction(row("PROCEDURE", "sp_run"))).toBe("open-source");
|
||||
it.each(["PROCEDURE", "FUNCTION"] as const)("returns open-source-tab for %s (editable source tab, not the side panel)", (type) => {
|
||||
expect(doubleClickRowAction(row(type, "sp_run"))).toBe("open-source-tab");
|
||||
});
|
||||
|
||||
it("keeps routines on the source tab across database types", () => {
|
||||
for (const dbType of ["mysql", "postgres", "oracle", "xugu"] as const) {
|
||||
expect(doubleClickRowAction(row("PROCEDURE", "sp_run"), dbType), String(dbType)).toBe("open-source-tab");
|
||||
expect(doubleClickRowAction(row("FUNCTION", "fn_run"), dbType), String(dbType)).toBe("open-source-tab");
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves the other source-backed types on the side panel", () => {
|
||||
expect(doubleClickRowAction(row("EVENT", "ev_nightly"))).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("SEQUENCE", "seq_test"))).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("PACKAGE", "pkg_test"))).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("PACKAGE_BODY", "pkg_body_test"))).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("TRIGGER", "trg_test"), "xugu")).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("TYPE", "app_status"), "xugu")).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("TYPE_BODY", "app_status"), "xugu")).toBe("open-source");
|
||||
expect(doubleClickRowAction(row("VIEW", "v_users"))).toBe("open-table");
|
||||
expect(doubleClickRowAction(row("MATERIALIZED_VIEW", "mv_users"))).toBe("open-table");
|
||||
});
|
||||
|
||||
it("returns none for null/undefined", () => {
|
||||
@@ -128,6 +147,12 @@ describe("resolveRowClickAction", () => {
|
||||
expect(result.action).toBe("open-table");
|
||||
expect(result.isDouble).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["PROCEDURE", "FUNCTION"] as const)("single click on %s returns open-source, double click returns open-source-tab", (type) => {
|
||||
const routineRow = row(type, "sp_run");
|
||||
expect(resolveRowClickAction(routineRow, 1, "single")).toEqual({ action: "open-source", isDouble: false });
|
||||
expect(resolveRowClickAction(routineRow, 2, "single")).toEqual({ action: "open-source-tab", isDouble: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("double-click activation mode", () => {
|
||||
@@ -154,6 +179,12 @@ describe("resolveRowClickAction", () => {
|
||||
expect(result.action).toBe("open-table");
|
||||
expect(result.isDouble).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["PROCEDURE", "FUNCTION"] as const)("single click on %s returns open-source, double click returns open-source-tab", (type) => {
|
||||
const routineRow = row(type, "sp_run");
|
||||
expect(resolveRowClickAction(routineRow, 1, "double")).toEqual({ action: "open-source", isDouble: false });
|
||||
expect(resolveRowClickAction(routineRow, 2, "double")).toEqual({ action: "open-source-tab", isDouble: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,8 +200,15 @@ describe("shouldDeferSingleClick", () => {
|
||||
expect(shouldDeferSingleClick(viewRow, "open-source")).toBe(true);
|
||||
});
|
||||
|
||||
it("does not defer PROCEDURE open-source (same single/double action)", () => {
|
||||
expect(shouldDeferSingleClick(row("PROCEDURE", "sp_run"), "open-source")).toBe(false);
|
||||
it.each(["PROCEDURE", "FUNCTION"] as const)("defers %s open-source so a second click can cancel the side panel", (type) => {
|
||||
// single → open-source, double → open-source-tab: the 250ms deferral is what
|
||||
// keeps a fast double click from also opening the side panel (issue #10202).
|
||||
expect(shouldDeferSingleClick(row(type, "sp_run"), "open-source")).toBe(true);
|
||||
expect(shouldDeferSingleClick(row(type, "sp_run"), "open-source-tab")).toBe(false);
|
||||
});
|
||||
|
||||
it("does not defer SEQUENCE open-source (same single/double action)", () => {
|
||||
expect(shouldDeferSingleClick(row("SEQUENCE", "seq_run"), "open-source")).toBe(false);
|
||||
});
|
||||
|
||||
it("does not defer none action", () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { DatabaseType } from "@/types/database";
|
||||
import { customTypeCapabilities, supportsTypeObjectSource } from "@/lib/database/databaseObjectCapabilities";
|
||||
import type { ObjectBrowserRow } from "@/lib/table/objectBrowserRows";
|
||||
|
||||
export type ObjectBrowserRowAction = "table-info" | "type-info" | "open-table" | "open-source" | "none";
|
||||
export type ObjectBrowserRowAction = "table-info" | "type-info" | "open-table" | "open-source" | "open-source-tab" | "none";
|
||||
|
||||
/**
|
||||
* Determine the action for a single click on an object browser row.
|
||||
@@ -28,7 +28,8 @@ export function singleClickRowAction(row: ObjectBrowserRow | null | undefined, d
|
||||
* Determine the action for a double click on an object browser row.
|
||||
* - TABLE/VIEW/MATERIALIZED_VIEW → open-table (open data tab, matching the
|
||||
* sidebar's data-node double-click behavior)
|
||||
* - PROCEDURE/FUNCTION/TRIGGER/SEQUENCE/PACKAGE/PACKAGE_BODY/TYPE/TYPE_BODY → open-source
|
||||
* - PROCEDURE/FUNCTION → open-source-tab (editable source tab; single click keeps the side panel)
|
||||
* - TRIGGER/SEQUENCE/PACKAGE/PACKAGE_BODY/TYPE/TYPE_BODY → open-source
|
||||
* - otherwise → none
|
||||
*/
|
||||
export function doubleClickRowAction(row: ObjectBrowserRow | null | undefined, dbType?: DatabaseType): ObjectBrowserRowAction {
|
||||
@@ -37,6 +38,10 @@ export function doubleClickRowAction(row: ObjectBrowserRow | null | undefined, d
|
||||
if (row.type === "TABLE" || row.type === "VIEW" || row.type === "MATERIALIZED_VIEW") return "open-table";
|
||||
if (row.type === "EVENT") return "open-source";
|
||||
if (row.type === "TYPE" && customTypeCapabilities(dbType).details) return "type-info";
|
||||
// Routines are the only source-backed rows with a dedicated double-click
|
||||
// gesture (issue #10202): single click keeps the side panel, double click
|
||||
// hands the object to the source tab. canOpenSource always accepts them.
|
||||
if (row.type === "PROCEDURE" || row.type === "FUNCTION") return "open-source-tab";
|
||||
if (canOpenSource(row, dbType)) return "open-source";
|
||||
return "none";
|
||||
}
|
||||
@@ -47,7 +52,8 @@ export function doubleClickRowAction(row: ObjectBrowserRow | null | undefined, d
|
||||
*
|
||||
* In both single-click and double-click activation modes, a single click
|
||||
* triggers the side-panel action (table-info / open-source). When a distinct
|
||||
* double-click action exists (e.g. TABLE single→table-info, double→open-table),
|
||||
* double-click action exists (e.g. TABLE single→table-info, double→open-table;
|
||||
* PROCEDURE single→open-source, double→open-source-tab),
|
||||
* the caller defers the single-click via shouldDeferSingleClick so the second
|
||||
* click can cancel it.
|
||||
*/
|
||||
@@ -65,8 +71,9 @@ export function resolveRowClickAction(row: ObjectBrowserRow | null | undefined,
|
||||
* Whether a single-click action should be deferred to distinguish it from a
|
||||
* possible upcoming double-click. Applies when the row's single-click and
|
||||
* double-click actions differ (e.g. TABLE: single → table-info, double →
|
||||
* open-table). For rows whose single and double actions are identical
|
||||
* (e.g. VIEW → open-source both), no deferral is needed.
|
||||
* open-table; PROCEDURE/FUNCTION: single → open-source, double →
|
||||
* open-source-tab). For rows whose single and double actions are identical
|
||||
* (e.g. SEQUENCE → open-source both), no deferral is needed.
|
||||
*/
|
||||
export function shouldDeferSingleClick(row: ObjectBrowserRow | null | undefined, action: ObjectBrowserRowAction, dbType?: DatabaseType): boolean {
|
||||
if (action === "none") return false;
|
||||
|
||||
Reference in new issue
Block a user