From 0f98f26604d73b82a06a020acd75da53fb798e17 Mon Sep 17 00:00:00 2001 From: Anton Sermyazhko Date: Fri, 5 Oct 2018 14:29:30 +0200 Subject: [PATCH] DataGrid - Add focused row events (#5637) --- js/ui/grid_core/ui.grid_core.focus.js | 69 +- .../ui.grid_core.keyboard_navigation.js | 429 ++++++-- .../focus.tests.js | 938 +++++++++++++++++- 3 files changed, 1352 insertions(+), 84 deletions(-) diff --git a/js/ui/grid_core/ui.grid_core.focus.js b/js/ui/grid_core/ui.grid_core.focus.js index 218a7d430b22..6c0d479dc7b9 100644 --- a/js/ui/grid_core/ui.grid_core.focus.js +++ b/js/ui/grid_core/ui.grid_core.focus.js @@ -21,10 +21,10 @@ exports.FocusController = core.ViewController.inherit((function() { var that = this; if(args.name === "focusedRowIndex") { - that.focusRowByIndex(args.value); + that._focusRowByIndex(args.value); args.handled = true; } else if(args.name === "focusedRowKey") { - that.focusRowByKey(args.value); + that.navigateToRow(args.value); args.handled = true; } else if(args.name === "focusedColumnIndex") { args.handled = true; @@ -35,7 +35,7 @@ exports.FocusController = core.ViewController.inherit((function() { } }, - focusRowByIndex: function(index) { + _focusRowByIndex: function(index) { index = index !== undefined ? index : this.option("focusedRowIndex"); var dataController = this.getController("data"), @@ -47,7 +47,16 @@ exports.FocusController = core.ViewController.inherit((function() { } }, - focusRowByKey: function(key) { + publicMethods: function() { + return ["navigateToRow"]; + }, + + /** + * @name dxDataGridMethods.navigateToRow + * @publicName navigateToRow(key) + * @param1 key:any + */ + navigateToRow: function(key) { var that = this, dataController = this.getController("data"), rowsView = this.getView("rowsView"), @@ -55,9 +64,6 @@ exports.FocusController = core.ViewController.inherit((function() { key = key !== undefined ? key : this.option("focusedRowKey"); - if(!this.option("focusedRowEnabled")) { - return; - } var rowIndexByKey = dataController.getRowIndexByKey(key) + dataController.getRowIndexOffset(); if(rowIndex >= 0 && rowIndex === rowIndexByKey) { @@ -66,7 +72,9 @@ exports.FocusController = core.ViewController.inherit((function() { dataController.getPageIndexByKey(key).done(function(pageIndex) { that._needRestoreFocus = $(rowsView._getRowElement(that.option("focusedRowIndex"))).is(":focus"); if(pageIndex === dataController.pageIndex()) { - dataController.reload(); + dataController.reload().done(function() { + that._triggerUpdateFocusedRow(key); + }); } else { dataController.pageIndex(pageIndex).done(function() { that._triggerUpdateFocusedRow(key); @@ -81,6 +89,7 @@ exports.FocusController = core.ViewController.inherit((function() { rowIndex = dataController.getRowIndexByKey(key) + dataController.getRowIndexOffset(); this.getController("keyboardNavigation").setFocusedRowIndex(rowIndex); + dataController.updateItems({ changeType: "updateFocusedRow", focusedRowKey: key @@ -101,7 +110,7 @@ exports.FocusController = core.ViewController.inherit((function() { } keyboardController.setFocusedRowIndex(focusedRowIndex); } else { - this.focusRowByKey(focusedRowKey); + this.navigateToRow(focusedRowKey); } } else { focusedRowKey = dataController.getKeyByRowIndex(focusedRowIndex); @@ -128,22 +137,24 @@ exports.FocusController = core.ViewController.inherit((function() { var that = this, focusedRowIndex = that._dataController.getRowIndexByKey(change.focusedRowKey), rowsView = that.getView("rowsView"), + $focusedRow, $tableElement; each(rowsView.getTableElements(), function(_, element) { $tableElement = $(element); - that.clearPreviousFocusedRow($tableElement); + that._clearPreviousFocusedRow($tableElement); if(focusedRowIndex >= 0) { - that.prepareFocusedRow(change.items[focusedRowIndex], $tableElement, focusedRowIndex); + $focusedRow = that._prepareFocusedRow(change.items[focusedRowIndex], $tableElement, focusedRowIndex); + that.getController("keyboardNavigation")._fireFocusedRowChanged($focusedRow); } }); }, - clearPreviousFocusedRow: function($tableElement) { + _clearPreviousFocusedRow: function($tableElement) { var $prevRowFocusedElement = $tableElement.find(".dx-row" + "." + ROW_FOCUSED_CLASS); $prevRowFocusedElement.removeClass(ROW_FOCUSED_CLASS).removeAttr("tabindex"); $prevRowFocusedElement.children("td").removeAttr("tabindex"); }, - prepareFocusedRow: function(changedItem, $tableElement, focusedRowIndex) { + _prepareFocusedRow: function(changedItem, $tableElement, focusedRowIndex) { var that = this, $row, keyboardController, @@ -161,6 +172,8 @@ exports.FocusController = core.ViewController.inherit((function() { } } } + + return $row; } }; })()); @@ -168,6 +181,7 @@ exports.FocusController = core.ViewController.inherit((function() { module.exports = { defaultOptions: function() { return { + /** * @name GridBaseOptions.focusedRowEnabled * @type boolean @@ -195,6 +209,12 @@ module.exports = { * @default -1 */ focusedColumnIndex: -1 + + /** + * @name GridBaseMethods.navigateToRow + * @publicName navigateToRow(key) + * @param1 key:any + */ }; }, @@ -209,6 +229,14 @@ module.exports = { var rowIndex = this.option("focusedRowIndex"), columnIndex = this.option("focusedColumnIndex"); + if(this.option("focusedRowEnabled")) { + this.createAction("onFocusedRowChanging", { excludeValidators: ["disabled", "readOnly"] }); + this.createAction("onFocusedRowChanged", { excludeValidators: ["disabled", "readOnly"] }); + } + + this.createAction("onFocusedCellChanging", { excludeValidators: ["disabled", "readOnly"] }); + this.createAction("onFocusedCellChanged", { excludeValidators: ["disabled", "readOnly"] }); + this.callBase(); this.setRowFocusType(); @@ -248,7 +276,16 @@ module.exports = { this.setRowFocusType(); this._focus(this._getCellElementFromTarget(eventArgs.originalEvent.target), true); } - } + }, + + _updateFocusedCellPosition: function($cell, direction) { + var prevRowIndex = this.option("focusedRowIndex"), + prevColumnIndex = this.option("focusedColumnIndex"); + + this.callBase($cell, direction); + + this._fireFocusedCellChanged($cell, prevColumnIndex, prevRowIndex); + }, }, selection: { @@ -293,13 +330,13 @@ module.exports = { this._prevPageIndex = this.pageIndex(); if(operationTypes.reload) { - focusController.focusRowByKey(); + focusController.navigateToRow(); return; } if(paging) { if(!this.getController("keyboardNavigation")._isVirtualScrolling()) { - focusController.focusRowByIndex(); + focusController._focusRowByIndex(); } } else { focusController._handleDataChanged(e); diff --git a/js/ui/grid_core/ui.grid_core.keyboard_navigation.js b/js/ui/grid_core/ui.grid_core.keyboard_navigation.js index 578423867695..ef85aa9eacdd 100644 --- a/js/ui/grid_core/ui.grid_core.keyboard_navigation.js +++ b/js/ui/grid_core/ui.grid_core.keyboard_navigation.js @@ -128,34 +128,57 @@ var KeyboardNavigationController = core.ViewController.inherit({ var event = e.event, $target = $(event.currentTarget), $grid = $(event.target).closest("." + this.getWidgetContainerClass()).parent(), - data = event.data, - isCellEditMode = this._isCellEditMode(), - columnIndex, - column; + data = event.data; if($grid.is(this.component.$element()) && this._isCellValid($target)) { $target = this._isInsideEditForm($target) ? $(event.target) : $target; this._focusView(data.view, data.viewIndex); - this._updateFocusedCellPosition($target); if($target.parent().hasClass(FREESPACE_ROW_CLASS)) { + + this._updateFocusedCellPosition($target); + this._focusedView.element().attr("tabindex", 0); this._focusedView.focus(); } else if(!this._editingController.isEditing() && !this._isMasterDetailCell($target)) { - columnIndex = this.getView("rowsView").getCellIndex($target); - column = this._columnsController.getVisibleColumns()[columnIndex]; - if(isCellEditMode && column && column.allowEditing) { - this._isHiddenFocus = false; - } else { - var isInteractiveTarget = $(event.target).not($target).is(INTERACTIVE_ELEMENTS_SELECTOR); - this._focus($target, !this.isRowFocusType(), isInteractiveTarget); - } + this._clickTargetCellHandler(event, $target); + } else { + this._updateFocusedCellPosition($target); } } else if($target.is("td")) { this._resetFocusedCell(); } }, + _clickTargetCellHandler: function(event, $cell) { + var columnIndex = this.getView("rowsView").getCellIndex($cell), + column = this._columnsController.getVisibleColumns()[columnIndex], + isCellEditMode = this._isCellEditMode(), + args; + + args = this._fireFocusedRowChanging(event, $cell.parent()); + if(!args.cancel) { + if(args.rowIndexChanged) { + $cell = this._getFocusedCell(); + } + + this._updateFocusedCellPosition($cell); + + if(isCellEditMode && column && column.allowEditing) { + this._isHiddenFocus = false; + } else { + var isInteractiveTarget = $(event.target).not($cell).is(INTERACTIVE_ELEMENTS_SELECTOR); + this._focus($cell, true, isInteractiveTarget); + } + } else { + this.setFocusedRowIndex(args.prevRowIndex); + $cell = this._getFocusedCell(); + if(this._isCellValid($cell)) { + this._focus($cell, true); + } + } + }, + _initFocusedViews: function() { var that = this, clickAction = that.createAction(that._clickHandler); @@ -233,6 +256,12 @@ var KeyboardNavigationController = core.ViewController.inherit({ }, _updateFocusedCellPosition: function($cell, direction) { + var position = this._getCellPosition($cell, direction); + if(position) { + this.setFocusedCellPosition(position.rowIndex, position.columnIndex); + } + }, + _getCellPosition: function($cell, direction) { var that = this, rowIndex, columnIndex, @@ -248,7 +277,7 @@ var KeyboardNavigationController = core.ViewController.inherit({ columnIndex = that._applyColumnIndexBoundaries(columnIndex); } - this.setFocusedCellPosition(rowIndex, columnIndex); + return { rowIndex: rowIndex, columnIndex: columnIndex }; } }, @@ -281,6 +310,13 @@ var KeyboardNavigationController = core.ViewController.inherit({ return null; }, + getVisibleColumnIndex: function() { + if(this._focusedCellPosition) { + return isDefined(this._focusedCellPosition.columnIndex) ? this._focusedCellPosition.columnIndex : -1; + } + return -1; + }, + getFocusedColumnIndex: function() { return this._focusedCellPosition ? this._focusedCellPosition.columnIndex : null; }, @@ -420,6 +456,7 @@ var KeyboardNavigationController = core.ViewController.inherit({ _leftRightKeysHandler: function(eventArgs, isEditing) { var rowIndex = this.getVisibleRowIndex(), + $event = eventArgs.originalEvent, $row = this._focusedView && this._focusedView.getRow(rowIndex), directionCode, $cell; @@ -429,9 +466,17 @@ var KeyboardNavigationController = core.ViewController.inherit({ directionCode = this._getDirectionCodeByKey(eventArgs.key); $cell = this._getNextCell(directionCode); if($cell && this._isCellValid($cell)) { - this._focus($cell); + var args = this._fireFocusedCellChanging($event, $cell); + if(!args.cancel) { + if(args.$newCellElement) { + $cell = args.$newCellElement; + } + this._focus($cell); + } + } + if($event) { + $event.preventDefault(); } - eventArgs.originalEvent.preventDefault(); } }, @@ -450,15 +495,23 @@ var KeyboardNavigationController = core.ViewController.inherit({ _upDownKeysHandler: function(eventArgs, isEditing) { var rowIndex = this.getVisibleRowIndex(), $row = this._focusedView && this._focusedView.getRow(rowIndex), + $event = eventArgs.originalEvent, + args, $cell; if(!isEditing && $row && !isDetailRow($row)) { $cell = this._getNextCell(eventArgs.key); if($cell && this._isCellValid($cell)) { - this._focus($cell); + args = this._fireFocusedRowChanging($event, $cell.parent()); + if(!args.cancel) { + if(args.rowIndexChanged) { + $cell = this._getFocusedCell(); + } + this._focus($cell); + } } - if(eventArgs.originalEvent) { - eventArgs.originalEvent.preventDefault(); + if($event) { + $event.preventDefault(); } } }, @@ -577,8 +630,7 @@ var KeyboardNavigationController = core.ViewController.inherit({ var editingOptions = this.option("editing"), direction = eventArgs.shift ? "previous" : "next", isOriginalHandlerRequired = !eventArgs.shift && this._isLastValidCell(this._focusedCellPosition) || (eventArgs.shift && this._isFirstValidCell(this._focusedCellPosition)), - eventTarget = eventArgs.originalEvent.target, - $cell; + eventTarget = eventArgs.originalEvent.target; if(this._handleTabKeyOnMasterDetailCell(eventTarget, direction)) { return; @@ -589,56 +641,12 @@ var KeyboardNavigationController = core.ViewController.inherit({ this._resetFocusedCell(); } if(isEditing) { - var column, - row, - isEditingAllowed; - - this._updateFocusedCellPosition(this._getCellElementFromTarget(eventTarget)); - $cell = this._getNextCell(direction); - - if(!$cell || this._handleTabKeyOnMasterDetailCell($cell, direction)) { + if(!this._editingCellTabHandler(eventArgs, direction)) { return; } - - column = this._columnsController.getVisibleColumns()[this.getView("rowsView").getCellIndex($cell)]; - row = this._dataController.items()[this._getRowIndex($cell && $cell.parent())]; - - isEditingAllowed = (this._editingController.allowUpdating({ row: row }) || row && row.inserted) && column.allowEditing; - - if(!isEditingAllowed) { - this._editingController.closeEditCell(); - } - - if(this._focusCell($cell)) { - if(!this._isRowEditMode() && isEditingAllowed) { - this._editingController.editCell(this.getVisibleRowIndex(), this._focusedCellPosition.columnIndex); - } else { - this._focusInteractiveElement($cell, eventArgs.shift); - } - } } else { - $cell = this._getCellElementFromTarget(eventTarget); - var $lastInteractiveElement = this._getInteractiveElement($cell, !eventArgs.shift); - if($lastInteractiveElement.length && eventTarget !== $lastInteractiveElement.get(0)) { + if(this._targetCellTabHandler(eventArgs, direction)) { isOriginalHandlerRequired = true; - } else { - if(this._focusedCellPosition.rowIndex === undefined && $(eventTarget).hasClass(ROW_CLASS)) { - this._updateFocusedCellPosition($(eventTarget).children().first()); - } - - var elementType = this._getElementType(eventTarget); - if(this.isRowFocusType() && elementType === "row") { - if(isDataRow($(eventTarget))) { - this.setCellFocusType(); - eventTarget = this.getFirstValidCellInRow($(eventTarget)); - elementType = this._getElementType(eventTarget); - } - } - - $cell = this._getNextCell(direction, elementType); - this._focusCell($cell); - - this._focusInteractiveElement($cell, eventArgs.shift); } } } @@ -653,6 +661,109 @@ var KeyboardNavigationController = core.ViewController.inherit({ eventArgs.originalEvent.preventDefault(); } }, + _editingCellTabHandler: function(eventArgs, direction) { + var editingOptions = this.option("editing"), + eventTarget = eventArgs.originalEvent.target, + column, + row, + $cell, + isEditingAllowed; + + this._updateFocusedCellPosition(this._getCellElementFromTarget(eventTarget)); + $cell = this._getNextCell(direction); + + if(!$cell || this._handleTabKeyOnMasterDetailCell($cell, direction)) { + return false; + } + + column = this._columnsController.getVisibleColumns()[this.getView("rowsView").getCellIndex($cell)]; + row = this._dataController.items()[this._getRowIndex($cell && $cell.parent())]; + + isEditingAllowed = (editingOptions.allowUpdating || row && row.inserted) && column.allowEditing; + + if(!isEditingAllowed) { + this._editingController.closeEditCell(); + } + + if(this._focusCell($cell)) { + if(!this._isRowEditMode() && isEditingAllowed) { + this._editingController.editCell(this.getVisibleRowIndex(), this._focusedCellPosition.columnIndex); + } else { + this._focusInteractiveElement($cell, eventArgs.shift); + } + } + + return true; + }, + _targetCellTabHandler: function(eventArgs, direction) { + var $event = eventArgs.originalEvent, + eventTarget = $event.target, + $cell = this._getCellElementFromTarget(eventTarget), + $lastInteractiveElement = this._getInteractiveElement($cell, !eventArgs.shift), + isOriginalHandlerRequired = false, + elementType; + + if($lastInteractiveElement.length && eventTarget !== $lastInteractiveElement.get(0)) { + isOriginalHandlerRequired = true; + } else { + if(this._focusedCellPosition.rowIndex === undefined && $(eventTarget).hasClass(ROW_CLASS)) { + this._updateFocusedCellPosition($(eventTarget).children().first()); + } + + elementType = this._getElementType(eventTarget); + if(this.isRowFocusType()) { + this.setCellFocusType(); + if(elementType === "row" && isDataRow($(eventTarget))) { + eventTarget = this.getFirstValidCellInRow($(eventTarget)); + elementType = this._getElementType(eventTarget); + } + } + + $cell = this._getNextCellByTabKey($event, direction, elementType); + if(!$cell) { + return false; + } + + $cell = this._checkNewLineTransition($event, $cell); + if(!$cell) { + return false; + } + + this._focusCell($cell); + this._focusInteractiveElement($cell, eventArgs.shift); + } + + return isOriginalHandlerRequired; + }, + _getNextCellByTabKey: function($event, direction, elementType) { + var $cell = this._getNextCell(direction, elementType), + args = this._fireFocusedCellChanging($event, $cell); + if(args.cancel) { + return; + } + if(args.$newCellElement) { + $cell = args.$newCellElement; + } + return $cell; + }, + _checkNewLineTransition: function($event, $cell) { + var rowIndex = this.getVisibleRowIndex(), + $row = $cell.parent(); + + if(rowIndex !== this._getRowIndex($row)) { + var cellPosition = this._getCellPosition($cell), + args = this._fireFocusedRowChanging($event, $row); + if(args.cancel) { + return; + } + if(args.rowIndexChanged) { + this.setFocusedColumnIndex(cellPosition.columnIndex); + $cell = this._getFocusedCell(); + } + } + + return $cell; + }, getFirstValidCellInRow: function($row) { var that = this, @@ -1074,6 +1185,84 @@ var KeyboardNavigationController = core.ViewController.inherit({ this._focusedViews = null; this._keyDownProcessor && this._keyDownProcessor.dispose(); eventsEngine.off(domAdapter.getDocument(), eventUtils.addNamespace(pointerEvents.down, "dxDataGridKeyboardNavigation"), this._documentClickHandler); + }, + + _fireFocusedCellChanging: function($event, $cellElement) { + var that = this, + prevCellIndex = that.option("focusedColumnIndex"), + prevRowIndex = that.option("focusedRowIndex"), + cellPosition = that._getCellPosition($cellElement), + args = { + cellElement: $cellElement, + prevColumnIndex: prevCellIndex, + prevRowIndex: prevRowIndex, + newColumnIndex: cellPosition.columnIndex, + newRowIndex: cellPosition.rowIndex, + rows: this.component.getVisibleRows(), + columns: this.component.getVisibleColumns(), + event: $event, + cancel: false + }; + + that.executeAction("onFocusedCellChanging", args); + if(args.newColumnIndex !== cellPosition.columnIndex || args.newRowIndex !== cellPosition.rowIndex) { + args.$newCellElement = this._getCell({ columnIndex: args.newColumnIndex, rowIndex: args.newRowIndex }); + } + + return args; + }, + + _fireFocusedCellChanged: function($cellElement, prevCellIndex, prevRowIndex) { + var that = this, + columnIndex = that.option("focusedColumnIndex"), + focusedRowIndex = that.option("focusedRowIndex"); + + if(prevCellIndex !== columnIndex || prevRowIndex !== focusedRowIndex) { + that.executeAction("onFocusedCellChanged", { + cellElement: $cellElement, + columnIndex: columnIndex, + rowIndex: focusedRowIndex, + row: that.getController("data").getVisibleRows()[focusedRowIndex], + column: that.component.getVisibleColumns()[columnIndex] + }); + } + }, + + _fireFocusedRowChanging: function(eventArgs, $newFocusedRow) { + var newRowIndex = this._getRowIndex($newFocusedRow), + prevFocusedRowIndex = this.getVisibleRowIndex(), + args = { + rowElement: $newFocusedRow, + prevRowIndex: prevFocusedRowIndex, + newRowIndex: newRowIndex, + event: eventArgs, + rows: this.component.getVisibleRows(), + cancel: false + }; + + if(this.option("focusedRowEnabled")) { + this.executeAction("onFocusedRowChanging", args); + if(!args.cancel && args.newRowIndex !== newRowIndex) { + this.setFocusedRowIndex(args.newRowIndex); + args.rowIndexChanged = true; + } + } + + return args; + }, + + _fireFocusedRowChanged: function($rowElement) { + var that = this, + focusedRowKey = that.option("focusedRowKey"), + focusedRowIndex = that.option("focusedRowIndex"); + + if(focusedRowKey && that.option("focusedRowEnabled")) { + that.executeAction("onFocusedRowChanged", { + rowElement: $rowElement, + rowIndex: focusedRowIndex, + row: that.getController("data").getVisibleRows()[focusedRowIndex] + }); + } } }); @@ -1098,6 +1287,112 @@ module.exports = { * @extends Action * @action */ + + /** + * @name dxDataGridOptions.onFocusedCellChanging + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 cellElement:dxElement + * @type_function_param1_field2 prevColumnIndex:number + * @type_function_param1_field3 prevRowIndex:number + * @type_function_param1_field4 newColumnIndex:number + * @type_function_param1_field5 newRowIndex:number + * @type_function_param1_field7 event:event + * @type_function_param1_field8 rows:Array + * @type_function_param1_field9 columns:Array + * @type_function_param1_field10 cancel:boolean + * @extends Action + * @action + */ + /** + * @name dxTreeListOptions.onFocusedCellChanging + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 cellElement:dxElement + * @type_function_param1_field2 prevColumnIndex:number + * @type_function_param1_field3 prevRowIndex:number + * @type_function_param1_field4 newColumnIndex:number + * @type_function_param1_field5 newRowIndex:number + * @type_function_param1_field7 event:event + * @type_function_param1_field8 rows:Array + * @type_function_param1_field9 columns:Array + * @type_function_param1_field10 cancel:boolean + * @extends Action + * @action + */ + + /** + * @name dxDataGridOptions.onFocusedCellChanged + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 cellElement:dxElement + * @type_function_param1_field2 columnIndex:number + * @type_function_param1_field3 rowIndex:number + * @type_function_param1_field4 row:dxDataGridRowObject + * @type_function_param1_field5 column:dxDataGridColumn + * @extends Action + * @action + */ + /** + * @name dxTreeListOptions.onFocusedCellChanged + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 cellElement:dxElement + * @type_function_param1_field2 columnIndex:number + * @type_function_param1_field3 rowIndex:number + * @type_function_param1_field4 row:dxTreeListRowObject + * @type_function_param1_field5 column:dxTreeListColumn + * @extends Action + * @action + */ + + /** + * @name dxDataGridOptions.onFocusedRowChanging + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 rowElement:dxElement + * @type_function_param1_field2 prevRowIndex:number + * @type_function_param1_field3 newRowIndex:number + * @type_function_param1_field5 event:event + * @type_function_param1_field6 rows:Array + * @type_function_param1_field7 cancel:boolean + * @extends Action + * @action + */ + /** + * @name dxTreeListOptions.onFocusedRowChanging + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 rowElement:dxElement + * @type_function_param1_field2 prevRowIndex:number + * @type_function_param1_field3 newRowIndex:number + * @type_function_param1_field5 event:event + * @type_function_param1_field6 rows:Array + * @type_function_param1_field7 cancel:boolean + * @extends Action + * @action + */ + + /** + * @name dxDataGridOptions.onFocusedRowChanged + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 rowElement:dxElement + * @type_function_param1_field2 rowIndex:number + * @type_function_param1_field4 row:dxDataGridRowObject + * @extends Action + * @action + */ + /** + * @name dxTreeListOptions.onFocusedRowChanged + * @type function(e) + * @type_function_param1 e:object + * @type_function_param1_field1 rowElement:dxElement + * @type_function_param1_field2 rowIndex:number + * @type_function_param1_field4 row:dxTreeListRowObject + * @extends Action + * @action + */ }; }, controllers: { diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js index b66bc5c8f7b6..f80984015392 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js @@ -506,7 +506,7 @@ QUnit.testInActiveWindow("Tab index should not exist for the previous focused ro // assert assert.equal($(rowsView.getRow(0)).find('[tabindex="0"]').length, 1, "Row 0 has tabindex"); // act - this.getController("focus").clearPreviousFocusedRow($(rowsView.getRow(0).parent())); + this.getController("focus")._clearPreviousFocusedRow($(rowsView.getRow(0).parent())); // assert assert.equal(this.option("focusedRowIndex"), 1, "FocusedRowIndex = 1"); assert.equal($(rowsView.getRow(0)).find('[tabindex="0"]').length, 0, "Row 0 has no tabindex"); @@ -796,3 +796,939 @@ QUnit.testInActiveWindow("DataGrid - Should paginate to the defined focusedRowKe assert.strictEqual(this.dataController.getVisibleRows()[0].data, this.data[4], "Row 0, Data 4"); assert.ok(this.gridView.getView("rowsView").getRow(0).hasClass("dx-row-focused"), "Row 0 is the focused row"); }); + + +QUnit.testInActiveWindow("Fire onFocusedRowChanging by click", function(assert) { + // arrange + var focusedRowChangingCount = 0; + + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + ++focusedRowChangingCount; + assert.equal(e.cancel, false); + assert.equal(e.event.type, "dxpointerdown"); + assert.equal(e.newRowIndex, 1); + assert.equal(e.prevRowIndex, 4); + assert.equal(e.rows.length, 6); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(0)).trigger("dxpointerdown").click(); + this.clock.tick(); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleRowIndex(), 1, "Focused row index is 1"); + assert.equal(focusedRowChangingCount, 1, "onFocusedRowChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedRowChanging by UpArrow key", function(assert) { + var rowsView, + focusedRowChangingCount = 0, + keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + ++focusedRowChangingCount; + assert.equal(e.cancel, false); + assert.equal(e.newRowIndex, 3); + assert.equal(e.prevRowIndex, 4); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex is 4"); + // act + keyboardController._upDownKeysHandler({ key: "upArrow" }); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleRowIndex(), 3, "Focused row index is 3"); + assert.equal(focusedRowChangingCount, 1, "onFocusedRowChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedRowChanging by DownArrow key", function(assert) { + var rowsView, + focusedRowChangingCount = 0, + keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + ++focusedRowChangingCount; + assert.equal(e.cancel, false); + assert.equal(e.newRowIndex, 5); + assert.equal(e.prevRowIndex, 4); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex is 4"); + // act + keyboardController._upDownKeysHandler({ key: "downArrow" }); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleRowIndex(), 5, "Focused row index is 5"); + assert.equal(focusedRowChangingCount, 1, "onFocusedRowChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedRowChanging by Tab key", function(assert) { + var rowsView, + keyboardController, + focusedRowChangingCounter = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + if(++focusedRowChangingCounter > 1) { + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.newRowIndex, 2, "New row index"); + assert.equal(e.prevRowIndex, 1, "Prev row index"); + } + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(0)).trigger("dxpointerdown").click(); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(keyboardController.getVisibleRowIndex(), 1, "FocusedRowIndex"); + assert.equal(keyboardController.getFocusedColumnIndex(), 0, "FocusedColumnIndex"); + // assert, act + this.triggerKeyDown("tab", false, false, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 1, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 1, "focusedRowChanging count"); + // assert, act + this.triggerKeyDown("tab", false, false, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getFocusedColumnIndex(), 2, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 1, "focusedRowChanging count"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + // assert, act + this.triggerKeyDown("tab", false, false, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 2, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 0, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 2, "focusedRowChanging count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedRowChanging by Tab key in back order (shift presset)", function(assert) { + var rowsView, + keyboardController, + focusedRowChangingCounter = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + if(++focusedRowChangingCounter > 1) { + assert.equal(e.cancel, false); + assert.equal(e.newRowIndex, 0); + assert.equal(e.prevRowIndex, 1); + } + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(2)).trigger("dxpointerdown").click(); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(keyboardController.getVisibleRowIndex(), 1, "FocusedRowIndex"); + assert.equal(keyboardController.getFocusedColumnIndex(), 2, "FocusedColumnIndex"); + // assert, act + this.triggerKeyDown("tab", false, true, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 1, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 1, "focusedRowChanging count"); + // assert, act + this.triggerKeyDown("tab", false, true, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 0, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 1, "focusedRowChanging count"); + // assert, act + this.triggerKeyDown("tab", false, true, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 0, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 2, "FocusedColumnIndex"); + assert.equal(focusedRowChangingCounter, 2, "focusedRowChanging count"); +}); + +QUnit.testInActiveWindow("Setting cancel in onFocusedRowChanging event args should prevent change focused row", function(assert) { + var focusedRowChangingCount = 0, + focusedRowChangedCount = 0; + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanging: function(e) { + focusedRowChangingCount++; + e.cancel = true; + }, + onFocusedRowChanged: function(e) { + focusedRowChangedCount++; + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(0)).trigger("dxpointerdown").click(); + this.clock.tick(); + + assert.equal(focusedRowChangingCount, 1, "focusedRowChanging count"); + assert.equal(focusedRowChangedCount, 0, "focusedRowChanged count"); + assert.equal(this.getController("keyboardNavigation").getVisibleRowIndex(), 4, "Focused row index is 5"); +}); + +QUnit.testInActiveWindow("onFocusedRowChanged event", function(assert) { + // arrange + var focusedRowChangedCount = 0; + + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedRowChanged: function(e) { + ++focusedRowChangedCount; + assert.equal(e.row.key, "Dan", "Row"); + assert.equal(e.rowIndex, 1, "Row index"); + assert.ok(e.rowElement, "Row element"); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + this.gridView.getController("focus").optionChanged({ name: "focusedRowKey", value: "Dan" }); + // assert + assert.equal(focusedRowChangedCount, 1, "onFocusedRowChanged fires count"); +}); + +QUnit.testInActiveWindow("onFocusedCellChanged event", function(assert) { + var rowsView, + focusedCellChangedCount = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedCellChanged: function(e) { + ++focusedCellChangedCount; + assert.deepEqual(e.cellElement.text(), rowsView.getRow(1).find("td").eq(1).text(), "Cell element"); + assert.equal(e.columnIndex, 1, "Column index"); + assert.deepEqual(e.row.data, { name: "Dan", phone: "2222222", room: 5 }, "Row data"); + assert.deepEqual(e.rowIndex, 1, "Row index"); + assert.equal(e.column.dataField, "phone", "Column"); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + rowsView = this.gridView.getView("rowsView"); + $(rowsView.getRow(1).find("td").eq(1)).trigger("dxpointerdown").click(); + assert.equal(focusedCellChangedCount, 1, "onFocusedCellChanged fires count"); +}); + +QUnit.testInActiveWindow("onFocusedCellChanged event should fire if row index changed", function(assert) { + var rowsView, + focusedCellChangedCount = 0, + keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + editing: { + allowEditing: false + }, + onFocusedCellChanged: function(e) { + ++focusedCellChangedCount; + assert.deepEqual(e.cellElement.text(), rowsView.getRow(3).find("td").eq(1).text(), "Cell element"); + assert.equal(e.columnIndex, 1, "Column index"); + assert.deepEqual(e.row.data, { name: "Sean", phone: "4545454", room: 3 }, "Row data"); + assert.deepEqual(e.rowIndex, 3, "Row index"); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex is 4"); + // act + keyboardController._updateFocusedCellPosition($(rowsView.getRow(3).find("td").eq(1))); + // assert + assert.equal(this.option("focusedRowIndex"), 3, "FocusedRowIndex is 3"); + assert.equal(focusedCellChangedCount, 1, "onFocusedCellChanged fires count"); +}); + +QUnit.testInActiveWindow("onFocusedCellChanged event should not fire if cell position not changed", function(assert) { + var rowsView, + focusedCellChangedCount = 0, + keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + focusedColumnIndex: 2, + editing: { + allowEditing: false + }, + onFocusedCellChanged: function(e) { + ++focusedCellChangedCount; + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + // act + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex is 4"); + assert.equal(this.option("focusedColumnIndex"), 2, "FocusedColumnIndex is 2"); + // act + keyboardController._updateFocusedCellPosition($(rowsView.getRow(4).find("td").eq(2))); + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex is 4"); + assert.equal(this.option("focusedColumnIndex"), 2, "FocusedColumnIndex is 2"); + assert.equal(focusedCellChangedCount, 0, "onFocusedCellChanged fires count"); +}); + +QUnit.testInActiveWindow("Setting cancel in onFocusedCellChanging event should prevent focusing next cell", function(assert) { + var rowsView, + keyboardController, + focusedColumnChangingCount = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + focusedColumnIndex: 1, + editing: { + allowEditing: false + }, + onFocusedCellChanging: function(e) { + ++focusedColumnChangingCount; + + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.cellElement.text(), $(rowsView.getRow(4).find("td").eq(0)).text(), "Cell element"); + assert.equal(e.newColumnIndex, 0); + assert.equal(e.prevColumnIndex, 1); + assert.equal(e.newRowIndex, 4); + assert.equal(e.prevRowIndex, 4); + assert.equal(e.rows.length, 6); + assert.equal(e.columns.length, 3); + + e.cancel = true; + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex"); + assert.equal(this.option("focusedColumnIndex"), 1, "FocusedColumnIndex"); + // act + keyboardController._leftRightKeysHandler({ key: "leftArrow" }); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleColumnIndex(), 1, "Focused column index"); + assert.equal(focusedColumnChangingCount, 1, "onFocusedCellChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedCellChanging by LeftArrow key", function(assert) { + var rowsView, + keyboardController, + focusedColumnChangingCount = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + focusedColumnIndex: 1, + editing: { + allowEditing: false + }, + onFocusedCellChanging: function(e) { + ++focusedColumnChangingCount; + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.cellElement.text(), $(rowsView.getRow(4).find("td").eq(0)).text(), "Cell element"); + assert.equal(e.newColumnIndex, 0); + assert.equal(e.prevColumnIndex, 1); + assert.equal(e.newRowIndex, 4); + assert.equal(e.prevRowIndex, 4); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex"); + assert.equal(this.option("focusedColumnIndex"), 1, "FocusedColumnIndex"); + // act + keyboardController._leftRightKeysHandler({ key: "leftArrow" }); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleColumnIndex(), 0, "Focused column index"); + assert.equal(focusedColumnChangingCount, 1, "onFocusedCellChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedCellChanging by RightArrow key", function(assert) { + var rowsView, + keyboardController, + focusedColumnChangingCount = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + focusedRowEnabled: true, + focusedRowKey: "Smith", + focusedColumnIndex: 1, + editing: { + allowEditing: false + }, + onFocusedCellChanging: function(e) { + ++focusedColumnChangingCount; + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.cellElement.text(), $(rowsView.getRow(4).find("td").eq(2)).text(), "Cell element"); + assert.equal(e.newColumnIndex, 2); + assert.equal(e.prevColumnIndex, 1); + assert.equal(e.newRowIndex, 4); + assert.equal(e.prevRowIndex, 4); + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(this.option("focusedRowIndex"), 4, "FocusedRowIndex"); + assert.equal(this.option("focusedColumnIndex"), 1, "FocusedColumnIndex"); + // act + keyboardController._leftRightKeysHandler({ key: "rightArrow" }); + // assert + assert.equal(this.getController("keyboardNavigation").getVisibleColumnIndex(), 2, "Focused column index"); + assert.equal(focusedColumnChangingCount, 1, "onFocusedCellChanging fires count"); +}); + +QUnit.testInActiveWindow("Fire onFocusedCellChanging by Tab key", function(assert) { + var rowsView, + keyboardController, + focusedCellChangingCounter = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + editing: { + allowEditing: false + }, + onFocusedCellChanging: function(e) { + if(++focusedCellChangingCounter > 1) { + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.cellElement.text(), $(rowsView.getRow(1).find("td").eq(focusedCellChangingCounter)).text(), "Cell element"); + assert.equal(e.newColumnIndex, focusedCellChangingCounter); + assert.equal(e.prevColumnIndex, focusedCellChangingCounter - 1); + assert.equal(e.newRowIndex, 1); + assert.equal(e.prevRowIndex, 1); + } + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(0)).trigger("dxpointerdown").click(); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(keyboardController.getVisibleRowIndex(), 1, "FocusedRowIndex"); + assert.equal(keyboardController.getFocusedColumnIndex(), 0, "FocusedColumnIndex"); + // assert, act + this.triggerKeyDown("tab", false, false, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 1, "FocusedColumnIndex"); + assert.equal(focusedCellChangingCounter, 1, "focusedCelChanging count"); + // assert, act + this.triggerKeyDown("tab", false, false, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getFocusedColumnIndex(), 2, "FocusedColumnIndex"); + assert.equal(focusedCellChangingCounter, 2, "focusedCelChanging count"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); +}); + +QUnit.testInActiveWindow("Fire onFocusedCellChanging by Tab key in back order (shift presset)", function(assert) { + var rowsView, + keyboardController, + focusedCellChangingCounter = 0; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + editing: { + allowEditing: false + }, + onFocusedCellChanging: function(e) { + if(++focusedCellChangingCounter > 1) { + var columnIndex = 2 - focusedCellChangingCounter; + assert.equal(e.cancel, false, "Not canceled"); + assert.equal(e.cellElement.text(), $(rowsView.getRow(1).find("td").eq(columnIndex)).text(), "Cell element"); + assert.equal(e.newColumnIndex, columnIndex); + assert.equal(e.prevColumnIndex, columnIndex + 1); + assert.equal(e.newRowIndex, 1); + assert.equal(e.prevRowIndex, 1); + } + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + $(this.gridView.getView("rowsView").getRow(1).find("td").eq(2)).trigger("dxpointerdown").click(); + this.clock.tick(); + + rowsView = this.gridView.getView("rowsView"); + keyboardController = this.getController("keyboardNavigation"); + keyboardController._focusedView = rowsView; + + // assert + assert.equal(keyboardController.getVisibleRowIndex(), 1, "FocusedRowIndex"); + assert.equal(keyboardController.getFocusedColumnIndex(), 2, "FocusedColumnIndex"); + // assert, act + this.triggerKeyDown("tab", false, true, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); + assert.equal(keyboardController.getFocusedColumnIndex(), 1, "FocusedColumnIndex"); + assert.equal(focusedCellChangingCounter, 1, "focusedCelChanging count"); + // assert, act + this.triggerKeyDown("tab", false, true, rowsView.getRow(1).find("td:focus")); + assert.ok(keyboardController.isCellFocusType(), "Cell focus type"); + assert.equal(keyboardController.getFocusedColumnIndex(), 0, "FocusedColumnIndex"); + assert.equal(focusedCellChangingCounter, 2, "focusedCelChanging count"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); +}); + +QUnit.testInActiveWindow("Test navigateToRow method if paging", function(assert) { + var keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + editing: { + allowEditing: false + }, + paging: { + pageSize: 2 + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + keyboardController = this.getController("keyboardNavigation"); + + assert.equal(this.pageIndex(), 0, "Page index"); + assert.equal(keyboardController.getVisibleRowIndex(), undefined, "Focused row index"); + + this.navigateToRow("Zeb"); + this.clock.tick(); + + assert.equal(this.pageIndex(), 2, "Page index"); + assert.equal(keyboardController.getVisibleRowIndex(), 1, "Focused row index"); +}); + +QUnit.testInActiveWindow("Test navigateToRow method if virtualScrolling", function(assert) { + var keyboardController; + + // arrange + this.$element = function() { + return $("#container"); + }; + + this.data = [ + { name: "Alex", phone: "111111", room: 6 }, + { name: "Dan", phone: "2222222", room: 5 }, + { name: "Ben", phone: "333333", room: 4 }, + { name: "Sean", phone: "4545454", room: 3 }, + { name: "Smith", phone: "555555", room: 2 }, + { name: "Zeb", phone: "6666666", room: 1 } + ]; + + this.options = { + keyExpr: "name", + editing: { + allowEditing: false + }, + paging: { + pageSize: 2 + }, + scrolling: { + mode: "virtual" + } + }; + + this.setupModule(); + + this.gridView.render($("#container")); + this.clock.tick(); + + keyboardController = this.getController("keyboardNavigation"); + + assert.equal(this.pageIndex(), 0, "Page index"); + assert.equal(keyboardController.getVisibleRowIndex(), undefined, "Focused row index"); + + this.navigateToRow("Zeb"); + this.clock.tick(); + + assert.equal(this.pageIndex(), 2, "Page index"); + assert.equal(keyboardController.getVisibleRowIndex(), 5, "Focused row index"); +}); +