diff --git a/.github/workflows/testcafe_tests.yml b/.github/workflows/testcafe_tests.yml index 71ec7a6607a9..9990a5113e64 100644 --- a/.github/workflows/testcafe_tests.yml +++ b/.github/workflows/testcafe_tests.yml @@ -90,8 +90,6 @@ jobs: { componentFolder: "scheduler", name: "scheduler (3/5)", indices: "3/5" }, { componentFolder: "scheduler", name: "scheduler (4/5)", indices: "4/5" }, { componentFolder: "scheduler", name: "scheduler (5/5)", indices: "5/5" }, - { componentFolder: "scheduler/timezones", name: "scheduler (Europe/Berlin)", timezone: "Europe/Berlin" }, - { componentFolder: "scheduler/timezones", name: "scheduler (America/Los_Angeles)", timezone: "America/Los_Angeles" }, { componentFolder: "form", name: "form (1/2)", indices: "1/2" }, { componentFolder: "form", name: "form (2/2)", indices: "2/2" }, { componentFolder: "form", name: "form - material (1/2)", theme: 'material.blue.light', indices: "1/2" }, @@ -121,11 +119,6 @@ jobs: timeout-minutes: 90 steps: - - name: Set machine timezone - run: | - sudo ln -sf "/usr/share/zoneinfo/$([ "${{ matrix.ARGS.timezone }}" != "" ] && echo "${{ matrix.ARGS.timezone }}" || echo "GMT")" /etc/localtime - date - - name: Get sources uses: actions/checkout@v4 diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts index 359ba0d9c9da..83c870e33e30 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts @@ -1965,7 +1965,7 @@ class EditingControllerImpl extends modules.ViewController { } } - _needUpdateRow(column?) { + _needUpdateRow(column) { const visibleColumns = this._columnsController.getVisibleColumns(); if (!column) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts index a7c78a5f3a31..faeae2f5f79c 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/m_editing_form_based.ts @@ -181,6 +181,7 @@ const editingControllerExtender = (Base: ModuleType) => class .appendTo(this.component.$element()) .addClass(editPopupClass); + // @ts-expect-error this._editPopup = this._createComponent($popupContainer, Popup); this._editPopup.on('hiding', this._getEditPopupHiddenHandler()); this._editPopup.on('shown', (e) => { @@ -213,6 +214,7 @@ const editingControllerExtender = (Base: ModuleType) => class return (container) => { const formTemplate = this.getEditFormTemplate(); + // @ts-expect-error const scrollable = this._createComponent($('
').appendTo(container), Scrollable); this._$popupContent = $((scrollable as any).content()); diff --git a/packages/devextreme/js/__internal/grids/grid_core/header_panel/m_header_panel.ts b/packages/devextreme/js/__internal/grids/grid_core/header_panel/m_header_panel.ts index 3fe46d7d4c0f..61d10c963c76 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/header_panel/m_header_panel.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/header_panel/m_header_panel.ts @@ -119,7 +119,7 @@ export class HeaderPanel extends ColumnsView { $headerPanel.addClass(this.addWidgetPrefix(HEADER_PANEL_CLASS)); const label = messageLocalization.format(this.component.NAME + TOOLBAR_ARIA_LABEL); const $toolbar = $('
').attr('aria-label', label).appendTo($headerPanel); - this._toolbar = this._createComponent($toolbar, Toolbar, this._toolbarOptions); + this._toolbar = this._createComponent($toolbar, Toolbar, this._toolbarOptions!); } else { this._toolbar.option(this._toolbarOptions!); } diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts index 9b0a2b5ffe37..4a381a6e630c 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_types.ts @@ -62,7 +62,7 @@ export interface InternalGrid extends GridBaseType { _createComponent: >( $container: dxElementWrapper, component: new (...args) => TComponent, - options?: TComponent extends Component ? TOptions : never + options: TComponent extends Component ? TOptions : never ) => TComponent; } diff --git a/packages/devextreme/js/__internal/grids/grid_core/validating/m_validating.ts b/packages/devextreme/js/__internal/grids/grid_core/validating/m_validating.ts index 18f520c7c0b7..b717ee67ca6d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/validating/m_validating.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/validating/m_validating.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable max-classes-per-file */ import $ from '@js/core/renderer'; import browser from '@js/core/utils/browser'; // @ts-expect-error @@ -28,9 +26,7 @@ import { focused } from '@js/ui/widget/selectors'; import errors from '@js/ui/widget/ui.errors'; import { EDITORS_INPUT_SELECTOR } from '../editing/const'; -import type { EditingController } from '../editing/m_editing'; import modules from '../m_modules'; -import type { ModuleType } from '../m_types'; import gridCoreUtils from '../m_utils'; const INVALIDATE_CLASS = 'invalid'; @@ -77,557 +73,546 @@ const cellValueShouldBeValidated = function (value, rowOptions) { return value !== undefined || (value === undefined && rowOptions && !rowOptions.isNewRow); }; -class ValidatingController extends modules.Controller { - _isValidationInProgress = false; +const ValidatingController = modules.Controller.inherit((function () { + return { + init() { + this._editingController = this.getController('editing'); + this.createAction('onRowValidating'); - _disableApplyValidationResults = false; - - _editingController: any; + if (!this._validationState) { + this.initValidationState(); + } + }, - _validationState: any; + initValidationState() { + this._validationState = []; + this._validationStateCache = {}; + }, - _validationStateCache: any; + _rowIsValidated(change) { + const validationData = this._getValidationData(change?.key); - _currentCellValidator: any; + return !!validationData && !!validationData.validated; + }, - init() { - this._editingController = this.getController('editing'); - this.createAction('onRowValidating'); + _getValidationData(key, create) { + const keyHash = getKeyHash(key); + const isObjectKeyHash = isObject(keyHash); + let validationData; - if (!this._validationState) { - this.initValidationState(); - } - } + if (isObjectKeyHash) { + // eslint-disable-next-line prefer-destructuring + validationData = this._validationState.filter((data) => equalByValue(data.key, key))[0]; + } else { + validationData = this._validationStateCache[keyHash]; + } - initValidationState() { - this._validationState = []; - this._validationStateCache = {}; - } + if (!validationData && create) { + validationData = { key, isValid: true }; + this._validationState.push(validationData); + if (!isObjectKeyHash) { + this._validationStateCache[keyHash] = validationData; + } + } - _rowIsValidated(change) { - const validationData = this._getValidationData(change?.key); + return validationData; + }, - return !!validationData && !!validationData.validated; - } + _getBrokenRules(validationData, validationResults) { + let brokenRules; - _getValidationData(key, create?) { - const keyHash = getKeyHash(key); - const isObjectKeyHash = isObject(keyHash); - let validationData; + if (validationResults) { + brokenRules = validationResults.brokenRules || validationResults.brokenRule && [validationResults.brokenRule]; + } else { + brokenRules = validationData.brokenRules || []; + } - if (isObjectKeyHash) { - // eslint-disable-next-line prefer-destructuring - validationData = this._validationState.filter((data) => equalByValue(data.key, key))[0]; - } else { - validationData = this._validationStateCache[keyHash]; - } + return brokenRules; + }, - if (!validationData && create) { - validationData = { key, isValid: true }; - this._validationState.push(validationData); - if (!isObjectKeyHash) { - this._validationStateCache[keyHash] = validationData; - } - } - - return validationData; - } - - _getBrokenRules(validationData, validationResults) { - let brokenRules; - - if (validationResults) { - brokenRules = validationResults.brokenRules || validationResults.brokenRule && [validationResults.brokenRule]; - } else { - brokenRules = validationData.brokenRules || []; - } - - return brokenRules; - } - - _rowValidating(validationData, validationResults) { - // @ts-expect-error - const deferred = new Deferred(); - const change = this._editingController.getChangeByKey(validationData?.key); - const brokenRules = this._getBrokenRules(validationData, validationResults); - const isValid = validationResults ? validationResults.isValid : validationData.isValid; - const parameters = { - brokenRules, - isValid, - key: change.key, - newData: change.data, - oldData: this._editingController._getOldData(change.key), - promise: null, - errorText: this.getHiddenValidatorsErrorText(brokenRules), - }; + _rowValidating(validationData, validationResults) { + // @ts-expect-error + const deferred = new Deferred(); + const change = this._editingController.getChangeByKey(validationData?.key); + const brokenRules = this._getBrokenRules(validationData, validationResults); + const isValid = validationResults ? validationResults.isValid : validationData.isValid; + const parameters = { + brokenRules, + isValid, + key: change.key, + newData: change.data, + oldData: this._editingController._getOldData(change.key), + promise: null, + errorText: this.getHiddenValidatorsErrorText(brokenRules), + }; - this.executeAction('onRowValidating', parameters as any); + this.executeAction('onRowValidating', parameters); - when(fromPromise(parameters.promise)).always(() => { - validationData.isValid = parameters.isValid; - validationData.errorText = parameters.errorText; - deferred.resolve(parameters); - }); + when(fromPromise(parameters.promise)).always(() => { + validationData.isValid = parameters.isValid; + validationData.errorText = parameters.errorText; + deferred.resolve(parameters); + }); - return deferred.promise(); - } + return deferred.promise(); + }, - getHiddenValidatorsErrorText(brokenRules) { - const brokenRulesMessages: any[] = []; + getHiddenValidatorsErrorText(brokenRules) { + const brokenRulesMessages: any[] = []; - each(brokenRules, (_, brokenRule) => { - const { column } = brokenRule; - const isGroupExpandColumn = column && column.groupIndex !== undefined && !column.showWhenGrouped; - const isVisibleColumn = column && column.visible; + each(brokenRules, (_, brokenRule) => { + const { column } = brokenRule; + const isGroupExpandColumn = column && column.groupIndex !== undefined && !column.showWhenGrouped; + const isVisibleColumn = column && column.visible; - if (!brokenRule.validator.$element().parent().length && (!isVisibleColumn || isGroupExpandColumn)) { - brokenRulesMessages.push(brokenRule.message); - } - }); - return brokenRulesMessages.join(', '); - } - - validate(isFull) { - let isValid = true; - const editingController = this._editingController; - // @ts-expect-error - const deferred = new Deferred(); - const completeList: any[] = []; - - const editMode = editingController.getEditMode(); - isFull = isFull || editMode === EDIT_MODE_ROW; - - if (this._isValidationInProgress) { - return deferred.resolve(false).promise(); - } - - this._isValidationInProgress = true; - if (isFull) { - editingController.addDeferred(deferred); - const changes = editingController.getChanges(); - each(changes, (index, { type, key }) => { - if (type !== 'remove') { - const validationData = this._getValidationData(key, true); - const validationResult = this.validateGroup(validationData); - completeList.push(validationResult); - validationResult.done((validationResult) => { - validationData.validated = true; - isValid = isValid && validationResult.isValid; - }); + if (!brokenRule.validator.$element().parent().length && (!isVisibleColumn || isGroupExpandColumn)) { + brokenRulesMessages.push(brokenRule.message); } }); - } else if (this._currentCellValidator) { - const validationResult = this.validateGroup(this._currentCellValidator._findGroup()); - completeList.push(validationResult); - validationResult.done((validationResult) => { - isValid = validationResult.isValid; + return brokenRulesMessages.join(', '); + }, + + validate(isFull) { + let isValid = true; + const editingController = this._editingController; + // @ts-expect-error + const deferred = new Deferred(); + const completeList: any[] = []; + + const editMode = editingController.getEditMode(); + isFull = isFull || editMode === EDIT_MODE_ROW; + + if (this._isValidationInProgress) { + return deferred.resolve(false).promise(); + } + + this._isValidationInProgress = true; + if (isFull) { + editingController.addDeferred(deferred); + const changes = editingController.getChanges(); + each(changes, (index, { type, key }) => { + if (type !== 'remove') { + const validationData = this._getValidationData(key, true); + const validationResult = this.validateGroup(validationData); + completeList.push(validationResult); + validationResult.done((validationResult) => { + validationData.validated = true; + isValid = isValid && validationResult.isValid; + }); + } + }); + } else if (this._currentCellValidator) { + const validationResult = this.validateGroup(this._currentCellValidator._findGroup()); + completeList.push(validationResult); + validationResult.done((validationResult) => { + isValid = validationResult.isValid; + }); + } + + when(...completeList).done(() => { + this._isValidationInProgress = false; + deferred.resolve(isValid); }); - } - when(...completeList).done(() => { - this._isValidationInProgress = false; - deferred.resolve(isValid); - }); + return deferred.promise(); + }, - return deferred.promise(); - } + validateGroup(validationData) { + // @ts-expect-error + const result = new Deferred(); + const validateGroup = validationData && ValidationEngine.getGroupConfig(validationData); + let validationResult; - validateGroup(validationData) { - // @ts-expect-error - const result = new Deferred(); - const validateGroup = validationData && ValidationEngine.getGroupConfig(validationData); - let validationResult; + if (validateGroup?.validators.length) { + this.resetRowValidationResults(validationData); + validationResult = ValidationEngine.validateGroup(validationData); + } - if (validateGroup?.validators.length) { - this.resetRowValidationResults(validationData); - validationResult = ValidationEngine.validateGroup(validationData); - } + when(validationResult?.complete || validationResult).done((validationResult) => { + when(this._rowValidating(validationData, validationResult)).done(result.resolve); + }); - when(validationResult?.complete || validationResult).done((validationResult) => { - when(this._rowValidating(validationData, validationResult)).done(result.resolve); - }); + return result.promise(); + }, - return result.promise(); - } + isRowDataModified(change) { + return !isEmptyObject(change.data); + }, - isRowDataModified(change) { - return !isEmptyObject(change.data); - } + updateValidationState(change) { + const editMode = this._editingController.getEditMode(); + const { key } = change; + const validationData = this._getValidationData(key, true); - updateValidationState(change) { - const editMode = this._editingController.getEditMode(); - const { key } = change; - const validationData = this._getValidationData(key, true); + if (!FORM_BASED_MODES.includes(editMode)) { + if (change.type === EDIT_DATA_INSERT_TYPE && !this.isRowDataModified(change)) { + validationData.isValid = true; + return; + } - if (!FORM_BASED_MODES.includes(editMode)) { - if (change.type === EDIT_DATA_INSERT_TYPE && !this.isRowDataModified(change)) { + this.setDisableApplyValidationResults(true); + const groupConfig = ValidationEngine.getGroupConfig(validationData); + if (groupConfig) { + const validationResult = ValidationEngine.validateGroup(validationData); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + when(validationResult.complete || validationResult).done((validationResult) => { + // @ts-expect-error + validationData.isValid = validationResult.isValid; + // @ts-expect-error + validationData.brokenRules = validationResult.brokenRules; + }); + } else if (!validationData.brokenRules || !validationData.brokenRules.length) { + validationData.isValid = true; + } + this.setDisableApplyValidationResults(false); + } else { validationData.isValid = true; - return; } + }, - this.setDisableApplyValidationResults(true); - const groupConfig = ValidationEngine.getGroupConfig(validationData); - if (groupConfig) { - const validationResult = ValidationEngine.validateGroup(validationData); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - when(validationResult.complete || validationResult).done((validationResult) => { - // @ts-expect-error - validationData.isValid = validationResult.isValid; - // @ts-expect-error - validationData.brokenRules = validationResult.brokenRules; - }); - } else if (!validationData.brokenRules || !validationData.brokenRules.length) { - validationData.isValid = true; + setValidator(validator) { + this._currentCellValidator = validator; + }, + + renderCellPendingIndicator($container) { + let $indicator = $container.find(`.${PENDING_INDICATOR_CLASS}`); + if (!$indicator.length) { + const $indicatorContainer = $container; + $indicator = $('
').appendTo($indicatorContainer) + .addClass(PENDING_INDICATOR_CLASS); + this._createComponent($indicator, LoadIndicator); + $container.addClass(VALIDATION_PENDING_CLASS); } - this.setDisableApplyValidationResults(false); - } else { - validationData.isValid = true; - } - } - - setValidator(validator) { - this._currentCellValidator = validator; - } - - renderCellPendingIndicator($container) { - let $indicator = $container.find(`.${PENDING_INDICATOR_CLASS}`); - if (!$indicator.length) { - const $indicatorContainer = $container; - $indicator = $('
').appendTo($indicatorContainer) - .addClass(PENDING_INDICATOR_CLASS); - this._createComponent($indicator, LoadIndicator); - $container.addClass(VALIDATION_PENDING_CLASS); - } - } - - disposeCellPendingIndicator($container) { - const $indicator = $container.find(`.${PENDING_INDICATOR_CLASS}`); - if ($indicator.length) { - const indicator = LoadIndicator.getInstance($indicator); - if (indicator) { - indicator.dispose(); - indicator.$element().remove(); + }, + + disposeCellPendingIndicator($container) { + const $indicator = $container.find(`.${PENDING_INDICATOR_CLASS}`); + if ($indicator.length) { + const indicator = LoadIndicator.getInstance($indicator); + if (indicator) { + indicator.dispose(); + indicator.$element().remove(); + } + $container.removeClass(VALIDATION_PENDING_CLASS); } - $container.removeClass(VALIDATION_PENDING_CLASS); - } - } - - validationStatusChanged(result) { - const { validator } = result; - const validationGroup = validator.option('validationGroup'); - const { column } = validator.option('dataGetter')(); - - this.updateCellValidationResult({ - rowKey: validationGroup.key, - columnIndex: column.index, - validationResult: result, - }); - } - - validatorInitialized(arg) { - arg.component.on('validating', this.validationStatusChanged.bind(this)); - arg.component.on('validated', this.validationStatusChanged.bind(this)); - } - - validatorDisposing(arg) { - const validator = arg.component; - const validationGroup = validator.option('validationGroup'); - const { column } = validator.option('dataGetter')(); - - const result = this.getCellValidationResult({ - rowKey: validationGroup?.key, - columnIndex: column.index, - }); - if (validationResultIsValid(result) && result.status === VALIDATION_STATUS.pending) { - this.cancelCellValidationResult({ - change: validationGroup, + }, + + validationStatusChanged(result) { + const { validator } = result; + const validationGroup = validator.option('validationGroup'); + const { column } = validator.option('dataGetter')(); + + this.updateCellValidationResult({ + rowKey: validationGroup.key, columnIndex: column.index, + validationResult: result, }); - } - } - - applyValidationResult($container, result) { - const { validator } = result; - const validationGroup = validator.option('validationGroup'); - const { column } = validator.option('dataGetter')(); - - result.brokenRules && result.brokenRules.forEach((rule) => { - rule.columnIndex = column.index; - rule.column = column; - }); - if ($container) { - const validationResult = this.getCellValidationResult({ - rowKey: validationGroup.key, + }, + + validatorInitialized(arg) { + arg.component.on('validating', this.validationStatusChanged.bind(this)); + arg.component.on('validated', this.validationStatusChanged.bind(this)); + }, + + validatorDisposing(arg) { + const validator = arg.component; + const validationGroup = validator.option('validationGroup'); + const { column } = validator.option('dataGetter')(); + + const result = this.getCellValidationResult({ + rowKey: validationGroup?.key, columnIndex: column.index, }); - const requestIsDisabled = validationResultIsValid(validationResult) && validationResult.disabledPendingId === result.id; - if (this._disableApplyValidationResults || requestIsDisabled) { - return; + if (validationResultIsValid(result) && result.status === VALIDATION_STATUS.pending) { + this.cancelCellValidationResult({ + change: validationGroup, + columnIndex: column.index, + }); } - if (result.status === VALIDATION_STATUS.invalid) { - const $focus = $container.find(':focus'); - if (!focused($focus)) { - // @ts-expect-error - eventsEngine.trigger($focus, 'focus'); - // @ts-expect-error - eventsEngine.trigger($focus, pointerEvents.down); + }, + + applyValidationResult($container, result) { + const { validator } = result; + const validationGroup = validator.option('validationGroup'); + const { column } = validator.option('dataGetter')(); + + result.brokenRules && result.brokenRules.forEach((rule) => { + rule.columnIndex = column.index; + rule.column = column; + }); + if ($container) { + const validationResult = this.getCellValidationResult({ + rowKey: validationGroup.key, + columnIndex: column.index, + }); + const requestIsDisabled = validationResultIsValid(validationResult) && validationResult.disabledPendingId === result.id; + if (this._disableApplyValidationResults || requestIsDisabled) { + return; } - } - const editor = !column.editCellTemplate && this.getController('editorFactory').getEditorInstance($container); - if (result.status === VALIDATION_STATUS.pending) { - if (editor) { - editor.option('validationStatus', VALIDATION_STATUS.pending); + if (result.status === VALIDATION_STATUS.invalid) { + const $focus = $container.find(':focus'); + if (!focused($focus)) { + // @ts-expect-error + eventsEngine.trigger($focus, 'focus'); + // @ts-expect-error + eventsEngine.trigger($focus, pointerEvents.down); + } + } + const editor = !column.editCellTemplate && this.getController('editorFactory').getEditorInstance($container); + if (result.status === VALIDATION_STATUS.pending) { + if (editor) { + editor.option('validationStatus', VALIDATION_STATUS.pending); + } else { + this.renderCellPendingIndicator($container); + } + } else if (editor) { + editor.option('validationStatus', VALIDATION_STATUS.valid); } else { - this.renderCellPendingIndicator($container); + this.disposeCellPendingIndicator($container); } - } else if (editor) { - editor.option('validationStatus', VALIDATION_STATUS.valid); - } else { - this.disposeCellPendingIndicator($container); + $container.toggleClass(this.addWidgetPrefix(INVALIDATE_CLASS), result.status === VALIDATION_STATUS.invalid); } - $container.toggleClass(this.addWidgetPrefix(INVALIDATE_CLASS), result.status === VALIDATION_STATUS.invalid); - } - } - - _syncInternalEditingData(parameters) { - const editingController = this._editingController; - const change = editingController.getChangeByKey(parameters.key); - const oldDataFromState = editingController._getOldData(parameters.key); - const oldData = parameters.row?.oldData; - - if (change && oldData && !oldDataFromState) { - editingController._addInternalData({ key: parameters.key, oldData }); - } - } - - createValidator(parameters, $container) { - const editingController = this._editingController; - const { column } = parameters; - let { showEditorAlways } = column; - - if (isDefined(column.command) || !column.validationRules || !Array.isArray(column.validationRules) || !column.validationRules.length) return; - - const editIndex = editingController.getIndexByKey(parameters.key, editingController.getChanges()); - let needCreateValidator = editIndex > -1; - - if (!needCreateValidator) { - if (!showEditorAlways) { - const columnsController = this.getController('columns'); - const visibleColumns = columnsController?.getVisibleColumns() || []; - showEditorAlways = visibleColumns.some((column) => column.showEditorAlways); + }, + + _syncInternalEditingData(parameters) { + const editingController = this._editingController; + const change = editingController.getChangeByKey(parameters.key); + const oldDataFromState = editingController._getOldData(parameters.key); + const oldData = parameters.row?.oldData; + + if (change && oldData && !oldDataFromState) { + editingController._addInternalData({ key: parameters.key, oldData }); } + }, - const isEditRow = equalByValue(this.option('editing.editRowKey'), parameters.key); - const isCellOrBatchEditingAllowed = editingController.isCellOrBatchEditMode() && editingController.allowUpdating({ row: parameters.row }); + createValidator(parameters, $container) { + const editingController = this._editingController; + const { column } = parameters; + let { showEditorAlways } = column; - needCreateValidator = isEditRow || isCellOrBatchEditingAllowed && showEditorAlways; + if (isDefined(column.command) || !column.validationRules || !Array.isArray(column.validationRules) || !column.validationRules.length) return; - if (isCellOrBatchEditingAllowed && showEditorAlways) { - editingController._addInternalData({ key: parameters.key, oldData: parameters.row?.oldData ?? parameters.data }); - } - } + const editIndex = editingController.getIndexByKey(parameters.key, editingController.getChanges()); + let needCreateValidator = editIndex > -1; - if (needCreateValidator) { - if ($container && !$container.length) { - errors.log('E1050'); - return; + if (!needCreateValidator) { + if (!showEditorAlways) { + const columnsController = this.getController('columns'); + const visibleColumns = columnsController?.getVisibleColumns() || []; + showEditorAlways = visibleColumns.some((column) => column.showEditorAlways); + } + + const isEditRow = equalByValue(this.option('editing.editRowKey'), parameters.key); + const isCellOrBatchEditingAllowed = editingController.isCellOrBatchEditMode() && editingController.allowUpdating({ row: parameters.row }); + + needCreateValidator = isEditRow || isCellOrBatchEditingAllowed && showEditorAlways; + + if (isCellOrBatchEditingAllowed && showEditorAlways) { + editingController._addInternalData({ key: parameters.key, oldData: parameters.row?.oldData ?? parameters.data }); + } } - this._syncInternalEditingData(parameters); - const validationData = this._getValidationData(parameters.key, true); + if (needCreateValidator) { + if ($container && !$container.length) { + errors.log('E1050'); + return; + } - const getValue = () => { - const change = editingController.getChangeByKey(validationData?.key); - const value = column.calculateCellValue(change?.data || {}); - return value !== undefined ? value : parameters.value; - }; + this._syncInternalEditingData(parameters); + const validationData = this._getValidationData(parameters.key, true); - const useDefaultValidator = $container && $container.hasClass('dx-widget'); - $container && $container.addClass(this.addWidgetPrefix(VALIDATOR_CLASS)); - const validator = new Validator($container || $('
'), { - name: column.caption, - validationRules: extend(true, [], column.validationRules), - validationGroup: validationData, - // @ts-expect-error - adapter: useDefaultValidator ? null : { - getValue, - applyValidationResults: (result) => { - this.applyValidationResult($container, result); - }, - }, - dataGetter() { - const key = validationData?.key; - const change = editingController.getChangeByKey(key); - const oldData = editingController._getOldData(key); - return { - data: createObjectWithChanges(oldData, change?.data), - column, - }; - }, - onInitialized: this.validatorInitialized.bind(this), - onDisposing: this.validatorDisposing.bind(this), - }); - if (useDefaultValidator) { - const adapter = validator.option('adapter'); - if (adapter) { - const originBypass = adapter.bypass; - const defaultAdapterBypass = () => parameters.row.isNewRow && !this._isValidationInProgress && !editingController.isCellModified(parameters); - - adapter.getValue = getValue; - adapter.validationRequestsCallbacks = []; + const getValue = () => { + const change = editingController.getChangeByKey(validationData?.key); + const value = column.calculateCellValue(change?.data || {}); + return value !== undefined ? value : parameters.value; + }; + + const useDefaultValidator = $container && $container.hasClass('dx-widget'); + $container && $container.addClass(this.addWidgetPrefix(VALIDATOR_CLASS)); + const validator = new Validator($container || $('
'), { + name: column.caption, + validationRules: extend(true, [], column.validationRules), + validationGroup: validationData, // @ts-expect-error - adapter.bypass = () => originBypass.call(adapter) || defaultAdapterBypass(); + adapter: useDefaultValidator ? null : { + getValue, + applyValidationResults: (result) => { + this.applyValidationResult($container, result); + }, + }, + dataGetter() { + const key = validationData?.key; + const change = editingController.getChangeByKey(key); + const oldData = editingController._getOldData(key); + return { + data: createObjectWithChanges(oldData, change?.data), + column, + }; + }, + onInitialized: this.validatorInitialized.bind(this), + onDisposing: this.validatorDisposing.bind(this), + }); + if (useDefaultValidator) { + const adapter = validator.option('adapter'); + if (adapter) { + const originBypass = adapter.bypass; + const defaultAdapterBypass = () => parameters.row.isNewRow && !this._isValidationInProgress && !editingController.isCellModified(parameters); + + adapter.getValue = getValue; + adapter.validationRequestsCallbacks = []; + // @ts-expect-error + adapter.bypass = () => originBypass.call(adapter) || defaultAdapterBypass(); + } } - } - return validator; - } + return validator; + } - return undefined; - } + return undefined; + }, - setDisableApplyValidationResults(flag) { - this._disableApplyValidationResults = flag; - } + setDisableApplyValidationResults(flag) { + this._disableApplyValidationResults = flag; + }, - getDisableApplyValidationResults() { - return this._disableApplyValidationResults; - } + getDisableApplyValidationResults() { + return this._disableApplyValidationResults; + }, - isCurrentValidatorProcessing({ rowKey, columnIndex }) { - return this._currentCellValidator && equalByValue(this._currentCellValidator.option('validationGroup').key, rowKey) + isCurrentValidatorProcessing({ rowKey, columnIndex }) { + return this._currentCellValidator && equalByValue(this._currentCellValidator.option('validationGroup').key, rowKey) && this._currentCellValidator.option('dataGetter')().column.index === columnIndex; - } + }, - validateCell(validator) { - const cellParams = { - rowKey: validator.option('validationGroup').key, - columnIndex: validator.option('dataGetter')().column.index, - validationResult: null, - }; - let validationResult = this.getCellValidationResult(cellParams); - const stateRestored = validationResultIsValid(validationResult); - const adapter = validator.option('adapter'); - if (!stateRestored) { - validationResult = validator.validate(); - } else { - const currentCellValue = adapter.getValue(); - if (!equalByValue(currentCellValue, validationResult.value)) { + validateCell(validator) { + const cellParams = { + rowKey: validator.option('validationGroup').key, + columnIndex: validator.option('dataGetter')().column.index, + }; + let validationResult = this.getCellValidationResult(cellParams); + const stateRestored = validationResultIsValid(validationResult); + const adapter = validator.option('adapter'); + if (!stateRestored) { validationResult = validator.validate(); + } else { + const currentCellValue = adapter.getValue(); + if (!equalByValue(currentCellValue, validationResult.value)) { + validationResult = validator.validate(); + } } - } - // @ts-expect-error - const deferred = new Deferred(); - if (stateRestored && validationResult.status === VALIDATION_STATUS.pending) { - this.updateCellValidationResult(cellParams); - adapter.applyValidationResults(validationResult); - } - when(validationResult.complete || validationResult).done((validationResult) => { - stateRestored && adapter.applyValidationResults(validationResult); - deferred.resolve(validationResult); - }); - return deferred.promise(); - } - - updateCellValidationResult({ rowKey, columnIndex, validationResult }) { - const validationData = this._getValidationData(rowKey); - if (!validationData) { - return; - } - if (!validationData.validationResults) { - validationData.validationResults = {}; - } - let result; - if (validationResult) { - result = extend({}, validationResult); - validationData.validationResults[columnIndex] = result; - if (validationResult.status === VALIDATION_STATUS.pending) { - if (this._editingController.getEditMode() === EDIT_MODE_CELL) { - // @ts-expect-error - result.deferred = new Deferred(); - result.complete.always(() => { - result.deferred.resolve(); - }); - this._editingController.addDeferred(result.deferred); + // @ts-expect-error + const deferred = new Deferred(); + if (stateRestored && validationResult.status === VALIDATION_STATUS.pending) { + this.updateCellValidationResult(cellParams); + adapter.applyValidationResults(validationResult); + } + when(validationResult.complete || validationResult).done((validationResult) => { + stateRestored && adapter.applyValidationResults(validationResult); + deferred.resolve(validationResult); + }); + return deferred.promise(); + }, + + updateCellValidationResult({ rowKey, columnIndex, validationResult }) { + const validationData = this._getValidationData(rowKey); + if (!validationData) { + return; + } + if (!validationData.validationResults) { + validationData.validationResults = {}; + } + let result; + if (validationResult) { + result = extend({}, validationResult); + validationData.validationResults[columnIndex] = result; + if (validationResult.status === VALIDATION_STATUS.pending) { + if (this._editingController.getEditMode() === EDIT_MODE_CELL) { + // @ts-expect-error + result.deferred = new Deferred(); + result.complete.always(() => { + result.deferred.resolve(); + }); + this._editingController.addDeferred(result.deferred); + } + if (this._disableApplyValidationResults) { + result.disabledPendingId = validationResult.id; + return; + } } - if (this._disableApplyValidationResults) { - result.disabledPendingId = validationResult.id; - return; + } else { + result = validationData.validationResults[columnIndex]; + } + if (result && result.disabledPendingId) { + delete result.disabledPendingId; + } + }, + + getCellValidationResult({ rowKey, columnIndex }) { + const validationData = this._getValidationData(rowKey, true); + return validationData?.validationResults?.[columnIndex]; + }, + + removeCellValidationResult({ change, columnIndex }) { + const validationData = this._getValidationData(change?.key); + + if (validationData && validationData.validationResults) { + this.cancelCellValidationResult({ change, columnIndex }); + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete validationData.validationResults[columnIndex]; + } + }, + + cancelCellValidationResult({ change, columnIndex }) { + const validationData = this._getValidationData(change.key); + + if (change && validationData.validationResults) { + const result = validationData.validationResults[columnIndex]; + if (result) { + result.deferred && result.deferred.reject(VALIDATION_CANCELLED); + validationData.validationResults[columnIndex] = VALIDATION_CANCELLED; } } - } else { - result = validationData.validationResults[columnIndex]; - } - if (result && result.disabledPendingId) { - delete result.disabledPendingId; - } - } - - getCellValidationResult({ rowKey, columnIndex }) { - const validationData = this._getValidationData(rowKey, true); - return validationData?.validationResults?.[columnIndex]; - } - - removeCellValidationResult({ change, columnIndex }) { - const validationData = this._getValidationData(change?.key); - - if (validationData && validationData.validationResults) { - this.cancelCellValidationResult({ change, columnIndex }); - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete validationData.validationResults[columnIndex]; - } - } - - cancelCellValidationResult({ change, columnIndex }) { - const validationData = this._getValidationData(change.key); - - if (change && validationData.validationResults) { - const result = validationData.validationResults[columnIndex]; - if (result) { - result.deferred && result.deferred.reject(VALIDATION_CANCELLED); - validationData.validationResults[columnIndex] = VALIDATION_CANCELLED; + }, + + resetRowValidationResults(validationData) { + if (validationData) { + validationData.validationResults && delete validationData.validationResults; + delete validationData.validated; } - } - } - - resetRowValidationResults(validationData) { - if (validationData) { - validationData.validationResults && delete validationData.validationResults; - delete validationData.validated; - } - } - - isInvalidCell({ rowKey, columnIndex }) { - const result = this.getCellValidationResult({ - rowKey, - columnIndex, - }); - return validationResultIsValid(result) && result.status === VALIDATION_STATUS.invalid; - } - - getCellValidator({ rowKey, columnIndex }) { - const validationData = this._getValidationData(rowKey); - const groupConfig = validationData && ValidationEngine.getGroupConfig(validationData); - const validators = groupConfig && groupConfig.validators; - return validators && validators.filter((v) => { - const { column } = v.option('dataGetter')(); - return column ? column.index === columnIndex : false; - })[0]; - } - - setCellValidationStatus(cellOptions) { - const validationResult = this.getCellValidationResult({ - rowKey: cellOptions.key, - columnIndex: cellOptions.column.index, - }); - - if (isDefined(validationResult)) { - cellOptions.validationStatus = validationResult !== VALIDATION_CANCELLED ? validationResult.status : VALIDATION_CANCELLED; - } else { - delete cellOptions.validationStatus; - } - } -} + }, + + isInvalidCell({ rowKey, columnIndex }) { + const result = this.getCellValidationResult({ + rowKey, + columnIndex, + }); + return validationResultIsValid(result) && result.status === VALIDATION_STATUS.invalid; + }, + + getCellValidator({ rowKey, columnIndex }) { + const validationData = this._getValidationData(rowKey); + const groupConfig = validationData && ValidationEngine.getGroupConfig(validationData); + const validators = groupConfig && groupConfig.validators; + return validators && validators.filter((v) => { + const { column } = v.option('dataGetter')(); + return column ? column.index === columnIndex : false; + })[0]; + }, + + setCellValidationStatus(cellOptions) { + const validationResult = this.getCellValidationResult({ + rowKey: cellOptions.key, + columnIndex: cellOptions.column.index, + }); + + if (isDefined(validationResult)) { + cellOptions.validationStatus = validationResult !== VALIDATION_CANCELLED ? validationResult.status : VALIDATION_CANCELLED; + } else { + delete cellOptions.validationStatus; + } + }, + }; +})()); export const validatingModule = { defaultOptions() { @@ -644,19 +629,9 @@ export const validatingModule = { }, extenders: { controllers: { - editing: (Base: ModuleType) => class ValidateEditingController extends Base { - processDataItemTreeListHack(item) { - // @ts-expect-error - super.processDataItem.apply(this, arguments); - } - - processItemsTreeListHack(items, e) { - // @ts-expect-error - return super.processItems.apply(this, arguments); - } - + editing: { _addChange(changeParams) { - const change = super._addChange.apply(this, arguments as any); + const change = this.callBase.apply(this, arguments); const validatingController = this.getController('validating'); if (change && changeParams.type !== EDIT_DATA_REMOVE_TYPE) { @@ -664,10 +639,10 @@ export const validatingModule = { } return change; - } + }, _handleChangesChange(args) { - super._handleChangesChange.apply(this, arguments as any); + this.callBase.apply(this, arguments); const validatingController = this.getController('validating'); @@ -676,7 +651,7 @@ export const validatingModule = { validatingController.updateValidationState(change); } }); - } + }, _updateRowAndPageIndices() { const that = this; @@ -695,7 +670,7 @@ export const validatingModule = { rowIndex++; } }); - } + }, _getValidationGroupsInForm(detailOptions) { const validatingController = this.getController('validating'); @@ -704,20 +679,19 @@ export const validatingModule = { return { validationGroup: validationData, }; - } + }, - _validateEditFormAfterUpdate(row?, isCustomSetCellValue?) { + _validateEditFormAfterUpdate(row, isCustomSetCellValue) { // T816256, T844143 if (isCustomSetCellValue && this._editForm) { this._editForm.validate(); } - super._validateEditFormAfterUpdate.apply(this, arguments as any); - } + this.callBase.apply(this, arguments); + }, _prepareEditCell(params) { - // @ts-expect-error - const isNotCanceled = super._prepareEditCell.apply(this, arguments as any); + const isNotCanceled = this.callBase.apply(this, arguments); const validatingController = this.getController('validating'); if (isNotCanceled && params.column.showEditorAlways) { @@ -725,7 +699,7 @@ export const validatingModule = { } return isNotCanceled; - } + }, processItems(items, changeType) { const changes = this.getChanges(); @@ -748,7 +722,7 @@ export const validatingModule = { return index; }; - items = super.processItems(items, changeType); + items = this.callBase(items, changeType); const itemsCount = items.length; const addInValidItem = function (change, validationData) { @@ -777,7 +751,7 @@ export const validatingModule = { } return items; - } + }, processDataItem(item) { const isInserted = item.data[INSERT_INDEX]; @@ -799,8 +773,8 @@ export const validatingModule = { } } - super.processDataItem.apply(this, arguments as any); - } + this.callBase.apply(this, arguments); + }, _createInvisibleColumnValidators(changes) { const that = this; @@ -849,11 +823,11 @@ export const validatingModule = { return function () { invisibleColumnValidators.forEach((validator) => { validator.dispose(); }); }; - } + }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - _beforeSaveEditData(change, editIndex?) { - let result: any = super._beforeSaveEditData.apply(this, arguments as any); + _beforeSaveEditData(change, editIndex) { + let result = this.callBase.apply(this, arguments); const validatingController = this.getController('validating'); const validationData = validatingController._getValidationData(change?.key); @@ -869,7 +843,7 @@ export const validatingModule = { disposeValidators(); this._updateRowAndPageIndices(); - // eslint-disable-next-line default-case, @typescript-eslint/switch-exhaustiveness-check + // eslint-disable-next-line default-case switch (this.getEditMode()) { case EDIT_MODE_CELL: if (!isFullValid) { @@ -889,7 +863,7 @@ export const validatingModule = { }); } return result.promise ? result.promise() : result; - } + }, /** * @param rowIndex Row index @@ -898,8 +872,7 @@ export const validatingModule = { * @returns A deferred object that resolves to a boolean or just a boolean to determine whether to cancel cell editing */ _beforeEditCell(rowIndex: number, columnIndex: number, item: any): DeferredObj | boolean { - // @ts-expect-error - const result = super._beforeEditCell(rowIndex, columnIndex, item); + const result = this.callBase(rowIndex, columnIndex, item); if (this.getEditMode() === EDIT_MODE_CELL) { const $cell = this._rowsView._getCellElement(rowIndex, columnIndex); @@ -919,9 +892,9 @@ export const validatingModule = { } } return false; - } + }, - _afterSaveEditData(cancel?) { + _afterSaveEditData(cancel) { let $firstErrorRow; const isCellEditMode = this.getEditMode() === EDIT_MODE_CELL; @@ -959,7 +932,7 @@ export const validatingModule = { this.getController('validating').initValidationState(); } } - } + }, _handleDataChanged(args) { const validationState = this.getController('validating')._validationState; @@ -974,8 +947,8 @@ export const validatingModule = { }); } - super._handleDataChanged(args); - } + this.callBase(args); + }, resetRowAndPageIndices() { const validationState = this.getController('validating')._validationState; @@ -986,17 +959,16 @@ export const validatingModule = { delete validationData.rowIndex; } }); - } + }, _beforeCancelEditData() { this.getController('validating').initValidationState(); - super._beforeCancelEditData(); - } + this.callBase(); + }, _showErrorRow(change) { let $popupContent; - // @ts-expect-error const errorHandling = this.getController('errorHandling'); const items = this.getController('data').items(); const rowIndex = this.getIndexByKey(change.key, items); @@ -1006,7 +978,7 @@ export const validatingModule = { $popupContent = this.getPopupContent(); return errorHandling && errorHandling.renderErrorRow(validationData?.errorText, rowIndex, $popupContent); } - } + }, updateFieldValue(e) { const validatingController = this.getController('validating'); @@ -1017,7 +989,7 @@ export const validatingModule = { columnIndex: e.column.index, }); - super.updateFieldValue.apply(this, arguments as any).done(() => { + this.callBase.apply(this, arguments).done(() => { const currentValidator = validatingController.getCellValidator({ rowKey: e.key, columnIndex: e.column.index, @@ -1029,10 +1001,10 @@ export const validatingModule = { }); }); return deferred.promise(); - } + }, highlightDataCell($cell, parameters) { - super.highlightDataCell.apply(this, arguments as any); + this.callBase.apply(this, arguments); const validatingController = this.getController('validating'); validatingController.setCellValidationStatus(parameters); @@ -1049,22 +1021,22 @@ export const validatingModule = { }); } } - } + }, getChangeByKey(key) { const changes = this.getChanges(); return changes[gridCoreUtils.getIndexByKey(key, changes)]; - } + }, isCellModified(parameters) { - const cellModified = super.isCellModified(parameters); + const cellModified = this.callBase(parameters); const change = this.getChangeByKey(parameters.key); const isCellInvalid = !!parameters.row && this.getController('validating').isInvalidCell({ rowKey: parameters.key, columnIndex: parameters.column.index, }); return cellModified || (this.getController('validating')._rowIsValidated(change) && isCellInvalid); - } + }, }, editorFactory: (function () { const getWidthOfVisibleCells = function (that, element) { diff --git a/packages/devextreme/js/__internal/grids/tree_list/m_validating.ts b/packages/devextreme/js/__internal/grids/tree_list/m_validating.ts index 84a7db49d1ec..29660970f8db 100644 --- a/packages/devextreme/js/__internal/grids/tree_list/m_validating.ts +++ b/packages/devextreme/js/__internal/grids/tree_list/m_validating.ts @@ -1,26 +1,18 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ +import { extend } from '@js/core/utils/extend'; import { validatingModule } from '@ts/grids/grid_core/validating/m_validating'; -import type { EditingController } from '../grid_core/editing/m_editing'; -import type { ModuleType } from '../grid_core/m_types'; import treeListCore from './m_core'; -const editingControllerExtender = (Base: ModuleType) => class TreeListEditingControllerExtender extends validatingModule.extenders.controllers.editing(Base) { - processDataItem(item) { - super.processDataItemTreeListHack.apply(this, arguments as any); - } - - processItems(items, e) { - return super.processItemsTreeListHack.apply(this, arguments as any); - } -}; +const EditingControllerExtender = extend({}, validatingModule.extenders.controllers.editing); +delete EditingControllerExtender.processItems; +delete EditingControllerExtender.processDataItem; treeListCore.registerModule('validating', { defaultOptions: validatingModule.defaultOptions, controllers: validatingModule.controllers, extenders: { controllers: { - editing: editingControllerExtender, + editing: EditingControllerExtender, editorFactory: validatingModule.extenders.controllers.editorFactory, }, views: validatingModule.extenders.views, diff --git a/packages/devextreme/js/ui/calendar/ui.calendar.js b/packages/devextreme/js/ui/calendar/ui.calendar.js index 92493631e5ab..bcd4aa16da1c 100644 --- a/packages/devextreme/js/ui/calendar/ui.calendar.js +++ b/packages/devextreme/js/ui/calendar/ui.calendar.js @@ -741,8 +741,6 @@ const Calendar = Editor.inherit({ } }, - _setAriaReadonly: noop, - _getKeyboardListeners() { return this.callBase().concat([this._view]); }, diff --git a/packages/devextreme/js/ui/editor/editor.js b/packages/devextreme/js/ui/editor/editor.js index 8c5a75f957d8..82847001fe6e 100644 --- a/packages/devextreme/js/ui/editor/editor.js +++ b/packages/devextreme/js/ui/editor/editor.js @@ -275,10 +275,6 @@ const Editor = Widget.inherit({ this._toggleBackspaceHandler(readOnly); this.$element().toggleClass(READONLY_STATE_CLASS, !!readOnly); - this._setAriaReadonly(readOnly); - }, - - _setAriaReadonly(readOnly) { this.setAria('readonly', readOnly || undefined); }, diff --git a/packages/devextreme/js/ui/switch.js b/packages/devextreme/js/ui/switch.js index d5975cf87198..7a24da75440d 100644 --- a/packages/devextreme/js/ui/switch.js +++ b/packages/devextreme/js/ui/switch.js @@ -94,7 +94,7 @@ const Switch = Editor.inherit({ this._renderClick(); - this.setAria('role', 'switch'); + this.setAria('role', 'button'); this._renderSwipeable(); @@ -316,17 +316,17 @@ const Switch = Editor.inherit({ }); }, - _renderValue() { + _renderValue: function() { this._validateValue(); - const value = this.option('value'); - this._renderPosition(value, 0); + const val = this.option('value'); + this._renderPosition(val, 0); - this.$element().toggleClass(SWITCH_ON_VALUE_CLASS, value); - this._getSubmitElement().val(value); + this.$element().toggleClass(SWITCH_ON_VALUE_CLASS, val); + this._getSubmitElement().val(val); this.setAria({ - 'checked': value, - 'label': value ? this.option('switchedOnText') : this.option('switchedOffText') + 'pressed': val, + 'label': val ? this.option('switchedOnText') : this.option('switchedOffText') }); }, diff --git a/packages/devextreme/js/ui/tile_view.js b/packages/devextreme/js/ui/tile_view.js index f255a3a6a1f2..6ab3b55a1da7 100644 --- a/packages/devextreme/js/ui/tile_view.js +++ b/packages/devextreme/js/ui/tile_view.js @@ -7,7 +7,7 @@ import { isDefined } from '../core/utils/type'; import { extend } from '../core/utils/extend'; import { hasWindow } from '../core/utils/window'; import { getPublicElement } from '../core/element'; -import { noop, deferRender } from '../core/utils/common'; +import { deferRender } from '../core/utils/common'; import { nativeScrolling } from '../core/utils/support'; import ScrollView from './scroll_view'; import CollectionWidget from './collection/ui.collection_widget.edit'; @@ -271,8 +271,6 @@ const TileView = CollectionWidget.inherit({ }).bind(this)); }, - _refreshActiveDescendant: noop, - _getItemPosition: function(item) { const config = this._config; const mainPosition = config.mainPosition; diff --git a/packages/devextreme/scss/widgets/base/textEditor/_mixins.scss b/packages/devextreme/scss/widgets/base/textEditor/_mixins.scss index a37911b651f9..7adb5dfb0173 100644 --- a/packages/devextreme/scss/widgets/base/textEditor/_mixins.scss +++ b/packages/devextreme/scss/widgets/base/textEditor/_mixins.scss @@ -144,7 +144,6 @@ $texteditor-border-color, $texteditor-border-bottom-color, $texteditor-disabled-color, - $texteditor-readonly-color, $texteditor-hover-border-color, $texteditor-hover-border-bottom-color, $texteditor-focused-border-color, @@ -257,7 +256,7 @@ &.dx-state-readonly { @include dx-state-border-style($label-readonly-border-style); - @include dx-state-border-color($texteditor-readonly-color, $texteditor-readonly-color); + @include dx-state-border-color($texteditor-disabled-color, $texteditor-disabled-color); } &.dx-state-disabled { diff --git a/packages/devextreme/scss/widgets/fluent/textEditor/_index.scss b/packages/devextreme/scss/widgets/fluent/textEditor/_index.scss index 0e2b483721dc..847b5c2a8349 100644 --- a/packages/devextreme/scss/widgets/fluent/textEditor/_index.scss +++ b/packages/devextreme/scss/widgets/fluent/textEditor/_index.scss @@ -513,7 +513,6 @@ $fluent-editor-border-width: 1px; $texteditor-border-color, $texteditor-border-bottom-color, $texteditor-border-color-disabled, - $texteditor-border-color-disabled, $texteditor-border-color-hover, $texteditor-border-bottom-color-hover, $texteditor-border-color-focused, diff --git a/packages/devextreme/scss/widgets/generic/textEditor/_colors.scss b/packages/devextreme/scss/widgets/generic/textEditor/_colors.scss index 7c1d6fec7d56..d83e5abef68a 100644 --- a/packages/devextreme/scss/widgets/generic/textEditor/_colors.scss +++ b/packages/devextreme/scss/widgets/generic/textEditor/_colors.scss @@ -52,8 +52,6 @@ $texteditor-invalid-faded-border-color: $base-invalid-faded-border-color !defaul $texteditor-filled-invalid-background: null !default; $texteditor-border-radius: $base-border-radius !default; $texteditor-input-border-radius: $base-border-radius !default; -$texteditor-disabled-border-color: color.change($texteditor-color, $alpha: 0.5) !default; -$texteditor-readonly-border-color: $base-border-color !default; $texteditor-label-transition: font-size 0.2s cubic-bezier(0, 0, 0.2, 1) 0ms, transform 0.2s cubic-bezier(0, 0, 0.2, 1) 0ms, diff --git a/packages/devextreme/scss/widgets/generic/textEditor/_index.scss b/packages/devextreme/scss/widgets/generic/textEditor/_index.scss index 2d010a18f8aa..2b6893313191 100644 --- a/packages/devextreme/scss/widgets/generic/textEditor/_index.scss +++ b/packages/devextreme/scss/widgets/generic/textEditor/_index.scss @@ -255,8 +255,7 @@ $generic-texteditor-invalid-badge-size: $generic-invalid-badge-size + 2 * $gener none, $texteditor-border-color, $texteditor-border-color, - $texteditor-disabled-border-color, - $texteditor-readonly-border-color, + color.change($texteditor-color, $alpha: 0.5), $texteditor-hover-border-color, $texteditor-hover-border-color, $texteditor-focused-border-color, diff --git a/packages/devextreme/scss/widgets/material/textEditor/_index.scss b/packages/devextreme/scss/widgets/material/textEditor/_index.scss index 660625a685f9..5e45166462a0 100644 --- a/packages/devextreme/scss/widgets/material/textEditor/_index.scss +++ b/packages/devextreme/scss/widgets/material/textEditor/_index.scss @@ -375,7 +375,6 @@ $material-editor-custom-button-margin: 5px; $texteditor-border-color, $texteditor-border-color, $texteditor-disabled-color, - $texteditor-disabled-color, $texteditor-hover-border-color, $texteditor-hover-border-color, $texteditor-focused-border-color, diff --git a/packages/devextreme/testing/testcafe/helpers/machineTimezones.ts b/packages/devextreme/testing/testcafe/helpers/machineTimezones.ts deleted file mode 100644 index 480f8fc7e34d..000000000000 --- a/packages/devextreme/testing/testcafe/helpers/machineTimezones.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const MACHINE_TIMEZONES = { - EuropeBerlin: 'Europe/Berlin', - AmericaLosAngeles: 'America/Los_Angeles', -} as const; -export type MachineTimezonesType = typeof MACHINE_TIMEZONES[keyof typeof MACHINE_TIMEZONES]; - -export const getMachineTimezone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone; - -export const getTimezoneTest = (timezones: MachineTimezonesType[]): TestFn => { - const machineTimezone = getMachineTimezone(); - return timezones.includes(machineTimezone as MachineTimezonesType) ? test : test.skip; -}; - -export const getTimezoneFixture = (timezones: MachineTimezonesType[]): FixtureFn => { - const machineTimezone = getMachineTimezone(); - return timezones.includes(machineTimezone as MachineTimezonesType) ? fixture : fixture.skip; -}; diff --git a/packages/devextreme/testing/testcafe/helpers/widgetTypings.ts b/packages/devextreme/testing/testcafe/helpers/widgetTypings.ts index 2fafc5649994..669b62b1a431 100644 --- a/packages/devextreme/testing/testcafe/helpers/widgetTypings.ts +++ b/packages/devextreme/testing/testcafe/helpers/widgetTypings.ts @@ -41,7 +41,6 @@ export type WidgetName = | 'dxTextBox' | 'dxTextArea' | 'dxToolbar' - | 'dxTileView' | 'dxTreeView' | 'dxDateBox' | 'dxDateRangeBox' diff --git a/packages/devextreme/testing/testcafe/tests/accessibility/calendar.ts b/packages/devextreme/testing/testcafe/tests/accessibility/calendar.ts index 54e54f6e2a7c..901cb1aeff35 100644 --- a/packages/devextreme/testing/testcafe/tests/accessibility/calendar.ts +++ b/packages/devextreme/testing/testcafe/tests/accessibility/calendar.ts @@ -26,6 +26,8 @@ const a11yCheckConfig = { rules: { // NOTE: color-contrast issues 'color-contrast': { enabled: false }, + // NOTE: aria-allowed-attr issues + 'aria-allowed-attr': { enabled: false }, // NOTE: empty-table-header issues 'empty-table-header': { enabled: false }, }, diff --git a/packages/devextreme/testing/testcafe/tests/accessibility/switch.ts b/packages/devextreme/testing/testcafe/tests/accessibility/switch.ts index c46daeebd945..b8e97a4317f5 100644 --- a/packages/devextreme/testing/testcafe/tests/accessibility/switch.ts +++ b/packages/devextreme/testing/testcafe/tests/accessibility/switch.ts @@ -19,6 +19,7 @@ const a11yCheckConfig = { // NOTE: color-contrast issues rules: { 'color-contrast': { enabled: false }, + 'aria-allowed-attr': { enabled: false }, }, }; diff --git a/packages/devextreme/testing/testcafe/tests/accessibility/tileView.ts b/packages/devextreme/testing/testcafe/tests/accessibility/tileView.ts deleted file mode 100644 index 38c59c3b7fde..000000000000 --- a/packages/devextreme/testing/testcafe/tests/accessibility/tileView.ts +++ /dev/null @@ -1,35 +0,0 @@ -import url from '../../helpers/getPageUrl'; -import { clearTestPage } from '../../helpers/clearPage'; -import { testAccessibility, Configuration } from '../../helpers/accessibility/test'; -import { Options } from '../../helpers/generateOptionMatrix'; -import { Properties } from '../../../../js/ui/tile_view.d'; - -fixture.disablePageReloads`Accessibility` - .page(url(__dirname, '../container.html')) - .afterEach(async () => clearTestPage()); - -const options: Options = { - items: [[{ text: 'test 1' }]], - focusStateEnabled: [true], -}; - -const created = async (t: TestController): Promise => { - await t.pressKey('tab'); -}; - -const a11yCheckConfig = { - rules: { - 'color-contrast': { - enabled: false, - }, - }, -}; - -const configuration: Configuration = { - component: 'dxTileView', - a11yCheckConfig, - options, - created, -}; - -testAccessibility(configuration); diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (fluent-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (fluent-blue-light).png deleted file mode 100644 index 31b1def81a8a..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (fluent-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (generic-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (generic-light).png deleted file mode 100644 index d1fff1db2bc8..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (generic-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (material-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (material-blue-light).png deleted file mode 100644 index dd9a80a7f36c..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=filled (material-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (fluent-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (fluent-blue-light).png deleted file mode 100644 index 72e082c59dd2..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (fluent-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (generic-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (generic-light).png deleted file mode 100644 index 0f8b500a3b6f..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (generic-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (material-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (material-blue-light).png deleted file mode 100644 index d5271d800f9b..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=outlined (material-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (fluent-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (fluent-blue-light).png deleted file mode 100644 index e7e683c3ef04..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (fluent-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (generic-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (generic-light).png deleted file mode 100644 index 17d0565f7fe0..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (generic-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (material-blue-light).png b/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (material-blue-light).png deleted file mode 100644 index 56b27d51fbc9..000000000000 Binary files a/packages/devextreme/testing/testcafe/tests/editors/textBox/etalons/Textbox render readonly,stylingMode=underlined (material-blue-light).png and /dev/null differ diff --git a/packages/devextreme/testing/testcafe/tests/editors/textBox/label.ts b/packages/devextreme/testing/testcafe/tests/editors/textBox/label.ts index 3a86cfe97213..bce12d816b41 100644 --- a/packages/devextreme/testing/testcafe/tests/editors/textBox/label.ts +++ b/packages/devextreme/testing/testcafe/tests/editors/textBox/label.ts @@ -24,7 +24,6 @@ const stylingModes = ['outlined', 'underlined', 'filled']; const TEXTBOX_CLASS = 'dx-textbox'; const HOVER_STATE_CLASS = 'dx-state-hover'; const FOCUSED_STATE_CLASS = 'dx-state-focused'; -const READONLY_STATE_CLASS = 'dx-state-readonly'; const INVALID_STATE_CLASS = 'dx-invalid'; [ @@ -74,15 +73,7 @@ stylingModes.forEach((stylingMode) => { await testScreenshot(t, takeScreenshot, `Textbox render stylingMode=${stylingMode}.png`); - const states = [ - HOVER_STATE_CLASS, - FOCUSED_STATE_CLASS, - READONLY_STATE_CLASS, - INVALID_STATE_CLASS, - `${INVALID_STATE_CLASS} ${FOCUSED_STATE_CLASS}`, - ]; - - for (const state of states as any[]) { + for (const state of [HOVER_STATE_CLASS, FOCUSED_STATE_CLASS, INVALID_STATE_CLASS, `${INVALID_STATE_CLASS} ${FOCUSED_STATE_CLASS}`] as any[]) { for (const id of t.ctx.ids) { await setClassAttribute(Selector(`#${id}`), state); } diff --git a/packages/devextreme/testing/testcafe/tests/scheduler/timezones/check.ts b/packages/devextreme/testing/testcafe/tests/scheduler/timezones/check.ts deleted file mode 100644 index ac3525da6f9a..000000000000 --- a/packages/devextreme/testing/testcafe/tests/scheduler/timezones/check.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ClientFunction } from 'testcafe'; -import { getTimezoneTest, MACHINE_TIMEZONES, MachineTimezonesType } from '../../../helpers/machineTimezones'; -import url from '../../../helpers/getPageUrl'; - -fixture - .disablePageReloads`Runner machine timezone checks` - .page(url(__dirname, '../../container.html')); - -type CheckType = [MachineTimezonesType, string]; -const checks: CheckType[] = [ - [MACHINE_TIMEZONES.AmericaLosAngeles, 'Mon Jan 01 2024 10:00:00 GMT-0800 (Pacific Standard Time)'], - [MACHINE_TIMEZONES.EuropeBerlin, 'Mon Jan 01 2024 10:00:00 GMT+0100 (Central European Standard Time)'], -]; - -checks.forEach(([timezone, expectedResult]) => { - getTimezoneTest([timezone])(`${timezone} check`, async (t) => { - const dateFromBrowser = await ClientFunction(() => new Date(2024, 0, 1, 10).toString())(); - await t.expect(dateFromBrowser).eql(expectedResult); - }); -}); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js index 74a428452ce7..f5d79b5ead88 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/calendar.tests.js @@ -4707,15 +4707,6 @@ QUnit.module('Aria accessibility', { assert.equal($cell.attr('role'), 'gridcell', 'Cell: aria role is correct'); }); - QUnit.test('The calendar view wrapper does not have an aria-readonly attribute', function(assert) { - this.$element.dxCalendar({ - readOnly: true, - }); - const $viewsWrapper = $(this.$element.find(toSelector(CALENDAR_VIEWS_WRAPPER_CLASS))); - - assert.equal($viewsWrapper.attr('aria-readonly'), undefined); - }); - QUnit.test('aria id on contoured date cell', function(assert) { const calendar = this.$element.dxCalendar({ value: new Date(2015, 5, 1), diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.markup.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.markup.tests.js index 39d34f7411a9..cd47c9e56b71 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.markup.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.markup.tests.js @@ -157,59 +157,27 @@ QUnit.module('Switch markup', () => { }); }); -QUnit.module('Accessibility', () => { - QUnit.test('Switch should have correct role attribute', function(assert) { - const $element = $('#switch').dxSwitch(); +QUnit.module('aria accessibility', () => { + QUnit.test('aria role', function(assert) { + const $element = $('#switch').dxSwitch({}); - assert.strictEqual($element.attr('role'), 'switch', 'Element should have role=switch'); + assert.equal($element.attr('role'), 'button', 'aria role is correct'); }); - QUnit.test('Switch should have correct aria-checked attribute', function(assert) { - const $element = $('#switch').dxSwitch(); - const instance = $element.dxSwitch('instance'); - - assert.strictEqual($element.attr('aria-checked'), 'false', 'aria-checked must be false if the value is false'); - - instance.option({ value: true }); - - assert.strictEqual($element.attr('aria-checked'), 'true', 'aria-checked must be true if the value is true'); - }); - - QUnit.test('Switch should have correct aria-label attribute', function(assert) { + QUnit.test('aria properties', function(assert) { const $element = $('#switch').dxSwitch({ switchedOnText: 'on test', switchedOffText: 'off test', + value: true }); const instance = $element.dxSwitch('instance'); - assert.strictEqual($element.attr('aria-label'), 'off test', 'aria-label must have switchOffText if the value is false'); - - instance.option({ value: true }); + assert.equal($element.attr('aria-label'), 'on test', 'aria \'on state\' label is correct'); + assert.equal($element.attr('aria-pressed'), 'true', 'aria \'on state\' pressed attribute is correct'); - assert.strictEqual($element.attr('aria-label'), 'on test', 'aria-label must have switchedOnText if the value if true'); - }); - - QUnit.test('Switch should have correct aria-disabled and tabindex attributes', function(assert) { - const $element = $('#switch').dxSwitch({ focusStateEnabled: true }); - const instance = $element.dxSwitch('instance'); - - assert.strictEqual($element.attr('aria-disabled'), undefined, 'The element should not have an aria-disabled attribute unless it is disabled'); - assert.strictEqual($element.attr('tabindex'), '0', 'The element must have tabindex=0 if it is not disabled'); - - instance.option({ disabled: true }); - - assert.strictEqual($element.attr('aria-disabled'), 'true', 'The element must have aria-disabled=true if it is disabled'); - assert.strictEqual($element.attr('tabindex'), undefined, 'The element should not have an aria-disabled attribute if it is disabled'); - }); - - QUnit.test('Switch should have correct aria-readonly attribute', function(assert) { - const $element = $('#switch').dxSwitch(); - const instance = $element.dxSwitch('instance'); - - assert.strictEqual($element.attr('aria-readonly'), undefined, 'The element should not have aria-readonly attribute if readOnly is false'); - - instance.option({ readOnly: true }); - - assert.strictEqual($element.attr('aria-readonly'), 'true', 'The element must have aria-readonly=true if readOnly is true'); + instance.option('value', false); + assert.equal($element.attr('aria-label'), 'off test', 'aria \'off state\' label is correct'); + assert.equal($element.attr('aria-pressed'), 'false', 'aria \'off state\' pressed attribute is correct'); }); }); + diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.tests.js index 3c80857a2593..601574d141a4 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/switch.tests.js @@ -638,3 +638,4 @@ QUnit.module('valueChanged handler should receive correct event parameter', { }); }); }); + diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/tileView.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/tileView.tests.js index fd557ec0ca3e..7066ad96b393 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/tileView.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/tileView.tests.js @@ -276,28 +276,6 @@ QUnit.module('rendering', { assert.strictEqual($tile.outerHeight(), DEFAULT_ITEMSIZE, 'Tile height updated correctly'); assert.strictEqual($tile.outerWidth(), DEFAULT_ITEMSIZE, 'Tile width updated correctly'); }); - - QUnit.testInActiveWindow('aria-activedescendant should not be set for the component after tile focus (T1217255)', function(assert) { - const clock = sinon.useFakeTimers(); - - try { - this.$element.dxTileView({ - items: [{ text: 'test 1' }], - focusStateEnabled: true, - }); - - const $firstItem = this.$element.find(TILEVIEW_ITEM_SELECTOR).eq(0); - - $firstItem.trigger('dxpointerdown'); - - clock.tick(10); - - assert.strictEqual($firstItem.hasClass('dx-state-focused'), true); - assert.strictEqual(this.$element.attr('aria-activedescendant'), undefined); - } finally { - clock.restore(); - } - }); });