fix(grid): show deleted row details in confirmation

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-08-20 20:49:47 +08:00
1 parent b3acf1e558
commit e2a4b26379
7 files changed
+284 -23

No files matched your search

+31 -5
View File
@@ -146,7 +146,18 @@ import {
} from "@/lib/dataGrid/binaryCellDownload";
import { buildBinaryHexViewRows } from "@/lib/dataGrid/binaryHexViewer";
import { canFormatCellDetailJson, cellDetailEditorText, compactJsonText, defaultCellDetailTab, formatJsonText, isGeometryColumnType, linkedCellDetailTarget, looksLikeJsonContainerText, valueEditorActions, visibleCellDetailTabs, type CellDetailTab } from "@/lib/dataGrid/cellDetailPresentation";
import { buildDataGridCellDetail, buildDataGridColumnDetail, buildDataGridRowDetail, CELL_DETAIL_VALUE_PREVIEW_MAX_LENGTH, dataGridColumnDetailJson, dataGridColumnDetailTsv, dataGridRowDetailJson, dataGridRowDetailTsv, type DataGridCellDetail } from "@/lib/dataGrid/dataGridDetail";
import {
buildDataGridCellDetail,
buildDataGridColumnDetail,
buildDataGridRowDetail,
buildDeleteRowConfirmDetails,
CELL_DETAIL_VALUE_PREVIEW_MAX_LENGTH,
dataGridColumnDetailJson,
dataGridColumnDetailTsv,
dataGridRowDetailJson,
dataGridRowDetailTsv,
type DataGridCellDetail,
} from "@/lib/dataGrid/dataGridDetail";
import {
applyColumnFormatter,
buildColumnFormatterKey,
@@ -4463,6 +4474,10 @@ function formatGridItemCell(item: RowItem, columnIndex: number): string {
return formatCellCached(item.data[columnIndex], columnIndex, largeValueOriginalBytes(item, columnIndex));
}
function formatGridItemCellForConfirmation(item: RowItem, columnIndex: number): string {
return formatCell(item.data[columnIndex], columnIndex, largeValueOriginalBytes(item, columnIndex), false);
}
function normalizedLargeValueIdentityPart(value: CellValue): string {
if (value === null) return "null";
return `${typeof value}:${JSON.stringify(value)}`;
@@ -4656,7 +4671,16 @@ const exportContextCell = computed(() => {
return { ...contextCell.value, col: visibleCol };
});
const deleteRowDetails = computed(() => (props.tableMeta?.tableName ? t("dangerDialog.deleteRowDetails", { table: props.tableMeta.tableName }) : t("dangerDialog.deleteRowDetailsNoTable")));
const deleteRowDetails = computed(() => {
const header = props.tableMeta?.tableName ? t("dangerDialog.deleteRowDetails", { table: props.tableMeta.tableName }) : t("dangerDialog.deleteRowDetailsNoTable");
return buildDeleteRowConfirmDetails({
header,
rowIds: pendingDeleteRowIds.value,
columns: resultSourceColumns.value,
getRow: getRowItem,
formatCell: (item, columnIndex) => formatGridItemCellForConfirmation(item, columnIndex),
});
});
const hasVisibleRows = computed(() => displayRowCount.value > 0);
const hasActiveFilter = computed(() => (dataGridSearchMode.value === "filter" && !!deferredClientSearchText.value) || rowStatusFilter.value !== "all" || hasLocalColumnFilters.value || hasServerColumnFilters.value);
@@ -5894,10 +5918,11 @@ function primitiveCellFormatKey(value: CellValue, columnIndex?: number): string
return `${columnIndex ?? -1}\u0000${typeof value}\u0000${String(value)}`;
}
function formatCell(value: CellValue, columnIndex?: number, originalBytes?: number): string {
function formatCell(value: CellValue, columnIndex?: number, originalBytes?: number, limitDisplay = true): string {
const formatter = columnIndex === undefined ? undefined : resolvedColumnFormatters.value[columnIndex];
if (formatter?.kind === "foreign-key-display" && columnIndex !== undefined) {
return limitDataGridCellDisplay(formatForeignKeyDisplayValue(value, foreignKeyDisplayLabels.value.get(columnIndex)), resolvedDatabaseType.value === "sqlserver" ? SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH : undefined);
const display = formatForeignKeyDisplayValue(value, foreignKeyDisplayLabels.value.get(columnIndex));
return limitDisplay ? limitDataGridCellDisplay(display, resolvedDatabaseType.value === "sqlserver" ? SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH : undefined) : display;
}
const columnInfo = columnIndex === undefined ? undefined : tableColumnForGridColumn(columnIndex);
const displayColumnInfo = columnInfo ?? (columnIndex === undefined ? undefined : resultColumnInfoForGridColumn(columnIndex));
@@ -5912,7 +5937,7 @@ function formatCell(value: CellValue, columnIndex?: number, originalBytes?: numb
const binaryDisplay = formatter ? null : binaryCellDisplayText(value, columnIndex === undefined ? undefined : allColumnTypes.value[columnIndex], originalBytes);
if (binaryDisplay !== null) return binaryDisplay;
const s = applyColumnFormatter(value, formatter);
return limitDataGridCellDisplay(s, resolvedDatabaseType.value === "sqlserver" ? SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH : undefined);
return limitDisplay ? limitDataGridCellDisplay(s, resolvedDatabaseType.value === "sqlserver" ? SQLSERVER_DATA_GRID_CELL_DISPLAY_MAX_LENGTH : undefined) : s;
}
function formatCellCached(value: CellValue, columnIndex?: number, originalBytes?: number): string {
@@ -12609,6 +12634,7 @@ function currentGridContextMenuItems(): ContextMenuItem[] {
"
:details="deleteRowDetails"
:confirm-label="pendingDeleteRowIds.length > 1 ? t('grid.deleteRows', { count: pendingDeleteRowIds.length }) : t('grid.deleteRow')"
:close-on-confirm="false"
@confirm="confirmDeleteRow"
/>
<DangerConfirmDialog
@@ -0,0 +1,38 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dataGridSource = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
describe("DataGrid delete row confirmation details", () => {
it("still wires deleteRowDetails into the delete-row DangerConfirmDialog", () => {
const dialogStart = dataGridSource.indexOf('v-model:open="showDeleteRowConfirm"');
const dialogEnd = dataGridSource.indexOf('@confirm="confirmDeleteRow"', dialogStart);
expect(dialogStart).toBeGreaterThanOrEqual(0);
expect(dialogEnd).toBeGreaterThan(dialogStart);
const dialogSource = dataGridSource.slice(dialogStart, dialogEnd);
expect(dialogSource).toContain(':details="deleteRowDetails"');
});
it("uses the unbounded confirmation formatter for delete details", () => {
const detailsStart = dataGridSource.indexOf("const deleteRowDetails = computed");
const detailsEnd = dataGridSource.indexOf("const hasVisibleRows", detailsStart);
expect(detailsStart).toBeGreaterThanOrEqual(0);
expect(detailsEnd).toBeGreaterThan(detailsStart);
expect(dataGridSource.slice(detailsStart, detailsEnd)).toContain("formatGridItemCellForConfirmation");
expect(dataGridSource).toContain("formatCell(item.data[columnIndex], columnIndex, largeValueOriginalBytes(item, columnIndex), false)");
});
it("keeps close-on-confirm disabled so confirmDeleteRow runs before the dialog auto-closes", () => {
const dialogStart = dataGridSource.indexOf('v-model:open="showDeleteRowConfirm"');
const dialogEnd = dataGridSource.indexOf('@confirm="confirmDeleteRow"', dialogStart);
expect(dialogStart).toBeGreaterThanOrEqual(0);
expect(dialogEnd).toBeGreaterThan(dialogStart);
const dialogSource = dataGridSource.slice(dialogStart, dialogEnd);
expect(dialogSource).toContain(':close-on-confirm="false"');
});
});