Skip to content

Commit 1a04a94

Browse files
authored
fix: save focus if target is set while popover is open (#9826)
1 parent b4d90c2 commit 1a04a94

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

packages/popover/src/vaadin-popover-overlay.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
55
*/
66
import { html, LitElement } from 'lit';
7+
import { isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js';
78
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
89
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
910
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
@@ -44,6 +45,21 @@ class PopoverOverlay extends PopoverOverlayMixin(
4445
`;
4546
}
4647

48+
/** @protected */
49+
updated(props) {
50+
super.updated(props);
51+
52+
if (props.has('restoreFocusNode') && this.opened) {
53+
// Save focus to be restored when target is set while opened
54+
if (this.restoreFocusNode && isElementFocused(this.restoreFocusNode.focusElement || this.restoreFocusNode)) {
55+
this.__focusRestorationController.saveFocus();
56+
} else if (!this.restoreFocusNode) {
57+
// Do not restore focus when target is cleared while opened
58+
this.__focusRestorationController.focusNode = null;
59+
}
60+
}
61+
}
62+
4763
/**
4864
* @override
4965
* @protected

packages/popover/test/basic.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,64 @@ describe('popover', () => {
8585
await nextUpdate(popover);
8686
expect(overlay.restoreFocusNode).to.eql(target);
8787
});
88+
89+
it('should restore focus when target is set on already opened popover', async () => {
90+
// Focus target before opening
91+
target.focus();
92+
93+
// Open popover
94+
popover.renderer = (root) => {
95+
if (!root.firstChild) {
96+
const input = document.createElement('input');
97+
root.appendChild(input);
98+
}
99+
};
100+
popover.opened = true;
101+
await oneEvent(overlay, 'vaadin-overlay-open');
102+
103+
// Set target
104+
popover.target = target;
105+
await nextUpdate(popover);
106+
107+
popover.querySelector('input').focus();
108+
109+
// Close popover
110+
popover.opened = false;
111+
await nextRender();
112+
113+
expect(document.activeElement).to.equal(target);
114+
});
115+
116+
it('should not restore focus when target is cleared on already opened popover', async () => {
117+
// Focus target before opening
118+
target.focus();
119+
120+
// Open popover
121+
popover.renderer = (root) => {
122+
if (!root.firstChild) {
123+
const input = document.createElement('input');
124+
root.appendChild(input);
125+
}
126+
};
127+
popover.opened = true;
128+
await oneEvent(overlay, 'vaadin-overlay-open');
129+
130+
// Set target
131+
popover.target = target;
132+
await nextUpdate(popover);
133+
134+
popover.querySelector('input').focus();
135+
136+
// Clear target
137+
popover.target = null;
138+
await nextUpdate(popover);
139+
140+
// Close popover
141+
popover.opened = false;
142+
await nextRender();
143+
144+
expect(document.activeElement).to.not.equal(target);
145+
});
88146
});
89147

90148
describe('for', () => {

0 commit comments

Comments
 (0)