diff --git a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow-integration-tests/src/test/java/com/vaadin/flow/component/gridpro/tests/BasicIT.java b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow-integration-tests/src/test/java/com/vaadin/flow/component/gridpro/tests/BasicIT.java index cb5cefe092f..f001d3a683d 100644 --- a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow-integration-tests/src/test/java/com/vaadin/flow/component/gridpro/tests/BasicIT.java +++ b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow-integration-tests/src/test/java/com/vaadin/flow/component/gridpro/tests/BasicIT.java @@ -135,6 +135,61 @@ public void customComboBox_circularReferencesInData_isEdited() { getPanelText("prop-panel")); } + private String LOADING_EDITOR_ATTRIBUTE = "loading-editor"; + + @Test + public void customComboBox_loadingEditorStateOnEdit() { + var cell = grid.getCell(0, 4); + + var hasLoadingStateOnEditStart = (Boolean) executeScript( + """ + const [cell, grid, attribute] = arguments; + + // Enter edit mode with double click + cell.dispatchEvent(new CustomEvent('dblclick', {composed: true, bubbles: true})); + + return grid.hasAttribute(attribute); + """, + cell, grid, LOADING_EDITOR_ATTRIBUTE); + // Expect the editor to be hidden when the edit starts + Assert.assertTrue(hasLoadingStateOnEditStart); + + // After the round trip to the server... + var editor = cell.$("vaadin-combo-box").first(); + // The editor should be visible + Assert.assertFalse(grid.hasAttribute(LOADING_EDITOR_ATTRIBUTE)); + // The editor should have focus + Assert.assertTrue("Editor should have focus", + (Boolean) executeScript( + "return arguments[0].contains(document.activeElement)", + editor)); + } + + @Test + public void customComboBox_loadingEditorStateClearedOnEditStop() { + var cell = grid.getCell(0, 4); + var nonCustomEditorCell = grid.getCell(0, 1); + + var hasLoadingStateAttribute = (Boolean) executeScript( + """ + const [cell, nonCustomEditorCell, grid, attribute] = arguments; + + // Enter edit mode with double click + cell.dispatchEvent(new CustomEvent('dblclick', {composed: true, bubbles: true})); + await new Promise(resolve => requestAnimationFrame(resolve)); + + // Focus another cell + nonCustomEditorCell.focus(); + await new Promise(resolve => requestAnimationFrame(resolve)); + + return grid.hasAttribute(attribute); + + """, + cell, nonCustomEditorCell, grid, LOADING_EDITOR_ATTRIBUTE); + + Assert.assertFalse(hasLoadingStateAttribute); + } + @Test public void textEditorIsUsedForTextColumn() { assertCellEnterEditModeOnDoubleClick(0, 1, diff --git a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/java/com/vaadin/flow/component/gridpro/GridPro.java b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/java/com/vaadin/flow/component/gridpro/GridPro.java index 40735f5a321..6b1da23de21 100644 --- a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/java/com/vaadin/flow/component/gridpro/GridPro.java +++ b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/java/com/vaadin/flow/component/gridpro/GridPro.java @@ -148,9 +148,12 @@ private void setup() { if (column.getEditorType().equals("custom")) { column.getEditorField() .setValue(column.getValueProvider().apply(e.getItem())); + var itemKey = getDataCommunicator().getKeyMapper() + .key(e.getItem()); UI.getCurrent().getPage().executeJs( - "window.Vaadin.Flow.gridProConnector.selectAll($0)", - column.getEditorField().getElement()); + "window.Vaadin.Flow.gridProConnector.selectAll($0, $1, $2)", + column.getEditorField().getElement(), itemKey, + this.getElement()); } }); } diff --git a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/resources/META-INF/resources/frontend/gridProConnector.js b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/resources/META-INF/resources/frontend/gridProConnector.js index 604b9be2e31..6d8f3864a34 100644 --- a/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/resources/META-INF/resources/frontend/gridProConnector.js +++ b/vaadin-grid-pro-flow-parent/vaadin-grid-pro-flow/src/main/resources/META-INF/resources/frontend/gridProConnector.js @@ -11,8 +11,19 @@ function isEditedRow(grid, rowData) { return grid.__edited && grid.__edited.model.item.key === rowData.item.key; } +const LOADING_EDITOR_CELL_ATTRIBUTE = 'loading-editor'; + window.Vaadin.Flow.gridProConnector = { - selectAll: (editor) => { + selectAll: (editor, itemKey, grid) => { + if (editor.__itemKey !== itemKey) { + // This is an outdated call that can occur if the user starts editing a cell, + // and quickly starts editing another cell on the same column before the editor + // is unhidden for the first cell. + return; + } + + grid.toggleAttribute(LOADING_EDITOR_CELL_ATTRIBUTE, false); + if (editor instanceof HTMLInputElement) { editor.select(); } else if (editor.focusElement && editor.focusElement instanceof HTMLInputElement) { @@ -34,11 +45,20 @@ window.Vaadin.Flow.gridProConnector = { root.appendChild(component); this._grid._cancelStopEdit(); component.focus(); + + component.__itemKey = rowData.item.key; + this._grid.toggleAttribute(LOADING_EDITOR_CELL_ATTRIBUTE, true); }; // Not needed in case of custom editor as value is set on server-side. // Overridden in order to avoid blinking of the cell content. column._setEditorValue = function (editor, value) {}; + + const stopCellEdit = column._stopCellEdit; + column._stopCellEdit = function () { + stopCellEdit.apply(this, arguments); + this._grid.toggleAttribute(LOADING_EDITOR_CELL_ATTRIBUTE, false); + }; }, patchEditModeRenderer(column) {