From 2c50e7824dd2624227851151cebb94b971308533 Mon Sep 17 00:00:00 2001 From: Ruslan Farkhutdinov Date: Tue, 30 Jun 2026 17:32:41 +0300 Subject: [PATCH 1/5] DateBox: Fix IME input after focus re-entry and Backspace handling (T1331089) --- .../__internal/ui/date_box/date_box.mask.ts | 64 ++++++++++++++---- .../devextreme/js/__internal/ui/tag_box.ts | 2 +- .../ui/text_box/text_editor.base.ts | 4 +- .../datebox.mask.tests.js | 67 +++++++++++++++++++ 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts index b34c85cf1b92..ec8b12243e6b 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts @@ -4,6 +4,7 @@ import { getFormat } from '@js/common/core/localization/ldml/date.format'; import { getRegExpInfo } from '@js/common/core/localization/ldml/date.parser'; import numberLocalization from '@js/common/core/localization/number'; import devices from '@js/core/devices'; +import type { dxElementWrapper } from '@js/core/renderer'; import browser from '@js/core/utils/browser'; import { clipboardText } from '@js/core/utils/dom'; import { fitIntoRange, inRange, sign } from '@js/core/utils/math'; @@ -52,6 +53,8 @@ class DateBoxMask extends DateBoxBase { _isIMECommitPending?: boolean; + _wasAllSelectedOnBeforeInput = false; + _supportedKeys(): Record boolean | undefined> { const originalHandlers = super._supportedKeys(); const callOriginalHandler = (e: KeyboardEvent): boolean | undefined => { @@ -243,6 +246,7 @@ class DateBoxMask extends DateBoxBase { _maskBeforeInputHandler(e: DxEvent): boolean { this._maskInputHandler = null; + this._wasAllSelectedOnBeforeInput = this._isAllSelected(); const { inputType } = e.originalEvent; @@ -252,16 +256,6 @@ class DateBoxMask extends DateBoxBase { }; } - const isBackwardDeletion = inputType === 'deleteContentBackward'; - const isForwardDeletion = inputType === 'deleteContentForward'; - if (isBackwardDeletion || isForwardDeletion) { - const direction = isBackwardDeletion ? BACKWARD : FORWARD; - this._maskInputHandler = (): void => { - this._revertPart(); - this._selectNextPart(direction); - }; - } - if (!this._useMaskBehavior() || !this._isSingleCharKey(e)) { return false; } @@ -280,12 +274,36 @@ class DateBoxMask extends DateBoxBase { _keyPressHandler(e: { originalEvent: InputEvent & KeyboardEvent }): void { const { originalEvent: event } = e; + const isBackwardDeletion = event?.inputType === 'deleteContentBackward'; + const isForwardDeletion = event?.inputType === 'deleteContentForward'; + + if (this._useMaskBehavior() && (isBackwardDeletion || isForwardDeletion)) { + const isInputCleared = this._input().val() === ''; + + if (this._wasAllSelectedOnBeforeInput || isInputCleared) { + this._wasAllSelectedOnBeforeInput = false; + + super._keyPressHandler(e); + + return; + } + + const direction = isBackwardDeletion ? BACKWARD : FORWARD; + + this._revertPart(direction); + this._syncInputWithMask(); + + this._wasAllSelectedOnBeforeInput = false; + + return; + } + const isCompositionDigit = event?.inputType === 'insertCompositionText' - && this._isSingleDigitKey(e); + && this._isSingleDigitKey(e); const isIMECommitDigit = event?.inputType === 'insertText' - && this._isSingleDigitKey(e) - && this._isIMECommitPending; + && this._isSingleDigitKey(e) + && this._isIMECommitPending; if (isCompositionDigit && event.data) { if (!this._isIMEDigitProcessed) { @@ -307,12 +325,15 @@ class DateBoxMask extends DateBoxBase { return; } + super._keyPressHandler(e); if (this._maskInputHandler) { this._maskInputHandler(); this._maskInputHandler = null; } + + this._wasAllSelectedOnBeforeInput = false; } _processInputKey(key: string): void { @@ -733,6 +754,7 @@ class DateBoxMask extends DateBoxBase { _maskCompositionStartHandler(): void { this._isIMEDigitProcessed = false; this._isIMECommitPending = false; + this._wasAllSelectedOnBeforeInput = this._isAllSelected(); } _maskCompositionEndHandler(): void { @@ -792,6 +814,22 @@ class DateBoxMask extends DateBoxBase { } } + _focusInHandler(e: DxEvent): void { + super._focusInHandler(e); + + if (!this._useMaskBehavior()) { + return; + } + + const relatedTarget = e?.relatedTarget as HTMLElement | dxElementWrapper | null; + const isFocusMovedFromAnotherElement = Boolean(relatedTarget); + + if (isFocusMovedFromAnotherElement) { + this._clearSearchValue(); + this._selectFirstPart(); + } + } + _focusOutHandler(e: DxEvent): void { const shouldFireChangeEvent = this._useMaskBehavior() && !e.isDefaultPrevented(); diff --git a/packages/devextreme/js/__internal/ui/tag_box.ts b/packages/devextreme/js/__internal/ui/tag_box.ts index 7ad9129f7f49..6fd3eb91e9e3 100644 --- a/packages/devextreme/js/__internal/ui/tag_box.ts +++ b/packages/devextreme/js/__internal/ui/tag_box.ts @@ -729,7 +729,7 @@ class TagBox< this.option('text', ''); } - _focusInHandler(e: DxEvent & { relatedTarget: Element | dxElementWrapper }): void { + _focusInHandler(e: DxEvent): void { if (!this._preventNestedFocusEvent(e)) { this._scrollContainer('end'); } diff --git a/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts b/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts index 7d8a76d2a93f..92d031764f28 100644 --- a/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts +++ b/packages/devextreme/js/__internal/ui/text_box/text_editor.base.ts @@ -860,9 +860,7 @@ class TextEditorBase< return this.$element(); } - _focusInHandler(event: DxEvent & { - relatedTarget: Element | dxElementWrapper; - }): void { + _focusInHandler(event: DxEvent): void { this._preventNestedFocusEvent(event); super._focusInHandler(event); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js index e43ab2f2eb97..edc9e1ec59f8 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js @@ -798,6 +798,26 @@ module('Keyboard navigation', setupModule, () => { assert.deepEqual(this.keyboard.caret(), { start: 9, end: 11 }, 'first group has been filled again'); }); + QUnit.testInActiveWindow('first part should be active when re-focusing from another input after all parts are completed (T1331089)', function(assert) { + const $firstInput = $('').insertBefore(this.$element); + + try { + this.instance.option({ + displayFormat: 'MM/dd/yyyy', + value: new Date(2025, 0, 1), + }); + + this.keyboard.type('10162025'); // Oct 16, 2025 + + $firstInput.get(0).focus(); + this.$input.get(0).focus(); + + assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is selected after re-focusing from another input'); + } finally { + $firstInput.remove(); + } + }); + test('enter should clear search value', function(assert) { this.keyboard.type('1'); @@ -1355,6 +1375,53 @@ module('Search', setupModule, () => { assert.strictEqual(this.$input.val(), '2012/10/10', 'year was not changed'); }); + + test('deleteContentBackward input event should revert the active date part to its minimum value without clearing the value (Chinese MS IME composition backspace) (T1331089)', function(assert) { + this.instance.option({ + displayFormat: 'MM/dd/yyyy', + value: new Date(2025, 9, 16), // Oct 16, 2025; text = '10/16/2025' + }); + + this.$input.get(0).focus(); + + this.$input.trigger($.Event('compositionstart')); + + this.$input.trigger($.Event('input', { + type: 'input', + originalEvent: $.Event('input', { + inputType: 'deleteContentBackward', + }) + })); + + assert.strictEqual(this.instance.option('text'), '01/16/2025', 'text is not cleared, month is reset'); + assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'first date part (month) is still active'); + }); + + test('deleteContentBackward input event during composition with all text selected should clear the value (Chinese MS IME composition backspace) (T1331089)', function(assert) { + this.instance.option({ + displayFormat: 'MM/dd/yyyy', + value: new Date(2025, 9, 16), // Oct 16, 2025; text = '10/16/2025' + }); + + this.$input.get(0).focus(); + this.keyboard.caret({ start: 0, end: 10 }); + + this.$input.trigger($.Event('compositionstart')); + + this.$input.val(''); + + this.$input.trigger($.Event('input', { + type: 'input', + originalEvent: $.Event('input', { + inputType: 'deleteContentBackward', + }) + })); + + this.$input.change(); + + assert.strictEqual(this.instance.option('text'), '', 'text has been cleared'); + assert.strictEqual(this.instance.option('value'), null, 'value has been cleared'); + }); }); module('Date AM/PM Handling', setupModule, () => { From 1a5e73165fc28b64711606f896a402b873c4f51e Mon Sep 17 00:00:00 2001 From: Ruslan Farkhutdinov Date: Wed, 1 Jul 2026 11:19:28 +0300 Subject: [PATCH 2/5] DateBox: Remove focusin handler & select first part only for IME path --- .../__internal/ui/date_box/date_box.mask.ts | 36 +++++++------------ .../datebox.mask.tests.js | 13 +++++-- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts index ec8b12243e6b..d5c1f613ec0d 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts @@ -4,7 +4,6 @@ import { getFormat } from '@js/common/core/localization/ldml/date.format'; import { getRegExpInfo } from '@js/common/core/localization/ldml/date.parser'; import numberLocalization from '@js/common/core/localization/number'; import devices from '@js/core/devices'; -import type { dxElementWrapper } from '@js/core/renderer'; import browser from '@js/core/utils/browser'; import { clipboardText } from '@js/core/utils/dom'; import { fitIntoRange, inRange, sign } from '@js/core/utils/math'; @@ -53,7 +52,7 @@ class DateBoxMask extends DateBoxBase { _isIMECommitPending?: boolean; - _wasAllSelectedOnBeforeInput = false; + _wasAllSelectedBeforeComposition = false; _supportedKeys(): Record boolean | undefined> { const originalHandlers = super._supportedKeys(); @@ -246,7 +245,6 @@ class DateBoxMask extends DateBoxBase { _maskBeforeInputHandler(e: DxEvent): boolean { this._maskInputHandler = null; - this._wasAllSelectedOnBeforeInput = this._isAllSelected(); const { inputType } = e.originalEvent; @@ -280,8 +278,8 @@ class DateBoxMask extends DateBoxBase { if (this._useMaskBehavior() && (isBackwardDeletion || isForwardDeletion)) { const isInputCleared = this._input().val() === ''; - if (this._wasAllSelectedOnBeforeInput || isInputCleared) { - this._wasAllSelectedOnBeforeInput = false; + if (this._wasAllSelectedBeforeComposition || isInputCleared) { + this._wasAllSelectedBeforeComposition = false; super._keyPressHandler(e); @@ -293,7 +291,7 @@ class DateBoxMask extends DateBoxBase { this._revertPart(direction); this._syncInputWithMask(); - this._wasAllSelectedOnBeforeInput = false; + this._wasAllSelectedBeforeComposition = false; return; } @@ -307,6 +305,11 @@ class DateBoxMask extends DateBoxBase { if (isCompositionDigit && event.data) { if (!this._isIMEDigitProcessed) { + if (this._wasAllSelectedBeforeComposition || this._isAllSelected()) { + this._clearSearchValue(); + this._selectFirstPart(); + } + this._processInputKey(event.data); this._isIMEDigitProcessed = true; this._isIMECommitPending = true; @@ -320,6 +323,7 @@ class DateBoxMask extends DateBoxBase { if (isIMECommitDigit) { this._isIMECommitPending = false; this._pendingIMEDigit = null; + this._wasAllSelectedBeforeComposition = false; this._syncInputWithMask(); @@ -333,7 +337,7 @@ class DateBoxMask extends DateBoxBase { this._maskInputHandler = null; } - this._wasAllSelectedOnBeforeInput = false; + this._wasAllSelectedBeforeComposition = false; } _processInputKey(key: string): void { @@ -754,7 +758,7 @@ class DateBoxMask extends DateBoxBase { _maskCompositionStartHandler(): void { this._isIMEDigitProcessed = false; this._isIMECommitPending = false; - this._wasAllSelectedOnBeforeInput = this._isAllSelected(); + this._wasAllSelectedBeforeComposition = this._isAllSelected(); } _maskCompositionEndHandler(): void { @@ -814,22 +818,6 @@ class DateBoxMask extends DateBoxBase { } } - _focusInHandler(e: DxEvent): void { - super._focusInHandler(e); - - if (!this._useMaskBehavior()) { - return; - } - - const relatedTarget = e?.relatedTarget as HTMLElement | dxElementWrapper | null; - const isFocusMovedFromAnotherElement = Boolean(relatedTarget); - - if (isFocusMovedFromAnotherElement) { - this._clearSearchValue(); - this._selectFirstPart(); - } - } - _focusOutHandler(e: DxEvent): void { const shouldFireChangeEvent = this._useMaskBehavior() && !e.isDefaultPrevented(); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js index edc9e1ec59f8..14f01ff09c2a 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js @@ -798,7 +798,7 @@ module('Keyboard navigation', setupModule, () => { assert.deepEqual(this.keyboard.caret(), { start: 9, end: 11 }, 'first group has been filled again'); }); - QUnit.testInActiveWindow('first part should be active when re-focusing from another input after all parts are completed (T1331089)', function(assert) { + QUnit.testInActiveWindow('first IME input should start from the first date part after re-focusing from another input (T1331089)', function(assert) { const $firstInput = $('').insertBefore(this.$element); try { @@ -812,7 +812,16 @@ module('Keyboard navigation', setupModule, () => { $firstInput.get(0).focus(); this.$input.get(0).focus(); - assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is selected after re-focusing from another input'); + this.keyboard.caret({ start: 0, end: this.instance.option('text').length }); + + this.$input.trigger($.Event('compositionstart')); + + this.keyboard + .beforeInput('1', 'insertCompositionText') + .input('1', 'insertCompositionText'); + + assert.strictEqual(this.instance.option('text'), '01/16/2025', 'month part is updated'); + assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is active'); } finally { $firstInput.remove(); } From 98a98983d64d81a53e525f18cfc51d9f5433681b Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 1 Jul 2026 17:38:41 +0300 Subject: [PATCH 3/5] refactoring + change tests --- .../__internal/ui/date_box/date_box.mask.ts | 20 +++--- .../datebox.mask.tests.js | 63 ++++++++++--------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts index d5c1f613ec0d..a0b0125ff07f 100644 --- a/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts +++ b/packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts @@ -279,29 +279,25 @@ class DateBoxMask extends DateBoxBase { const isInputCleared = this._input().val() === ''; if (this._wasAllSelectedBeforeComposition || isInputCleared) { - this._wasAllSelectedBeforeComposition = false; - super._keyPressHandler(e); + } else { + const direction = isBackwardDeletion ? BACKWARD : FORWARD; - return; + this._revertPart(direction); + this._syncInputWithMask(); } - const direction = isBackwardDeletion ? BACKWARD : FORWARD; - - this._revertPart(direction); - this._syncInputWithMask(); - this._wasAllSelectedBeforeComposition = false; return; } const isCompositionDigit = event?.inputType === 'insertCompositionText' - && this._isSingleDigitKey(e); + && this._isSingleDigitKey(e); const isIMECommitDigit = event?.inputType === 'insertText' - && this._isSingleDigitKey(e) - && this._isIMECommitPending; + && this._isSingleDigitKey(e) + && this._isIMECommitPending; if (isCompositionDigit && event.data) { if (!this._isIMEDigitProcessed) { @@ -313,6 +309,7 @@ class DateBoxMask extends DateBoxBase { this._processInputKey(event.data); this._isIMEDigitProcessed = true; this._isIMECommitPending = true; + this._wasAllSelectedBeforeComposition = false; } this._syncInputWithMask(); @@ -766,6 +763,7 @@ class DateBoxMask extends DateBoxBase { this._caret(this._getActivePartProp('caret')); this._maskInputHandler = null; + this._wasAllSelectedBeforeComposition = false; } _maskPasteHandler(e: DxEvent): void { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js index 14f01ff09c2a..9b859c97fee0 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js @@ -798,33 +798,24 @@ module('Keyboard navigation', setupModule, () => { assert.deepEqual(this.keyboard.caret(), { start: 9, end: 11 }, 'first group has been filled again'); }); - QUnit.testInActiveWindow('first IME input should start from the first date part after re-focusing from another input (T1331089)', function(assert) { - const $firstInput = $('').insertBefore(this.$element); - - try { - this.instance.option({ - displayFormat: 'MM/dd/yyyy', - value: new Date(2025, 0, 1), - }); + test('Windows Bopomofo IME changes selection to last active part before insertCompositionText — first date part should still be updated (T1331089)', function(assert) { + this.instance.option({ + displayFormat: 'MM/dd/yyyy', + value: new Date(2025, 9, 16), + }); - this.keyboard.type('10162025'); // Oct 16, 2025 + this.keyboard.type('1016'); - $firstInput.get(0).focus(); - this.$input.get(0).focus(); + this.keyboard.caret({ start: 0, end: this.instance.option('text').length }); - this.keyboard.caret({ start: 0, end: this.instance.option('text').length }); + this.$input.trigger($.Event('compositionstart')); - this.$input.trigger($.Event('compositionstart')); + this.keyboard.caret({ start: 6, end: 10 }); - this.keyboard - .beforeInput('1', 'insertCompositionText') - .input('1', 'insertCompositionText'); + this.keyboard.input('1', 'insertCompositionText'); - assert.strictEqual(this.instance.option('text'), '01/16/2025', 'month part is updated'); - assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is active'); - } finally { - $firstInput.remove(); - } + assert.strictEqual(this.instance.option('text'), '01/16/2025', 'month (first part) is updated, not year'); + assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is active'); }); test('enter should clear search value', function(assert) { @@ -1388,7 +1379,7 @@ module('Search', setupModule, () => { test('deleteContentBackward input event should revert the active date part to its minimum value without clearing the value (Chinese MS IME composition backspace) (T1331089)', function(assert) { this.instance.option({ displayFormat: 'MM/dd/yyyy', - value: new Date(2025, 9, 16), // Oct 16, 2025; text = '10/16/2025' + value: new Date(2025, 9, 16), }); this.$input.get(0).focus(); @@ -1406,18 +1397,31 @@ module('Search', setupModule, () => { assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'first date part (month) is still active'); }); - test('deleteContentBackward input event during composition with all text selected should clear the value (Chinese MS IME composition backspace) (T1331089)', function(assert) { + test('IME digit on non-first date part without all-selected state should update that specific part and not reset to first (T1331089 regression)', function(assert) { this.instance.option({ displayFormat: 'MM/dd/yyyy', - value: new Date(2025, 9, 16), // Oct 16, 2025; text = '10/16/2025' + value: new Date(2025, 9, 16), }); - this.$input.get(0).focus(); - this.keyboard.caret({ start: 0, end: 10 }); + this.keyboard.type('10'); this.$input.trigger($.Event('compositionstart')); - this.$input.val(''); + this.keyboard.input('2', 'insertCompositionText'); + + assert.strictEqual(this.instance.option('text'), '10/02/2025', 'day part is updated, month is not reset to first'); + assert.deepEqual(this.keyboard.caret(), { start: 3, end: 5 }, 'day part remains active'); + }); + + test('deleteContentBackward during IME composition on non-first date part should revert that part to minimum without affecting other parts (T1331089)', function(assert) { + this.instance.option({ + displayFormat: 'MM/dd/yyyy', + value: new Date(2025, 9, 16), + }); + + this.keyboard.type('10'); + + this.$input.trigger($.Event('compositionstart')); this.$input.trigger($.Event('input', { type: 'input', @@ -1426,10 +1430,7 @@ module('Search', setupModule, () => { }) })); - this.$input.change(); - - assert.strictEqual(this.instance.option('text'), '', 'text has been cleared'); - assert.strictEqual(this.instance.option('value'), null, 'value has been cleared'); + assert.strictEqual(this.instance.option('text'), '10/01/2025', 'day is reverted to minimum, month is unchanged'); }); }); From a79840846f56c1cce9eb75126e2462bb7a60ed49 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 1 Jul 2026 17:56:38 +0300 Subject: [PATCH 4/5] move regression test to bottom --- .../datebox.mask.tests.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js index 9b859c97fee0..cb22fd9cea9a 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js @@ -1397,7 +1397,7 @@ module('Search', setupModule, () => { assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'first date part (month) is still active'); }); - test('IME digit on non-first date part without all-selected state should update that specific part and not reset to first (T1331089 regression)', function(assert) { + test('deleteContentBackward during IME composition on non-first date part should revert that part to minimum without affecting other parts (T1331089)', function(assert) { this.instance.option({ displayFormat: 'MM/dd/yyyy', value: new Date(2025, 9, 16), @@ -1407,13 +1407,17 @@ module('Search', setupModule, () => { this.$input.trigger($.Event('compositionstart')); - this.keyboard.input('2', 'insertCompositionText'); + this.$input.trigger($.Event('input', { + type: 'input', + originalEvent: $.Event('input', { + inputType: 'deleteContentBackward', + }) + })); - assert.strictEqual(this.instance.option('text'), '10/02/2025', 'day part is updated, month is not reset to first'); - assert.deepEqual(this.keyboard.caret(), { start: 3, end: 5 }, 'day part remains active'); + assert.strictEqual(this.instance.option('text'), '10/01/2025', 'day is reverted to minimum, month is unchanged'); }); - test('deleteContentBackward during IME composition on non-first date part should revert that part to minimum without affecting other parts (T1331089)', function(assert) { + test('IME digit on non-first date part without all-selected state should update that specific part and not reset to first (T1331089 regression)', function(assert) { this.instance.option({ displayFormat: 'MM/dd/yyyy', value: new Date(2025, 9, 16), @@ -1423,14 +1427,10 @@ module('Search', setupModule, () => { this.$input.trigger($.Event('compositionstart')); - this.$input.trigger($.Event('input', { - type: 'input', - originalEvent: $.Event('input', { - inputType: 'deleteContentBackward', - }) - })); + this.keyboard.input('2', 'insertCompositionText'); - assert.strictEqual(this.instance.option('text'), '10/01/2025', 'day is reverted to minimum, month is unchanged'); + assert.strictEqual(this.instance.option('text'), '10/02/2025', 'day part is updated, month is not reset to first'); + assert.deepEqual(this.keyboard.caret(), { start: 3, end: 5 }, 'day part remains active'); }); }); From b53ab2352c6cfc2ae6e04038488544cdb8c5620d Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 1 Jul 2026 19:03:48 +0300 Subject: [PATCH 5/5] update by copilot's review --- .../datebox.mask.tests.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js index cb22fd9cea9a..69b3b7bd9bbb 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/datebox.mask.tests.js @@ -1386,12 +1386,7 @@ module('Search', setupModule, () => { this.$input.trigger($.Event('compositionstart')); - this.$input.trigger($.Event('input', { - type: 'input', - originalEvent: $.Event('input', { - inputType: 'deleteContentBackward', - }) - })); + this.keyboard.input(null, 'deleteContentBackward'); assert.strictEqual(this.instance.option('text'), '01/16/2025', 'text is not cleared, month is reset'); assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'first date part (month) is still active'); @@ -1407,12 +1402,7 @@ module('Search', setupModule, () => { this.$input.trigger($.Event('compositionstart')); - this.$input.trigger($.Event('input', { - type: 'input', - originalEvent: $.Event('input', { - inputType: 'deleteContentBackward', - }) - })); + this.keyboard.input(null, 'deleteContentBackward'); assert.strictEqual(this.instance.option('text'), '10/01/2025', 'day is reverted to minimum, month is unchanged'); });