diff --git a/packages/core/src/data-editor/data-editor-keybindings.ts b/packages/core/src/data-editor/data-editor-keybindings.ts index b68d339cf..3f71dfec6 100644 --- a/packages/core/src/data-editor/data-editor-keybindings.ts +++ b/packages/core/src/data-editor/data-editor-keybindings.ts @@ -67,6 +67,8 @@ export interface ConfigurableKeybinds { readonly selectAll: Keybind; readonly selectRow: Keybind; readonly selectColumn: Keybind; + + readonly contextMenu: Keybind; } export type Keybinds = ConfigurableKeybinds & ForcedKeybinds & Partial; @@ -118,6 +120,7 @@ export const keybindingDefaults: Keybinds = { selectGrowRight: true, selectGrowDown: true, selectGrowLeft: true, + contextMenu: true, }; function realizeKeybind(keybind: Keybind, defaultVal: string): string { @@ -174,6 +177,7 @@ export function realizeKeybinds(keybinds: Keybinds): RealizedKeybinds { selectToLastCell: realizeKeybind(keybinds.selectToLastCell, "primary+shift+End"), selectToLastColumn: realizeKeybind(keybinds.selectToLastColumn, "primary+shift+ArrowRight"), selectToLastRow: realizeKeybind(keybinds.selectToLastRow, "primary+shift+ArrowDown"), + contextMenu: realizeKeybind(keybinds.contextMenu, "shift+F10"), }; } diff --git a/packages/core/src/data-editor/data-editor.tsx b/packages/core/src/data-editor/data-editor.tsx index 6674c6f7f..755057331 100644 --- a/packages/core/src/data-editor/data-editor.tsx +++ b/packages/core/src/data-editor/data-editor.tsx @@ -3185,6 +3185,42 @@ const DataEditorImpl: React.ForwardRefRenderFunction void) => { + const adjustedCol = args.location[0] - rowMarkerOffset; + if (args.kind === "header") { + onHeaderContextMenu?.(adjustedCol, { ...args, preventDefault }); + } + + if (args.kind === groupHeaderKind) { + if (adjustedCol < 0) { + return; + } + onGroupHeaderContextMenu?.(adjustedCol, { ...args, preventDefault }); + } + + if (args.kind === "cell") { + const [col, row] = args.location; + onCellContextMenu?.([adjustedCol, row], { + ...args, + preventDefault, + }); + + if (!gridSelectionHasItem(gridSelection, args.location)) { + updateSelectedCell(col, row, false, false); + } + } + }, + [ + gridSelection, + onCellContextMenu, + onGroupHeaderContextMenu, + onHeaderContextMenu, + rowMarkerOffset, + updateSelectedCell, + ] + ); + const handleFixedKeybindings = React.useCallback( (event: GridKeyEventArgs): boolean => { const cancel = () => { @@ -3362,6 +3398,35 @@ const DataEditorImpl: React.ForwardRefRenderFunction void) => { - const adjustedCol = args.location[0] - rowMarkerOffset; - if (args.kind === "header") { - onHeaderContextMenu?.(adjustedCol, { ...args, preventDefault }); - } - - if (args.kind === groupHeaderKind) { - if (adjustedCol < 0) { - return; - } - onGroupHeaderContextMenu?.(adjustedCol, { ...args, preventDefault }); - } - - if (args.kind === "cell") { - const [col, row] = args.location; - onCellContextMenu?.([adjustedCol, row], { - ...args, - preventDefault, - }); - - if (!gridSelectionHasItem(gridSelection, args.location)) { - updateSelectedCell(col, row, false, false); - } - } - }, - [ - gridSelection, - onCellContextMenu, - onGroupHeaderContextMenu, - onHeaderContextMenu, - rowMarkerOffset, - updateSelectedCell, - ] - ); - const onPasteInternal = React.useCallback( async (e?: ClipboardEvent) => { if (!keybindings.paste) return; diff --git a/packages/core/test/data-editor.test.tsx b/packages/core/test/data-editor.test.tsx index 4a0d97369..d5d57d859 100644 --- a/packages/core/test/data-editor.test.tsx +++ b/packages/core/test/data-editor.test.tsx @@ -1149,6 +1149,78 @@ describe("data-editor", () => { expect(spy).toHaveBeenCalledWith(expect.objectContaining({ location: [1, 1] })); }); + test("opens context menu with Shift+F10 when cell is selected", async () => { + const spy = vi.fn(); + + vi.useFakeTimers(); + render(, { + wrapper: Context, + }); + prep(false); + + const canvas = screen.getByTestId("data-grid-canvas"); + sendClick(canvas, { + clientX: 300, + clientY: 84, + }); + + fireEvent.keyDown(canvas, { + key: "F10", + keyCode: 121, + shiftKey: true, + }); + + expect(spy).toHaveBeenCalledWith([1, 1], expect.anything()); + + const eventArgs = spy.mock.calls[0][1]; + expect(eventArgs).toMatchObject({ + kind: "cell", + shiftKey: true, + location: [1, 1], + bounds: expect.objectContaining({ + x: expect.any(Number), + y: expect.any(Number), + width: expect.any(Number), + height: expect.any(Number), + }), + localEventX: eventArgs.bounds.width / 2, + localEventY: eventArgs.bounds.height / 2, + }); + + }); + + test("does not open context menu with Shift+F10 when no cells are selected", async () => { + const spy = vi.fn(); + + vi.useFakeTimers(); + render( + , + { + wrapper: Context, + } + ); + prep(false); + + const canvas = screen.getByTestId("data-grid-canvas"); + + // Press Shift+F10 without selecting any cell + fireEvent.keyDown(canvas, { + key: "F10", + keyCode: 121, + shiftKey: true, + }); + + expect(spy).not.toHaveBeenCalled(); + }); + test("Delete cell", async () => { const spy = vi.fn();