diff --git a/packages/date-picker/src/vaadin-date-picker-mixin.js b/packages/date-picker/src/vaadin-date-picker-mixin.js index a556ca9f877..44cd1b89e36 100644 --- a/packages/date-picker/src/vaadin-date-picker-mixin.js +++ b/packages/date-picker/src/vaadin-date-picker-mixin.js @@ -616,6 +616,24 @@ export const DatePickerMixin = (subclass) => return !this._shouldKeepFocusRing; } + /** + * Override method inherited from `ClearButtonMixin` + * to not blur on clear button mousedown when opened + * so that focus remains in the input field. + * + * @param {Event} event + * @return {boolean} + * @protected + * @override + */ + _shouldKeepFocusOnClearMousedown(event) { + if (this.opened) { + return true; + } + + return super._shouldKeepFocusOnClearMousedown(event); + } + /** * Override method inherited from `FocusMixin` * to prevent removing the `focused` attribute: diff --git a/packages/date-picker/test/basic.test.js b/packages/date-picker/test/basic.test.js index be12b5aafe0..79a4d5a7b33 100644 --- a/packages/date-picker/test/basic.test.js +++ b/packages/date-picker/test/basic.test.js @@ -362,6 +362,30 @@ describe('clear button', () => { click(clearButton); expect(datePicker.opened).to.be.not.ok; }); + + it('should not prevent default on clear button mousedown if input is not focused', () => { + datePicker.value = '2001-01-01'; + const event = new CustomEvent('mousedown', { cancelable: true }); + clearButton.dispatchEvent(event); + expect(event.defaultPrevented).to.be.false; + }); + + it('should prevent default on clear button mousedown if input is focused', () => { + datePicker.value = '2001-01-01'; + datePicker.inputElement.focus(); + const event = new CustomEvent('mousedown', { cancelable: true }); + clearButton.dispatchEvent(event); + expect(event.defaultPrevented).to.be.true; + }); + + it('should prevent default on clear button mousedown when opened', async () => { + datePicker.value = '2001-01-01'; + await open(datePicker); + datePicker.inputElement.blur(); + const event = new CustomEvent('mousedown', { cancelable: true }); + clearButton.dispatchEvent(event); + expect(event.defaultPrevented).to.be.true; + }); }); describe('initial value attribute', () => { diff --git a/packages/field-base/src/clear-button-mixin.js b/packages/field-base/src/clear-button-mixin.js index 0fe18bb8f94..c22f4192f34 100644 --- a/packages/field-base/src/clear-button-mixin.js +++ b/packages/field-base/src/clear-button-mixin.js @@ -3,6 +3,7 @@ * Copyright (c) 2021 - 2025 Vaadin Ltd. * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ +import { isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js'; import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js'; import { isTouch } from '@vaadin/component-base/src/browser-utils.js'; import { InputMixin } from './input-mixin.js'; @@ -71,7 +72,10 @@ export const ClearButtonMixin = (superclass) => * @protected */ _onClearButtonMouseDown(event) { - event.preventDefault(); + if (this._shouldKeepFocusOnClearMousedown(event)) { + event.preventDefault(); + } + if (!isTouch) { this.inputElement.focus(); } @@ -109,4 +113,17 @@ export const ClearButtonMixin = (superclass) => this.inputElement.dispatchEvent(new Event('input', { bubbles: true, composed: true })); this.inputElement.dispatchEvent(new Event('change', { bubbles: true })); } + + /** + * Whether to keep focus inside the field on clear button + * mousedown. By default, if the field has focus, it gets + * preserved using `preventDefault()` on mousedown event + * in order to avoid blur and change events. + * + * @protected + * @return {boolean} + */ + _shouldKeepFocusOnClearMousedown() { + return isElementFocused(this.inputElement); + } }; diff --git a/packages/field-base/test/clear-button-mixin.test.js b/packages/field-base/test/clear-button-mixin.test.js index 2d4cb57ba8d..5f5a4e06709 100644 --- a/packages/field-base/test/clear-button-mixin.test.js +++ b/packages/field-base/test/clear-button-mixin.test.js @@ -67,7 +67,14 @@ describe('ClearButtonMixin', () => { expect(spy.calledOnce).to.be.true; }); - it('should prevent default on clear button mousedown', () => { + it('should not prevent default on clear button mousedown if input is not focused', () => { + const event = new CustomEvent('mousedown', { cancelable: true }); + clearButton.dispatchEvent(event); + expect(event.defaultPrevented).to.be.false; + }); + + it('should prevent default on clear button mousedown if input is focused', () => { + input.focus(); const event = new CustomEvent('mousedown', { cancelable: true }); clearButton.dispatchEvent(event); expect(event.defaultPrevented).to.be.true;