diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 2440b633df93..000000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": [ "dbaeumer.vscode-eslint" ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index dc8891d543ca..000000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - } -} \ No newline at end of file diff --git a/js/ui/grid_core/ui.grid_core.editing.js b/js/ui/grid_core/ui.grid_core.editing.js index bdf74ec74146..67c8577bc92f 100644 --- a/js/ui/grid_core/ui.grid_core.editing.js +++ b/js/ui/grid_core/ui.grid_core.editing.js @@ -2670,9 +2670,7 @@ module.exports = { editingController.showHighlighting($cell); $cell.addClass(CELL_MODIFIED); } else if(isEditableCell) { - const skipValidation = parameters.row.isNewRow; - - editingController.showHighlighting($cell, skipValidation); + editingController.showHighlighting($cell, true); } this.callBase.apply(this, arguments); diff --git a/js/ui/grid_core/ui.grid_core.validating.js b/js/ui/grid_core/ui.grid_core.validating.js index c44d0b1fcdc6..1070105700ac 100644 --- a/js/ui/grid_core/ui.grid_core.validating.js +++ b/js/ui/grid_core/ui.grid_core.validating.js @@ -448,7 +448,6 @@ module.exports = { _createInvisibleColumnValidators: function(editData) { const validatingController = this.getController('validating'); const columnsController = this.getController('columns'); - const invisibleColumns = this._getInvisibleColumns(editData).filter((column) => !column.isBand); const groupColumns = columnsController.getGroupColumns().filter((column) => !column.showWhenGrouped && invisibleColumns.indexOf(column) === -1); const invisibleColumnValidators = []; diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js index 63c41891e935..1d844c344fcf 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/dataGrid.tests.js @@ -19752,515 +19752,3 @@ QUnit.test('DataGrid should scroll horizontally without scroll back if focused r // assert assert.equal(scrollable.scrollOffset().left, 300, 'Content was scrolled'); }); - -QUnit.module('Validation with virtual scrolling and rendering', { - beforeEach: function() { - this.addHiddenColumn = () => { - this.columns.push({ - dataField: 'hiddenField', - dataType: 'number', - visible: false, - validationRules: [{ - type: 'required', - }] - }); - }; - - this.data = []; - - for(let i = 0; i < 100; i++) { - this.data.push({ field: i, hiddenField: i }); - } - - this.columns = [{ - dataField: 'field', - dataType: 'number', - validationRules: [{ - type: 'required', - }] - }]; - - this.gridOptions = { - height: 400, - dataSource: this.data, - showBorders: true, - scrolling: { - mode: 'virtual', - rowRenderingMode: 'virtual' - }, - paging: { pageSize: 50 }, - editing: { - mode: 'cell', - allowAdding: true, - allowUpdating: true - }, - columns: this.columns - }; - - this.clock = sinon.useFakeTimers(); - }, - afterEach: function() { - this.clock.restore(); - } -}, () => { - - // T838674 - QUnit.test('Validation error hightlighting should not disappear after scrolling', function(assert) { - // arrange - let $input; - let dataGrid; - let $firstCell; - - dataGrid = createDataGrid(this.gridOptions); - - // act - this.clock.tick(500); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $firstCell.trigger('dxclick'); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $input.val(''); - $input.trigger('change'); - - $firstCell.trigger('dxclick'); - - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell is invalid'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $firstCell.trigger('dxclick'); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - - // act - scrollable.scrollTo({ y: 0 }); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell is invalid'); - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - }); - - // T838674 - QUnit.test('Validation error hightlighting should disappear after scrolling if newly added row failed validation', function(assert) { - // arrange - let $input; - let dataGrid; - let $firstCell; - - dataGrid = createDataGrid(this.gridOptions); - - // act - this.clock.tick(500); - - dataGrid.addRow(); - - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $firstCell.trigger('dxclick'); - - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.notOk($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $firstCell.trigger('dxclick'); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - - // act - scrollable.scrollTo({ y: 0 }); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - // assert - assert.notOk($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - }); - - // T838674 - QUnit.test('Validation should work after editing row and scrolling if grid has hidden column with validationRules. Cell edit mode', function(assert) { - // arrange - let $input; - let dataGrid; - let $firstCell; - - this.addHiddenColumn(); - - dataGrid = createDataGrid(this.gridOptions); - - // act - this.clock.tick(500); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $firstCell.trigger('dxclick'); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $input.val(''); - $input.trigger('change'); - - $firstCell.trigger('dxclick'); - - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $firstCell.trigger('dxclick'); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - - // act - scrollable.scrollTo({ y: 0 }); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - assert.equal(this.data[0].field, 0, 'changes were not saved'); - }); - - // T838674 - QUnit.test('Validation should work after editing row and scrolling if grid has hidden column with validationRules. Batch edit mode', function(assert) { - // arrange - let $input; - let dataGrid; - let $firstCell; - let $saveButton; - let $errorRow; - - this.gridOptions.editing.mode = 'batch'; - this.addHiddenColumn(); - - dataGrid = createDataGrid(this.gridOptions); - - // act - this.clock.tick(500); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $firstCell.trigger('dxclick'); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $input.val(''); - $input.trigger('change'); - - $firstCell.trigger('dxclick'); - - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell has invalid class'); - assert.ok($firstCell.hasClass('dx-cell-modified'), 'modified cell'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - - $saveButton = $('.dx-datagrid-save-button'); - $saveButton.trigger('dxclick'); - this.clock.tick(); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - assert.notOk($saveButton.hasClass('dx-state-disabled'), 'save button is not disabled'); - - // act - scrollable.scrollTo({ y: 0 }); - this.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - $errorRow = $(dataGrid.$element().find('.dx-error-message')); - - // assert - assert.ok($firstCell.hasClass('dx-datagrid-invalid'), 'cell has invalid class'); - assert.ok($firstCell.hasClass('dx-cell-modified'), 'modified cell'); - assert.ok($input, 'cell has input'); - - assert.equal(this.data[0].field, 0, 'changes were not saved'); - - assert.ok($errorRow, 'error row'); - assert.equal($errorRow.text(), '', 'error message'); - }); - - function rowAddingValidationWithInvalidHiddenColumnTest(that, assert, editMode) { - // arrange - let $input; - let dataGrid; - const onRowValidatingSpy = sinon.spy(); - let $errorRow; - let $firstCell; - - that.gridOptions.editing.mode = editMode; - that.gridOptions.onRowValidating = onRowValidatingSpy; - - that.addHiddenColumn(); - - dataGrid = createDataGrid(that.gridOptions); - - // act - that.clock.tick(500); - - dataGrid.addRow(); - - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $firstCell.trigger('dxclick'); - - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.notOk($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - dataGrid.saveEditData(); - that.clock.tick(); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - - // act - scrollable.scrollTo({ y: 0 }); - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - $errorRow = $(dataGrid.$element().find('.dx-error-message')); - - // assert - if(editMode === 'cell') { - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - } else { - assert.ok($firstCell.hasClass('dx-cell-modified'), 'modified cell'); - } - assert.ok($input, 'cell has input'); - - assert.ok($errorRow, 'error row'); - assert.equal($errorRow.text(), 'Hidden Field is required', 'error message'); - - assert.equal(that.data.length, 100, 'data was not modified'); - - assert.equal(onRowValidatingSpy.callCount, 1, 'onRowValidating call count'); - - const onRowValidatingArguments = onRowValidatingSpy.args[0][0]; - const brokenRules = onRowValidatingArguments.brokenRules; - - assert.equal(brokenRules.length, 2, 'brokenRules length'); - - assert.notOk(brokenRules[0].isValid, 'is not valid'); - assert.equal(brokenRules[0].type, 'required', 'rule type'); - assert.equal(brokenRules[0].columnIndex, 0, 'column index'); - - assert.notOk(brokenRules[1].isValid, 'is not valid'); - assert.equal(brokenRules[1].type, 'required', 'rule type'); - assert.equal(brokenRules[1].columnIndex, 1, 'column index'); - } - - // T838674 - QUnit.test('Validation should work after adding new row and scrolling if grid has invalid hidden column with validationRules. Cell edit mode', function(assert) { - rowAddingValidationWithInvalidHiddenColumnTest(this, assert, 'cell'); - }); - - // T838674 - QUnit.test('Validation should work after adding new row and scrolling if grid has invalid hidden column with validationRules. Batch edit mode', function(assert) { - rowAddingValidationWithInvalidHiddenColumnTest(this, assert, 'batch'); - }); - - function rowAddingValidationWithValidHiddenColumnTest(that, assert, editMode) { - // arrange - let $input; - let dataGrid; - const onRowValidatingSpy = sinon.spy(); - let $errorRow; - let $firstCell; - - that.addHiddenColumn(); - - that.gridOptions.onRowValidating = onRowValidatingSpy; - that.gridOptions.onInitNewRow = function(e) { - e.data.hiddenField = 100; - }; - that.gridOptions.editing.mode = editMode; - - dataGrid = createDataGrid(that.gridOptions); - - // act - that.clock.tick(500); - - dataGrid.addRow(); - - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - $input = $firstCell.find('input'); - - // assert - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - assert.ok($input, 'cell has input'); - - // act - $firstCell.trigger('dxclick'); - - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - // assert - assert.notOk($firstCell.hasClass('dx-datagrid-invalid'), 'cell has not invalid class'); - - // act - const scrollable = dataGrid.getScrollable(); - scrollable.scrollTo({ y: 1000 }); - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - - dataGrid.saveEditData(); - that.clock.tick(); - - // assert - assert.notOk(dataGrid.$element().find('dx-datagrid-invalid').length, 'no invalid cells'); - - // act - scrollable.scrollTo({ y: 0 }); - that.clock.tick(); - - $firstCell = $(dataGrid.getCellElement(0, 0)); - $input = $firstCell.find('input'); - - $errorRow = $(dataGrid.$element().find('.dx-error-message')); - - // assert - if(editMode === 'cell') { - assert.ok($firstCell.hasClass('dx-editor-cell'), 'editor cell'); - } else { - assert.ok($firstCell.hasClass('dx-cell-modified'), 'modified cell'); - } - - assert.ok($input, 'cell has input'); - - assert.ok($errorRow, 'error row'); - assert.equal($errorRow.text(), '', 'error message'); - - assert.equal(that.data.length, 100, 'data was not modified'); - - assert.equal(onRowValidatingSpy.callCount, 1, 'onRowValidating call count'); - - const onRowValidatingArguments = onRowValidatingSpy.args[0][0]; - const brokenRules = onRowValidatingArguments.brokenRules; - - assert.equal(brokenRules.length, 1, 'brokenRules length'); - - assert.notOk(brokenRules[0].isValid, 'is not valid'); - assert.equal(brokenRules[0].type, 'required', 'rule type'); - assert.equal(brokenRules[0].columnIndex, 0, 'column index'); - } - - // T838674 - QUnit.test('Validation should work after adding new row and scrolling if grid has valid hidden column with validationRules. Cell edit mode', function(assert) { - rowAddingValidationWithValidHiddenColumnTest(this, assert, 'cell'); - }); - - // T838674 - QUnit.test('Validation should work after adding new row and scrolling if grid has valid hidden column with validationRules. Batch edit mode', function(assert) { - rowAddingValidationWithValidHiddenColumnTest(this, assert, 'batch'); - }); -}); diff --git a/testing/tests/DevExpress.ui.widgets.dataGrid/editing.tests.js b/testing/tests/DevExpress.ui.widgets.dataGrid/editing.tests.js index aec339491b85..0c3d87ed6a6b 100644 --- a/testing/tests/DevExpress.ui.widgets.dataGrid/editing.tests.js +++ b/testing/tests/DevExpress.ui.widgets.dataGrid/editing.tests.js @@ -8548,6 +8548,7 @@ QUnit.module('Editing with validation', { this.columnHeadersView.getColumnCount = function() { return 3; }; + this.clock = sinon.useFakeTimers(); }, afterEach: function() { @@ -12481,6 +12482,45 @@ QUnit.test('No exceptions on editing a column with given setCellValue when repai } }); +QUnit.test('Row - An untouched cell should not be validated (T872003)', function(assert) { + // arrange + const rowsView = this.rowsView; + const testElement = $('#container'); + + rowsView.render(testElement); + + this.applyOptions({ + editing: { + mode: 'row' + }, + columns: [ + { + dataField: 'name' + }, + { + dataField: 'age', + validationRules: [ + { + type: 'custom', + validationCallback: function() { + return false; + } + } + ] + } + ] + }); + + this.editRow(0); + this.clock.tick(); + + const $secondCell = $(this.getCellElement(0, 1)); + + // assert + assert.notOk($secondCell.hasClass('dx-focused'), 'cell is not focused'); + assert.notOk($secondCell.hasClass('dx-datagrid-invalid'), 'cell is not marked as invalid'); +}); + // T865329 [true, false].forEach(withConfirm => { QUnit.test('Validation should not block deleting newly added row in cell edit mode ' + withConfirm ? '(with confirm)' : '(no confirm)', function(assert) {