Skip to content

Commit

Permalink
DataGrid(T1158801)
Browse files Browse the repository at this point in the history
- Fix react async templates editing cell template issue.
- Add e2e test cases.
  • Loading branch information
Ilya Vinogradov committed Apr 13, 2023
1 parent ab3a838 commit 7172f09
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 45 deletions.
112 changes: 67 additions & 45 deletions js/__internal/grids/grid_core/keyboard_navigation/module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// @ts-check

import { Deferred, DeferredObj, when } from '@js/core/utils/deferred';
import {
getOuterHeight, getHeight, getWidth, getOuterWidth,
} from '@js/core/utils/size';
import $ from '@js/core/../core/renderer';
import domAdapter from '@js/core/../core/dom_adapter';
import eventsEngine from '@js/core/../events/core/events_engine';
import { isDefined, isEmptyObject } from '@js/core/../core/utils/type';
import { isDeferred, isDefined, isEmptyObject } from '@js/core/../core/utils/type';
import { focused } from '@js/ui/widget/selectors';
import { addNamespace, createEvent, isCommandKeyPressed } from '@js/core/../events/utils/index';
import pointerEvents from '@js/events/pointer';
Expand Down Expand Up @@ -116,6 +117,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
this._selectionController = this.getController('selection');
this._editingController = this.getController('editing');
this._headerPanel = this.getView('headerPanel');
this._rowsView = this.getView('rowsView');
this._columnsController = this.getController('columns');
this._editorFactory = this.getController('editorFactory');

Expand Down Expand Up @@ -146,16 +148,22 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
}
},
_initViewHandlers() {
const rowsView = this.getView('rowsView');
const rowsViewFocusHandler = (event) => {
const $element = $(event.target);
const isRelatedTargetInRowsView = $(event.relatedTarget).closest(rowsView.element()).length;
const isRelatedTargetInRowsView = $(event.relatedTarget)
.closest(this._rowsView.element())
.length;
const isLink = $element.is('a');

if (event.relatedTarget && isLink && !isRelatedTargetInRowsView && this._isEventInCurrentGrid(event)) {
if (event.relatedTarget
&& isLink
&& !isRelatedTargetInRowsView
&& this._isEventInCurrentGrid(event)) {
let $focusedCell = this._getFocusedCell();

$focusedCell = !isElementDefined($focusedCell) ? rowsView.getCellElements(0).filter('[tabindex]').eq(0) : $focusedCell;
$focusedCell = !isElementDefined($focusedCell)
? this._rowsView.getCellElements(0).filter('[tabindex]').eq(0)
: $focusedCell;

if (!$element.closest($focusedCell).length) {
event.preventDefault();
Expand All @@ -165,10 +173,10 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
}
};

rowsView.renderCompleted.add((e) => {
const $rowsView = rowsView.element();
this._rowsView.renderCompleted.add((e) => {
const $rowsView = this._rowsView.element();
const isFullUpdate = !e || e.changeType === 'refresh';
const isFocusedViewCorrect = this._focusedView && this._focusedView.name === rowsView.name;
const isFocusedViewCorrect = this._focusedView && this._focusedView.name === this._rowsView.name;
let needUpdateFocus = false;
const isAppend = e && (e.changeType === 'append' || e.changeType === 'prepend');
// @ts-expect-error
Expand Down Expand Up @@ -454,7 +462,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
const pagingEnabled = this.option('paging.enabled');
const isPageUp = eventArgs.keyName === 'pageUp';
const pageStep = isPageUp ? -1 : 1;
const scrollable = this.getView('rowsView').getScrollable();
const scrollable = this._rowsView.getScrollable();

if (pagingEnabled && !this._isVirtualScrolling()) {
if ((isPageUp ? pageIndex > 0 : pageIndex < pageCount - 1) && !this._isVirtualScrolling()) {
Expand Down Expand Up @@ -545,9 +553,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
},
_getMaxHorizontalOffset() {
const scrollable = this.component.getScrollable();
const rowsView = this.getView('rowsView');
const offset = scrollable ? scrollable.scrollWidth() - getWidth(rowsView.element()) : 0;
return offset;
return scrollable ? scrollable.scrollWidth() - getWidth(this._rowsView.element()) : 0;
},
_isColumnRendered(columnIndex) {
const allVisibleColumns = this._columnsController.getVisibleColumns(null, true);
Expand Down Expand Up @@ -645,7 +651,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
}

const columnsController = this._columnsController;
const cellIndex = this.getView('rowsView').getCellIndex($cell);
const cellIndex = this._rowsView.getCellIndex($cell);
const columnIndex = cellIndex + columnsController.getColumnIndexOffset();
const column = columnsController.getVisibleColumns(null, true)[columnIndex];
const $row = $cell.parent();
Expand Down Expand Up @@ -910,8 +916,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
_pointerEventHandler(e) {
const event = e.event || e;
let $target = $(event.currentTarget);
const rowsView = this.getView('rowsView');
const focusedViewElement = rowsView && rowsView.element();
const focusedViewElement = this._rowsView && this._rowsView.element();
const $parent = $target.parent();
const isInteractiveElement = $(event.target).is(INTERACTIVE_ELEMENTS_SELECTOR);
const isRevertButton = !!$(event.target).closest(`.${REVERT_BUTTON_CLASS}`).length;
Expand Down Expand Up @@ -942,7 +947,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
},

_clickTargetCellHandler(event, $cell) {
const columnIndex = this.getView('rowsView').getCellIndex($cell);
const columnIndex = this._rowsView.getCellIndex($cell);
const column = this._columnsController.getVisibleColumns()[columnIndex];
const isCellEditMode = this._isCellEditMode();

Expand Down Expand Up @@ -1058,7 +1063,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
},

_focusView() {
this._focusedView = this.getView('rowsView');
this._focusedView = this._rowsView;
},

_resetFocusedView() {
Expand Down Expand Up @@ -1218,12 +1223,11 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
_getCellPosition($cell, direction) {
let columnIndex;
const $row = isElementDefined($cell) && $cell.closest('tr');
const rowsView = this.getView('rowsView');

if (isElementDefined($row)) {
const rowIndex = this._getRowIndex($row);

columnIndex = rowsView.getCellIndex($cell, rowIndex);
columnIndex = this._rowsView.getCellIndex($cell, rowIndex);
columnIndex += this._getFocusedColumnIndexOffset(columnIndex);

if (direction) {
Expand Down Expand Up @@ -1264,20 +1268,21 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
let args;
let $rowElement;
const isUpArrow = isDefined(rowIndex);
const rowsView = that.getView('rowsView');
const $rowsViewElement = rowsView.element();
const $rowsViewElement = this._rowsView.element();
const { columnIndex } = that._focusedCellPosition;
const rowIndexOffset = that._dataController.getRowIndexOffset();

rowIndex = isUpArrow ? rowIndex : rowsView.getTopVisibleItemIndex() + rowIndexOffset;
rowIndex = isUpArrow
? rowIndex
: this._rowsView.getTopVisibleItemIndex() + rowIndexOffset;

if (!isUpArrow) {
that._editorFactory.loseFocus();
that._applyTabIndexToElement($rowsViewElement);
// @ts-expect-error
eventsEngine.trigger($rowsViewElement, 'focus');
} else {
$rowElement = rowsView.getRow(rowIndex - rowIndexOffset);
$rowElement = this._rowsView.getRow(rowIndex - rowIndexOffset);
args = that._fireFocusedRowChanging($event, $rowElement);

if (!args.cancel && args.rowIndexChanged) {
Expand Down Expand Up @@ -1460,10 +1465,9 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
// #region DOM_Manipulation
_isCellValid($cell, isClick) {
if (isElementDefined($cell)) {
const rowsView = this.getView('rowsView');
const $row = $cell.parent();
const columnsController = this._columnsController;
const columnIndex = rowsView.getCellIndex($cell) + columnsController.getColumnIndexOffset();
const columnIndex = this._rowsView.getCellIndex($cell) + columnsController.getColumnIndexOffset();
const column = columnsController.getVisibleColumns(null, true)[columnIndex];
const visibleColumnCount = this._getVisibleColumnCount();
const editingController = this._editingController;
Expand All @@ -1486,7 +1490,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga

if (visibleColumnCount > columnIndex && isValidGroupSpaceColumn()) {
const rowItems = this._dataController.items();
const visibleRowIndex = rowsView.getRowIndex($row);
const visibleRowIndex = this._rowsView.getRowIndex($row);
const row = rowItems[visibleRowIndex];
const isCellEditing = editingController && this._isCellEditMode() && editingController.isEditing();
const isRowEditingInCurrentRow = editingController && editingController.isEditRow(visibleRowIndex);
Expand Down Expand Up @@ -1585,7 +1589,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
_isAllowEditing(row, column) {
return this._editingController.allowUpdating({ row }) && column && column.allowEditing;
},
_editFocusedCell() {
_editFocusedCell(): DeferredObj<unknown> | boolean {
const rowIndex = this.getVisibleRowIndex();
const colIndex = this.getVisibleColumnIndex();

Expand All @@ -1594,27 +1598,47 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
_startEditCell(eventArgs, fastEditingKey) {
this._fastEditingStarted = isDefined(fastEditingKey);
const editResult = this._editFocusedCell();
const isEditResultDeferred = isDeferred(editResult);
const isFastEditingStarted = this._isFastEditingStarted();

if (this._isFastEditingStarted()) {
if (editResult === true) {
this._editingCellHandler(eventArgs, fastEditingKey);
} else if (editResult && editResult.done) {
const editorValue = fastEditingKey !== FAST_EDITING_DELETE_KEY ? fastEditingKey : '';
editResult.done(() => this._editingCellHandler(eventArgs, editorValue));
}
if (!isFastEditingStarted) {
return;
}

if (!isEditResultDeferred && !editResult) {
return;
}

const editorValue = isEditResultDeferred && fastEditingKey === FAST_EDITING_DELETE_KEY
? ''
: fastEditingKey;
const editResultDeferred = isEditResultDeferred ? editResult : Deferred().resolve();
const waitTemplatesDeferred = this._rowsView.waitAsyncTemplates(true);

// NOTE T1158801: wait async templates before handle cell editing.
when(editResultDeferred, waitTemplatesDeferred)
.done(() => { this._editingCellHandler(eventArgs, editorValue); });
},
_editingCellHandler(eventArgs, editorValue) {
const $input = this._getFocusedCell().find(INTERACTIVE_ELEMENTS_SELECTOR).eq(0);
const keyDownEvent = createEvent(eventArgs, { type: 'keydown', target: $input.get(0) });
const keyPressEvent = createEvent(eventArgs, { type: 'keypress', target: $input.get(0) });
const inputEvent = createEvent(eventArgs, { type: 'input', target: $input.get(0) });
const $inputElement = $input.get(0);

if (!$inputElement) {
return;
}

const keyDownEvent = createEvent(eventArgs, { type: 'keydown', target: $inputElement });
const keyPressEvent = createEvent(eventArgs, { type: 'keypress', target: $inputElement });
const inputEvent = createEvent(eventArgs, { type: 'input', target: $inputElement });

if (inputEvent.originalEvent) {
inputEvent.originalEvent = createEvent(inputEvent.originalEvent, { data: editorValue }); // T1116105
inputEvent.originalEvent = createEvent(
inputEvent.originalEvent,
{ data: editorValue },
); // T1116105
}

$input.get(0).select?.();
$inputElement.select?.();
// @ts-expect-error
eventsEngine.trigger($input, keyDownEvent);

Expand Down Expand Up @@ -1707,7 +1731,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
_fireFocusedCellChanged($cellElement, prevCellIndex, prevRowIndex) {
const that = this;
const dataController = that._dataController;
const columnIndex = that.getView('rowsView').getCellIndex($cellElement);
const columnIndex = that._rowsView.getCellIndex($cellElement);
const rowIndex = this._getRowIndex($cellElement && $cellElement.parent());
const localRowIndex = Math.min(rowIndex - dataController.getRowIndexOffset(), dataController.items().length - 1);
const isEditingCell = that._editingController.isEditCell(localRowIndex, columnIndex);
Expand Down Expand Up @@ -1814,8 +1838,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
}
},
_getRowIndex($row) {
const rowsView = this.getView('rowsView');
let rowIndex = rowsView.getRowIndex($row);
let rowIndex = this._rowsView.getRowIndex($row);

if (rowIndex >= 0) {
rowIndex += this._dataController.getRowIndexOffset();
Expand Down Expand Up @@ -1866,7 +1889,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
},
_scrollBy(left, top, rowIndex, $event) {
const that = this;
const scrollable = this.getView('rowsView').getScrollable();
const scrollable = this._rowsView.getScrollable();

if (that._focusedCellPosition) {
const scrollHandler = function () {
Expand Down Expand Up @@ -1933,8 +1956,7 @@ const keyboardNavigationMembers: Partial<import('./module_types').KeyboardNaviga
return $cell;
},
_getRowsViewElement() {
const rowsView = this.getView('rowsView');
return rowsView && rowsView.element();
return this._rowsView && this._rowsView.element();
},
isKeyboardEnabled() {
return this.option('keyboardNavigation.enabled');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface State {
_selectionController: Controllers['selection'];
_editingController: Controllers['editing'];
_headerPanel: Views['headerPanel'];
_rowsView: Views['rowsView'];
_columnsController: Controllers['columns'];
_editorFactory: Controllers['editorFactory'];
_adaptiveController: Controllers['adaptiveColumns'];
Expand Down
15 changes: 15 additions & 0 deletions testing/testcafe/tests/dataGrid/helpers/asyncTemplates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ClientFunction } from 'testcafe';

export const makeRowsViewTemplatesAsync = async (selector: string): Promise<void> => {
await ClientFunction(() => {
const dataGrid = ($(selector) as any).dxDataGrid('instance');

const rowsView = dataGrid.getView('rowsView');
const originRender = rowsView.renderDelayedTemplates.bind(rowsView);
rowsView.renderDelayedTemplates = (changes) => {
setTimeout(() => { originRender(changes); });
};
}, {
dependencies: { selector },
})();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { createScreenshotsComparer } from 'devextreme-screenshot-comparer';
import url from '../../../helpers/getPageUrl';
import createWidget from '../../../helpers/createWidget';
import DataGrid from '../../../model/dataGrid';
import { makeRowsViewTemplatesAsync } from '../helpers/asyncTemplates';

fixture
.disablePageReloads`Keyboard Navigation - editOnKeyPress`
.page(url(__dirname, '../../container.html'));

const DATA_GRID_SELECTOR = '#container';

[
{ name: 'input', template: () => $('<input>') },
{ name: 'div', template: () => $('<div>').text('Hi, I\'m the template!') },
].forEach(({ name, template }) => {
test(`should render edit cell template without errors, template: ${name}`, async (t) => {
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);

const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
const dataCell = dataGrid.getDataCell(0, 0);

await t.click(dataCell.element)
.pressKey('f');

await takeScreenshot(
`edit-cell-keypress-with-custom-cell-template_template-${name}.png`,
dataGrid.element,
);

await t.expect(compareResults.isValid())
.ok(compareResults.errorMessages());
}).before(async () => {
await createWidget('dxDataGrid', {
dataSource: [
{
data_A: 'data_A',
data_B: 'data_B',
},
],
columns: [
{
dataField: 'data_A',
editCellTemplate: template,
},
'data_B',
],
keyboardNavigation: {
enabled: true,
editOnKeyPress: true,
enterKeyDirection: 'column',
},
editing: {
mode: 'cell',
allowUpdating: true,
allowAdding: true,
startEditAction: 'dblClick',
},
templatesRenderAsynchronously: true,
});
await makeRowsViewTemplatesAsync(DATA_GRID_SELECTOR);
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7172f09

Please sign in to comment.