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 8f5aa9983318..13cb01b97c25 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 @@ -312,7 +312,7 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo if (needUpdateFocus) { const isScrollEvent = !!e?.event?.type; const skipFocusEvent = e?.virtualColumnsScrolling && isScrollEvent; - this._updateFocus(true, skipFocusEvent); + this._updateFocus(true, skipFocusEvent, true); } } } @@ -1732,7 +1732,7 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo gridCoreUtils.focusAndSelectElement(this, $focusedElement); } - public _focus($cell, disableFocus?, skipFocusEvent?) { + public _focus($cell: dxElementWrapper, disableFocus?: boolean, skipFocusEvent?: boolean, preventScroll?: boolean) { const $row = $cell && !$cell.hasClass(ROW_CLASS) ? $cell.closest(`.${ROW_CLASS}`) : $cell; @@ -1784,8 +1784,12 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo }); if (!skipFocusEvent) { this._applyTabIndexToElement($focusElement); - // @ts-expect-error - eventsEngine.trigger($focusElement, 'focus'); + if (preventScroll) { + $focusElement.get(0)?.focus({ preventScroll }); + } else { + // @ts-expect-error + eventsEngine.trigger($focusElement, 'focus'); + } } if (disableFocus) { $focusElement.addClass(CELL_FOCUS_DISABLED_CLASS); @@ -1798,7 +1802,7 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo } } - public _updateFocus(isRenderView?, skipFocusEvent = false) { + public _updateFocus(isRenderView?: boolean, skipFocusEvent = false, preventScroll = false) { this._updateFocusTimeout = setTimeout(() => { if (this._needFocusEditingCell()) { this._editingController._focusEditingCell(); @@ -1837,12 +1841,12 @@ export class KeyboardNavigationController extends KeyboardNavigationControllerCo ); return; } - !isFocusedElementDefined && this._focus($cell, false, skipFocusEvent); + !isFocusedElementDefined && this._focus($cell, false, skipFocusEvent, preventScroll); } else if ( !isFocusedElementDefined && (this._isNeedFocus || this._isHiddenFocus) ) { - this._focus($cell, this._isHiddenFocus, skipFocusEvent); + this._focus($cell, this._isHiddenFocus, skipFocusEvent, preventScroll); } if (isEditing && !column?.showEditorAlways) { this._focusInteractiveElement.bind(this)($cell); diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/scrollable_a11y.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/scrollable_a11y.ts index 0a7087cb9ede..b2f327fc28d8 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/scrollable_a11y.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/scrollable_a11y.ts @@ -61,8 +61,13 @@ export const keyboardNavigationScrollableA11yExtender = (Base: ModuleType { // assert assert.equal(dataGrid.getScrollable().scrollTop(), 10, 'scroll top is not changed'); }); + + // T1310557 + QUnit.testInActiveWindow('Browser should not scroll back to the grid when a focused cell is updated or rerendered', function(assert) { + // arrange + const dataGrid = createDataGrid({ + dataSource: { + store: new ArrayStore({ + key: 'id', + data: [{ id: 1, name: 'test1' }], + }), + pushAggregationTimeout: 0, + }, + }); + $('body').css('height', 2000); + window.scrollTo(0, 0); + this.clock.tick(10); + + const keyboardController = dataGrid.getController('keyboardNavigation'); + const $firstCell = $(dataGrid.getCellElement(0, 0)); + const scrollPosition = 300; + + // assert + assert.strictEqual(window.pageYOffset, 0, 'document scroll is at the top'); + + // act + $firstCell.trigger(CLICK_EVENT); + this.clock.tick(10); + + keyboardController.keyDownHandler({ + key: 'Tab', + keyName: 'tab', + originalEvent: $.Event('keydown', { target: $firstCell.get(0) }), + }); + this.clock.tick(10); + + // assert + assert.ok($(dataGrid.getCellElement(0, 1)).hasClass('dx-focused'), 'second cell is focused'); + + // act + window.scrollTo(0, scrollPosition); + + // assert + assert.strictEqual(window.pageYOffset, scrollPosition, 'document scroll is changed'); + + // act + dataGrid.getDataSource().store().push([{ type: 'update', key: 1, data: { name: 'updated' } }]); + this.clock.tick(10); + + // assert + assert.strictEqual($(dataGrid.getCellElement(0, 1)).text(), 'updated', 'second cell text is updated'); + assert.strictEqual(window.pageYOffset, scrollPosition, 'document scroll is preserved after push update'); + + // act + dataGrid.refresh(); + this.clock.tick(10); + + // assert + assert.strictEqual(window.pageYOffset, scrollPosition, 'document scroll is preserved after refresh'); + + // act + dataGrid.repaint(); + this.clock.tick(10); + + // assert + assert.strictEqual(window.pageYOffset, scrollPosition, 'document scroll is preserved after repaint'); + + // act + keyboardController.keyDownHandler({ + key: 'ArrowLeft', + keyName: 'leftArrow', + originalEvent: $.Event('keydown', { target: $(dataGrid.getCellElement(0, 1)).get(0) }), + }); + this.clock.tick(10); + + // assert + assert.ok($(dataGrid.getCellElement(0, 0)).hasClass('dx-focused'), 'first cell is focused'); + assert.strictEqual(window.pageYOffset, 0, 'document scroll is changed after keyboard navigation'); + + // cleanup + $('body').css('height', ''); + window.scrollTo(0, 0); + }); });