Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { validatingModule } from '@ts/grids/grid_core/validating/m_validating';
import { validatingModule } from '@ts/grids/grid_core/validating/validating_module';

import gridCore from '../m_core';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import type {
import gridCoreUtils from '@ts/grids/grid_core/m_utils';
import type { SelectionController } from '@ts/grids/grid_core/selection/m_selection';
import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core';
import type { ValidatingController } from '@ts/grids/grid_core/validating/m_validating';
import type { VirtualScrollController } from '@ts/grids/grid_core/virtual_scrolling/m_virtual_scrolling_core';

import { DataHelperMixin } from './data_helper_mixin';
Expand Down Expand Up @@ -154,8 +153,6 @@ export class DataController extends DataHelperMixin(modules.Controller) {

protected _stateStoringController!: StateStoringController;

protected _validatingController!: ValidatingController;

private loadErrorHandlerProxy!: (e: Error | string) => void;

private dataPushedHandlerProxy!: (changes: StoreChange[]) => void;
Expand All @@ -177,7 +174,6 @@ export class DataController extends DataHelperMixin(modules.Controller) {
this._headerFilterController = this.getController('headerFilter');
this._selectionController = this.getController('selection');
this._stateStoringController = this.getController('stateStoring');
this._validatingController = this.getController('validating');

this._isPaging = false;
this._currentOperationTypes = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isDefined } from '@js/core/utils/type';

export const INVALIDATE_CLASS = 'invalid';

export const VALIDATION_STATUS = {
valid: 'valid',
invalid: 'invalid',
pending: 'pending',
} as const;

export type ValidationStatus = typeof VALIDATION_STATUS[keyof typeof VALIDATION_STATUS];

export const VALIDATION_CANCELLED = 'cancel';

export interface CellValidationResult {
status?: ValidationStatus;
disabledPendingId?: unknown;
}

export const validationResultIsValid = (
result: unknown,
): result is CellValidationResult => isDefined(result) && result !== VALIDATION_CANCELLED;
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import $ from '@js/core/renderer';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type { Cell, ProcessedItem } from '@ts/grids/grid_core/data_controller/types';
import type { ModuleType } from '@ts/grids/grid_core/m_types';

import type { CellValidationResult, ValidationStatus } from '../const';
import {
INVALIDATE_CLASS,
VALIDATION_STATUS,
validationResultIsValid,
} from '../const';

type ValidationResult = CellValidationResult | string | undefined;

interface ValidationData {
isValid?: boolean;
}

type ValidatedCell = Cell & {
validationStatus?: ValidationStatus;
cellElement?: Element;
};

interface ValidatingControllerReader {
getCellValidationResult: (options: { rowKey: unknown; columnIndex: number }) => ValidationResult;
getValidationData: (key: unknown) => ValidationData | undefined;
}

export const validatingDataControllerExtender = (
Base: ModuleType<DataController>,
): ModuleType<DataController> => class ValidatingDataControllerExtender extends Base {
protected _validatingController!: ValidatingControllerReader;

public init(): void {
this._validatingController = this.getController('validating');
super.init();
}

private _getValidationStatus(validationResult: ValidationResult): string {
if (!validationResultIsValid(validationResult)) {
return validationResult ?? VALIDATION_STATUS.valid;
}

return validationResult.status ?? VALIDATION_STATUS.valid;
}

private _isRowEditStateChanged(
oldRow: ProcessedItem,
newRow: ProcessedItem,
columnIndex: number,
): boolean {
const cell = oldRow.cells?.[columnIndex];
const hasValidationRules = !!cell?.column?.validationRules?.length;

return oldRow.isEditing !== newRow.isEditing && hasValidationRules;
}

private _isCellValidationStateChanged(
oldRow: ProcessedItem,
newRow: ProcessedItem,
columnIndex: number,
): boolean {
const cell = oldRow.cells?.[columnIndex] as ValidatedCell | undefined;

const oldValidationStatus = this._getValidationStatus({ status: cell?.validationStatus });
const newValidationStatus = this._getValidationStatus(
this._validatingController.getCellValidationResult({ rowKey: oldRow.key, columnIndex }),
);
const rowIsModified = JSON.stringify(newRow.modifiedValues)
!== JSON.stringify(oldRow.modifiedValues);
const validationStatusChanged = oldValidationStatus !== newValidationStatus && rowIsModified;

if (validationStatusChanged) {
return true;
}

const validationData = this._validatingController.getValidationData(oldRow.key);
const cellIsMarkedAsInvalid = $(cell?.cellElement)
.hasClass(this.addWidgetPrefix(INVALIDATE_CLASS));

return !!validationData?.isValid && cellIsMarkedAsInvalid;
}
Comment thread
anna-shakhova marked this conversation as resolved.

protected _isCellChanged(
oldRow: ProcessedItem,
newRow: ProcessedItem,
visibleRowIndex: number,
columnIndex: number,
isLiveUpdate?: boolean,
): boolean {
const rowEditStateChanged = this._isRowEditStateChanged(oldRow, newRow, columnIndex);
const cellValidationStateChanged = this._isCellValidationStateChanged(
oldRow,
newRow,
columnIndex,
);

if (rowEditStateChanged || cellValidationStateChanged) {
return true;
}

return super._isCellChanged(oldRow, newRow, visibleRowIndex, columnIndex, isLiveUpdate);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import Validator from '@js/ui/validator';
import errors from '@js/ui/widget/ui.errors';
import { focused } from '@ts/core/utils/m_selectors';
import type { ColumnsController } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type { EditorFactory } from '@ts/grids/grid_core/editor_factory/m_editor_factory';
import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view';

Expand All @@ -37,8 +36,13 @@ import type { NormalizedEditCellOptions } from '../editing/types';
import modules from '../m_modules';
import type { ModuleType } from '../m_types';
import gridCoreUtils from '../m_utils';
import {
INVALIDATE_CLASS,
VALIDATION_CANCELLED,
VALIDATION_STATUS,
validationResultIsValid,
} from './const';

const INVALIDATE_CLASS = 'invalid';
const REVERT_TOOLTIP_CLASS = 'revert-tooltip';
const INVALID_MESSAGE_CLASS = 'dx-invalid-message';
const INVALID_MESSAGE_ID = 'dxInvalidMessage';
Expand All @@ -64,19 +68,8 @@ const FORM_BASED_MODES = [EDIT_MODE_POPUP, EDIT_MODE_FORM];

const COMMAND_TRANSPARENT = 'transparent';

const VALIDATION_STATUS = {
valid: 'valid',
invalid: 'invalid',
pending: 'pending',
};

const EDIT_DATA_INSERT_TYPE = 'insert';
const EDIT_DATA_REMOVE_TYPE = 'remove';
const VALIDATION_CANCELLED = 'cancel';

const validationResultIsValid = function (result) {
return isDefined(result) && result !== VALIDATION_CANCELLED;
};

const cellValueShouldBeValidated = function (value, rowOptions) {
return value !== undefined || (value === undefined && rowOptions && !rowOptions.isNewRow);
Expand Down Expand Up @@ -149,6 +142,10 @@ export class ValidatingController extends modules.Controller {
return !!validationData && !!validationData.validated;
}

public getValidationData(key) {
return this._getValidationData(key);
}

public _getValidationData(key, create?) {
const keyHash = getKeyHash(key);
const isObjectKeyHash = isObject(keyHash);
Expand Down Expand Up @@ -1519,37 +1516,6 @@ export const validatingEditorFactoryExtender = (Base: ModuleType<EditorFactory>)
}
};

export const validatingDataControllerExtender = (Base: ModuleType<DataController>) => class ValidatingDataControllerExtender extends Base {
private _getValidationStatus(validationResult) {
const validationStatus = validationResultIsValid(validationResult) ? validationResult.status : validationResult;

return validationStatus || VALIDATION_STATUS.valid;
}

protected _isCellChanged(oldRow, newRow, visibleRowIndex, columnIndex, isLiveUpdate) {
const cell = oldRow.cells?.[columnIndex];
const oldValidationStatus = this._getValidationStatus({ status: cell?.validationStatus });
const validationResult = this._validatingController.getCellValidationResult({
rowKey: oldRow.key,
columnIndex,
});
const validationData = this._validatingController._getValidationData(oldRow.key);
const newValidationStatus = this._getValidationStatus(validationResult);
const rowIsModified = JSON.stringify(newRow.modifiedValues) !== JSON.stringify(oldRow.modifiedValues);
const validationStatusChanged = oldValidationStatus !== newValidationStatus && rowIsModified;
const cellIsMarkedAsInvalid = $(cell?.cellElement).hasClass(this.addWidgetPrefix(INVALIDATE_CLASS));
const hasValidationRules = cell?.column.validationRules?.length;
const rowEditStateChanged = oldRow.isEditing !== newRow.isEditing && hasValidationRules;
const cellValidationStateChanged = validationStatusChanged || validationData.isValid && cellIsMarkedAsInvalid;

if (rowEditStateChanged || cellValidationStateChanged) {
return true;
}

return super._isCellChanged.apply(this, arguments as any);
}
};

export const validatingRowsViewExtender = (Base: ModuleType<RowsView>) => class ValidatingRowsViewExtender extends Base {
public updateFreeSpaceRowHeight($table) {
const that = this;
Expand Down Expand Up @@ -1621,28 +1587,3 @@ export const validatingRowsViewExtender = (Base: ModuleType<RowsView>) => class
});
}
};

export const validatingModule = {
defaultOptions() {
return {
editing: {
texts: {
validationCancelChanges: messageLocalization.format('dxDataGrid-validationCancelChanges'),
},
},
};
},
controllers: {
validating: ValidatingController,
},
extenders: {
controllers: {
editing: validatingEditingExtender,
editorFactory: validatingEditorFactoryExtender,
data: validatingDataControllerExtender,
},
views: {
rowsView: validatingRowsViewExtender,
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import messageLocalization from '@js/common/core/localization/message';
import type { EditingTextsBase } from '@js/common/grids';

import { validatingDataControllerExtender } from './extenders/validating_data_controller';
import {
ValidatingController,
validatingEditingExtender,
validatingEditorFactoryExtender,
validatingRowsViewExtender,
} from './m_validating';

interface ValidatingModuleOptions {
editing: { texts: Pick<EditingTextsBase, 'validationCancelChanges'> };
}

export const validatingModule = {
defaultOptions(): ValidatingModuleOptions {
return {
editing: {
texts: {
validationCancelChanges: messageLocalization.format('dxDataGrid-validationCancelChanges'),
},
},
};
},
controllers: {
validating: ValidatingController,
},
extenders: {
controllers: {
editing: validatingEditingExtender,
editorFactory: validatingEditorFactoryExtender,
data: validatingDataControllerExtender,
},
views: {
rowsView: validatingRowsViewExtender,
},
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { validatingModule } from '@ts/grids/grid_core/validating/m_validating';
import { validatingModule } from '@ts/grids/grid_core/validating/validating_module';

import type { EditingController } from '../grid_core/editing/m_editing';
import type { ModuleType } from '../grid_core/m_types';
Expand Down
Loading