From dab39c690abcce586fbe499e199b95d3651ad8fc Mon Sep 17 00:00:00 2001 From: lxk955 <164191365+lxk955@users.noreply.github.com> Date: Thu, 8 Oct 2026 10:21:55 +0800 Subject: [PATCH] feat(grid): support F2 shortcut to edit focused result cell --- apps/desktop/src/components/grid/DataGrid.vue | 5 +- .../DataGridEditCellShortcut.spec.ts | 231 ++++++++++++++++++ apps/desktop/src/i18n/locales/az.ts | 1 + apps/desktop/src/i18n/locales/en.ts | 1 + apps/desktop/src/i18n/locales/es.ts | 1 + apps/desktop/src/i18n/locales/id.ts | 1 + apps/desktop/src/i18n/locales/it.ts | 1 + apps/desktop/src/i18n/locales/ja.ts | 1 + apps/desktop/src/i18n/locales/ko.ts | 1 + apps/desktop/src/i18n/locales/pt-BR.ts | 1 + apps/desktop/src/i18n/locales/ru.ts | 1 + apps/desktop/src/i18n/locales/tr.ts | 1 + apps/desktop/src/i18n/locales/zh-CN.ts | 1 + apps/desktop/src/i18n/locales/zh-TW.ts | 1 + .../editor/keyboardShortcuts.spec.ts | 17 ++ .../__tests__/editor/shortcutRegistry.spec.ts | 12 + .../src/lib/editor/keyboardShortcuts.ts | 4 + .../src/lib/editor/shortcutRegistry.ts | 7 + packages/app-tests/keyboardShortcuts.test.ts | 8 + 19 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/components/grid/__tests__/DataGridEditCellShortcut.spec.ts diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 3faa84ad72..eafdde8874 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -207,6 +207,7 @@ import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, + isEditCellShortcut, isEditTableStructureShortcut, isFocusSearchShortcut, isFocusWhereShortcut, @@ -10088,7 +10089,7 @@ async function onGridKeydown(event: KeyboardEvent) { event.stopPropagation(); return; } - if (event.key === "Enter" && editSelectedCell()) { + if ((isEditCellShortcut(event, settingsStore.editorSettings.shortcuts) || (!event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey && event.key === "Enter")) && editSelectedCell()) { event.preventDefault(); return; } @@ -12468,6 +12469,8 @@ defineExpose({ toggleMultiRowTranspose, focusSearch, focusWhere, + editSelectedCell, + selectSingleCell, openGoToColumn, visibleColumnCount, displayableColumnCount, diff --git a/apps/desktop/src/components/grid/__tests__/DataGridEditCellShortcut.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridEditCellShortcut.spec.ts new file mode 100644 index 0000000000..402caa49d9 --- /dev/null +++ b/apps/desktop/src/components/grid/__tests__/DataGridEditCellShortcut.spec.ts @@ -0,0 +1,231 @@ +// @vitest-environment happy-dom + +import { createApp, defineComponent, h, markRaw, nextTick, ref, type App, type PropType } from "vue"; +import { createPinia, setActivePinia } from "pinia"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import i18n from "@/i18n"; +import type { ColumnInfo, DatabaseType, QueryResult } from "@/types/database"; +import { TooltipProvider } from "@/components/ui/tooltip"; + +vi.mock("@/composables/useDataGridColumnResize", async (importOriginal) => { + const actual = await importOriginal(); + const { ref } = await import("vue"); + return { + ...actual, + useDataGridColumnResize: () => ({ + initColumnWidths: vi.fn(), + onResizeStart: vi.fn(), + autoFitColumn: vi.fn(), + renderedColumnWidths: ref([120, 120]), + totalWidth: ref(240), + columnVars: ref({ "--total-w": "240px" }), + getIsResizing: () => false, + }), + }; +}); + +import DataGrid from "../DataGrid.vue"; +import { useSettingsStore } from "@/stores/settingsStore"; + +const mountedApps: Array<{ app: App; host: HTMLElement }> = []; + +const RecycleScroller = defineComponent({ + props: { + items: { + type: Array as PropType, + default: () => [], + }, + }, + setup(props, { attrs, slots }) { + return () => + h( + "div", + attrs, + props.items.map((item) => slots.default?.({ item })), + ); + }, +}); + +interface TableMeta { + catalog?: string; + schema?: string; + tableName: string; + columns: ColumnInfo[]; + primaryKeys: string[]; +} + +function mountGrid( + options: { + context?: "results" | "table-data"; + databaseType?: DatabaseType; + connectionId?: string; + database?: string; + tableMeta?: TableMeta | null; + shortcut?: string; + editable?: boolean; + } = {}, +) { + const pinia = createPinia(); + setActivePinia(pinia); + const settingsStore = useSettingsStore(); + settingsStore.updateEditorSettings({ + dataGridRenderMode: "canvas", + shortcuts: { + ...settingsStore.editorSettings.shortcuts, + editCell: options.shortcut ?? "F2", + }, + }); + const result = markRaw({ + columns: ["id", "name"], + rows: [ + [1, "Alice"], + [2, "Bob"], + ], + affected_rows: 0, + execution_time_ms: 0, + }); + const defaultTableMeta: TableMeta = { + catalog: "warehouse", + schema: "public", + tableName: "users", + columns: [], + primaryKeys: ["id"], + }; + + const host = document.createElement("div"); + const grid = ref<{ + editSelectedCell: () => boolean; + selectSingleCell: (row: number, col: number) => void; + }>(); + document.body.append(host); + const Root = defineComponent({ + setup() { + return () => + h( + TooltipProvider, + { delayDuration: 0 }, + { + default: () => + h(DataGrid, { + ref: grid, + result, + databaseType: options.databaseType ?? "mysql", + context: options.context ?? "table-data", + editable: options.editable ?? true, + connectionId: options.connectionId ?? "connection-1", + database: options.database ?? "app", + tableMeta: options.tableMeta === null ? undefined : (options.tableMeta ?? defaultTableMeta), + }), + }, + ); + }, + }); + const app = createApp(Root); + app.use(pinia); + app.use(i18n); + app.component("RecycleScroller", RecycleScroller); + app.mount(host); + mountedApps.push({ app, host }); + return { host, grid }; +} + +async function settle() { + await nextTick(); + await Promise.resolve(); + await nextTick(); +} + +function gridRoot(host: HTMLElement): HTMLElement { + const root = host.querySelector("[data-grid-root]"); + if (!root) throw new Error("Data grid root not found"); + return root; +} + +function dispatchKey(target: HTMLElement, key: string, modifiers: { ctrlKey?: boolean; metaKey?: boolean; altKey?: boolean; shiftKey?: boolean } = {}): KeyboardEvent { + const event = new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key, ...modifiers }); + target.dispatchEvent(event); + return event; +} + +afterEach(() => { + window.getSelection()?.removeAllRanges(); + for (const { app, host } of mountedApps.splice(0)) { + app.unmount(); + host.remove(); + } +}); + +describe("DataGrid editCell shortcut", () => { + it("enters cell editing when F2 is pressed on a selected cell", async () => { + const { host, grid } = mountGrid(); + await settle(); + const root = gridRoot(host); + grid.value?.selectSingleCell(0, 1); + await settle(); + + const event = dispatchKey(root, "F2"); + await settle(); + + expect(event.defaultPrevented).toBe(true); + expect(host.querySelector(".cell-edit-input")).not.toBeNull(); + }); + + it("still enters cell editing when plain Enter is pressed", async () => { + const { host, grid } = mountGrid(); + await settle(); + const root = gridRoot(host); + grid.value?.selectSingleCell(0, 1); + await settle(); + + const event = dispatchKey(root, "Enter"); + await settle(); + + expect(event.defaultPrevented).toBe(true); + expect(host.querySelector(".cell-edit-input")).not.toBeNull(); + }); + + it("uses a configured shortcut and leaves F2 unhandled when rebound", async () => { + const { host, grid } = mountGrid({ shortcut: "Mod+E" }); + await settle(); + const root = gridRoot(host); + grid.value?.selectSingleCell(0, 1); + await settle(); + + const defaultEvent = dispatchKey(root, "F2"); + await settle(); + expect(defaultEvent.defaultPrevented).toBe(false); + expect(host.querySelector(".cell-edit-input")).toBeNull(); + + const customEvent = dispatchKey(root, "e", { ctrlKey: true }); + await settle(); + expect(customEvent.defaultPrevented).toBe(true); + expect(host.querySelector(".cell-edit-input")).not.toBeNull(); + }); + + it("does not consume F2 when the grid is read-only", async () => { + const { host, grid } = mountGrid({ editable: false }); + await settle(); + const root = gridRoot(host); + grid.value?.selectSingleCell(0, 0); + await settle(); + + const event = dispatchKey(root, "F2"); + await settle(); + + expect(event.defaultPrevented).toBe(false); + expect(host.querySelector(".cell-edit-input")).toBeNull(); + }); + + it("leaves editable text targets untouched on F2", async () => { + const { host } = mountGrid(); + await settle(); + const root = gridRoot(host); + const input = document.createElement("input"); + root.append(input); + + const event = dispatchKey(input, "F2"); + await settle(); + + expect(event.defaultPrevented).toBe(false); + }); +}); diff --git a/apps/desktop/src/i18n/locales/az.ts b/apps/desktop/src/i18n/locales/az.ts index 044ba83f69..f57a99183d 100644 --- a/apps/desktop/src/i18n/locales/az.ts +++ b/apps/desktop/src/i18n/locales/az.ts @@ -7811,6 +7811,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Növbəti uyğunluğu əlavə et", shortcutSelectAllSelectionOccurrences: "Bütün uyğunluqları seç", shortcutExtendSelection: "Seçimi genişləndir", + shortcutEditCell: "Xananı redaktə et", shortcutEditTableStructure: "Cədvəlin quruluşunu redaktə et", shortcutCopyCurrentRow: "Cari məlumat sətrini kopyala", shortcutDeleteCurrentRow: "Cari məlumat sətrini sil", diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index eeb5e92376..e2ba000e43 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -8837,6 +8837,7 @@ export default { shortcutSelectAllSelectionOccurrences: "Select all occurrences", shortcutSelectLineEnds: "Select line ends", shortcutExtendSelection: "Extend selection", + shortcutEditCell: "Edit cell", shortcutEditTableStructure: "Edit table structure", shortcutCopyCurrentRow: "Copy current data row", shortcutDeleteCurrentRow: "Delete current data row", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index f734942d16..9c14044a39 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -8086,6 +8086,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Añadir siguiente coincidencia", shortcutSelectAllSelectionOccurrences: "Seleccionar todas las coincidencias", shortcutExtendSelection: "Ampliar selección", + shortcutEditCell: "Editar celda", shortcutEditTableStructure: "Editar estructura de tabla", shortcutCopyCurrentRow: "Copiar fila de datos actual", shortcutDeleteCurrentRow: "Eliminar fila de datos actual", diff --git a/apps/desktop/src/i18n/locales/id.ts b/apps/desktop/src/i18n/locales/id.ts index e3ab6fa8c5..82b0868633 100644 --- a/apps/desktop/src/i18n/locales/id.ts +++ b/apps/desktop/src/i18n/locales/id.ts @@ -8610,6 +8610,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Tambah kemunculan berikutnya", shortcutSelectAllSelectionOccurrences: "Pilih semua kemunculan", shortcutExtendSelection: "Perluas seleksi", + shortcutEditCell: "Edit sel", shortcutEditTableStructure: "Edit struktur tabel", shortcutCopyCurrentRow: "Salin baris data saat ini", shortcutDeleteCurrentRow: "Hapus baris data saat ini", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 0ca0da565c..d11460f3be 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -7848,6 +7848,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Aggiungi occorrenza successiva", shortcutSelectAllSelectionOccurrences: "Seleziona tutte le occorrenze", shortcutExtendSelection: "Estendi selezione", + shortcutEditCell: "Modifica cella", shortcutEditTableStructure: "Modifica struttura tabella", shortcutCopyCurrentRow: "Copia riga dati corrente", shortcutDeleteCurrentRow: "Elimina riga dati corrente", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 2aee310871..3e1c3df866 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -7842,6 +7842,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "次の出現箇所を選択に追加", shortcutSelectAllSelectionOccurrences: "すべての出現箇所を選択", shortcutExtendSelection: "選択範囲を拡張", + shortcutEditCell: "セルを編集", shortcutEditTableStructure: "テーブル構造を編集", shortcutCopyCurrentRow: "現在のデータ行をコピー", shortcutDeleteCurrentRow: "現在のデータ行を削除", diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts index 4104a29430..5031f782f0 100644 --- a/apps/desktop/src/i18n/locales/ko.ts +++ b/apps/desktop/src/i18n/locales/ko.ts @@ -7696,6 +7696,7 @@ export default withEnglishFallback({ shortcutSelectAll: "모두 선택", shortcutAddNextSelectionOccurrence: "다음 일치 항목 선택 추가", shortcutSelectAllSelectionOccurrences: "모든 일치 항목 선택", + shortcutEditCell: "셀 편집", shortcutEditTableStructure: "테이블 구조 편집", shortcutCopyCurrentRow: "현재 데이터 행 복사", shortcutDeleteCurrentRow: "현재 데이터 행 삭제", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index 19a95e1453..2b2ff57dae 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -7848,6 +7848,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Adicionar próxima ocorrência", shortcutSelectAllSelectionOccurrences: "Selecionar todas as ocorrências", shortcutExtendSelection: "Expandir seleção", + shortcutEditCell: "Editar célula", shortcutEditTableStructure: "Editar estrutura da tabela", shortcutCopyCurrentRow: "Copiar linha de dados atual", shortcutDeleteCurrentRow: "Excluir linha de dados atual", diff --git a/apps/desktop/src/i18n/locales/ru.ts b/apps/desktop/src/i18n/locales/ru.ts index 5e3aa61df8..572e1d1825 100644 --- a/apps/desktop/src/i18n/locales/ru.ts +++ b/apps/desktop/src/i18n/locales/ru.ts @@ -9137,6 +9137,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Добавить следующее вхождение", shortcutSelectAllSelectionOccurrences: "Выделить все вхождения", shortcutExtendSelection: "Расширить выделение", + shortcutEditCell: "Редактировать ячейку", shortcutEditTableStructure: "Изменить структуру таблицы", shortcutCopyCurrentRow: "Копировать текущую строку данных", shortcutDeleteCurrentRow: "Удалить текущую строку данных", diff --git a/apps/desktop/src/i18n/locales/tr.ts b/apps/desktop/src/i18n/locales/tr.ts index 6aef26d3a6..ad6b08f137 100644 --- a/apps/desktop/src/i18n/locales/tr.ts +++ b/apps/desktop/src/i18n/locales/tr.ts @@ -7712,6 +7712,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "Sonraki eşleşmeyi ekle", shortcutSelectAllSelectionOccurrences: "Tüm eşleşmeleri seç", shortcutExtendSelection: "Seçimi genişlet", + shortcutEditCell: "Hücreyi düzenle", shortcutEditTableStructure: "Tablo yapısını düzenle", shortcutCopyCurrentRow: "Geçerli veri satırını kopyala", shortcutDeleteCurrentRow: "Geçerli veri satırını sil", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 59608d7f65..1fee86a2cd 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -8824,6 +8824,7 @@ export default withEnglishFallback({ shortcutToggleCaseSelection: "切换选中内容大小写", shortcutConvertNamingStyle: "切换命名风格", shortcutExPasteSqlInCondition: "ExPaste:粘贴为 IN 条件", + shortcutEditCell: "编辑单元格", shortcutEditTableStructure: "编辑表结构", shortcutCopyCurrentRow: "复制当前数据行", shortcutDeleteCurrentRow: "删除当前数据行", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index 5e74bad07b..21549ac064 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -7167,6 +7167,7 @@ export default withEnglishFallback({ shortcutAddNextSelectionOccurrence: "增加下一個選取詞", shortcutSelectAllSelectionOccurrences: "選取所有選取詞", shortcutExtendSelection: "擴展選取範圍", + shortcutEditCell: "編輯儲存格", shortcutEditTableStructure: "編輯資料表結構", shortcutCopyCurrentRow: "複製目前資料列", shortcutDeleteCurrentRow: "刪除目前資料列", diff --git a/apps/desktop/src/lib/__tests__/editor/keyboardShortcuts.spec.ts b/apps/desktop/src/lib/__tests__/editor/keyboardShortcuts.spec.ts index ac34ee1bf9..c3fb387ff3 100644 --- a/apps/desktop/src/lib/__tests__/editor/keyboardShortcuts.spec.ts +++ b/apps/desktop/src/lib/__tests__/editor/keyboardShortcuts.spec.ts @@ -4,6 +4,7 @@ import { eventToShortcut, isBrowserTaskManagerShortcut, isConvertNamingStyleShortcut, + isEditCellShortcut, isEditTableStructureShortcut, isExecuteSqlInNewResultTabShortcut, isGoToColumnShortcut, @@ -215,6 +216,22 @@ describe("keyboard shortcut matching", () => { expect(isGoToColumnShortcut({ key: "g", ctrlKey: true, isComposing: true }, { goToColumn: "Mod+G" })).toBe(false); }); + it("matches the edit-cell shortcut F2 by default on Windows and macOS", () => { + expect(isEditCellShortcut({ key: "F2" }, undefined, "Win32")).toBe(true); + expect(isEditCellShortcut({ key: "F2" }, undefined, "MacIntel")).toBe(true); + expect(isEditCellShortcut({ key: "F2", ctrlKey: true }, undefined, "Win32")).toBe(false); + expect(isEditCellShortcut({ key: "F2", shiftKey: true }, undefined, "Win32")).toBe(false); + expect(isEditCellShortcut({ key: "F2", altKey: true }, undefined, "Win32")).toBe(false); + expect(isEditCellShortcut({ key: "F3" }, undefined, "Win32")).toBe(false); + }); + + it("honors custom and disabled edit-cell shortcuts", () => { + expect(isEditCellShortcut({ key: "e", ctrlKey: true }, { editCell: "Mod+E" }, "Win32")).toBe(true); + expect(isEditCellShortcut({ key: "F2" }, { editCell: "Mod+E" }, "Win32")).toBe(false); + expect(isEditCellShortcut({ key: "F2" }, { editCell: "" }, "Win32")).toBe(false); + expect(isEditCellShortcut({ key: "F2", isComposing: true }, undefined, "Win32")).toBe(false); + }); + it("matches the edit-table-structure shortcut on Windows and macOS", () => { expect(isEditTableStructureShortcut({ key: "d", ctrlKey: true, shiftKey: true }, undefined, "Win32")).toBe(true); expect(isEditTableStructureShortcut({ key: "d", metaKey: true, shiftKey: true }, undefined, "MacIntel")).toBe(true); diff --git a/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts b/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts index d78f297617..518e66741c 100644 --- a/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts +++ b/apps/desktop/src/lib/__tests__/editor/shortcutRegistry.spec.ts @@ -122,6 +122,18 @@ describe("shortcutRegistry editor actions", () => { expect(findShortcutConflict("goToColumn", "Mod+F", shortcuts)).toBeNull(); }); + it("registers edit-cell F2 as a conflict-free grid default", () => { + const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === "editCell"); + + expect(definition).toMatchObject({ + labelKey: "settings.shortcutEditCell", + scope: "grid", + defaultShortcut: "F2", + }); + expect(DEFAULT_SHORTCUT_SETTINGS.editCell).toBe("F2"); + expect(findShortcutConflict("editCell", DEFAULT_SHORTCUT_SETTINGS.editCell, DEFAULT_SHORTCUT_SETTINGS)).toBeNull(); + }); + it("registers copy-current-row Mod+D and edit-table-structure Mod+Shift+D as conflict-free grid defaults", () => { const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === "editTableStructure"); diff --git a/apps/desktop/src/lib/editor/keyboardShortcuts.ts b/apps/desktop/src/lib/editor/keyboardShortcuts.ts index 34de62f538..bf10637218 100644 --- a/apps/desktop/src/lib/editor/keyboardShortcuts.ts +++ b/apps/desktop/src/lib/editor/keyboardShortcuts.ts @@ -265,6 +265,10 @@ export function isCopyCurrentRowShortcut(event: ShortcutLikeEvent, shortcuts?: P return matchesShortcut(event, actionShortcut("copyCurrentRow", shortcuts)); } +export function isEditCellShortcut(event: ShortcutLikeEvent, shortcuts?: Partial, platform = globalThis.navigator?.platform || ""): boolean { + return matchesShortcut(event, actionShortcut("editCell", shortcuts, platform), platform); +} + export function isEditTableStructureShortcut(event: ShortcutLikeEvent, shortcuts?: Partial, platform = globalThis.navigator?.platform || ""): boolean { return matchesShortcut(event, actionShortcut("editTableStructure", shortcuts, platform), platform); } diff --git a/apps/desktop/src/lib/editor/shortcutRegistry.ts b/apps/desktop/src/lib/editor/shortcutRegistry.ts index 92699a3057..7d795abd2b 100644 --- a/apps/desktop/src/lib/editor/shortcutRegistry.ts +++ b/apps/desktop/src/lib/editor/shortcutRegistry.ts @@ -36,6 +36,7 @@ export type ShortcutActionId = | "toggleFold" | "foldAll" | "unfoldAll" + | "editCell" | "editTableStructure" | "copyCurrentRow" | "deleteCurrentRow" @@ -381,6 +382,12 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [ scope: "editor", defaultShortcut: foldAllDefaultShortcut("unfoldAll"), }, + { + id: "editCell", + labelKey: "settings.shortcutEditCell", + scope: "grid", + defaultShortcut: "F2", + }, { id: "editTableStructure", labelKey: "settings.shortcutEditTableStructure", diff --git a/packages/app-tests/keyboardShortcuts.test.ts b/packages/app-tests/keyboardShortcuts.test.ts index 672f04f4be..74a7d84899 100644 --- a/packages/app-tests/keyboardShortcuts.test.ts +++ b/packages/app-tests/keyboardShortcuts.test.ts @@ -27,6 +27,7 @@ import { isSwitchToPreviousTabShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, + isEditCellShortcut, isToggleResultsPaneShortcut, isToggleTransposeShortcut, isZoomInShortcut, @@ -427,6 +428,13 @@ test("matches copy current row Mod+D by default while honoring custom shortcuts" assert.equal(isCopyCurrentRowShortcut({ key: "d", altKey: true }, { copyCurrentRow: "Alt+D" }), true); }); +test("matches edit cell F2 by default while honoring custom shortcuts", () => { + assert.equal(isEditCellShortcut({ key: "F2" }), true); + assert.equal(isEditCellShortcut({ key: "e", ctrlKey: true }, { editCell: "Mod+E" } as any), true); + assert.equal(isEditCellShortcut({ key: "F2" }, { editCell: "Mod+E" } as any), false); + assert.equal(isEditCellShortcut({ key: "F2", ctrlKey: true }), false); +}); + test("matches Delete for deleting current row", () => { assert.equal(isDeleteCurrentRowShortcut({ key: "Delete" }), true); });