fix(sidebar): correct connection search activation
This commit is contained in:
1 parent
e9538ee1dd
commit
5173bbafd1
3 files changed
+153
-1
No files matched your search
+59
@@ -4,6 +4,7 @@ import { createApp, defineComponent, h, nextTick, type App } from "vue";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import i18n from "@/i18n";
|
||||
import TreeItem from "@/components/sidebar/TreeItem.vue";
|
||||
import { filterSidebarTree } from "@/lib/sidebar/sidebarSearchTree";
|
||||
import { createSidebarTreeRuntime, sidebarTreeRuntimeKey, type SidebarTreeRuntimeHost } from "@/lib/sidebar/sidebarTreeRuntime";
|
||||
import type { TreeNode } from "@/types/database";
|
||||
|
||||
@@ -11,9 +12,12 @@ const connectionStore = {
|
||||
activeConnectionId: "connection-1",
|
||||
connectedIds: new Set(["connection-1"]),
|
||||
connectingIds: new Set<string>(),
|
||||
connectionErrors: {},
|
||||
connectionMultiSelectActive: false,
|
||||
connections: [],
|
||||
getConfig: () => ({ id: "connection-1", db_type: "sqlserver" }),
|
||||
getSidebarVisibleFilterSummary: () => null,
|
||||
clearConnectionError: vi.fn(),
|
||||
isDefaultDatabase: () => false,
|
||||
isDefaultSchema: () => false,
|
||||
isPinnedTreeNodeReorderTarget: () => false,
|
||||
@@ -182,3 +186,58 @@ describe("TreeItem load-more activation", () => {
|
||||
expect(unknown.row.querySelector('[data-invalid-object-indicator="true"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("TreeItem searched connection activation", () => {
|
||||
function searchedConnection(): TreeNode {
|
||||
const filtered = filterSidebarTree(
|
||||
[
|
||||
{
|
||||
id: "connection-1",
|
||||
label: "1000",
|
||||
type: "connection",
|
||||
connectionId: "connection-1",
|
||||
isExpanded: false,
|
||||
children: [
|
||||
{
|
||||
id: "connection-1:__user_admin",
|
||||
label: "tree.userAdmin",
|
||||
type: "user-admin",
|
||||
connectionId: "connection-1",
|
||||
database: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"1000",
|
||||
new Set(),
|
||||
)[0];
|
||||
if (!filtered) throw new Error("Expected the matching connection search result");
|
||||
return filtered;
|
||||
}
|
||||
|
||||
it("activates a connection search result on one click in single-click mode", async () => {
|
||||
settingsStore.editorSettings.sidebarActivation = "single";
|
||||
const node = searchedConnection();
|
||||
const { row, host } = await mountTreeItem(node);
|
||||
|
||||
row.dispatchEvent(new MouseEvent("click", { bubbles: true, detail: 1 }));
|
||||
|
||||
expect(node.children).toEqual([]);
|
||||
expect(node.isExpanded).toBe(false);
|
||||
expect(host.handleRowClick).toHaveBeenCalledWith(node, 1);
|
||||
expect(host.handleRowDoubleClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("waits for a double click before activating a connection search result in double-click mode", async () => {
|
||||
settingsStore.editorSettings.sidebarActivation = "double";
|
||||
const node = searchedConnection();
|
||||
const { row, host } = await mountTreeItem(node);
|
||||
|
||||
row.dispatchEvent(new MouseEvent("click", { bubbles: true, detail: 1 }));
|
||||
expect(host.handleRowClick).not.toHaveBeenCalled();
|
||||
|
||||
row.dispatchEvent(new MouseEvent("dblclick", { bubbles: true, detail: 2 }));
|
||||
|
||||
expect(host.handleRowDoubleClick).toHaveBeenCalledWith(node, expect.any(MouseEvent));
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import type { TreeNode, TreeNodeType } from "@/types/database";
|
||||
import { createSidebarLabelMatcher, type SidebarLabelMatcher } from "@/lib/sidebar/sidebarSearch";
|
||||
|
||||
const preserveMatchedSubtreeTypes = new Set(["connection", "database", "schema", "table", "view", "mongo-db", "mongo-collection"]);
|
||||
const hiddenSearchNodeTypes = new Set<TreeNodeType>(["user-admin", "dameng-job-admin"]);
|
||||
|
||||
function bestMatch(matchLabel: SidebarLabelMatcher, label: string, comment?: string | null) {
|
||||
const lm = matchLabel(label);
|
||||
@@ -43,10 +44,16 @@ function applySearchCollapsedState(node: TreeNode, collapsedIds: ReadonlySet<str
|
||||
};
|
||||
}
|
||||
|
||||
function preservedSearchChildren(node: TreeNode, collapsedIds: ReadonlySet<string>): TreeNode[] | undefined {
|
||||
if (!node.children) return undefined;
|
||||
return node.children.filter((child) => !hiddenSearchNodeTypes.has(child.type)).map((child) => applySearchCollapsedState(child, collapsedIds));
|
||||
}
|
||||
|
||||
function filterSidebarTreeWithMatcher(nodes: TreeNode[], matchLabel: SidebarLabelMatcher | undefined, collapsedIds: ReadonlySet<string>, searchableNodeTypes?: ReadonlySet<TreeNodeType>): TreeNode[] {
|
||||
const filteredNodes: { node: TreeNode; score: number }[] = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (matchLabel && hiddenSearchNodeTypes.has(node.type)) continue;
|
||||
if (node.type === "object-browser" && node.hiddenChildren) {
|
||||
const matches = node.hiddenChildren.flatMap((child) => {
|
||||
if (searchableNodeTypes && !searchableNodeTypes.has(child.type)) return [];
|
||||
@@ -67,7 +74,10 @@ function filterSidebarTreeWithMatcher(nodes: TreeNode[], matchLabel: SidebarLabe
|
||||
// A type-matched table keeps its loaded detail groups after the text query
|
||||
// is cleared instead of being rebuilt with an empty filtered child list.
|
||||
const preservesTypeMatchedTable = !matchLabel && !!selfMatch && node.type === "table";
|
||||
const filteredChildren = preservesSubtree ? node.children?.map((child) => applySearchCollapsedState(child, collapsedIds)) : node.children ? filterSidebarTreeWithMatcher(node.children, matchLabel, collapsedIds, searchableNodeTypes) : undefined;
|
||||
// Connection utility entries are synthetic navigation actions, not schema
|
||||
// search results. Keep real loaded descendants for connection-name matches,
|
||||
// but do not let those actions make a disconnected result look expanded.
|
||||
const filteredChildren = preservesSubtree ? preservedSearchChildren(node, collapsedIds) : node.children ? filterSidebarTreeWithMatcher(node.children, matchLabel, collapsedIds, searchableNodeTypes) : undefined;
|
||||
|
||||
if (selfMatch || (filteredChildren && filteredChildren.length > 0)) {
|
||||
if (!node.children || preservesTypeMatchedTable) {
|
||||
|
||||
Reference in new issue
Block a user