From 3a632a826de2cc2c22782492d693e673ccf2655d Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov <84278206+Tucchhaa@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:47:27 +0800 Subject: [PATCH] DataGrid - Focus gets removed from rowsview when datagrid repaints on focusChanged event - T1224663 (#28715) --- .../tests/dataGrid/focus/focus.ts | 27 +++++++++++++ .../grid_core/keyboard_navigation/const.ts | 1 + .../m_keyboard_navigation.ts | 40 +++++++++++++------ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts b/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts index c08f532dc9a8..b1c9e7a0697f 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts @@ -241,3 +241,30 @@ test('DataGrid - FilterRow cell loses focus when focusedRowEnabled is true and e }); }); }); + +['onFocusedRowChanged', 'onFocusedRowChanging'].forEach((event) => { + test(`Focus should be preserved on datagrid when rowsview repaints in ${event} event (T1224663)`, async (t) => { + const dataGrid = new DataGrid('#container'); + + await t + .click(dataGrid.getDataCell(0, 0).element) + .expect(dataGrid.getDataRow(0).isFocusedRow) + .ok(); + + await t + .pressKey('down') + .expect(dataGrid.getDataRow(1).isFocusedRow) + .ok(); + }).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'name 1' }, + { id: 2, name: 'name 2' }, + { id: 3, name: 'name 3' }, + ], + keyExpr: 'id', + focusedRowEnabled: true, + [event]: (e) => { + e.component.repaint(); + }, + })); +}); diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/const.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/const.ts index 168f941d76c7..8812dc7038f5 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/const.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/const.ts @@ -4,6 +4,7 @@ export const ATTRIBUTES = { }; export const ROWS_VIEW_CLASS = 'rowsview'; +export const TABLE_CLASS = 'table'; export const EDIT_FORM_CLASS = 'edit-form'; export const GROUP_FOOTER_CLASS = 'group-footer'; export const ROW_CLASS = 'dx-row'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts index 18c210ed55f3..31a898aaabe4 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts @@ -6,6 +6,7 @@ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import browser from '@js/core/utils/browser'; import { Deferred, when } from '@js/core/utils/deferred'; +import { isElementInDom } from '@js/core/utils/dom'; import { getHeight, getOuterHeight, @@ -71,6 +72,7 @@ import { REVERT_BUTTON_CLASS, ROWS_VIEW, ROWS_VIEW_CLASS, + TABLE_CLASS, WIDGET_CLASS, } from './const'; import { GridCoreKeyboardNavigationDom } from './dom'; @@ -307,18 +309,32 @@ export class KeyboardNavigationController extends modules.ViewController { this._documentClickHandler = this._documentClickHandler || this.createAction((e) => { const $target = $(e.event.target); - const isCurrentRowsViewClick = this._isEventInCurrentGrid(e.event) - && $target.closest(`.${this.addWidgetPrefix(ROWS_VIEW_CLASS)}`).length; - const isEditorOverlay = $target.closest( - `.${DROPDOWN_EDITOR_OVERLAY_CLASS}`, - ).length; - const isColumnResizing = !!this._columnResizerController && this._columnResizerController.isResizing(); - if (!isCurrentRowsViewClick && !isEditorOverlay && !isColumnResizing) { - const targetInsideFocusedView = this._focusedView - ? $target.parents().filter(this._focusedView.element()).length > 0 - : false; - - !targetInsideFocusedView && this._resetFocusedCell(true); + + const tableSelector = `.${this.addWidgetPrefix(TABLE_CLASS)}`; + const rowsViewSelector = `.${this.addWidgetPrefix(ROWS_VIEW_CLASS)}`; + const editorOverlaySelector = `.${DROPDOWN_EDITOR_OVERLAY_CLASS}`; + + // if click was on the datagrid table, but the target element is no more presented in the DOM + const needKeepFocus = !!$target.closest(tableSelector).length && !isElementInDom($target); + + if (needKeepFocus) { + e.event.preventDefault(); + return; + } + + const isRowsViewClick = this._isEventInCurrentGrid(e.event) && !!$target.closest(rowsViewSelector).length; + const isEditorOverlayClick = !!$target.closest(editorOverlaySelector).length; + const isColumnResizing = !!this._columnResizerController?.isResizing(); + + if (!isRowsViewClick && !isEditorOverlayClick && !isColumnResizing) { + const isClickOutsideFocusedView = this._focusedView + ? $target.closest(this._focusedView.element()).length === 0 + : true; + + if (isClickOutsideFocusedView) { + this._resetFocusedCell(true); + } + this._resetFocusedView(); } });