1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
<!DOCTYPE html>
<meta charset="utf-8">
<link rel="author" href="mailto:masonf@chromium.org">
<link rel=help href="https://open-ui.org/components/popup.research.explainer">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<!-- Enumerate all the ways of creating an ancestor popover relationship -->
<div class="example">
<p>Direct DOM children</p>
<div popover class=ancestor><p>Ancestor popover</p>
<div popover class=child><p>Child popover</p></div>
</div>
</div>
<div class="example">
<p>Grandchildren</p>
<div popover class=ancestor><p>Ancestor popover</p>
<div>
<div>
<div popover class=child><p>Child popover</p></div>
</div>
</div>
</div>
</div>
<div class="example">
<p>popovertoggletarget attribute relationship</p>
<div popover class=ancestor><p>Ancestor popover</p>
<button popovertoggletarget=trigger1 class=clickme>Button</button>
</div>
<div id=trigger1 popover class=child><p>Child popover</p></div>
</div>
<div class="example">
<p>nested popovertoggletarget attribute relationship</p>
<div popover class=ancestor><p>Ancestor popover</p>
<div>
<div>
<button popovertoggletarget=trigger2 class=clickme>Button</button>
</div>
</div>
</div>
<div id=trigger2 popover class=child><p>Child popover</p></div>
</div>
<div class="example">
<p>anchor attribute relationship</p>
<div id=anchor1 popover class=ancestor><p>Ancestor popover</p></div>
<div anchor=anchor1 popover class=child><p>Child popover</p></div>
</div>
<div class="example">
<p>indirect anchor attribute relationship</p>
<div popover class=ancestor>
<p>Ancestor popover</p>
<div>
<div>
<span id=anchor2>Anchor</span>
</div>
</div>
</div>
<div anchor=anchor2 popover class=child><p>Child popover</p></div>
</div>
<!-- Other examples -->
<div popover id=p1 anchor=b1><p>This is popover #1</p>
<button id=b2 onclick='p2.showPopover()'>Popover 2</button>
<button id=b4 onclick='p4.showPopover()'>Popover 4</button>
</div>
<div popover id=p2 anchor=b2><p>This is popover #2</p>
<button id=b3 onclick='p3.showPopover()'>Popover 3</button>
</div>
<div popover id=p3 anchor=b3><p>This is popover #3</p></div>
<div popover id=p4 anchor=b4><p>This is popover #4</p></div>
<button id=b1 onclick='p1.showPopover()'>Popover 1</button>
<dialog id=d1>This is a dialog<button onclick='this.parentElement.close()'>Close</button></dialog>
<button id=b5 onclick='d1.showPopover()'>Dialog</button>
<script>
// Test basic ancestor relationships
for(let example of document.querySelectorAll('.example')) {
const descr = example.querySelector('p').textContent;
const ancestor = example.querySelector('[popover].ancestor');
const child = example.querySelector('[popover].child');
const clickToActivate = example.querySelector('.clickme');
test(function() {
assert_true(!!descr && !!ancestor && !!child);
assert_false(ancestor.matches(':open'));
assert_false(child.matches(':open'));
ancestor.showPopover();
if (clickToActivate)
clickToActivate.click();
else
child.showPopover();
assert_true(child.matches(':open'));
assert_true(ancestor.matches(':open'));
ancestor.hidePopover();
assert_false(ancestor.matches(':open'));
assert_false(child.matches(':open'));
},descr);
}
const popovers = [p1, p2, p3, p4];
function assertState(...states) {
assert_equals(popovers.length,states.length);
for(let i=0;i<popovers.length;++i) {
assert_equals(popovers[i].matches(':open'),states[i],`Popover #${i+1} incorrect state`);
}
}
test(function() {
assertState(false,false,false,false);
p1.showPopover();
assertState(true,false,false,false);
p2.showPopover();
assertState(true,true,false,false);
p3.showPopover();
assertState(true,true,true,false);
// P4 is a sibling of P2, so showing it should
// close P2 and P3.
p4.showPopover();
assertState(true,false,false,true);
// P2 should close P4 now.
p2.showPopover();
assertState(true,true,false,false);
// Hiding P1 should hide all.
p1.hidePopover();
assertState(false,false,false,false);
}, "more complex nesting, all using anchor ancestry")
test(function() {
function openManyPopovers() {
p1.showPopover();
p2.showPopover();
p3.showPopover();
assertState(true,true,true,false);
}
openManyPopovers();
d1.show(); // Dialog.show() should hide all popovers.
assertState(false,false,false,false);
d1.close();
openManyPopovers();
d1.showModal(); // Dialog.showModal() should also hide all popovers.
assertState(false,false,false,false);
d1.close();
}, "popovers should be closed by dialogs")
test(function() {
// Note: d1 is a <dialog> element, not a popover.
assert_false(d1.open);
d1.show();
assert_true(d1.open);
p1.showPopover();
assertState(true,false,false,false);
assert_true(d1.open);
p1.hidePopover();
assert_true(d1.open);
d1.close();
assert_false(d1.open);
}, "dialogs should not be closed by popovers")
</script>
<style>
#p1 { top:350px; }
#p2 { top:350px; left:200px; }
#p3 { top:500px; }
#p4 { top:500px;left:200px; }
</style>
|