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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/devextreme/js/__internal/ui/date_box/date_box.mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,6 +16,7 @@ import type { DateLike, Properties } from '@js/ui/date_box';
import dateLocalization from '@ts/core/localization/date';
import type { OptionChanged } from '@ts/core/widget/types';
import type { KeyboardKeyDownEvent } from '@ts/events/core/m_keyboard_processor';
import type { ValueChangedEvent } from '@ts/ui/editor/editor';

import type { DxMouseWheelEvent } from '../scroll_view/types';
import type { DateBoxBaseProperties } from './date_box.base';
Expand All @@ -25,6 +27,7 @@ const MASK_EVENT_NAMESPACE = 'dateBoxMask';
const FORWARD = 1;
const BACKWARD = -1;
const IME_DIGIT_CODE_REGEXP = /^(?:Digit|Numpad)(\d)$/;
const IME_BACKSPACE_INPUT_TYPE = 'deleteContentBackward';

export interface DateBoxMaskProperties extends Properties {
emptyDateValue?: Date;
Expand Down Expand Up @@ -52,6 +55,8 @@ class DateBoxMask extends DateBoxBase {

_isIMECommitPending?: boolean;

_isClearingValue?: boolean;

_supportedKeys(): Record<string, (e: KeyboardEvent) => boolean | undefined> {
const originalHandlers = super._supportedKeys();
const callOriginalHandler = (e: KeyboardEvent): boolean | undefined => {
Expand Down Expand Up @@ -307,6 +312,17 @@ class DateBoxMask extends DateBoxBase {

return;
}

if (this._useMaskBehavior() && event?.inputType === IME_BACKSPACE_INPUT_TYPE) {
if (this._isAllSelected()) {
this._selectFirstPart();
}
this._revertPart(BACKWARD);
this._syncInputWithMask();

return;
}

super._keyPressHandler(e);

if (this._maskInputHandler) {
Expand Down Expand Up @@ -792,6 +808,20 @@ class DateBoxMask extends DateBoxBase {
}
}

_clearValueHandler(e: ValueChangedEvent & DxEvent): void {
this._isClearingValue = true;
super._clearValueHandler(e);
this._isClearingValue = false;
}
Comment on lines +811 to +815

_focusInHandler(e: DxEvent & { relatedTarget: Element | dxElementWrapper }): void {
super._focusInHandler(e);

if (this._useMaskBehavior() && !e.isDefaultPrevented() && !this._isClearingValue) {
this._selectFirstPart();
}
}

_focusOutHandler(e: DxEvent): void {
const shouldFireChangeEvent = this._useMaskBehavior() && !e.isDefaultPrevented();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,26 @@ module('Keyboard navigation', setupModule, () => {
assert.strictEqual(this.instance.option('value'), null, 'value has been cleared');
});

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('input', {
type: 'input',
originalEvent: $.Event('input', {
inputType: 'deleteContentBackward',
})
}));
Comment on lines +798 to +803

assert.ok(this.instance.option('text').length > 0, 'text is not cleared after IME composition backspace');
assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'first date part (month) is still active');
assert.strictEqual(this.instance.option('text'), '01/16/2025', 'month part is reset to minimum value, other parts unchanged');
});

QUnit.testInActiveWindow('focusout should clear search value', function(assert) {
this.keyboard.type('1');
assert.strictEqual(this.instance.option('text'), 'January 10 2012', 'text has been changed');
Expand All @@ -798,6 +818,20 @@ 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 after all parts are completed (T1331089)', function(assert) {
this.instance.option({
displayFormat: 'MM/dd/yyyy',
value: new Date(2025, 0, 1),
});

this.keyboard.type('10162025'); // Oct 16, 2025

this.$input.focusout();
this.$input.get(0).focus();

assert.deepEqual(this.keyboard.caret(), { start: 0, end: 2 }, 'month part is selected after re-focusing');
});

test('enter should clear search value', function(assert) {
this.keyboard.type('1');

Expand Down
Loading