Skip to content

Commit f976796

Browse files
fix: blur active element on step buttons touchend (#7512) (#7514)
Co-authored-by: Serhii Kulykov <iamkulykov@gmail.com>
1 parent d9797c9 commit f976796

2 files changed

Lines changed: 88 additions & 0 deletions

File tree

packages/number-field/src/vaadin-number-field-mixin.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (c) 2021 - 2023 Vaadin Ltd.
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
6+
import { getDeepActiveElement } from '@vaadin/a11y-base/src/focus-utils.js';
67
import { InputController } from '@vaadin/field-base/src/input-controller.js';
78
import { InputFieldMixin } from '@vaadin/field-base/src/input-field-mixin.js';
89
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
@@ -194,6 +195,7 @@ export const NumberFieldMixin = (superClass) =>
194195
// it means scrolling is in progress, therefore we shouldn't update field value.
195196
if (e.cancelable) {
196197
e.preventDefault();
198+
this.__blurActiveElement();
197199
this._decreaseValue();
198200
}
199201
}
@@ -204,10 +206,22 @@ export const NumberFieldMixin = (superClass) =>
204206
// it means scrolling is in progress, therefore we shouldn't update field value.
205207
if (e.cancelable) {
206208
e.preventDefault();
209+
this.__blurActiveElement();
207210
this._increaseValue();
208211
}
209212
}
210213

214+
/** @private */
215+
__blurActiveElement() {
216+
// If another element is focused, blur it on step button touch to hide
217+
// the mobile keyboard that might still be open for the other element.
218+
// See https://github.com/vaadin/web-components/issues/7494
219+
const activeElement = getDeepActiveElement();
220+
if (activeElement && activeElement !== this.inputElement) {
221+
activeElement.blur();
222+
}
223+
}
224+
211225
/** @protected */
212226
_onDecreaseButtonClick() {
213227
this._decreaseValue();

packages/number-field/test/value-control-buttons.common.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,3 +656,77 @@ describe('value control buttons', () => {
656656
});
657657
});
658658
});
659+
660+
describe('multiple fields', () => {
661+
let container, fields;
662+
663+
beforeEach(async () => {
664+
container = fixtureSync(`
665+
<div>
666+
<vaadin-number-field step-buttons-visible></vaadin-number-field>
667+
<vaadin-number-field step-buttons-visible></vaadin-number-field>
668+
</div>
669+
`);
670+
await nextRender();
671+
fields = [...container.children];
672+
});
673+
674+
['increase', 'decrease'].forEach((type) => {
675+
describe(`${type} button`, () => {
676+
let button;
677+
678+
beforeEach(() => {
679+
button = fields[1].shadowRoot.querySelector(`[part=${type}-button]`);
680+
});
681+
682+
it(`should blur the other field on ${type} button touchend`, () => {
683+
const input = fields[0].inputElement;
684+
input.focus();
685+
686+
const spy = sinon.spy(input, 'blur');
687+
const e = new CustomEvent('touchend', { cancelable: true });
688+
button.dispatchEvent(e);
689+
690+
expect(spy).to.be.calledOnce;
691+
});
692+
693+
it(`should not blur the other field on ${type} button touchend if not cancelable`, () => {
694+
const input = fields[0].inputElement;
695+
input.focus();
696+
697+
const spy = sinon.spy(input, 'blur');
698+
const e = new CustomEvent('touchend', { cancelable: false });
699+
button.dispatchEvent(e);
700+
701+
expect(spy).to.be.not.called;
702+
});
703+
704+
it(`should not blur the field on its own ${type} button touchend`, () => {
705+
const input = fields[1].inputElement;
706+
input.focus();
707+
708+
const spy = sinon.spy(input, 'blur');
709+
const e = new CustomEvent('touchend', { cancelable: true });
710+
button.dispatchEvent(e);
711+
712+
expect(spy).to.be.not.called;
713+
});
714+
715+
it(`should not blur the field on its own ${type} button touchend when in shadow root`, () => {
716+
// Move the field into shadow root to verify the deep active element logic
717+
const inner = document.createElement('div');
718+
inner.attachShadow({ mode: 'open' });
719+
container.appendChild(inner);
720+
inner.shadowRoot.appendChild(fields[1]);
721+
722+
const input = fields[1].inputElement;
723+
input.focus();
724+
725+
const e = new CustomEvent('touchend', { cancelable: true });
726+
button.dispatchEvent(e);
727+
728+
expect(inner.shadowRoot.activeElement).to.be.equal(input);
729+
});
730+
});
731+
});
732+
});

0 commit comments

Comments
 (0)