312 lines
11 KiB
HTML
312 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset="utf-8">
|
|
<title>HTML Test: focus() on shadow host with delegatesFocus</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/resources/testdriver.js"></script>
|
|
<script src="/resources/testdriver-vendor.js"></script>
|
|
<script src="resources/shadow-utils.js"></script>
|
|
|
|
<body>
|
|
<div id="host">
|
|
<div id="slottedToSecondSlot" slot="secondSlot">slottedToSecondSlot</div>
|
|
<div id="slottedToFirstSlot" slot="firstSlot">slottedToFirstSlot</div>
|
|
</div>
|
|
<div id="outside">outside</div>
|
|
</body>
|
|
|
|
<script>
|
|
const host = document.getElementById("host");
|
|
const slottedToSecondSlot = document.getElementById("slottedToSecondSlot");
|
|
const slottedToFirstSlot = document.getElementById("slottedToFirstSlot");
|
|
const outside = document.getElementById("outside");
|
|
|
|
const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
|
|
const aboveSlots = document.createElement("div");
|
|
aboveSlots.innerText = "aboveSlots";
|
|
const firstSlot = document.createElement("slot");
|
|
firstSlot.name = "firstSlot";
|
|
const secondSlot = document.createElement("slot");
|
|
secondSlot.name = "secondSlot";
|
|
const belowSlots = document.createElement("div");
|
|
belowSlots.innerText = "belowSlots";
|
|
shadowRoot.appendChild(aboveSlots);
|
|
shadowRoot.appendChild(firstSlot);
|
|
shadowRoot.appendChild(secondSlot);
|
|
shadowRoot.appendChild(belowSlots);
|
|
|
|
const elementsInFlatTreeOrder = [host, aboveSlots, firstSlot,
|
|
slottedToFirstSlot, secondSlot, slottedToSecondSlot, belowSlots, outside];
|
|
|
|
// Final structure:
|
|
// <div #host> (delegatesFocus=true)
|
|
// #shadowRoot
|
|
// <div #aboveSlots>
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot>
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot>
|
|
// <div #belowSlots>
|
|
// <div #outside>
|
|
|
|
|
|
function setAllTabIndex(value) {
|
|
setTabIndex(elementsInFlatTreeOrder, value);
|
|
}
|
|
|
|
function removeAllTabIndex() {
|
|
removeTabIndex(elementsInFlatTreeOrder);
|
|
}
|
|
|
|
function resetTabIndexAndFocus() {
|
|
removeAllTabIndex();
|
|
resetFocus(document);
|
|
resetFocus(shadowRoot);
|
|
}
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setAllTabIndex(0);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots> tabindex=0
|
|
// <slot #firstSlot> tabindex=0
|
|
// (slotted) <div #slottedToFirstSlot> tabindex=0
|
|
// <slot #secondSlot> tabindex=0
|
|
// (slotted) <div #slottedToSecondSlot> tabindex=0
|
|
// <div #belowSlots> tabindex=0
|
|
// <div #outside> tabindex=0
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus, all tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setAllTabIndex(0);
|
|
setTabIndex([host], -1);
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex =-1, all other tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setTabIndex([aboveSlots, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & no tabindex, all other tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setAllTabIndex(-1);
|
|
setTabIndex([host], 0);
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex = 0, all other tabindex=-1");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
removeAllTabIndex();
|
|
// No focusable element under #host in the flat tree.
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, null);
|
|
assert_equals(document.activeElement, document.body);
|
|
}, "focus() on host with delegatesFocus, all without tabindex");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
// First focusable = #aboveSlots
|
|
setAllTabIndex(-1);
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus, all tabindex=-1");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
removeAllTabIndex();
|
|
setTabIndex([host, belowSlots], 0);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots>
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot>
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot>
|
|
// <div #belowSlots> tabindex=0
|
|
// <div #outside>
|
|
// First focusable = #belowSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, belowSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex=0, #belowSlots with tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
removeAllTabIndex();
|
|
setTabIndex([host, outside], 0);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots>
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot>
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot>
|
|
// <div #belowSlots>
|
|
// <div #outside> tabindex=0
|
|
// No focusable element under #host in the flat tree.
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, null);
|
|
assert_equals(document.activeElement, document.body);
|
|
}, "focus() on host with delegatesFocus & tabindex=0, #outside with tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setTabIndex([host, aboveSlots, belowSlots], 0);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots> tabindex=0
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot>
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot>
|
|
// <div #belowSlots> tabindex=0
|
|
// <div #outside>
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots and #belowSlots with tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setTabIndex([host, aboveSlots], 0);
|
|
setTabIndex([belowSlots], 1);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots> tabindex=0
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot>
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot>
|
|
// <div #belowSlots> tabindex=1
|
|
// <div #outside>
|
|
// First focusable = #aboveSlots
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, aboveSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots with tabindex=0 and #belowSlots with tabindex=1");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setTabIndex([host, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
|
|
// Structure:
|
|
// <div #host> (delegatesFocus=true) tabindex=0
|
|
// #shadowRoot
|
|
// <div #aboveSlots>
|
|
// <slot #firstSlot>
|
|
// (slotted) <div #slottedToFirstSlot> tabindex=0
|
|
// <slot #secondSlot>
|
|
// (slotted) <div #slottedToSecondSlot> tabindex=0
|
|
// <div #belowSlots> tabindex=0
|
|
// <div #outside>
|
|
// First focusable = #slottedToFirstSlot
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, belowSlots);
|
|
assert_equals(document.activeElement, host);
|
|
}, "focus() on host with delegatesFocus & tabindex=0, #slottedToFirstSlot, #slottedToSecondSlot, #belowSlots with tabindex=0");
|
|
|
|
test(() => {
|
|
resetTabIndexAndFocus();
|
|
setTabIndex([aboveSlots, belowSlots], 0);
|
|
belowSlots.focus();
|
|
host.focus();
|
|
assert_equals(shadowRoot.activeElement, belowSlots);
|
|
}, "focus() on host with delegatesFocus and already-focused non-first shadow descendant");
|
|
|
|
function createNestedHosts(innerDelegatesFocus) {
|
|
// Structure:
|
|
// <div> outerHost
|
|
// <input> outerLightChild
|
|
// #shadowRoot outerShadow delegatesFocus=true
|
|
// <span> innerHost
|
|
// #shadowRoot inneShadow delegatesFocus=true/false
|
|
// <input> innerShadowChild
|
|
// <input> outerShadowChild
|
|
const outerHost = document.createElement('div');
|
|
const outerLightChild = document.createElement('input');
|
|
outerHost.appendChild(outerLightChild);
|
|
const innerHost = document.createElement('span');
|
|
const outerShadow = outerHost.attachShadow({mode: 'closed', delegatesFocus:true});
|
|
outerShadow.appendChild(innerHost);
|
|
const outerShadowChild = document.createElement('input');
|
|
outerShadow.appendChild(outerShadowChild);
|
|
|
|
const innerShadow = innerHost.attachShadow({mode: 'closed', delegatesFocus:innerDelegatesFocus});
|
|
const innerShadowChild = document.createElement('input');
|
|
innerShadow.appendChild(innerShadowChild);
|
|
|
|
document.body.insertBefore(outerHost, document.body.firstChild);
|
|
return {outerHost: outerHost,
|
|
outerLightChild: outerLightChild,
|
|
outerShadow: outerShadow,
|
|
outerShadowChild: outerShadowChild,
|
|
innerHost: innerHost,
|
|
innerShadow: innerShadow,
|
|
innerShadowChild: innerShadowChild};
|
|
}
|
|
|
|
test(() => {
|
|
const dom = createNestedHosts(false);
|
|
dom.outerHost.focus();
|
|
assert_equals(document.activeElement, dom.outerHost);
|
|
assert_equals(dom.outerShadow.activeElement, dom.outerShadowChild);
|
|
}, 'focus() on host with delegatesFocus with another host with no delegatesFocus and a focusable child');
|
|
|
|
test(() => {
|
|
const dom = createNestedHosts(true);
|
|
dom.outerHost.focus();
|
|
assert_equals(document.activeElement, dom.outerHost);
|
|
assert_equals(dom.outerShadow.activeElement, dom.innerHost);
|
|
assert_equals(dom.innerShadow.activeElement, dom.innerShadowChild);
|
|
}, 'focus() on host with delegatesFocus with another host with delegatesFocus and a focusable child');
|
|
|
|
test(() => {
|
|
// Structure:
|
|
// <div> host
|
|
// #shadowRoot root delegatesFocus=true
|
|
// <slot>
|
|
// (slotted) <div>
|
|
// <input>
|
|
// <input #firstFocusable>
|
|
const host = document.createElement("div");
|
|
const slotted = document.createElement("div");
|
|
slotted.appendChild(document.createElement("input"));
|
|
host.appendChild(slotted);
|
|
|
|
const root = host.attachShadow({mode: "open", delegatesFocus: true});
|
|
|
|
const firstFocusable = document.createElement("input");
|
|
root.innerHTML = "<slot>";
|
|
root.appendChild(firstFocusable);
|
|
|
|
document.body.appendChild(host);
|
|
|
|
host.focus();
|
|
assert_equals(document.activeElement, host);
|
|
assert_equals(root.activeElement, firstFocusable);
|
|
}, "focus() on host with delegatesFocus and slotted focusable children");
|
|
</script>
|
|
|