From 5e8c754f412b941f0ececb377d98b851a1161bbd Mon Sep 17 00:00:00 2001 From: miracle Date: Sun, 18 Oct 2026 12:27:34 +0800 Subject: [PATCH] feat(grid): configurable column metadata placement --- .../editor/EditorSettingsDialog.vue | 30 +++++- ...gsDialog.externalClose.integration.spec.ts | 25 ++++- apps/desktop/src/components/grid/DataGrid.vue | 46 +++++++-- .../__tests__/DataGridTransposeSort.spec.ts | 95 ++++++++++++++++++- apps/desktop/src/i18n/locales/en.ts | 5 + apps/desktop/src/i18n/locales/es.ts | 5 + apps/desktop/src/i18n/locales/it.ts | 5 + apps/desktop/src/i18n/locales/ja.ts | 5 + apps/desktop/src/i18n/locales/ko.ts | 5 + apps/desktop/src/i18n/locales/pt-BR.ts | 5 + apps/desktop/src/i18n/locales/ru.ts | 5 + apps/desktop/src/i18n/locales/th.ts | 5 + apps/desktop/src/i18n/locales/zh-CN.ts | 5 + apps/desktop/src/i18n/locales/zh-TW.ts | 5 + .../__tests__/editorSettingsDraft.spec.ts | 6 ++ .../__tests__/settingsTransfer.spec.ts | 11 +++ .../src/lib/settings/editorSettingsDraft.ts | 2 + .../src/lib/settings/settingsSearch.ts | 2 + .../src/lib/settings/settingsTransfer.ts | 4 + apps/desktop/src/stores/settingsStore.ts | 8 ++ packages/app-tests/settingsStore.test.ts | 22 +++++ 21 files changed, 283 insertions(+), 18 deletions(-) diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index e8efa86437..82952b0906 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -815,12 +815,14 @@ const editSidebarTablePageSize = ref(settingsStore.desktopSettings.sidebar_table const debugLogCopied = ref(false); const debugLogDownloaded = ref(false); const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader); +const editDataGridColumnCommentsInInfoArea = ref(settingsStore.editorSettings.dataGridColumnCommentsInInfoArea); const editShowColumnTypesInHeader = ref(settingsStore.editorSettings.showColumnTypesInHeader); const editShowColumnHeaderTooltips = ref(settingsStore.editorSettings.showColumnHeaderTooltips); const editShowResultSourceDatabase = ref(settingsStore.editorSettings.showResultSourceDatabase); const editResultTabNamingMode = ref(settingsStore.editorSettings.resultTabNamingMode); const editResultTabPreferComments = ref(settingsStore.editorSettings.resultTabPreferComments); const editDataGridShowTransposeFieldMetadata = ref(settingsStore.editorSettings.dataGridShowTransposeFieldMetadata); +const editDataGridTransposeMetadataInInfoColumn = ref(settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn); const editColorizeDataGridCellTypes = ref(settingsStore.editorSettings.colorizeDataGridCellTypes); const editShowIndexIndicatorsInHeader = ref(settingsStore.editorSettings.showIndexIndicatorsInHeader); const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions); @@ -1196,12 +1198,14 @@ function currentEditorSettingsDraft(): EditorSettingsDraft { tabSortMode: editTabSortMode.value, tabMaxWidth: editTabMaxWidth.value, showColumnCommentsInHeader: editShowColumnCommentsInHeader.value, + dataGridColumnCommentsInInfoArea: editDataGridColumnCommentsInInfoArea.value, showColumnTypesInHeader: editShowColumnTypesInHeader.value, showColumnHeaderTooltips: editShowColumnHeaderTooltips.value, showResultSourceDatabase: editShowResultSourceDatabase.value, resultTabNamingMode: editResultTabNamingMode.value, resultTabPreferComments: editResultTabPreferComments.value, dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata.value, + dataGridTransposeMetadataInInfoColumn: editDataGridTransposeMetadataInInfoColumn.value, colorizeDataGridCellTypes: editColorizeDataGridCellTypes.value, dataGridTypeColorSchemes: editDataGridTypeColorSchemes.value, activeDataGridTypeColorSchemeId: editActiveDataGridTypeColorSchemeId.value, @@ -1891,12 +1895,14 @@ function syncEditorSettingsDraftFromStore() { editTabSortMode.value = settingsStore.editorSettings.tabSortMode; editTabMaxWidth.value = settingsStore.editorSettings.tabMaxWidth; editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader; + editDataGridColumnCommentsInInfoArea.value = settingsStore.editorSettings.dataGridColumnCommentsInInfoArea; editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader; editShowColumnHeaderTooltips.value = settingsStore.editorSettings.showColumnHeaderTooltips; editShowResultSourceDatabase.value = settingsStore.editorSettings.showResultSourceDatabase; editResultTabNamingMode.value = settingsStore.editorSettings.resultTabNamingMode; editResultTabPreferComments.value = settingsStore.editorSettings.resultTabPreferComments; editDataGridShowTransposeFieldMetadata.value = settingsStore.editorSettings.dataGridShowTransposeFieldMetadata; + editDataGridTransposeMetadataInInfoColumn.value = settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn; editColorizeDataGridCellTypes.value = settingsStore.editorSettings.colorizeDataGridCellTypes; editDataGridTypeColorSchemes.value = cloneDataGridTypeColorSchemes(settingsStore.editorSettings.dataGridTypeColorSchemes); editActiveDataGridTypeColorSchemeId.value = settingsStore.editorSettings.activeDataGridTypeColorSchemeId; @@ -2060,12 +2066,14 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = { tabSortMode: editTabSortMode, tabMaxWidth: editTabMaxWidth, showColumnCommentsInHeader: editShowColumnCommentsInHeader, + dataGridColumnCommentsInInfoArea: editDataGridColumnCommentsInInfoArea, showColumnTypesInHeader: editShowColumnTypesInHeader, showColumnHeaderTooltips: editShowColumnHeaderTooltips, showResultSourceDatabase: editShowResultSourceDatabase, resultTabNamingMode: editResultTabNamingMode, resultTabPreferComments: editResultTabPreferComments, dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata, + dataGridTransposeMetadataInInfoColumn: editDataGridTransposeMetadataInInfoColumn, colorizeDataGridCellTypes: editColorizeDataGridCellTypes, dataGridTypeColorSchemes: editDataGridTypeColorSchemes, activeDataGridTypeColorSchemeId: editActiveDataGridTypeColorSchemeId, @@ -2705,12 +2713,14 @@ function resetDefaultsForTab(tab: SettingsCategory) { historyRetention.reset(); mcpHistoryRetention.reset(); editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader; + editDataGridColumnCommentsInInfoArea.value = DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea; editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader; editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips; editShowResultSourceDatabase.value = DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase; editResultTabNamingMode.value = DEFAULT_EDITOR_SETTINGS.resultTabNamingMode; editResultTabPreferComments.value = DEFAULT_EDITOR_SETTINGS.resultTabPreferComments; editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata; + editDataGridTransposeMetadataInInfoColumn.value = DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn; editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes; // Back to the built-in palette, but keep the user's saved schemes available. editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId; @@ -2841,9 +2851,11 @@ function resetAllDefaults() { editDuckDbWorkerMaxProcesses.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_max_processes; editSidebarTablePageSize.value = DEFAULT_SIDEBAR_TABLE_PAGE_SIZE; editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader; + editDataGridColumnCommentsInInfoArea.value = DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea; editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader; editShowColumnHeaderTooltips.value = DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips; editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata; + editDataGridTransposeMetadataInInfoColumn.value = DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn; editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes; // Reset the selection only; saved schemes survive a full settings reset. editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId; @@ -9749,7 +9761,14 @@ onUnmounted(() => { {{ t("settings.showColumnCommentsInHeaderDescription") }}

- + + +
+
+ +

{{ t("settings.dataGridColumnCommentsInInfoAreaDescription") }}

+
+
@@ -9820,7 +9839,14 @@ onUnmounted(() => { {{ t("settings.dataGridShowTransposeFieldMetadataDescription") }}

- + +
+
+
+ +

{{ t("settings.dataGridTransposeMetadataInInfoColumnDescription") }}

+
+
diff --git a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts index 74128f28ae..524f6df39c 100644 --- a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts +++ b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.externalClose.integration.spec.ts @@ -54,7 +54,7 @@ afterEach(() => { document.body.innerHTML = ""; }); -async function mountPage() { +async function mountPage(initialTab: "editor" | "data" = "editor") { const pinia = createPinia(); setActivePinia(pinia); const store = useSettingsStore(); @@ -73,7 +73,7 @@ async function mountPage() { ref: page, variant: "page", open: true, - initialTab: "editor", + initialTab, "onUpdate:open": (value: boolean) => { open.value = value; }, @@ -102,6 +102,27 @@ function clickText(key: string) { } describe("settings page external close request", () => { + it("saves and restores independent normal and transpose information placement", async () => { + const { page, open, store } = await mountPage("data"); + const transposeSwitch = () => host.querySelector("#data-grid-transpose-metadata-in-info-column")!; + expect(transposeSwitch().getAttribute("aria-checked")).toBe("false"); + transposeSwitch().click(); + await settle(); + expect(host.querySelector("#data-grid-show-transpose-field-metadata")!.disabled).toBe(true); + expect(store.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(false); + clickText("settings.apply"); + await settle(); + expect(store.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(true); + expect(store.editorSettings.dataGridColumnCommentsInInfoArea).toBe(false); + expect(backend.saves).toHaveBeenCalledWith(expect.objectContaining({ dataGridTransposeMetadataInInfoColumn: true, dataGridColumnCommentsInInfoArea: false })); + page.value!.requestClose(); + await settle(); + open.value = true; + await settle(); + expect(transposeSwitch().getAttribute("aria-checked")).toBe("true"); + expect(host.querySelector("#column-comments-in-info-area")!.getAttribute("aria-checked")).toBe("false"); + }); + it("keeps a dirty draft when continuing, and closes without saving only after discarding", async () => { const { page, open, store, changeFontSize } = await mountPage(); const originalFontSize = store.editorSettings.fontSize; diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index dade7eeaa8..c795f099a3 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -845,10 +845,13 @@ const dataGridTopbarOverflowCompact = ref(false); const dataGridTopbarOverflowActionCount = ref(0); const dataGridTopbarExpandedRequiredWidth = ref(0); const dataGridFilterExpandedRequiredWidth = ref(0); -const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader); +const columnCommentsInInfoArea = computed(() => settingsStore.editorSettings.dataGridColumnCommentsInInfoArea); +const showColumnCommentsInHeader = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader && !columnCommentsInInfoArea.value); const showColumnTypesInHeader = computed(() => settingsStore.editorSettings.showColumnTypesInHeader); const showColumnHeaderTooltips = computed(() => settingsStore.editorSettings.showColumnHeaderTooltips !== false); -const showTransposeFieldMetadata = computed(() => settingsStore.editorSettings.dataGridShowTransposeFieldMetadata); +const transposeMetadataInInfoColumn = computed(() => settingsStore.editorSettings.dataGridTransposeMetadataInInfoColumn); +const showTransposeFieldMetadata = computed(() => settingsStore.editorSettings.dataGridShowTransposeFieldMetadata && !transposeMetadataInInfoColumn.value); +const showTransposeColumnComments = computed(() => settingsStore.editorSettings.showColumnCommentsInHeader); const showIndexIndicatorsInHeader = computed(() => settingsStore.editorSettings.showIndexIndicatorsInHeader !== false); const indexes = ref([]); const indexesLoaded = ref(false); @@ -6979,6 +6982,20 @@ const activeCellDetail = computed(() => { return cell ? cellDetailFor(cell.rowIndex, cell.col) : null; }); +function columnInfoText(actualColIdx: number, includeName = true): string { + const column = props.result.columns[actualColIdx]; + if (column === undefined) return ""; + const metadata = tableColumnForGridColumn(actualColIdx); + return [includeName ? column : undefined, allColumnTypes.value[actualColIdx], metadata ? `${t("structureEditor.nullable")}: ${t(metadata.is_nullable ? "structureEditor.yes" : "structureEditor.no")}` : undefined, resolvedColumnComment(column, actualColIdx)].filter(Boolean).join(" · "); +} + +const selectedColumnInfo = computed(() => { + if (!columnCommentsInInfoArea.value || hasRowSelection.value) return ""; + const visibleIndex = hasColumnSelection.value ? [...selectedColumnIndexes.value][0] : selectionFocus.value?.colIndex; + const actualIndex = visibleIndex === undefined ? undefined : visibleColumnIndexes.value[visibleIndex]; + return actualIndex === undefined ? "" : columnInfoText(actualIndex); +}); + const canShowMongoJsonPreview = computed(() => props.databaseType === "mongodb" && !!props.result.mongo_documents && props.result.mongo_documents.length === props.result.rows.length); const mongoJsonPreviewOpen = computed(() => showMongoJsonPreview.value && canShowMongoJsonPreview.value); const activeMongoJsonDocument = computed(() => { @@ -11472,6 +11489,7 @@ const transposePinnedWidth = computed( }), ); const transposeEndSpacerWidth = computed(() => { + if (transposeMetadataInInfoColumn.value) return 0; if (!transposeScopeRecordIndexes.value.length) return 0; return transposeEndAlignmentSpacerWidth({ viewportWidth: transposeViewportWidth.value, @@ -11515,10 +11533,11 @@ const transposeRows = computed(() => { }); }); const transposeReserveTypeLine = computed(() => showTransposeFieldMetadata.value && showColumnTypesInHeader.value && transposeRows.value.some((row) => row.type)); -const transposeReserveCommentLine = computed(() => showTransposeFieldMetadata.value && showColumnCommentsInHeader.value && transposeRows.value.some((row) => row.comment)); +const transposeReserveCommentLine = computed(() => showTransposeFieldMetadata.value && showTransposeColumnComments.value && transposeRows.value.some((row) => row.comment)); const transposeRowHeight = computed(() => 30 + (transposeReserveTypeLine.value ? 14 : 0) + (transposeReserveCommentLine.value ? 14 : 0)); const isTransposeMode = computed(() => showTranspose.value && transposeRows.value.length > 0); -const transposeTotalWidth = computed(() => transposePinnedWidth.value + transposeRecordOffsets.value[transposeRecordOffsets.value.length - 1] + transposeEndSpacerWidth.value); +const transposeInfoWidth = computed(() => (transposeMetadataInInfoColumn.value ? 360 : 0)); +const transposeTotalWidth = computed(() => transposePinnedWidth.value + transposeRecordOffsets.value[transposeRecordOffsets.value.length - 1] + transposeEndSpacerWidth.value + transposeInfoWidth.value); function transposeScrollElement(): HTMLElement | undefined { const raw = transposeScrollRef.value; @@ -11529,7 +11548,7 @@ function transposeScrollElement(): HTMLElement | undefined { function transposeFieldTitle(item: { column: string; type: string; comment?: string }): string { const details = [item.column]; if (showTransposeFieldMetadata.value && showColumnTypesInHeader.value && item.type) details.push(`${t("grid.columnType")}: ${item.type}`); - if (showTransposeFieldMetadata.value && showColumnCommentsInHeader.value && item.comment) details.push(`${t("grid.columnComment")}: ${item.comment}`); + if (showTransposeFieldMetadata.value && showTransposeColumnComments.value && item.comment) details.push(`${t("grid.columnComment")}: ${item.comment}`); return details.join("\n"); } @@ -14624,6 +14643,9 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
+
+ {{ t("grid.columnInfo") }} +
- @@ -16175,7 +16200,7 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val -
+
{{ @@ -16214,7 +16239,10 @@ useUpdateBlocker(() => (hasPendingChanges.value || hasPendingDataEditorDraft.val
-
+
+ {{ selectedColumnInfo }} +
+
diff --git a/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts index 3c9e7bdaf0..4226a22c24 100644 --- a/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts +++ b/apps/desktop/src/components/grid/__tests__/DataGridTransposeSort.spec.ts @@ -56,7 +56,7 @@ function transposeFieldNames(host: HTMLElement): string[] { return [...host.querySelectorAll("[data-grid-transpose-column-index]")].map((field) => field.textContent?.trim().split("\n")[0] ?? ""); } -function mountGrid() { +function mountGrid(context: "table-data" | "results" = "table-data") { const pinia = createPinia(); setActivePinia(pinia); const settingsStore = useSettingsStore(); @@ -76,6 +76,7 @@ function mountGrid() { execution_time_ms: 0, }), ); + const resultColumnComments = ref>(); const host = document.createElement("div"); document.body.append(host); @@ -89,13 +90,14 @@ function mountGrid() { default: () => h(DataGrid, { result: result.value, + resultColumnComments: resultColumnComments.value, databaseType: "mysql", - context: "table-data", + context, tableMeta: { tableName: "scores", columns: [ - { name: "zeta", data_type: "varchar", is_nullable: false, column_default: null, is_primary_key: true, extra: null }, - { name: "alpha", data_type: "bigint", is_nullable: true, column_default: null, is_primary_key: false, extra: null }, + { name: "zeta", data_type: "varchar", comment: "Display name with a long comment", is_nullable: false, column_default: null, is_primary_key: true, extra: null }, + { name: "alpha", data_type: "bigint", comment: "Score", is_nullable: true, column_default: null, is_primary_key: false, extra: null }, ], primaryKeys: ["zeta"], }, @@ -109,7 +111,7 @@ function mountGrid() { app.use(i18n); app.mount(host); mountedApps.push({ app, host }); - return { host }; + return { host, settingsStore, result, resultColumnComments }; } async function openTranspose(host: HTMLElement) { @@ -129,6 +131,89 @@ afterEach(() => { }); describe("DataGrid transpose sorting", () => { + it.each(["table-data", "results"] as const)("moves all field metadata into an independent information column for %s", async (context) => { + const { host, settingsStore } = mountGrid(context); + settingsStore.updateEditorSettings({ dataGridShowTransposeFieldMetadata: true }); + await settle(); + await openTranspose(host); + expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2); + expect(host.querySelector("[data-grid-transpose-column-info]")).toBeNull(); + + settingsStore.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: true }); + await settle(); + expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(0); + expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(0); + const info = [...host.querySelectorAll("[data-grid-transpose-column-info]")]; + expect(info).toHaveLength(2); + expect(info[0].textContent).toContain("Display name with a long comment"); + expect(info[0].textContent).toContain("varchar"); + expect(info[0].textContent).toContain("Nullable: No"); + expect(info[0].textContent).not.toContain("zeta"); + expect(info[0].title).toContain("Display name with a long comment"); + expect(info[1].textContent).toContain("Score"); + expect(info[1].textContent).toContain("Nullable: Yes"); + + settingsStore.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: false }); + await settle(); + expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(0); + expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2); + expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(2); + }); + + it("keeps normal and transposed comment placement independent", async () => { + const { host, settingsStore } = mountGrid("results"); + settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true, dataGridShowTransposeFieldMetadata: true }); + await settle(); + expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(0); + await openTranspose(host); + expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(0); + expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(2); + + settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false, dataGridTransposeMetadataInInfoColumn: true, dataGridShowTransposeFieldMetadata: false }); + await settle(); + expect(host.querySelectorAll("[data-grid-transpose-column-info]")).toHaveLength(2); + expect(host.querySelectorAll("[data-grid-transpose-type-line]")).toHaveLength(0); + expect(host.querySelectorAll("[data-grid-transpose-comment-line]")).toHaveLength(0); + }); + + it("uses ordinal query comments for duplicate names and clears stale metadata for a new result", async () => { + const { host, settingsStore, result, resultColumnComments } = mountGrid(); + settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true }); + result.value = { ...result.value, columns: ["name", "name"] }; + resultColumnComments.value = ["First source", "Second source"]; + await settle(); + host.querySelector('[data-grid-column-index="1"]')!.click(); + await settle(); + expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Second source"); + expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("First source"); + + resultColumnComments.value = [undefined, undefined]; + await settle(); + expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("bigint"); + expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("Second source"); + expect(host.querySelector("[data-grid-column-info]")?.textContent).not.toContain("undefined"); + }); + + it("shows selected column metadata in the status area and restores header comments when disabled", async () => { + const { host, settingsStore } = mountGrid(); + await settle(); + expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(2); + settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true }); + await settle(); + expect(host.querySelector("[data-grid-header-comment-line]")).toBeNull(); + host.querySelector('[data-grid-column-index="0"]')!.click(); + await settle(); + expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Display name with a long comment"); + host.querySelector('[data-grid-column-index="1"]')!.click(); + await settle(); + expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("Score"); + expect(host.querySelector("[data-grid-column-info]")?.textContent).toContain("bigint"); + settingsStore.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false }); + await settle(); + expect(host.querySelector("[data-grid-column-info]")).toBeNull(); + expect(host.querySelectorAll("[data-grid-header-comment-line]")).toHaveLength(2); + }); + it("sorts transpose field rows from the column-name header", async () => { const { host } = mountGrid(); await settle(); diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 443472a33c..fab163babd 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -3222,6 +3222,7 @@ export default { columnName: "Column", columnType: "Type", columnComment: "Comment", + columnInfo: "Column information", rowNumber: "Row", valueLength: "Length", nullValue: "NULL", @@ -9094,6 +9095,8 @@ export default { webSqlFileUploadMaxMbDescription: "Largest SQL file the web server accepts for streaming execution ({min}–{max} MB).", sqlFileUploadMaxMbSaved: "Web SQL file upload size saved.", showColumnCommentsInHeader: "Show column comments under names", + dataGridColumnCommentsInInfoArea: "Show column comments in the information area", + dataGridColumnCommentsInInfoAreaDescription: "Show the selected column's comment in the bottom status bar instead of the grid header.", showColumnCommentsInHeaderDescription: "Display table column comments directly below grid column names.", showColumnTypesInHeader: "Show column types under names", showColumnTypesInHeaderDescription: "Display each column's data type directly below grid column names.", @@ -9109,6 +9112,8 @@ export default { resultTabPreferComments: "Prefer SQL comments when available", resultTabPreferCommentsDescription: "Use the comment above the SQL statement when available; turn off to always use the source table name.", dataGridShowTransposeFieldMetadata: "Show field metadata in transpose view", + dataGridTransposeMetadataInInfoColumn: "Use a separate information column in transpose view", + dataGridTransposeMetadataInInfoColumnDescription: "Show column types, nullability, and comments beside values in transposed table data and query results. Field names stay on one line.", dataGridShowTransposeFieldMetadataDescription: "Display enabled column types and comments on separate lines in transpose view. This increases row height.", colorizeDataGridCellTypes: "Color-code data grid values", colorizeDataGridCellTypesDescription: "Use subtle, theme-aware text colors to distinguish values by their column data type.", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 9ecc10dff2..bfaf9a37d8 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -2893,6 +2893,7 @@ export default withEnglishFallback({ columnName: "Columna", columnType: "Tipo", columnComment: "Comentario", + columnInfo: "Información de columna", rowNumber: "Fila", valueLength: "Longitud", nullValue: "NULL", @@ -8196,6 +8197,10 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "Archivo SQL más grande que el servidor web acepta para ejecución en streaming ({min}–{max} MB).", sqlFileUploadMaxMbSaved: "Tamaño de carga de archivo SQL web guardado.", showColumnCommentsInHeader: "Mostrar comentarios bajo los nombres", + dataGridColumnCommentsInInfoArea: "Mostrar comentarios de columnas en el área de información", + dataGridColumnCommentsInInfoAreaDescription: "Mover los comentarios de las columnas seleccionadas a la barra de estado inferior y dejar de mostrarlos en el encabezado.", + dataGridTransposeMetadataInInfoColumn: "Usar una columna de información independiente en la vista transpuesta", + dataGridTransposeMetadataInInfoColumnDescription: "En la vista transpuesta de datos de tabla y resultados de consultas, mostrar el tipo, la nulabilidad y los comentarios junto a los valores. Los nombres de campo permanecen en una sola línea.", showColumnCommentsInHeaderDescription: "Muestra los comentarios de columnas directamente debajo del nombre de la columna.", showColumnTypesInHeader: "Mostrar tipos de columna bajo los nombres", showColumnTypesInHeaderDescription: "Muestra el tipo de dato de cada columna directamente debajo del nombre de la columna.", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 25a8749edf..41685e0eea 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -2788,6 +2788,7 @@ export default withEnglishFallback({ columnName: "Colonna", columnType: "Tipo", columnComment: "Commento", + columnInfo: "Informazioni sulla colonna", rowNumber: "Riga", valueLength: "Lunghezza", nullValue: "NULL", @@ -7975,6 +7976,10 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "File SQL più grande che il server web accetta per l'esecuzione in streaming ({min}–{max} MB).", sqlFileUploadMaxMbSaved: "Dimensione di caricamento file SQL web salvata.", showColumnCommentsInHeader: "Mostra i commenti delle colonne sotto i nomi", + dataGridColumnCommentsInInfoArea: "Mostra i commenti delle colonne nell'area informazioni", + dataGridColumnCommentsInInfoAreaDescription: "Mostra il commento della colonna selezionata nella barra di stato inferiore invece che nell'intestazione della griglia.", + dataGridTransposeMetadataInInfoColumn: "Usa una colonna informazioni separata nella vista trasposta", + dataGridTransposeMetadataInInfoColumnDescription: "Nella vista trasposta di dati tabellari e risultati delle query, mostra tipo, nullabilità e commenti accanto ai valori. I nomi dei campi restano su una riga.", showColumnCommentsInHeaderDescription: "Visualizza i commenti delle colonne della tabella direttamente sotto i nomi delle colonne nella griglia.", showColumnTypesInHeader: "Mostra i tipi di colonna sotto i nomi", showColumnTypesInHeaderDescription: "Visualizza il tipo di dato di ciascuna colonna direttamente sotto i nomi delle colonne nella griglia.", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index c3af9d64c4..74dd8c5be9 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -2792,6 +2792,7 @@ export default withEnglishFallback({ columnName: "列", columnType: "データ型", columnComment: "コメント", + columnInfo: "列情報", rowNumber: "行", valueLength: "長さ", nullValue: "NULL", @@ -7999,6 +8000,10 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "Webサーバーがストリーミング実行に受け付ける最大のSQLファイルサイズ({min}~{max} MB)。", sqlFileUploadMaxMbSaved: "Web SQLファイルのアップロードサイズを保存しました。", showColumnCommentsInHeader: "列名の下にコメントを表示", + dataGridColumnCommentsInInfoArea: "情報エリアに列コメントを表示", + dataGridColumnCommentsInInfoAreaDescription: "選択した列のコメントをグリッドヘッダーの代わりに下部のステータスバーに表示します。", + dataGridTransposeMetadataInInfoColumn: "転置ビューで独立した情報列を使用", + dataGridTransposeMetadataInInfoColumnDescription: "表データとクエリ結果の転置ビューで、型・NULL 許容・コメントを値の横に表示します。フィールド名は 1 行に保たれます。", showColumnCommentsInHeaderDescription: "グリッド列名の直下にテーブル列コメントを表示します。", showColumnTypesInHeader: "列名の下にデータ型を表示", showColumnTypesInHeaderDescription: "グリッド列名の直下に各列のデータ型を表示します。", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index 5be69d7d86..bcf61cd90b 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -2833,6 +2833,7 @@ export default withEnglishFallback({ columnName: "컬럼", columnType: "타입", columnComment: "주석", + columnInfo: "열 정보", rowNumber: "행", valueLength: "길이", nullValue: "NULL", @@ -7844,6 +7845,10 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "웹 서버가 스트리밍 실행에 허용하는 가장 큰 SQL 파일({min}–{max} MB).", sqlFileUploadMaxMbSaved: "웹 SQL 파일 업로드 크기가 저장되었습니다.", showColumnCommentsInHeader: "이름 아래에 컬럼 주석 표시", + dataGridColumnCommentsInInfoArea: "정보 영역에 열 주석 표시", + dataGridColumnCommentsInInfoAreaDescription: "선택한 열의 주석을 그리드 헤더 대신 하단 상태 표시줄에 표시합니다.", + dataGridTransposeMetadataInInfoColumn: "전치 보기에서 별도 정보 열 사용", + dataGridTransposeMetadataInInfoColumnDescription: "테이블 데이터 및 쿼리 결과의 전치 보기에서 유형, NULL 허용 여부, 주석을 값 옆에 표시합니다. 필드 이름은 한 줄로 유지됩니다.", showColumnCommentsInHeaderDescription: "그리드 컬럼 이름 바로 아래에 테이블 컬럼 주석을 표시합니다.", showColumnTypesInHeader: "이름 아래에 컬럼 타입 표시", showColumnTypesInHeaderDescription: "그리드 컬럼 이름 바로 아래에 각 컬럼의 데이터 타입을 표시합니다.", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index 2df2b5ff0e..d99ee86f75 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -2788,6 +2788,7 @@ export default withEnglishFallback({ columnName: "Coluna", columnType: "Tipo", columnComment: "Comentário", + columnInfo: "Informações da coluna", rowNumber: "Linha", valueLength: "Comprimento", nullValue: "NULL", @@ -7974,6 +7975,10 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "Maior arquivo SQL que o servidor web aceita para execução em streaming ({min}–{max} MB).", sqlFileUploadMaxMbSaved: "Tamanho de upload de arquivo SQL web salvo.", showColumnCommentsInHeader: "Mostrar comentários de coluna sob os nomes", + dataGridColumnCommentsInInfoArea: "Mostrar comentários de colunas na área de informações", + dataGridColumnCommentsInInfoAreaDescription: "Mostra o comentário da coluna selecionada na barra de status inferior em vez do cabeçalho da grade.", + dataGridTransposeMetadataInInfoColumn: "Usar uma coluna de informações separada na visão transposta", + dataGridTransposeMetadataInInfoColumnDescription: "Na visão transposta de dados de tabela e resultados de consultas, mostra tipos, nulidade e comentários ao lado dos valores. Os nomes dos campos permanecem em uma linha.", showColumnCommentsInHeaderDescription: "Exibir comentários de colunas da tabela diretamente abaixo dos nomes das colunas da grade.", showColumnTypesInHeader: "Mostrar tipos de coluna sob os nomes", showColumnTypesInHeaderDescription: "Exibir o tipo de dado de cada coluna diretamente abaixo dos nomes das colunas da grade.", diff --git a/apps/desktop/src/i18n/locales/ru.ts b/apps/desktop/src/i18n/locales/ru.ts index 4b82a44065..666e84ed49 100644 --- a/apps/desktop/src/i18n/locales/ru.ts +++ b/apps/desktop/src/i18n/locales/ru.ts @@ -3152,6 +3152,7 @@ export default withEnglishFallback({ cut: "Вырезать", uncommittedBlankRow: "Незаполненная новая строка будет проигнорирована при сохранении", copyExtractorSqlDeletesInserts: "SQL-операторы DELETEs/INSERTs", + columnInfo: "Информация о столбце", }, exportProgress: { streamingUnsupported: "Потоковый экспорт не поддерживается для этого запроса. Упростите его или используйте поддерживаемый драйвер.", @@ -10458,6 +10459,10 @@ export default withEnglishFallback({ shortcutSelectLineEnds: "Выделить концы строк", openQueryOnConnectionOpen: "Открывать страницу запроса при открытии подключения", openQueryOnConnectionOpenDescription: "При открытии подключения или базы данных открывается запрос default.sql. Изменения сохраняются автоматически.", + dataGridColumnCommentsInInfoArea: "Показывать комментарии к столбцам в информационной области", + dataGridColumnCommentsInInfoAreaDescription: "Переносить комментарии выбранных столбцов в нижнюю строку состояния и не отображать их в заголовке таблицы.", + dataGridTransposeMetadataInInfoColumn: "Отдельный информационный столбец в транспонированном представлении", + dataGridTransposeMetadataInInfoColumnDescription: "В транспонированном представлении данных таблицы и результатов запроса размещать тип, допустимость NULL и комментарии в отдельном информационном столбце справа от значений; имена полей остаются в одной строке.", }, driverStore: { jreDirRemoveFailed: "Не удалось удалить старый каталог JRE: {path} (исходная ошибка: {error})", diff --git a/apps/desktop/src/i18n/locales/th.ts b/apps/desktop/src/i18n/locales/th.ts index 96d9c1fcb1..a41b1b46ea 100644 --- a/apps/desktop/src/i18n/locales/th.ts +++ b/apps/desktop/src/i18n/locales/th.ts @@ -3376,6 +3376,7 @@ export default withEnglishFallback({ filterModeOrderReset: "คืนค่าลำดับเริ่มต้น", uncommittedBlankRow: "แถวใหม่ที่ยังไม่ได้กรอกจะถูกละเว้นเมื่อบันทึก", copyExtractorSqlDeletesInserts: "คำสั่ง SQL DELETEs/INSERTs", + columnInfo: "ข้อมูลคอลัมน์", }, exportProgress: { streamingUnsupported: "ไม่รองรับการส่งออกแบบ Streaming สำหรับ Query นี้ ทำให้เรียบง่ายขึ้นหรือใช้ Driver ที่รองรับ", @@ -10888,6 +10889,10 @@ export default withEnglishFallback({ shortcutToggleCaseSelection: "สลับตัวพิมพ์ของการเลือก", shortcutConvertNamingStyle: "สลับรูปแบบการตั้งชื่อ", shortcutExPasteSqlInCondition: "ExPaste: วางเป็นเงื่อนไข IN", + dataGridColumnCommentsInInfoArea: "แสดงคำอธิบายคอลัมน์ในพื้นที่ข้อมูล", + dataGridColumnCommentsInInfoAreaDescription: "ย้ายคำอธิบายของคอลัมน์ที่เลือกไปยังแถบสถานะด้านล่าง และไม่แสดงในส่วนหัวอีกต่อไป", + dataGridTransposeMetadataInInfoColumn: "มุมมองทรานสโพสใช้คอลัมน์ข้อมูลแยกต่างหาก", + dataGridTransposeMetadataInInfoColumnDescription: "ในมุมมองทรานสโพสของข้อมูลตารางและผลลัพธ์คิวรี ให้วางประเภท การเป็นค่าว่างได้ และคำอธิบายไว้ในคอลัมน์ข้อมูลแยกทางด้านขวาของค่า โดยชื่อคอลัมน์คงอยู่ในบรรทัดเดียว", }, driverStore: { jreDirRemoveFailed: "ลบไดเรกทอรี JRE เก่าไม่สำเร็จ: {path} (ข้อผิดพลาดเดิม: {error})", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 0bfbd34605..d53a50b266 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -3156,6 +3156,7 @@ export default withEnglishFallback({ columnName: "列名", columnType: "类型", columnComment: "注释", + columnInfo: "字段信息", rowNumber: "行号", valueLength: "长度", nullValue: "NULL", @@ -9076,6 +9077,8 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "Web 服务器接受用于流式执行的最大 SQL 文件({min}–{max} MB)。", sqlFileUploadMaxMbSaved: "Web SQL 文件上传大小已保存。", showColumnCommentsInHeader: "在字段名下方显示注释", + dataGridColumnCommentsInInfoArea: "在信息区显示字段注释", + dataGridColumnCommentsInInfoAreaDescription: "将选中字段的注释移至底部状态栏,不再显示在表头中。", showColumnCommentsInHeaderDescription: "把表字段注释直接显示在结果表头字段名下方。", showColumnTypesInHeader: "在字段名下方显示数据类型", showColumnTypesInHeaderDescription: "把每个字段的数据类型直接显示在结果表头字段名下方。", @@ -9091,6 +9094,8 @@ export default withEnglishFallback({ resultTabPreferComments: "有注释时优先展示注释", resultTabPreferCommentsDescription: "开启后优先使用 SQL 语句上方的注释;关闭后按来源表名显示。", dataGridShowTransposeFieldMetadata: "在转置视图中显示字段元数据", + dataGridTransposeMetadataInInfoColumn: "转置视图使用独立信息列", + dataGridTransposeMetadataInInfoColumnDescription: "在表数据和查询结果的转置视图中,将类型、可空性和注释放在值右侧的独立信息列,字段名保持单行。", dataGridShowTransposeFieldMetadataDescription: "在转置视图中分行显示已启用的数据类型和注释。开启后会增加行高。", colorizeDataGridCellTypes: "按数据类型为表格值着色", colorizeDataGridCellTypesDescription: "使用适配主题的克制文字颜色,按字段数据类型区分表格中的值。", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 11920f5370..8fb934b66f 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -2779,6 +2779,7 @@ export default withEnglishFallback({ columnName: "欄位名稱", columnType: "類型", columnComment: "註解", + columnInfo: "欄位資訊", rowNumber: "行號", valueLength: "長度", nullValue: "NULL", @@ -7318,6 +7319,8 @@ export default withEnglishFallback({ webSqlFileUploadMaxMbDescription: "Web 伺服器接受用於串流執行之最大 SQL 檔案({min}–{max} MB)。", sqlFileUploadMaxMbSaved: "Web SQL 檔案上傳大小已儲存。", showColumnCommentsInHeader: "在欄位名稱下方顯示註解", + dataGridColumnCommentsInInfoArea: "在資訊區顯示欄位註解", + dataGridColumnCommentsInInfoAreaDescription: "將選取欄位的註解移至底部狀態列,不再顯示於表頭中。", showColumnCommentsInHeaderDescription: "直接在資料表格欄位名稱下方顯示資料表欄位註解。", showColumnTypesInHeader: "在欄位名稱下方顯示資料類型", showColumnTypesInHeaderDescription: "直接在資料表格欄位名稱下方顯示每個欄位的資料類型。", @@ -7333,6 +7336,8 @@ export default withEnglishFallback({ resultTabPreferComments: "有註解時優先顯示註解", resultTabPreferCommentsDescription: "開啟後優先使用 SQL 語句上方的註解;關閉後依來源資料表名稱顯示。", dataGridShowTransposeFieldMetadata: "在轉置檢視中顯示欄位中繼資料", + dataGridTransposeMetadataInInfoColumn: "轉置檢視使用獨立資訊欄", + dataGridTransposeMetadataInInfoColumnDescription: "在資料表與查詢結果的轉置檢視中,將類型、可空性和註解放在值右側的獨立資訊欄,欄位名稱維持單行。", dataGridShowTransposeFieldMetadataDescription: "在轉置檢視中分行顯示已啟用的資料類型和註解。開啟後會增加列高。", colorizeDataGridCellTypes: "依資料類型為表格值著色", colorizeDataGridCellTypesDescription: "使用配合主題的柔和文字顏色,依欄位資料類型區分表格中的值。", diff --git a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts index ba625b730d..5c2d60336f 100644 --- a/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts +++ b/apps/desktop/src/lib/settings/__tests__/editorSettingsDraft.spec.ts @@ -43,6 +43,12 @@ function makeSettings(overrides: Partial = {}): EditorSettings { } describe("EDITOR_SETTINGS_DRAFT_KEYS", () => { + it("captures and saves the column comment placement preference", () => { + const base = editorSettingsDraftFromSettings(makeSettings({ dataGridColumnCommentsInInfoArea: false, dataGridTransposeMetadataInInfoColumn: false })); + const draft = editorSettingsDraftFromSettings(makeSettings({ dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true })); + expect(draft.dataGridColumnCommentsInInfoArea).toBe(true); + expect(editorSettingsPatchFromDraft(draft, base)).toEqual({ dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true }); + }); it("keeps connection and query timeout ownership outside editor settings", () => { expect(EDITOR_SETTINGS_DRAFT_KEYS).not.toContain("globalConnectTimeoutSecs"); expect(EDITOR_SETTINGS_DRAFT_KEYS).not.toContain("globalQueryTimeoutSecs"); diff --git a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts index 32d1314a82..5e3df9a3d1 100644 --- a/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts +++ b/apps/desktop/src/lib/settings/__tests__/settingsTransfer.spec.ts @@ -14,6 +14,17 @@ function fileWith(editor: Record, overrides: Record { + it("round-trips column comment placement as a data preference and rejects non-booleans", () => { + const text = serializeSettingsTransfer({ ...DEFAULT_EDITOR_SETTINGS, dataGridColumnCommentsInInfoArea: true, dataGridTransposeMetadataInInfoColumn: true }); + const result = parseSettingsTransferFile(text); + expect(result.ok).toBe(true); + if (result.ok) expect(result.value.editorSettings.dataGridColumnCommentsInInfoArea).toBe(true); + if (result.ok) expect(result.value.editorSettings.dataGridTransposeMetadataInInfoColumn).toBe(true); + expect(transferCategoryForKey("dataGridTransposeMetadataInInfoColumn")).toBe("data"); + expect(transferCategoryForKey("dataGridColumnCommentsInInfoArea")).toBe("data"); + expect(parseSettingsTransferFile(fileWith({ dataGridColumnCommentsInInfoArea: "true" })).ok).toBe(false); + expect(parseSettingsTransferFile(fileWith({ dataGridTransposeMetadataInInfoColumn: "true" })).ok).toBe(false); + }); it("round-trips an opt-out of comment-first result names and rejects non-booleans", () => { const text = serializeSettingsTransfer({ ...DEFAULT_EDITOR_SETTINGS, resultTabPreferComments: false }); const result = parseSettingsTransferFile(text); diff --git a/apps/desktop/src/lib/settings/editorSettingsDraft.ts b/apps/desktop/src/lib/settings/editorSettingsDraft.ts index 2f50d12d9c..cdf2484a04 100644 --- a/apps/desktop/src/lib/settings/editorSettingsDraft.ts +++ b/apps/desktop/src/lib/settings/editorSettingsDraft.ts @@ -61,12 +61,14 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [ "tabSortMode", "tabMaxWidth", "showColumnCommentsInHeader", + "dataGridColumnCommentsInInfoArea", "showColumnTypesInHeader", "showColumnHeaderTooltips", "showResultSourceDatabase", "resultTabNamingMode", "resultTabPreferComments", "dataGridShowTransposeFieldMetadata", + "dataGridTransposeMetadataInInfoColumn", "colorizeDataGridCellTypes", "dataGridTypeColorSchemes", "activeDataGridTypeColorSchemeId", diff --git a/apps/desktop/src/lib/settings/settingsSearch.ts b/apps/desktop/src/lib/settings/settingsSearch.ts index be3cae9455..b29da793c8 100644 --- a/apps/desktop/src/lib/settings/settingsSearch.ts +++ b/apps/desktop/src/lib/settings/settingsSearch.ts @@ -241,12 +241,14 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] = { id: "default-explain-view", category: "data", titleKey: "settings.defaultExplainView", descriptionKey: "settings.defaultExplainViewDescription", targetId: "default-explain-view" }, { id: "query-result-max-rows", category: "data", titleKey: "settings.queryResultMaxRows", descriptionKey: "settings.queryResultMaxRowsDescription", targetId: "data" }, { id: "data-grid-header-comments", category: "data", titleKey: "settings.showColumnCommentsInHeader", descriptionKey: "settings.showColumnCommentsInHeaderDescription", targetId: "data" }, + { id: "data-grid-comment-info-area", category: "data", titleKey: "settings.dataGridColumnCommentsInInfoArea", descriptionKey: "settings.dataGridColumnCommentsInInfoAreaDescription", targetId: "data" }, { id: "data-grid-header-types", category: "data", titleKey: "settings.showColumnTypesInHeader", descriptionKey: "settings.showColumnTypesInHeaderDescription", targetId: "data" }, { id: "data-grid-header-tooltips", category: "data", titleKey: "settings.showColumnHeaderTooltips", descriptionKey: "settings.showColumnHeaderTooltipsDescription", targetId: "data" }, { id: "result-source-database-name", category: "data", titleKey: "settings.showResultSourceDatabase", descriptionKey: "settings.showResultSourceDatabaseDescription", targetId: "data" }, { id: "result-tab-naming-mode", category: "data", titleKey: "settings.resultTabNamingMode", descriptionKey: "settings.resultTabNamingModeDescription", targetId: "data" }, { id: "result-tab-prefer-comments", category: "data", titleKey: "settings.resultTabPreferComments", descriptionKey: "settings.resultTabPreferCommentsDescription", targetId: "data" }, { id: "data-grid-transpose-field-metadata", category: "data", titleKey: "settings.dataGridShowTransposeFieldMetadata", descriptionKey: "settings.dataGridShowTransposeFieldMetadataDescription", targetId: "data" }, + { id: "data-grid-transpose-info-column", category: "data", titleKey: "settings.dataGridTransposeMetadataInInfoColumn", descriptionKey: "settings.dataGridTransposeMetadataInInfoColumnDescription", targetId: "data" }, { id: "data-grid-cell-type-colors", category: "data", titleKey: "settings.colorizeDataGridCellTypes", descriptionKey: "settings.colorizeDataGridCellTypesDescription", targetId: "data" }, { id: "data-grid-type-colors", category: "data", titleKey: "settings.dataGridTypeColorScheme", descriptionKey: "settings.dataGridTypeColorSchemeDescription", targetId: "data-grid-type-colors" }, { id: "data-grid-index-indicators", category: "data", titleKey: "settings.showIndexIndicatorsInHeader", descriptionKey: "settings.showIndexIndicatorsInHeaderDescription", targetId: "data" }, diff --git a/apps/desktop/src/lib/settings/settingsTransfer.ts b/apps/desktop/src/lib/settings/settingsTransfer.ts index 62cd2d7f62..cdfcb79b5d 100644 --- a/apps/desktop/src/lib/settings/settingsTransfer.ts +++ b/apps/desktop/src/lib/settings/settingsTransfer.ts @@ -155,12 +155,14 @@ const SETTINGS_TRANSFER_CATEGORY_KEYS: Record, exist autoCalculateTotalRows: settings.autoCalculateTotalRows ?? DEFAULT_EDITOR_SETTINGS.autoCalculateTotalRows, mongoViewMode: settings.mongoViewMode === "table" ? "table" : DEFAULT_EDITOR_SETTINGS.mongoViewMode, showColumnCommentsInHeader: settings.showColumnCommentsInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader, + dataGridColumnCommentsInInfoArea: settings.dataGridColumnCommentsInInfoArea ?? DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea, showColumnTypesInHeader: settings.showColumnTypesInHeader ?? DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader, showColumnHeaderTooltips: settings.showColumnHeaderTooltips ?? DEFAULT_EDITOR_SETTINGS.showColumnHeaderTooltips, showResultSourceDatabase: settings.showResultSourceDatabase ?? DEFAULT_EDITOR_SETTINGS.showResultSourceDatabase, @@ -2088,6 +2093,7 @@ export function normalizeEditorSettings(settings: Partial, exist resultTabNamingMode: normalizeResultTabNamingMode(settings.resultTabNamingMode), resultTabPreferComments: settings.resultTabPreferComments !== false, dataGridShowTransposeFieldMetadata: settings.dataGridShowTransposeFieldMetadata === true, + dataGridTransposeMetadataInInfoColumn: settings.dataGridTransposeMetadataInInfoColumn === true, colorizeDataGridCellTypes: settings.colorizeDataGridCellTypes ?? DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes, dataGridTypeColorSchemes, activeDataGridTypeColorSchemeId: normalizeActiveDataGridTypeColorSchemeId(dataGridTypeColorSchemes, settings.activeDataGridTypeColorSchemeId), @@ -2972,6 +2978,7 @@ export const useSettingsStore = defineStore("settings", () => { if (partial.autoCalculateTotalRows !== undefined) editorSettings.value.autoCalculateTotalRows = partial.autoCalculateTotalRows === true; if (partial.mongoViewMode !== undefined) editorSettings.value.mongoViewMode = partial.mongoViewMode; if (partial.showColumnCommentsInHeader !== undefined) editorSettings.value.showColumnCommentsInHeader = partial.showColumnCommentsInHeader; + if (partial.dataGridColumnCommentsInInfoArea !== undefined) editorSettings.value.dataGridColumnCommentsInInfoArea = partial.dataGridColumnCommentsInInfoArea; if (partial.showColumnTypesInHeader !== undefined) editorSettings.value.showColumnTypesInHeader = partial.showColumnTypesInHeader; if (partial.showColumnHeaderTooltips !== undefined) editorSettings.value.showColumnHeaderTooltips = partial.showColumnHeaderTooltips; if (partial.showResultSourceDatabase !== undefined) editorSettings.value.showResultSourceDatabase = partial.showResultSourceDatabase; @@ -2983,6 +2990,7 @@ export const useSettingsStore = defineStore("settings", () => { if (partial.resultTabNamingMode !== undefined) editorSettings.value.resultTabNamingMode = normalizeResultTabNamingMode(partial.resultTabNamingMode); if (partial.resultTabPreferComments !== undefined) editorSettings.value.resultTabPreferComments = partial.resultTabPreferComments !== false; if (partial.dataGridShowTransposeFieldMetadata !== undefined) editorSettings.value.dataGridShowTransposeFieldMetadata = partial.dataGridShowTransposeFieldMetadata === true; + if (partial.dataGridTransposeMetadataInInfoColumn !== undefined) editorSettings.value.dataGridTransposeMetadataInInfoColumn = partial.dataGridTransposeMetadataInInfoColumn === true; if (partial.colorizeDataGridCellTypes !== undefined) editorSettings.value.colorizeDataGridCellTypes = partial.colorizeDataGridCellTypes === true; if (partial.dataGridTypeColorSchemes !== undefined) { editorSettings.value.dataGridTypeColorSchemes = normalizeDataGridTypeColorSchemes(partial.dataGridTypeColorSchemes); diff --git a/packages/app-tests/settingsStore.test.ts b/packages/app-tests/settingsStore.test.ts index 4a27cceb99..d2c20a2ef4 100644 --- a/packages/app-tests/settingsStore.test.ts +++ b/packages/app-tests/settingsStore.test.ts @@ -636,6 +636,10 @@ test("defaults sidebar horizontal scroll to off", () => { }); test("defaults data grid header display settings", () => { + assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridTransposeMetadataInInfoColumn, false); + assert.equal(normalizeEditorSettings({}).dataGridTransposeMetadataInInfoColumn, false); + assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridColumnCommentsInInfoArea, false); + assert.equal(normalizeEditorSettings({}).dataGridColumnCommentsInInfoArea, false); assert.equal(DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader, true); assert.equal(DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata, false); assert.equal(DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions, true); @@ -646,16 +650,34 @@ test("defaults data grid header display settings", () => { test("keeps saved data grid header display settings", () => { const settings = normalizeEditorSettings({ + dataGridTransposeMetadataInInfoColumn: true, + dataGridColumnCommentsInInfoArea: true, showColumnCommentsInHeader: true, dataGridShowTransposeFieldMetadata: true, compactColumnHeaderActions: false, } as any); assert.equal(settings.showColumnCommentsInHeader, true); + assert.equal(settings.dataGridTransposeMetadataInInfoColumn, true); + assert.equal(settings.dataGridColumnCommentsInInfoArea, true); assert.equal(settings.dataGridShowTransposeFieldMetadata, true); assert.equal(settings.compactColumnHeaderActions, false); }); +test("updates column comment placement through the store", () => { + setActivePinia(createPinia()); + const store = useSettingsStore(); + store.updateEditorSettings({ dataGridColumnCommentsInInfoArea: true }); + assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, true); + store.updateEditorSettings({ dataGridColumnCommentsInInfoArea: false }); + assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, false); + store.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: true }); + assert.equal(store.editorSettings.dataGridTransposeMetadataInInfoColumn, true); + assert.equal(store.editorSettings.dataGridColumnCommentsInInfoArea, false); + store.updateEditorSettings({ dataGridTransposeMetadataInInfoColumn: false }); + assert.equal(store.editorSettings.dataGridTransposeMetadataInInfoColumn, false); +}); + test("defaults column header hover tooltips to on", () => { // Existing installs have no persisted flag, so the grid must keep showing the // header tooltip until the user opts out.