fix(sidebar): keep table search results on Enter
This commit is contained in:
1 parent
acc2137cde
commit
0908a944e8
4 files changed
+77
-11
No files matched your search
@@ -46,7 +46,7 @@ import {
|
||||
type SidebarNodeScrollAlign,
|
||||
} from "@/lib/sidebar/sidebarActiveTabTarget";
|
||||
import { findLoadedTableTargetForCandidate, queryContextTargetFromCandidate, queryCursorTableCandidate, type QueryCursorTableCandidate } from "@/lib/sql/queryCursorTableTarget";
|
||||
import { createFlatTreeIndex, SIDEBAR_TREE_ROW_HEIGHT, SIDEBAR_TREE_PRERENDER_COUNT, SIDEBAR_TREE_SCROLL_BUFFER, flattenTree, shouldVirtualizeFlatTree, type FlatTreeNode } from "@/composables/useFlatTree";
|
||||
import { createFlatTreeIndex, flatTreeRowsChanged, SIDEBAR_TREE_ROW_HEIGHT, SIDEBAR_TREE_PRERENDER_COUNT, SIDEBAR_TREE_SCROLL_BUFFER, flattenTree, shouldVirtualizeFlatTree, type FlatTreeNode } from "@/composables/useFlatTree";
|
||||
import { sidebarTreeContextKey } from "@/lib/sidebar/sidebarTreeContext";
|
||||
import { createSidebarTreeRuntime, sidebarTreeRuntimeKey, type SidebarTreeRuntimeHostInstance } from "@/lib/sidebar/sidebarTreeRuntime";
|
||||
import { createSidebarPasteHandlerRegistry } from "@/lib/sidebar/sidebarPasteHandlerRegistry";
|
||||
@@ -980,15 +980,15 @@ watch(flatNodes, (nodes, previousNodes) => {
|
||||
stickyScrollTop.value = 0;
|
||||
void nextTick(scheduleSidebarScrollMetricsUpdate);
|
||||
// After a structural change (list grew/shrunk, e.g. a Dameng connection
|
||||
// expands or collapses) reconcile the virtual scroller with the browser's
|
||||
// scroll position. The recycle pool is rebuilt from the live scrollTop, but
|
||||
// scrollTop is only clamped on the next layout, so right after a shrink the
|
||||
// pool can still target a window beyond the new content — the viewport then
|
||||
// lands inside the end spacer and shows a blank region until the next scroll
|
||||
// event. Reading scrollHeight forces that layout (applying the clamp), then
|
||||
// one explicit pool refresh keeps the rendered window aligned. Only needed
|
||||
// when the item count changed; reorders are already rebuilt by the library.
|
||||
if (nodes.length === (previousNodes?.length ?? -1)) return;
|
||||
// expands or collapses) or a same-length search projection replacement,
|
||||
// reconcile the virtual scroller with the browser's scroll position. The
|
||||
// recycle pool is rebuilt from the live scrollTop, but scrollTop is only
|
||||
// clamped on the next layout, so right after a shrink the pool can still
|
||||
// target a window beyond the new content — the viewport then lands inside
|
||||
// the end spacer and shows a blank region until the next scroll event.
|
||||
// Reading scrollHeight forces that layout (applying the clamp), then one
|
||||
// explicit pool refresh keeps the rendered window aligned.
|
||||
if (!flatTreeRowsChanged(nodes, previousNodes)) return;
|
||||
void nextTick(() => {
|
||||
const scroller = treeScrollerRef.value;
|
||||
if (!scroller || !useVirtualTree.value) return;
|
||||
|
||||
+48
@@ -4,6 +4,7 @@ import { createApp, defineComponent, h, nextTick, type App } from "vue";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import TreeItem from "@/components/sidebar/TreeItem.vue";
|
||||
import { sidebarTreeContextKey } from "@/lib/sidebar/sidebarTreeContext";
|
||||
import { createSidebarTreeRuntime, sidebarTreeRuntimeKey, type SidebarTreeRuntimeHost } from "@/lib/sidebar/sidebarTreeRuntime";
|
||||
import type { ConnectionConfig, SidebarLayout, TreeNode } from "@/types/database";
|
||||
|
||||
@@ -59,6 +60,7 @@ vi.mock("@/stores/settingsStore", () => ({
|
||||
sidebarAllowHorizontalScroll: false,
|
||||
sidebarHiddenTablePrefixes: [],
|
||||
sidebarObjectInfoMode: "none",
|
||||
sidebarTableSearchLocal: false,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
@@ -189,4 +191,50 @@ describe("TreeItem connection quick rename", () => {
|
||||
|
||||
await vi.waitFor(() => expect(toast).toHaveBeenCalledWith(expect.stringContaining("disk full"), 5000));
|
||||
});
|
||||
|
||||
it("keeps Enter scoped to the table-search input without triggering a tree row action", async () => {
|
||||
const container = document.createElement("div");
|
||||
document.body.append(container);
|
||||
let bubbledKeydowns = 0;
|
||||
container.addEventListener("keydown", () => {
|
||||
bubbledKeydowns += 1;
|
||||
});
|
||||
const host = runtimeHost();
|
||||
const runtime = createSidebarTreeRuntime();
|
||||
runtime.bindHost(host);
|
||||
const setTableSearchQuery = vi.fn();
|
||||
const node: TreeNode = {
|
||||
id: "connection-1:app:__table_search",
|
||||
label: "sidebar.searchTablesInCurrentScope",
|
||||
type: "table-search-control",
|
||||
connectionId: connection.id,
|
||||
database: "app",
|
||||
tableSearchParentId: "connection-1:app",
|
||||
};
|
||||
const app = createApp(
|
||||
defineComponent({
|
||||
setup: () => () => h(TreeItem, { node, depth: 2 }),
|
||||
}),
|
||||
);
|
||||
app.use(i18n);
|
||||
app.provide(sidebarTreeRuntimeKey, runtime);
|
||||
app.provide(sidebarTreeContextKey, {
|
||||
getVisibleNodes: () => [],
|
||||
getVisibleNodeIndex: () => -1,
|
||||
setTableSearchQuery,
|
||||
});
|
||||
app.mount(container);
|
||||
mountedApps.push(app);
|
||||
await nextTick();
|
||||
|
||||
const input = container.querySelector<HTMLInputElement>("[data-sidebar-table-search-parent-id]");
|
||||
expect(input).toBeTruthy();
|
||||
const event = new KeyboardEvent("keydown", { key: "Enter", bubbles: true, cancelable: true });
|
||||
input!.dispatchEvent(event);
|
||||
|
||||
expect(event.defaultPrevented).toBe(false);
|
||||
expect(bubbledKeydowns).toBe(0);
|
||||
expect(host.handleRowKeydown).not.toHaveBeenCalled();
|
||||
expect(setTableSearchQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user