Skip to content

Commit e8d0700

Browse files
authored
feat: add autofocus property for non-modal popovers (#7610)
1 parent 0c4b0b2 commit e8d0700

File tree

4 files changed

+54
-1
lines changed

4 files changed

+54
-1
lines changed

packages/popover/src/vaadin-popover.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ declare class Popover extends PopoverPositionMixin(
9191
*/
9292
accessibleNameRef: string | null | undefined;
9393

94+
/**
95+
* When true, the popover content automatically receives focus after
96+
* it is opened. Modal popovers use this behavior by default.
97+
*/
98+
autofocus: boolean;
99+
94100
/**
95101
* Height to be set on the overlay content.
96102
*

packages/popover/src/vaadin-popover.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ class Popover extends PopoverPositionMixin(
210210
type: String,
211211
},
212212

213+
/**
214+
* When true, the popover content automatically receives focus after
215+
* it is opened. Modal popovers use this behavior by default.
216+
*/
217+
autofocus: {
218+
type: Boolean,
219+
},
220+
213221
/**
214222
* Height to be set on the overlay content.
215223
*
@@ -425,6 +433,7 @@ class Popover extends PopoverPositionMixin(
425433
.restoreFocusNode="${this.target}"
426434
@vaadin-overlay-escape-press="${this.__onEscapePress}"
427435
@vaadin-overlay-outside-click="${this.__onOutsideClick}"
436+
@vaadin-overlay-open="${this.__onOverlayOpened}"
428437
@vaadin-overlay-closed="${this.__onOverlayClosed}"
429438
></vaadin-popover-overlay>
430439
`;
@@ -782,6 +791,13 @@ class Popover extends PopoverPositionMixin(
782791
this.opened = event.detail.value;
783792
}
784793

794+
/** @private */
795+
__onOverlayOpened() {
796+
if (this.autofocus && !this.modal) {
797+
this._overlayElement.$.overlay.focus();
798+
}
799+
}
800+
785801
/** @private */
786802
__onOverlayClosed() {
787803
// Reset restoring focus state after a timeout to make sure focus was restored

packages/popover/test/a11y.test.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { expect } from '@esm-bundle/chai';
2-
import { esc, fixtureSync, focusout, nextRender, nextUpdate, outsideClick, tab } from '@vaadin/testing-helpers';
2+
import {
3+
esc,
4+
fixtureSync,
5+
focusout,
6+
nextRender,
7+
nextUpdate,
8+
oneEvent,
9+
outsideClick,
10+
tab,
11+
} from '@vaadin/testing-helpers';
312
import { sendKeys } from '@web/test-runner-commands';
413
import sinon from 'sinon';
514
import './not-animated-styles.js';
@@ -128,6 +137,27 @@ describe('a11y', () => {
128137
});
129138
});
130139

140+
describe('autofocus', () => {
141+
let spy;
142+
143+
beforeEach(() => {
144+
spy = sinon.spy(overlay.$.overlay, 'focus');
145+
});
146+
147+
it('should not move focus to the overlay content when opened by default', async () => {
148+
target.click();
149+
await oneEvent(overlay, 'vaadin-overlay-open');
150+
expect(spy).to.not.be.called;
151+
});
152+
153+
it('should move focus to the overlay content when opened if autofocus is true', async () => {
154+
popover.autofocus = true;
155+
target.click();
156+
await oneEvent(overlay, 'vaadin-overlay-open');
157+
expect(spy).to.be.calledOnce;
158+
});
159+
});
160+
131161
describe('focus restoration', () => {
132162
describe('focus trigger', () => {
133163
beforeEach(async () => {

packages/popover/test/typings/popover.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ assertType<string>(popover.contentHeight);
3434
assertType<string>(popover.contentWidth);
3535
assertType<string>(popover.overlayClass);
3636
assertType<string>(popover.overlayRole);
37+
assertType<boolean>(popover.autofocus);
3738
assertType<boolean>(popover.opened);
3839
assertType<boolean>(popover.modal);
3940
assertType<boolean>(popover.withBackdrop);

0 commit comments

Comments
 (0)