Skip to content

refactor: open date picker overlay on element focus in fullscreen #9670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
12 changes: 12 additions & 0 deletions packages/date-picker/src/vaadin-date-picker-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,18 @@ export const DatePickerMixin = (subclass) =>
this.opened = false;
}

/**
* @protected
* @override
* */
focus(options) {
if (this._noInput && !isKeyboardActive()) {
this.open();
} else {
super.focus(options);
}
}

/**
* Opens the dropdown.
*/
Expand Down
23 changes: 23 additions & 0 deletions packages/date-picker/test/fullscreen.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ describe('fullscreen mode', () => {
expect(document.activeElement).to.not.equal(input);
});

it('should not focus input element when focusing the date picker', () => {
const spy = sinon.spy(input, 'focus');
datePicker.focus();
expect(spy.called).to.be.false;
expect(document.activeElement).to.not.equal(input);
});

it('should focus date element when focusing the date picker', async () => {
datePicker.focus();
await untilOverlayRendered(datePicker);
const cell = getFocusableCell(datePicker);
expect(cell).to.be.instanceOf(HTMLTableCellElement);
expect(cell.getAttribute('part')).to.include('today');
});

it('should not blur input element when focusing it with keyboard', () => {
const spy = sinon.spy(input, 'blur');
tabKeyDown(input);
Expand All @@ -67,6 +82,14 @@ describe('fullscreen mode', () => {
expect(document.activeElement).to.equal(input);
});

it('should not blur input element when focusing date picker with keyboard', () => {
const spy = sinon.spy(input, 'blur');
tabKeyDown(input);
datePicker.focus();
expect(spy.called).to.be.false;
expect(document.activeElement).to.equal(input);
});

it('should blur input element when opening overlay', async () => {
const spy = sinon.spy(input, 'blur');
await open(datePicker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
import { ComponentObserver } from './vaadin-component-observer.js';

export class DatePickerObserver extends ComponentObserver {
constructor(datePicker) {
super(datePicker);

this.datePicker = datePicker;
this.fullscreenFocus = false;
this.blurWhileOpened = false;

this.addListeners(datePicker);
Expand All @@ -19,14 +19,6 @@ export class DatePickerObserver extends ComponentObserver {
addListeners(datePicker) {
this.overlay = datePicker.$.overlay;

// Fullscreen date picker calls blur() to avoid focusing of the input:
// 1. first time when focus event is fired (before opening the overlay),
// 2. second time after the overlay is open, see "_onOverlayOpened".
// Here we set the flag to ignore related focusout events and then to
// mark date picker as being edited by user (to show field highlight).
// We have to use capture phase in order to catch this event early.
datePicker.addEventListener('blur', (event) => this.onBlur(event), true);

datePicker.addEventListener('opened-changed', (event) => this.onOpenedChanged(event));

this.overlay.addEventListener('focusout', (event) => this.onOverlayFocusOut(event));
Expand All @@ -40,11 +32,9 @@ export class DatePickerObserver extends ComponentObserver {
return this.datePicker._overlayContent && this.datePicker._overlayContent.contains(node);
}

onBlur(event) {
isFullscreen() {
const datePicker = this.datePicker;
if (datePicker._fullscreen && !this.isEventInOverlay(event.relatedTarget)) {
this.fullscreenFocus = true;
}
return datePicker._noInput && !isKeyboardActive();
}

onFocusIn(event) {
Expand All @@ -63,7 +53,7 @@ export class DatePickerObserver extends ComponentObserver {
}

onFocusOut(event) {
if (this.fullscreenFocus || this.isEventInOverlay(event.relatedTarget)) {
if (this.isEventInOverlay(event.relatedTarget)) {
// Do nothing, overlay is opening.
} else if (!this.datePicker.opened) {
// Field blur when closed.
Expand All @@ -83,8 +73,7 @@ export class DatePickerObserver extends ComponentObserver {
}

onOpenedChanged(event) {
if (event.detail.value === true && this.fullscreenFocus) {
this.fullscreenFocus = false;
if (event.detail.value === true && this.isFullscreen()) {
this.showOutline(this.datePicker);
}

Expand Down
5 changes: 0 additions & 5 deletions packages/field-highlighter/test/field-components.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,6 @@ describe('field components', () => {
field._fullscreen = true;
});

it('should not dispatch vaadin-highlight-show event on focus', () => {
field.focus();
expect(showSpy.callCount).to.equal(0);
});

it('should dispatch vaadin-highlight-show event on open', async () => {
field.focus();
field.click();
Expand Down