Skip to content

Commit dd243e0

Browse files
fix: save focus if target is set while popover is open (#9826) (CP: 24.7) (#9828)
* fix: save focus if target is set while popover is open (#9826) * fix: update tests to get input element from the overlay * fix: add missing oneEvent import --------- Co-authored-by: Ugur Saglam <106508695+ugur-vaadin@users.noreply.github.com>
1 parent a241a40 commit dd243e0

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
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 { css, 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';
@@ -107,6 +108,21 @@ class PopoverOverlay extends PopoverOverlayMixin(DirMixin(ThemableMixin(PolylitM
107108
</div>
108109
`;
109110
}
111+
112+
/** @protected */
113+
updated(props) {
114+
super.updated(props);
115+
116+
if (props.has('restoreFocusNode') && this.opened) {
117+
// Save focus to be restored when target is set while opened
118+
if (this.restoreFocusNode && isElementFocused(this.restoreFocusNode.focusElement || this.restoreFocusNode)) {
119+
this.__focusRestorationController.saveFocus();
120+
} else if (!this.restoreFocusNode) {
121+
// Do not restore focus when target is cleared while opened
122+
this.__focusRestorationController.focusNode = null;
123+
}
124+
}
125+
}
110126
}
111127

112128
defineCustomElement(PopoverOverlay);

packages/popover/test/basic.test.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@vaadin/chai-plugins';
2-
import { esc, fixtureSync, nextRender, nextUpdate, outsideClick } from '@vaadin/testing-helpers';
2+
import { esc, fixtureSync, nextRender, nextUpdate, oneEvent, outsideClick } from '@vaadin/testing-helpers';
33
import sinon from 'sinon';
44
import './not-animated-styles.js';
55
import '../vaadin-popover.js';
@@ -91,6 +91,64 @@ describe('popover', () => {
9191
await nextUpdate(popover);
9292
expect(overlay.restoreFocusNode).to.eql(target);
9393
});
94+
95+
it('should restore focus when target is set on already opened popover', async () => {
96+
// Focus target before opening
97+
target.focus();
98+
99+
// Open popover
100+
popover.renderer = (root) => {
101+
if (!root.firstChild) {
102+
const input = document.createElement('input');
103+
root.appendChild(input);
104+
}
105+
};
106+
popover.opened = true;
107+
await oneEvent(overlay, 'vaadin-overlay-open');
108+
109+
// Set target
110+
popover.target = target;
111+
await nextUpdate(popover);
112+
113+
overlay.querySelector('input').focus();
114+
115+
// Close popover
116+
popover.opened = false;
117+
await nextRender();
118+
119+
expect(document.activeElement).to.equal(target);
120+
});
121+
122+
it('should not restore focus when target is cleared on already opened popover', async () => {
123+
// Focus target before opening
124+
target.focus();
125+
126+
// Open popover
127+
popover.renderer = (root) => {
128+
if (!root.firstChild) {
129+
const input = document.createElement('input');
130+
root.appendChild(input);
131+
}
132+
};
133+
popover.opened = true;
134+
await oneEvent(overlay, 'vaadin-overlay-open');
135+
136+
// Set target
137+
popover.target = target;
138+
await nextUpdate(popover);
139+
140+
overlay.querySelector('input').focus();
141+
142+
// Clear target
143+
popover.target = null;
144+
await nextUpdate(popover);
145+
146+
// Close popover
147+
popover.opened = false;
148+
await nextRender();
149+
150+
expect(document.activeElement).to.not.equal(target);
151+
});
94152
});
95153

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

0 commit comments

Comments
 (0)