fix(data-grid): preserve resized column widths across query reruns

This commit is contained in:
jearhe authored and GitHub committed 2026-08-24 18:47:31 +08:00
1 parent ff4667b93f
commit 707296c71d
6 files changed
+78 -5

No files matched your search

@@ -452,6 +452,7 @@ interface DataGridProps {
countTotalRows?: () => Promise<number | undefined>;
loading?: boolean;
cacheKey?: string;
columnWidthCacheKey?: string;
pendingStateKey?: string;
exportSql?: string;
onExecuteSql?: (sql: string) => Promise<void>;
@@ -2714,7 +2715,7 @@ function scrollToColumnIndex(columnIndex: number) {
// --- Column resize composable ---
const columnWidthDensity = computed(() => settingsStore.editorSettings.columnWidthDensity);
const tableFontFamily = computed(() => settingsStore.editorSettings.tableFontFamily);
const columnWidthCacheKey = computed(() => props.cacheKey?.trim() || undefined);
const columnWidthCacheKey = computed(() => props.columnWidthCacheKey?.trim() || props.cacheKey?.trim() || undefined);
const columnStructureSignature = computed(() => createDataGridColumnStructureSignature(props.result.columns, props.result.column_types));
// Bumped once the configured header font is ready, so widths measured against a temporary fallback
// font get re-measured without reacting to unrelated fonts loaded elsewhere in the application.
@@ -125,7 +125,20 @@ import { TABLE_FONT_SIZE_MAX, TABLE_FONT_SIZE_MIN, useSettingsStore, type DataGr
import { useToast } from "@/composables/useToast";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import { canCancelQueryExecution, queryExecutionLabelKey } from "@/lib/sql/queryExecutionState";
import { databaseDisplayNameForTab, executionSummaryItems, queryResultExecutionSql, resultGridCacheKey, resultGridInstanceKey, resultRunItems, resultSourceRange, resultSqlForGrid, statementExecutionMarkers, tabularResultItems, type ExecutionSummaryItem } from "@/lib/tabs/tabPresentation";
import {
databaseDisplayNameForTab,
executionSummaryItems,
queryResultExecutionSql,
resultGridCacheKey,
resultGridColumnWidthCacheKey,
resultGridInstanceKey,
resultRunItems,
resultSourceRange,
resultSqlForGrid,
statementExecutionMarkers,
tabularResultItems,
type ExecutionSummaryItem,
} from "@/lib/tabs/tabPresentation";
import { defaultQueryResultArchiveFileName } from "@/lib/query/queryResultArchive";
import { saveQueryResultArchiveFile } from "@/lib/query/queryResultArchiveFile";
import { isTableDataEditable } from "@/lib/table/tableEditing";
@@ -443,6 +456,7 @@ const allResultExportSheets = computed(() =>
);
const resultRuns = computed(() => resultRunItems(props.activeTab));
const activeResultGridCacheKey = computed(() => resultGridCacheKey(props.activeTab));
const activeResultGridColumnWidthCacheKey = computed(() => resultGridColumnWidthCacheKey(props.activeTab));
const activeResultGridInstanceKey = computed(() => resultGridInstanceKey(props.activeTab));
const activeResultSql = computed(() => resultSqlForGrid(props.activeTab));
const activeResultExportSql = computed(() => queryResultExecutionSql(props.activeTab));
@@ -1716,6 +1730,7 @@ defineExpose({
ref="dataGridRef"
:key="activeResultGridInstanceKey"
:cache-key="activeResultGridCacheKey"
:column-width-cache-key="activeResultGridColumnWidthCacheKey"
:pending-state-key="activeResultGridInstanceKey"
class="flex-1 min-h-0"
:result="activeTab.result"
@@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it } from "vitest";
import { DATA_GRID_COL_AUTO_FIT_MAX_WIDTH, DATA_GRID_COL_MIN_WIDTH, sampleDataGridColumnValues } from "@/lib/dataGrid/dataGridColumnWidth";
import { clearDataGridColumnWidthStates, createDataGridColumnMeasurementSignature, createDataGridColumnStructureSignature, DATA_GRID_COLUMN_WIDTH_STATE_LIMIT, dataGridColumnWidthStateCount, loadDataGridColumnWidthState, saveDataGridColumnWidthState } from "@/lib/dataGrid/dataGridColumnWidthState";
import { DATA_GRID_ROW_NUM_WIDTH, dataGridRowNumberColumnWidth, resizeDataGridColumnWidth, useDataGridColumnResize } from "@/composables/useDataGridColumnResize";
import { resultGridColumnWidthCacheKey } from "@/lib/tabs/tabPresentation";
function createResizeState(options: {
columns: string[];
@@ -178,6 +179,32 @@ describe("useDataGridColumnResize", () => {
expect(remounted.columnWidths.value).toEqual(first.columnWidths.value);
});
it("restores manually resized widths when the active result run changes", () => {
const firstResult = { id: "tab-1", activeResultRunId: "run-1", activeResultIndex: 2 };
const rerunResult = { ...firstResult, activeResultRunId: "run-2" };
const firstCacheKey = resultGridColumnWidthCacheKey(firstResult);
expect(resultGridColumnWidthCacheKey(rerunResult)).toBe(firstCacheKey);
const first = createResizeState({
columns: ["id", "resource"],
rows: [[1, "lock"]],
cacheKey: firstCacheKey,
});
first.initColumnWidths();
first.onResizeStart(1, new MouseEvent("mousedown", { clientX: 100, cancelable: true }));
document.dispatchEvent(new MouseEvent("mouseup", { clientX: 260 }));
const remounted = createResizeState({
columns: ["id", "resource"],
rows: [[1, "lock"]],
cacheKey: resultGridColumnWidthCacheKey(rerunResult),
});
remounted.initColumnWidths();
expect(remounted.columnWidths.value).toEqual(first.columnWidths.value);
});
it("isolates widths by result cache key", () => {
const first = createResizeState({ columns: ["id"], rows: [["x".repeat(120)]], cacheKey: "result-a" });
first.initColumnWidths();
+19 -3
View File
@@ -1,7 +1,21 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPinia, setActivePinia } from "pinia";
import { useConnectionStore } from "@/stores/connectionStore";
import { connectionGroupDisplayName, executionSummaryItems, middleEllipsis, queryResultBaseSql, queryResultExecutionSql, resultGridCacheKey, resultGridInstanceKey, resultSourceRange, statementExecutionMarkers, tabDisplayTitle, tabTooltipLines, tabularResultItems } from "@/lib/tabs/tabPresentation";
import {
connectionGroupDisplayName,
executionSummaryItems,
middleEllipsis,
queryResultBaseSql,
queryResultExecutionSql,
resultGridCacheKey,
resultGridColumnWidthCacheKey,
resultGridInstanceKey,
resultSourceRange,
statementExecutionMarkers,
tabDisplayTitle,
tabTooltipLines,
tabularResultItems,
} from "@/lib/tabs/tabPresentation";
import { sqlTextFingerprint } from "@/lib/sql/sqlTextFingerprint";
import type { ConnectionConfig, QueryTab } from "@/types/database";
@@ -136,12 +150,14 @@ describe("query result labels", () => {
describe("query result grid identity", () => {
it("separates rerun payloads while retaining run and result-set identity", () => {
const first = queryTab({ activeResultRunId: "run-1", activeResultIndex: 2, resultGridRevision: "execution-1" });
const rerun = queryTab({ activeResultRunId: "run-1", activeResultIndex: 2, resultGridRevision: "execution-2" });
const rerun = queryTab({ activeResultRunId: "run-2", activeResultIndex: 2, resultGridRevision: "execution-2" });
expect(resultGridInstanceKey(first)).toBe("tab-1-run-1-2-execution-1");
expect(resultGridInstanceKey(rerun)).not.toBe(resultGridInstanceKey(first));
expect(resultGridInstanceKey({ ...first, activeResultIndex: 1 })).toBe("tab-1-run-1-1-execution-1");
expect(resultGridCacheKey(rerun)).toBe(resultGridCacheKey(first));
expect(resultGridCacheKey(rerun)).not.toBe(resultGridCacheKey(first));
expect(resultGridColumnWidthCacheKey(rerun)).toBe(resultGridColumnWidthCacheKey(first));
expect(resultGridColumnWidthCacheKey({ ...first, activeResultIndex: 1 })).not.toBe(resultGridColumnWidthCacheKey(first));
});
});
@@ -358,6 +358,10 @@ export function resultGridCacheKey(tab: Pick<QueryTab, "id" | "activeResultRunId
return `${tab.id}-${tab.activeResultRunId ?? "current"}-${tab.activeResultIndex ?? 0}`;
}
export function resultGridColumnWidthCacheKey(tab: Pick<QueryTab, "id"> & Partial<Pick<QueryTab, "activeResultIndex">>): string {
return `result-column-width-${tab.id}-${tab.activeResultIndex ?? 0}`;
}
export function resultGridInstanceKey(tab: Pick<QueryTab, "id" | "activeResultRunId" | "activeResultIndex" | "resultGridRevision">): string {
return `${resultGridCacheKey(tab)}-${tab.resultGridRevision ?? "initial"}`;
}