From b4cf87e0642135c6ba7bf2c5709171066aa431b9 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Thu, 12 Mar 2026 09:27:08 +0800 Subject: [PATCH] DataGrid - TextArea editor should have the aria-invalid attribute when invalid (T1296376) (#32801) (cherry picked from commit d6f5b115684598b2fd5888e3f58b315069a26dd3) --- .../__mock__/model/cell/data_cell.ts | 10 ++--- .../grids/grid_core/editing/const.ts | 3 +- .../__tests__/validating.integration.test.ts | 42 ++++++++++++++++++- .../grid_core/validating/m_validating.ts | 5 ++- .../ui/__tests__/__mock__/model/text_area.ts | 5 +-- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/model/cell/data_cell.ts b/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/model/cell/data_cell.ts index d124f6f8870a..0c25a9d98951 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/model/cell/data_cell.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/model/cell/data_cell.ts @@ -1,9 +1,7 @@ -import { TextBoxModel } from '@ts/ui/__tests__/__mock__/model/textbox'; - const SELECTORS = { editCell: 'dx-editor-cell', invalidCell: 'invalid', - textBox: 'dx-textbox', + widget: 'dx-widget', }; export class DataCellModel { @@ -31,8 +29,8 @@ export class DataCellModel { return this.root?.innerHTML ?? ''; } - public getEditor(): TextBoxModel { - const editorElement = this.root?.querySelector(`.${SELECTORS.textBox}`) as HTMLElement; - return new TextBoxModel(editorElement); + public getEditor(EditorModel: new (element: HTMLElement) => T): T { + const editorElement = this.root?.querySelector(`.${SELECTORS.widget}`) as HTMLElement; + return new EditorModel(editorElement); } } diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/const.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/const.ts index 7230c14cef6d..364083e3fd82 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/const.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/const.ts @@ -14,8 +14,9 @@ export const EDITING_EDITCOLUMNNAME_OPTION_NAME = 'editing.editColumnName'; export const TARGET_COMPONENT_NAME = 'targetComponent'; +export const EDITORS_TEXTAREA_SELECTOR = 'textarea:not([hidden])'; export const EDITORS_INPUT_SELECTOR = 'input:not([type=\'hidden\'])'; -export const FOCUSABLE_ELEMENT_SELECTOR = `[tabindex]:not([disabled]), ${EDITORS_INPUT_SELECTOR}:not([disabled])`; +export const FOCUSABLE_ELEMENT_SELECTOR = `[tabindex]:not([disabled]), ${EDITORS_INPUT_SELECTOR}:not([disabled]), ${EDITORS_TEXTAREA_SELECTOR}:not([disabled])`; export const EDIT_MODE_BATCH = 'batch'; export const EDIT_MODE_ROW = 'row'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/validating/__tests__/validating.integration.test.ts b/packages/devextreme/js/__internal/grids/grid_core/validating/__tests__/validating.integration.test.ts index c4200ccd0795..aed73f830c67 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/validating/__tests__/validating.integration.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/validating/__tests__/validating.integration.test.ts @@ -6,6 +6,8 @@ import { beforeTest, createDataGrid, } from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils'; +import { TextAreaModel } from '@ts/ui/__tests__/__mock__/model/text_area'; +import { TextBoxModel } from '@ts/ui/__tests__/__mock__/model/textbox'; describe('DataGrid Cell Editing', () => { beforeEach(beforeTest); @@ -42,7 +44,7 @@ describe('DataGrid Cell Editing', () => { }); const firstCell = component.getDataCell(0, 0); - const firstEditor = firstCell.getEditor(); + const firstEditor = firstCell.getEditor(TextBoxModel); firstEditor.setValue(''); jest.runAllTimers(); @@ -56,7 +58,7 @@ describe('DataGrid Cell Editing', () => { expect(component.getDataCell(0, 0).isValidCell).toBe(true); const secondCell = component.getDataCell(1, 0); - const secondEditor = secondCell.getEditor(); + const secondEditor = secondCell.getEditor(TextBoxModel); secondEditor.setValue(''); jest.runAllTimers(); @@ -70,4 +72,40 @@ describe('DataGrid Cell Editing', () => { expect(component.getDataCell(1, 0).isValidCell).toBe(true); }); }); + + // T1296376 + describe('when a TextArea editor is invalid', () => { + it('should have the aria-invalid attribute set to true', async () => { + const { component, instance } = await createDataGrid({ + dataSource: [ + { id: 1, text: 'value' }, + ], + keyExpr: 'id', + columns: [{ + dataField: 'text', + validationRules: [{ type: 'required' }], + }], + editing: { + mode: 'cell', + allowUpdating: true, + }, + onEditorPreparing(e) { + e.editorName = 'dxTextArea'; + }, + }); + + instance.editCell(0, 0); + jest.runAllTimers(); + const textCell = component.getDataCell(0, 0); + + expect(textCell.isEditCell).toBe(true); + + const editor = textCell.getEditor(TextAreaModel); + editor.setValue(''); + jest.runAllTimers(); + + expect(component.getDataCell(0, 0).isValidCell).toBe(false); + expect(editor.getInputElement().getAttribute('aria-invalid')).toBe('true'); + }); + }); }); 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 a8e5ac0d4485..3196191684a1 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 @@ -31,7 +31,7 @@ import type { DataController } from '@ts/grids/grid_core/data_controller/m_data_ 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'; -import { EDITORS_INPUT_SELECTOR } from '../editing/const'; +import { EDITORS_INPUT_SELECTOR, EDITORS_TEXTAREA_SELECTOR } from '../editing/const'; import type { EditingController } from '../editing/m_editing'; import type { NormalizedEditCellOptions } from '../editing/types'; import modules from '../m_modules'; @@ -1442,7 +1442,8 @@ export const validatingEditorFactoryExtender = (Base: ModuleType) private _getCurrentFocusElement($focus) { if (this._editingController.isEditing()) { - return $focus.find(EDITORS_INPUT_SELECTOR).first(); + const selector = [EDITORS_INPUT_SELECTOR, EDITORS_TEXTAREA_SELECTOR].join(', '); + return $focus.find(selector).first(); } return $focus; } diff --git a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/text_area.ts b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/text_area.ts index b408e7cbcdea..9ac26abfaeaa 100644 --- a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/text_area.ts +++ b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/text_area.ts @@ -16,10 +16,7 @@ export class TextAreaModel { } public setValue(value: string): void { - const input = this.getInputElement(); - - input.value = value; - input.dispatchEvent(new Event('input', { bubbles: true })); + this.getInstance()?.option('value', value); } public getInstance(): TextArea {