Skip to content

Commit

Permalink
feat: add autofocus property for non-modal popovers (#7610)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Aug 2, 2024
1 parent 0c4b0b2 commit e8d0700
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 1 deletion.
6 changes: 6 additions & 0 deletions packages/popover/src/vaadin-popover.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ declare class Popover extends PopoverPositionMixin(
*/
accessibleNameRef: string | null | undefined;

/**
* When true, the popover content automatically receives focus after
* it is opened. Modal popovers use this behavior by default.
*/
autofocus: boolean;

/**
* Height to be set on the overlay content.
*
Expand Down
16 changes: 16 additions & 0 deletions packages/popover/src/vaadin-popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,14 @@ class Popover extends PopoverPositionMixin(
type: String,
},

/**
* When true, the popover content automatically receives focus after
* it is opened. Modal popovers use this behavior by default.
*/
autofocus: {
type: Boolean,
},

/**
* Height to be set on the overlay content.
*
Expand Down Expand Up @@ -425,6 +433,7 @@ class Popover extends PopoverPositionMixin(
.restoreFocusNode="${this.target}"
@vaadin-overlay-escape-press="${this.__onEscapePress}"
@vaadin-overlay-outside-click="${this.__onOutsideClick}"
@vaadin-overlay-open="${this.__onOverlayOpened}"
@vaadin-overlay-closed="${this.__onOverlayClosed}"
></vaadin-popover-overlay>
`;
Expand Down Expand Up @@ -782,6 +791,13 @@ class Popover extends PopoverPositionMixin(
this.opened = event.detail.value;
}

/** @private */
__onOverlayOpened() {
if (this.autofocus && !this.modal) {
this._overlayElement.$.overlay.focus();
}
}

/** @private */
__onOverlayClosed() {
// Reset restoring focus state after a timeout to make sure focus was restored
Expand Down
32 changes: 31 additions & 1 deletion packages/popover/test/a11y.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { expect } from '@esm-bundle/chai';
import { esc, fixtureSync, focusout, nextRender, nextUpdate, outsideClick, tab } from '@vaadin/testing-helpers';
import {
esc,
fixtureSync,
focusout,
nextRender,
nextUpdate,
oneEvent,
outsideClick,
tab,
} from '@vaadin/testing-helpers';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import './not-animated-styles.js';
Expand Down Expand Up @@ -128,6 +137,27 @@ describe('a11y', () => {
});
});

describe('autofocus', () => {
let spy;

beforeEach(() => {
spy = sinon.spy(overlay.$.overlay, 'focus');
});

it('should not move focus to the overlay content when opened by default', async () => {
target.click();
await oneEvent(overlay, 'vaadin-overlay-open');
expect(spy).to.not.be.called;
});

it('should move focus to the overlay content when opened if autofocus is true', async () => {
popover.autofocus = true;
target.click();
await oneEvent(overlay, 'vaadin-overlay-open');
expect(spy).to.be.calledOnce;
});
});

describe('focus restoration', () => {
describe('focus trigger', () => {
beforeEach(async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/popover/test/typings/popover.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ assertType<string>(popover.contentHeight);
assertType<string>(popover.contentWidth);
assertType<string>(popover.overlayClass);
assertType<string>(popover.overlayRole);
assertType<boolean>(popover.autofocus);
assertType<boolean>(popover.opened);
assertType<boolean>(popover.modal);
assertType<boolean>(popover.withBackdrop);
Expand Down

0 comments on commit e8d0700

Please sign in to comment.