Skip to content
This repository was archived by the owner on Dec 19, 2024. It is now read-only.

Commit 14bd3b7

Browse files
authored
Merge pull request #305 from PolymerElements/composed-contains
Restore focus if the node with focus in the overlay is distributed into it.
2 parents c1f8563 + 1eb7ff4 commit 14bd3b7

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

iron-overlay-behavior.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ export const IronOverlayBehaviorImpl = {
451451
// button outside the overlay).
452452
var activeElement = this._manager.deepActiveElement;
453453
if (activeElement === document.body ||
454-
dom(this).deepContains(activeElement)) {
454+
composedContains(this, activeElement)) {
455455
this.__restoreFocusNode.focus();
456456
}
457457
}
@@ -763,6 +763,18 @@ export const IronOverlayBehaviorImpl = {
763763

764764
};
765765

766+
const composedParent = node =>
767+
node.assignedSlot || node.parentNode || node.host;
768+
769+
const composedContains = (ancestor, descendant) => {
770+
for (let element = descendant; element; element = composedParent(element)) {
771+
if (element === ancestor) {
772+
return true;
773+
}
774+
}
775+
return false;
776+
};
777+
766778
/**
767779
Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden
768780
or shown, and displays on top of other content. It includes an optional

test/has-shadow-overlay.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
@license
3+
Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
4+
This code may only be used under the BSD style license found at
5+
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
6+
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
7+
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
8+
part of the polymer project is also subject to an additional IP rights grant
9+
found at http://polymer.github.io/PATENTS.txt
10+
*/
11+
import '@polymer/polymer/polymer-legacy.js';
12+
13+
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
14+
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
15+
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
16+
17+
import {IronOverlayBehavior} from '../iron-overlay-behavior.js';
18+
19+
const trivialOverlayLocalName =
20+
`trivial-overlay-${Math.random().toString(32).substring(2)}`;
21+
22+
Polymer({
23+
_template: html`
24+
<style>
25+
:host {
26+
background: white;
27+
color: black;
28+
border: 1px solid black;
29+
}
30+
</style>
31+
32+
<slot></slot>
33+
`,
34+
35+
is: trivialOverlayLocalName,
36+
behaviors: [IronOverlayBehavior],
37+
});
38+
39+
Polymer({
40+
_template: html``,
41+
42+
is: 'has-shadow-overlay',
43+
44+
attached() {
45+
// Set up the shadow root:
46+
// ```html
47+
// <trivialOverlayLocalName>
48+
// <slot></slot>
49+
// </trivialOverlayLocalName>
50+
// ```
51+
this.overlay = document.createElement(trivialOverlayLocalName);
52+
dom(this.overlay).appendChild(document.createElement('slot'));
53+
dom(this.root).appendChild(this.overlay);
54+
},
55+
});

test/iron-overlay-behavior.html

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ <h2>Focusables (with tabindex)</h2>
127127
</template>
128128
</test-fixture>
129129

130+
<test-fixture id="has-shadow-overlay">
131+
<template>
132+
<has-shadow-overlay>
133+
<button>button</button>
134+
</has-shadow-overlay>
135+
</template>
136+
</test-fixture>
137+
130138
<test-buttons id="buttons"></test-buttons>
131139
<input id="focusInput" placeholder="focus input">
132140

@@ -136,6 +144,7 @@ <h2>Focusables (with tabindex)</h2>
136144
import './test-overlay2.js';
137145
import './test-buttons.js';
138146
import './test-menu-button.js';
147+
import './has-shadow-overlay.js';
139148

140149
import {dom, flush} from '@polymer/polymer/lib/legacy/polymer.dom.js';
141150
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';
@@ -984,6 +993,38 @@ <h2>Focusables (with tabindex)</h2>
984993
});
985994
});
986995

996+
test(
997+
'overlay returns focus on close (distributed focusable content)',
998+
function(done) {
999+
var hasShadowOverlay = fixture('has-shadow-overlay');
1000+
hasShadowOverlay.overlay.restoreFocusOnClose = true;
1001+
1002+
var button = dom(hasShadowOverlay).querySelector('button');
1003+
1004+
var focusable = document.getElementById('focusInput');
1005+
focusable.focus();
1006+
1007+
runAfterOpen(hasShadowOverlay.overlay, async function() {
1008+
button.focus();
1009+
assert.equal(
1010+
IronOverlayManager.deepActiveElement,
1011+
button,
1012+
'the distributed content is focused');
1013+
1014+
runAfterClose(hasShadowOverlay.overlay, async function() {
1015+
// Focus should be restored to the input even though the
1016+
// focused button inside the overlay was not a shadow-including
1017+
// descendant of the overlay.
1018+
assert.equal(
1019+
IronOverlayManager.deepActiveElement,
1020+
focusable,
1021+
'focus restored to focusable');
1022+
1023+
done();
1024+
});
1025+
});
1026+
});
1027+
9871028
test('avoids restoring focus if focus changed', function(done) {
9881029
var button0 = document.getElementById('buttons').$.button0;
9891030
var button1 = document.getElementById('buttons').$.button1;

0 commit comments

Comments
 (0)