Skip to content

Commit fe0cce5

Browse files
committed
Add referenceTarget support and WPT tests.
1 parent 74bbfe4 commit fe0cce5

40 files changed

+5151
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Reference Target tentative tests
2+
3+
Tests in this directory are for the proposed Reference Target feature for
4+
shadow dom. This is not yet standardized and browsers should not be expected to
5+
pass these tests.
6+
7+
See the explainer at
8+
https://github.com/WICG/aom/blob/gh-pages/reference-target-explainer.md for
9+
more information about the API.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Popover content
2+
3+
FAIL ShadowRoot ReferenceTarget works with anchor attribute. assert_equals: popover.offsetLeft expected 100 but got 682
4+
FAIL ShadowRoot ReferenceTarget works with anchor attribute via options. assert_equals: popover.offsetLeft expected 100 but got 682
5+
FAIL ShadowRoot ReferenceTarget works with .anchorElement property. assert_equals: popover.offsetLeft expected 100 but got 682
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<!DOCTYPE HTML><!-- webkit-test-runner [ ShadowRootReferenceTargetEnabled=true ] -->
2+
<html>
3+
4+
<head>
5+
<script src="/resources/testharness.js"></script>
6+
<script src="/resources/testharnessreport.js"></script>
7+
<script src="/resources/testdriver.js"></script>
8+
<script src="/resources/testdriver-vendor.js"></script>
9+
<script src="/resources/testdriver-actions.js"></script>
10+
<script src="/wai-aria/scripts/aria-utils.js"></script>
11+
<style>
12+
body {
13+
margin: 0;
14+
}
15+
16+
[popover] {
17+
position: absolute;
18+
left: anchor(right);
19+
top: anchor(top);
20+
margin: 0;
21+
}
22+
</style>
23+
</head>
24+
25+
<body>
26+
<div>
27+
<x-anchor id="x-anchor-1">
28+
<template shadowrootmode="open" shadowrootreferencetarget="anchor">
29+
<style>
30+
div {
31+
width: 100px;
32+
height: 100px;
33+
}
34+
35+
#anchor {
36+
background-color: yellow;
37+
}
38+
</style>
39+
40+
<div></div>
41+
<div id="anchor"></div>
42+
</template>
43+
</x-anchor>
44+
<div id="popover-1" popover anchor="x-anchor-1">Popover content</div>
45+
</div>
46+
47+
<div>
48+
<x-anchor-2 id="x-anchor-2"></x-anchor-2>
49+
<div id="popover-2" popover anchor="x-anchor-2">Popover content</div>
50+
</div>
51+
<script>
52+
const customAnchor = document.querySelector('x-anchor-2');
53+
customAnchor.attachShadow({ mode: 'open', referenceTarget: 'anchor' });
54+
customAnchor.shadowRoot.innerHTML =`
55+
<style>
56+
div {
57+
width: 100px;
58+
height: 100px;
59+
}
60+
61+
#anchor {
62+
background-color: yellow;
63+
}
64+
</style>
65+
66+
<div></div>
67+
<div id="anchor"></div>`;
68+
</script>
69+
70+
<div>
71+
<x-anchor id="x-anchor-3">
72+
<template shadowrootmode="open" shadowrootreferencetarget="anchor">
73+
<style>
74+
div {
75+
width: 100px;
76+
height: 100px;
77+
}
78+
79+
#anchor {
80+
background-color: yellow;
81+
}
82+
</style>
83+
84+
<div></div>
85+
<div id="anchor"></div>
86+
</template>
87+
</x-anchor>
88+
<div id="popover-3" popover>Popover content</div>
89+
</div>
90+
91+
<script>
92+
function testPopoverAnchor(id, name) {
93+
test(function () {
94+
const popover = document.getElementById(id);
95+
popover.showPopover();
96+
assert_equals(popover.offsetLeft, 100, "popover.offsetLeft");
97+
assert_equals(popover.offsetTop, 100, "popover.offsetTop");
98+
// Clean up the test context for future tests.
99+
popover.parentElement.remove();
100+
}, name);
101+
}
102+
testPopoverAnchor('popover-1', "ShadowRoot ReferenceTarget works with anchor attribute.");
103+
testPopoverAnchor('popover-2', "ShadowRoot ReferenceTarget works with anchor attribute via options.");
104+
document.getElementById('popover-3').anchorElement = document.getElementById('x-anchor-3');
105+
testPopoverAnchor('popover-3', "ShadowRoot ReferenceTarget works with .anchorElement property.");
106+
</script>
107+
</body>
108+
109+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Label 2
2+
3+
PASS Label 1
4+
PASS Label 2
5+
PASS Label 3
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<!DOCTYPE HTML><!-- webkit-test-runner [ ShadowRootReferenceTargetEnabled=true ] -->
2+
<html>
3+
4+
<head>
5+
<script src="/resources/testharness.js"></script>
6+
<script src="/resources/testharnessreport.js"></script>
7+
<script src="/resources/testdriver.js"></script>
8+
<script src="/resources/testdriver-vendor.js"></script>
9+
<script src="/resources/testdriver-actions.js"></script>
10+
<script src="/wai-aria/scripts/aria-utils.js"></script>
11+
</head>
12+
13+
<body>
14+
15+
<!-- 1. Declaratively defined custom label -->
16+
<x-label1 id="x-label1">
17+
<template shadowrootmode="closed" shadowrootreferencetarget="label1">
18+
<span>FAIL IF INCLUDED</span>
19+
<label id="label1">Label 1</label>
20+
</template>
21+
</x-label1>
22+
<input class="ex" aria-labelledby="x-label1" data-expectedlabel="Label 1">
23+
24+
<!-- 2. Imperatively defined custom element -->
25+
<script>
26+
customElements.define(
27+
"x-label2",
28+
class XLabel2 extends HTMLElement {
29+
constructor() {
30+
super();
31+
32+
this.shadowRoot_ = this.attachShadow({ mode: "closed" });
33+
this.shadowRoot_.innerHTML = `
34+
<span>FAIL IF INCLUDED</span>
35+
<label id="label2">
36+
<slot></slot>
37+
</label>
38+
`;
39+
this.shadowRoot_.referenceTarget = "label2";
40+
}
41+
}
42+
);
43+
</script>
44+
<x-label2 id="x-label2">Label 2</x-label2>
45+
<input class="ex" aria-labelledby="x-label2" data-expectedlabel="Label 2">
46+
47+
<!-- 3. Setting .ariaLabelledByElements to target a declaratively defined custom label -->
48+
<x-label3 id="x-label3">
49+
<template shadowrootmode="closed" shadowrootreferencetarget="label3">
50+
<span>FAIL IF INCLUDED</span>
51+
<label id="label3">Label 3</label>
52+
</template>
53+
</x-label3>
54+
<input id="input3" class="ex" data-expectedlabel="Label 3">
55+
<script>
56+
document.getElementById("input3").ariaLabelledByElements = [ document.getElementById("x-label3") ];
57+
</script>
58+
59+
<script>
60+
AriaUtils.verifyLabelsBySelector('.ex');
61+
</script>
62+
63+
</body>
64+
65+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
3+
FAIL Changing the ID of the referenced element updates the computed label assert_equals: expected "Outside the label Label 1 Label 2" but got ""
4+
FAIL Removing the referenced element updates the computed label assert_equals: expected "Outside the label Label 2" but got ""
5+
PASS New referenced element prepended to the shadow supercedes the existing label
6+
PASS Changing the reference target ID updates the computed label
7+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!DOCTYPE HTML><!-- webkit-test-runner [ ShadowRootReferenceTargetEnabled=true ] -->
2+
<html>
3+
<head>
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<script src="/resources/testdriver.js"></script>
7+
<script src="/resources/testdriver-vendor.js"></script>
8+
<script src="/resources/testdriver-actions.js"></script>
9+
<script src="/wai-aria/scripts/aria-utils.js"></script>
10+
</head>
11+
<body>
12+
13+
<div id="test-container"></div>
14+
15+
<script>
16+
async function setup_test() {
17+
const test_container = document.querySelector("#test-container");
18+
test_container.setHTMLUnsafe(`
19+
<div id="host1">
20+
<template shadowrootmode="open" shadowrootreferencetarget="label1">
21+
<span>Outside the label</span>
22+
<label id="label1">Label 1</label>
23+
<label id="label2">Label 2</label>
24+
</template>
25+
</div>
26+
<input id="input1" aria-labelledby="host1">`);
27+
const input1 = test_container.querySelector("#input1");
28+
assert_equals(await test_driver.get_computed_label(input1), "Label 1");
29+
return test_container
30+
}
31+
32+
promise_test(async t => {
33+
const test_container = await setup_test();
34+
const host1 = test_container.querySelector("#host1");
35+
const label1 = host1.shadowRoot.querySelector("#label1");
36+
label1.id = "changed";
37+
assert_equals(await test_driver.get_computed_label(input1), "Outside the label Label 1 Label 2");
38+
}, "Changing the ID of the referenced element updates the computed label");
39+
40+
promise_test(async t => {
41+
const test_container = await setup_test();
42+
const host1 = test_container.querySelector("#host1");
43+
const label1 = host1.shadowRoot.querySelector("#label1");
44+
label1.remove();
45+
assert_equals(await test_driver.get_computed_label(input1), "Outside the label Label 2");
46+
}, "Removing the referenced element updates the computed label");
47+
48+
promise_test(async t => {
49+
const test_container = await setup_test();
50+
const host1 = test_container.querySelector("#host1");
51+
const new_label = document.createElement("label");
52+
new_label.id = "label1";
53+
new_label.textContent = "New label";
54+
host1.shadowRoot.prepend(new_label);
55+
assert_equals(await test_driver.get_computed_label(input1), "New label");
56+
}, "New referenced element prepended to the shadow supercedes the existing label");
57+
58+
promise_test(async t => {
59+
const test_container = await setup_test();
60+
const host1 = test_container.querySelector("#host1");
61+
host1.shadowRoot.referenceTarget = "label2";
62+
assert_equals(await test_driver.get_computed_label(input1), "Label 2");
63+
}, "Changing the reference target ID updates the computed label");
64+
</script>
65+
</body>
66+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Input 1
2+
Input 1 via Options
3+
Input 2 Input 2 via Options
4+
5+
PASS Label applies to descendant custom element that uses shadowrootreferencetarget (Input 1)
6+
PASS Label applies to descendant custom element that uses shadowrootreferencetarget (Input 1 via Options)
7+
PASS Label applies to multiple layers of descendant custom elements that use shadowrootreferencetarget (Input 2)
8+
PASS Label applies to multiple layers of descendant custom elements that use shadowrootreferencetarget (Input 2 via Options)
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<!DOCTYPE HTML><!-- webkit-test-runner [ ShadowRootReferenceTargetEnabled=true ] -->
2+
<html>
3+
4+
<head>
5+
<script src="/resources/testharness.js"></script>
6+
<script src="/resources/testharnessreport.js"></script>
7+
<script src="/resources/testdriver.js"></script>
8+
<script src="/resources/testdriver-vendor.js"></script>
9+
<script src="/resources/testdriver-actions.js"></script>
10+
<script src="/wai-aria/scripts/aria-utils.js"></script>
11+
</head>
12+
13+
<body>
14+
<!-- 1. Label applies to descendant custom element that uses shadowrootreferencetarget -->
15+
16+
<label>
17+
Input 1
18+
<div>
19+
<x-input1 id="x-input1">
20+
<template shadowrootmode="open" shadowrootreferencetarget="input">
21+
<input id="input">
22+
</template>
23+
</x-input1>
24+
</div>
25+
</label>
26+
27+
<label>
28+
Input 1 via Options
29+
<div>
30+
<x-input1 id="x-input1-a"></x-input1>
31+
</div>
32+
</label>
33+
<script>
34+
const customInput1 = document.querySelector('#x-input1-a');
35+
customInput1.attachShadow({ mode: 'open', referenceTarget: 'input' });
36+
customInput1.shadowRoot.innerHTML = `<input id="input">`;
37+
</script>
38+
39+
<script>
40+
function testImplicitLabelAssociation(id, label) {
41+
promise_test(async t => {
42+
const x_input = document.getElementById(id);
43+
const input = x_input.shadowRoot.getElementById('input');
44+
45+
// The label should apply to the input element and not the host.
46+
assert_equals(await test_driver.get_computed_label(x_input), "");
47+
assert_equals(await test_driver.get_computed_label(input), label);
48+
}, "Label applies to descendant custom element that uses shadowrootreferencetarget (" + label + ")");
49+
}
50+
testImplicitLabelAssociation('x-input1', 'Input 1');
51+
testImplicitLabelAssociation('x-input1-a', 'Input 1 via Options');
52+
</script>
53+
54+
<!-- 2. Label applies to multiple layers of descendant custom elements that use shadowrootreferencetarget -->
55+
56+
<label>
57+
Input 2
58+
<x-outer2 id="x-outer2">
59+
<template shadowrootmode="open" shadowrootreferencetarget="x-inner2">
60+
<x-inner2 id="x-inner2">
61+
<template shadowrootmode="open" shadowrootreferencetarget="input">
62+
<input id="input">
63+
</template>
64+
</x-inner2>
65+
</template>
66+
</x-outer2>
67+
</label>
68+
69+
<label>
70+
Input 2 via Options
71+
<x-outer2 id="x-outer2-a"></x-outer2>
72+
</label>
73+
74+
<script>
75+
const customOuter2 = document.querySelector('#x-outer2-a');
76+
customOuter2.attachShadow({ mode: 'open', referenceTarget: 'x-inner2' });
77+
customOuter2.shadowRoot.innerHTML = `<x-inner2 id="x-inner2"></x-inner2>`;
78+
const customInner2 = customOuter2.shadowRoot.querySelector('x-inner2');
79+
customInner2.attachShadow({ mode: 'open', referenceTarget: 'input' });
80+
customInner2.shadowRoot.innerHTML = `<input id="input">`;
81+
</script>
82+
83+
<script>
84+
function testDeepImplicitLabelAssociation(id, label) {
85+
promise_test(async t => {
86+
const outer = document.getElementById(id);
87+
const inner = outer.shadowRoot.getElementById('x-inner2');
88+
const input = inner.shadowRoot.getElementById('input');
89+
90+
// The label should apply to the input element and not any of the hosts.
91+
assert_equals(await test_driver.get_computed_label(outer), "");
92+
assert_equals(await test_driver.get_computed_label(inner), "");
93+
assert_equals(await test_driver.get_computed_label(input), label);
94+
}, "Label applies to multiple layers of descendant custom elements that use shadowrootreferencetarget (" + label + ")");
95+
}
96+
testDeepImplicitLabelAssociation('x-outer2', 'Input 2');
97+
testDeepImplicitLabelAssociation('x-outer2-a', 'Input 2 via Options');
98+
</script>
99+
100+
</body>
101+
102+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Input 1 Input 2 A F Input 4
2+
3+
PASS Label for attribute targets a custom element using shadowrootreferencetarget
4+
PASS Label for attribute targets a custom element using shadowrootreferencetarget inside multiple layers of shadow roots
5+
PASS Multiple labels targeting a custom element using shadowrootreferencetarget inside multiple layers of shadow roots
6+
PASS Setting .htmlFor property to target a custom element using shadowrootreferencetarget
7+

0 commit comments

Comments
 (0)