From 75574e1aba8f863632f36addc2e3bd1e979141ed Mon Sep 17 00:00:00 2001 From: Alyar Date: Fri, 19 Jul 2024 18:21:23 +0400 Subject: [PATCH] DataGrid: Fix the onFocusedRowChanged event that does not raise when push API is used to remove a focused row (T1233973) (#27807) --- .../tests/dataGrid/focus/focus.ts | 66 +++++++++++++++++++ .../data_controller/m_data_controller.ts | 2 +- .../grids/grid_core/focus/m_focus.ts | 15 ++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts b/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts index 57bd8a206b52..4ab8d20b809d 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/focus/focus.ts @@ -81,4 +81,70 @@ test('Should remove dx-focused class on blur event from the cell', async (t) => }, { dependencies: { reshapeOnPush }, }))); + + // T1233973 + test(`DataGrid should restore focused row by index after row removed via push API' (reshapeOnPush=${reshapeOnPush})`, async (t) => { + const dataGrid = new DataGrid(GRID_SELECTOR); + + await t + .expect(dataGrid.getDataRow(2).isFocusedRow) + .ok() + .expect(dataGrid.getDataRow(2).element.textContent) + .eql('Item 3') + .expect(dataGrid.option('focusedRowKey')) + .eql(2) + .expect(ClientFunction(() => (window as any).onFocusedRowChangedCounter)()) + .eql(1); + + await dataGrid.apiPush([{ + type: 'remove', + key: 2, + }]); + + await t + .expect(dataGrid.getDataRow(2).isFocusedRow) + .ok() + .expect(dataGrid.getDataRow(2).element.textContent) + .eql('Item 4') + .expect(dataGrid.option('focusedRowKey')) + .eql(3) + .expect(ClientFunction(() => (window as any).onFocusedRowChangedCounter)()) + .eql(2); + }).before(async () => createWidget('dxDataGrid', ClientFunction(() => { + const { DevExpress } = (window as any); + const store = new DevExpress.data.ArrayStore({ + data: [ + { id: 0, name: 'Item 1 ' }, + { id: 1, name: 'Item 2' }, + { id: 2, name: 'Item 3' }, + { id: 3, name: 'Item 4' }, + { id: 4, name: 'Item 5' }, + ], + key: 'id', + }); + const dataSource = new DevExpress.data.DataSource({ store, reshapeOnPush }); + + return { + columns: ['name'], + dataSource, + keyExpr: 'id', + focusedRowEnabled: true, + focusedRowKey: 2, + onFocusedRowChanged: () => { + const global = window as Window & typeof globalThis + & { onFocusedRowChangedCounter: number }; + + if (!global.onFocusedRowChangedCounter) { + global.onFocusedRowChangedCounter = 0; + } + global.onFocusedRowChangedCounter += 1; + }, + }; + }, { + dependencies: { reshapeOnPush }, + }))).after(async () => { + await ClientFunction(() => { + delete (window as any).onFocusedRowChangedCounter; + })(); + }); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts index 3c6754123210..684022d0eccf 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts @@ -597,7 +597,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { this.dataErrorOccurred.fire(e); } - private _handleDataPushed(changes) { + protected _handleDataPushed(changes) { this.pushed.fire(changes); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts index 40705cbdeef3..bf4a374691c4 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/focus/m_focus.ts @@ -534,6 +534,8 @@ const columns = (Base: ModuleType) => class FocusColumnsExten }; const data = (Base: ModuleType) => class FocusDataControllerExtender extends Base { + private _needToUpdateFocusedRowByIndex = false; + protected _applyChange(change) { if (change && change.changeType === 'updateFocusedRow') return; @@ -548,7 +550,10 @@ const data = (Base: ModuleType) => class FocusDataControllerExte const isPartialUpdate = e.changeType === 'update' && e.repaintChangesOnly; const isPartialUpdateWithDeleting = isPartialUpdate && e.changeTypes && e.changeTypes.indexOf('remove') >= 0; - if (e.changeType === 'refresh' && e.items.length || isPartialUpdateWithDeleting) { + if (this._needToUpdateFocusedRowByIndex) { + this._needToUpdateFocusedRowByIndex = false; + this._focusController._focusRowByIndex(); + } else if (e.changeType === 'refresh' && e.items.length || isPartialUpdateWithDeleting) { this._updatePageIndexes(); this._updateFocusedRow(e); } else if (e.changeType === 'append' || e.changeType === 'prepend') { @@ -559,6 +564,14 @@ const data = (Base: ModuleType) => class FocusDataControllerExte } } + protected _handleDataPushed(changes) { + super._handleDataPushed(changes); + + const focusedRowKey = this.option('focusedRowKey'); + + this._needToUpdateFocusedRowByIndex = changes?.some((change) => change.type === 'remove' && equalByValue(change.key, focusedRowKey)); + } + private _updatePageIndexes() { const prevRenderingPageIndex = this._lastRenderingPageIndex || 0; const renderingPageIndex = this._rowsScrollController ? this._rowsScrollController.pageIndex() : 0;