diff --git a/frontend/src/components/DataGrid.ddl.test.tsx b/frontend/src/components/DataGrid.ddl.test.tsx index 39ff6d53..cabda244 100644 --- a/frontend/src/components/DataGrid.ddl.test.tsx +++ b/frontend/src/components/DataGrid.ddl.test.tsx @@ -1036,6 +1036,54 @@ describe('DataGrid DDL interactions', () => { }, ); + it('selects every editable cell in a column when its header is clicked in cell edit mode', async () => { + messageApi.info.mockResolvedValue(undefined); + let renderer: ReactTestRenderer; + await act(async () => { + renderer = create( + , + ); + }); + await waitForEffects(); + + await act(async () => { + renderer!.root.findByType(DataGridToolbarFrame).props.onToggleCellEditMode(); + }); + await waitForEffects(); + + const nameColumn = testRenderState.latestColumns.find((column) => column.key === 'name'); + expect(nameColumn?.editable).toBe(true); + const headerProps = nameColumn.onHeaderCell(nameColumn); + const event = { + target: { closest: vi.fn(() => null) }, + currentTarget: { querySelector: vi.fn(() => null) }, + preventDefault: vi.fn(), + stopPropagation: vi.fn(), + }; + + await act(async () => { + headerProps.onClickCapture(event); + }); + + const toolbar = renderer!.root.findByType(DataGridToolbarFrame); + expect(event.preventDefault).toHaveBeenCalledOnce(); + expect(event.stopPropagation).toHaveBeenCalledOnce(); + expect(toolbar.props.cellEditMode).toBe(true); + expect(toolbar.props.selectedCellsSize).toBe(2); + renderer!.unmount(); + }); + it('opens the v2 column header context menu from table headers', async () => { setCurrentLanguage('en-US'); storeState.appearance.uiVersion = 'v2'; diff --git a/frontend/src/components/DataGrid.tsx b/frontend/src/components/DataGrid.tsx index 098403a0..35003026 100644 --- a/frontend/src/components/DataGrid.tsx +++ b/frontend/src/components/DataGrid.tsx @@ -1599,6 +1599,27 @@ const DataGrid: React.FC = ({ resetCellSelection(); }, [resetCellSelection]); + const selectEditableColumnCells = useCallback((columnName: string) => { + if ( + !cellEditModeRef.current + || !canModifyData + || !isWritableResultColumn(columnName, effectiveEditLocator) + ) { + return; + } + + resetCellSelection(false); + const nextSelection = new Set(); + displayDataRef.current.forEach((row) => { + const rowKey = row?.[GONAVI_ROW_KEY]; + if (rowKey === undefined || rowKey === null) return; + nextSelection.add(makeCellKey(rowKeyStr(rowKey), columnName)); + }); + currentSelectionRef.current = nextSelection; + setSelectedCells(nextSelection); + updateCellSelection(nextSelection); + }, [canModifyData, effectiveEditLocator, makeCellKey, resetCellSelection, rowKeyStr, updateCellSelection]); + useEffect(() => { closeCellEditModeRef.current = closeCellEditMode; }, [closeCellEditMode]); @@ -2548,12 +2569,19 @@ const DataGrid: React.FC = ({ showColumnHeaderContextMenu(event, key); }, onClickCapture: (event: React.MouseEvent) => { - if (!onSort) return; const eventTarget = event.target as HTMLElement | null; if (eventTarget?.closest?.('[data-grid-fk-jump="true"]')) return; if (eventTarget?.closest?.('[data-grid-column-filter-trigger="true"]')) return; if (eventTarget?.closest?.('[data-grid-column-filter-popover="true"]')) return; if (eventTarget?.closest?.('.ant-select-dropdown')) return; + if (eventTarget?.closest?.('.react-resizable-handle')) return; + if (cellEditMode && canModifyData && isWritableResultColumn(key, effectiveEditLocator)) { + event.preventDefault(); + event.stopPropagation(); + selectEditableColumnCells(key); + return; + } + if (!onSort) return; const headerCell = event.currentTarget as HTMLElement; const upArrow = headerCell.querySelector('.ant-table-column-sorter-up') as HTMLElement | null; const downArrow = headerCell.querySelector('.ant-table-column-sorter-down') as HTMLElement | null; @@ -2574,7 +2602,7 @@ const DataGrid: React.FC = ({ }, }), })); - }, [canModifyData, columnWidths, currentConnConfig, dataTableDensity, displayColumnNames, displayColumnTypeMap, enableVirtual, handleResizeAutoFit, handleResizeStart, isV2Ui, language, normalizedPageFindText, onSort, pinnedLeftColumnSet, renderColumnTitle, showColumnComment, showColumnHeaderContextMenu, showColumnType, sortInfo]); + }, [canModifyData, cellEditMode, columnWidths, currentConnConfig, dataTableDensity, displayColumnNames, displayColumnTypeMap, effectiveEditLocator, enableVirtual, handleResizeAutoFit, handleResizeStart, isV2Ui, language, normalizedPageFindText, onSort, pinnedLeftColumnSet, renderColumnTitle, selectEditableColumnCells, showColumnComment, showColumnHeaderContextMenu, showColumnType, sortInfo]); const mergedColumns = useMemo(() => columns.map((col): ColumnType => { const dataIndex = String(col.dataIndex);