2 <meta charset=
"utf-8" />
3 <title>Popover invoking attribute
</title>
4 <link rel=
"author" href=
"mailto:masonf@chromium.org">
5 <link rel=help
href=
"https://open-ui.org/components/popover.research.explainer">
6 <link rel=help
href=
"https://html.spec.whatwg.org/multipage/popover.html">
7 <meta name=
"timeout" content=
"long">
8 <script src=
"/resources/testharness.js"></script>
9 <script src=
"/resources/testharnessreport.js"></script>
10 <script src=
"/resources/testdriver.js"></script>
11 <script src=
"/resources/testdriver-actions.js"></script>
12 <script src=
"/resources/testdriver-vendor.js"></script>
16 const actionReflectionLogic
= (action
) => {
17 switch (action
?.toLowerCase()) {
18 case "show": return "show";
19 case "hide": return "hide";
20 default: return "toggle";
23 const noActivationLogic
= (action
) => {
26 function makeElementWithType(element
,type
) {
28 const el
= Object
.assign(document
.createElement(element
),{type
});
29 document
.body
.appendChild(el
);
30 test
.add_cleanup(() => el
.remove());
34 const supportedButtonTypes
= ['button','reset','submit',''].map(type
=> {
36 name
: `<button type="${type}">`,
37 makeElement
: makeElementWithType('button',type
),
38 invokeFn
: el
=> {el
.focus(); el
.click()},
39 getExpectedLogic
: actionReflectionLogic
,
42 const supportedInputButtonTypes
= ['button','reset','submit','image'].map(type
=> {
44 name
: `<input type="${type}">`,
45 makeElement
: makeElementWithType('input',type
),
46 invokeFn
: el
=> {el
.focus(); el
.click()},
47 getExpectedLogic
: actionReflectionLogic
,
50 const unsupportedTypes
= ['text','email','password','search','tel','url','checkbox','radio','range','file','color','date','datetime-local','month','time','week','number'].map(type
=> {
52 name
: `<input type="${type}">`,
53 makeElement
: makeElementWithType('input',type
),
54 invokeFn
: (el
) => {el
.focus();},
55 getExpectedLogic
: noActivationLogic
, // None of these support popover invocation
59 ...supportedButtonTypes
,
60 ...supportedInputButtonTypes
,
63 window
.addEventListener('load', () => {
64 ["auto","manual"].forEach(type
=> {
65 invokers
.forEach(testcase
=> {
66 ["toggle","hide","show","ShOw","garbage",null,undefined].forEach(action
=> {
67 [false,true].forEach(use_idl_for_target
=> {
68 [false,true].forEach(use_idl_for_action
=> {
69 promise_test(async test
=> {
70 const popover
= Object
.assign(document
.createElement('div'),{popover
: type
, id
: 'my-popover'});
71 assert_equals(popover
.popover
,type
,'reflection');
72 const invoker
= testcase
.makeElement(test
);
73 if (use_idl_for_target
) {
74 invoker
.popoverTargetElement
= popover
;
75 assert_equals(invoker
.getAttribute('popovertarget'),'','attribute value');
77 invoker
.setAttribute('popovertarget',popover
.id
);
79 if (use_idl_for_action
) {
80 invoker
.popoverTargetAction
= action
;
81 assert_equals(invoker
.getAttribute('popovertargetaction'),String(action
),'action reflection');
83 invoker
.setAttribute('popovertargetaction',action
);
85 assert_true(!document
.getElementById(popover
.id
));
86 assert_equals(invoker
.popoverTargetElement
,null,'targetElement should be null before the popover is in the document');
87 assert_equals(invoker
.popoverTargetAction
,actionReflectionLogic(action
),'action should be correct immediately');
88 document
.body
.appendChild(popover
);
89 test
.add_cleanup(() => {popover
.remove();});
90 assert_equals(invoker
.popoverTargetElement
,popover
,'target element should be returned once it\'s in the document');
91 assert_false(popover
.matches(':open'));
92 await testcase
.invokeFn(invoker
);
93 assert_equals(document
.activeElement
,invoker
,'Focus should end up on the invoker');
94 expectedBehavior
= testcase
.getExpectedLogic(action
);
95 switch (expectedBehavior
) {
98 assert_true(popover
.matches(':open'),'Toggle or show should show the popover');
99 popover
.hidePopover(); // Hide the popover
103 assert_false(popover
.matches(':open'),'Hide or none should leave the popover hidden');
108 if (expectedBehavior
=== "none") {
109 // If no behavior is expected, then there is nothing left to test. Even re-focusing
110 // a control that has no expected behavior may hide an open popover via light dismiss.
113 assert_false(popover
.matches(':open'));
114 popover
.showPopover(); // Show the popover directly
115 assert_equals(document
.activeElement
,invoker
,'The popover should not shift focus');
116 assert_true(popover
.matches(':open'));
117 await testcase
.invokeFn(invoker
);
118 switch (expectedBehavior
) {
121 assert_false(popover
.matches(':open'),'Toggle or hide should hide the popover');
124 assert_true(popover
.matches(':open'),'Show should leave the popover showing');
129 },`Test ${testcase.name}, action=${action}, ${use_idl_for_target ? "popoverTarget IDL" : "popovertarget attr"}, ${use_idl_for_action ? "popoverTargetAction IDL" : "popovertargetaction attr"}, with popover=${type}`);
140 <button popovertarget=p1
>Toggle Popover
1</button>
141 <div popover id=p1
style=
"border: 5px solid red;top: 100px;left: 100px;">This is popover #
1</div>
144 function clickOn(element
) {
145 const actions
= new test_driver
.Actions();
146 return actions
.pointerMove(0, 0, {origin
: element
})
147 .pointerDown({button
: actions
.ButtonType
.LEFT
})
148 .pointerUp({button
: actions
.ButtonType
.LEFT
})
152 const popover
= document
.querySelector('[popover]');
153 const button
= document
.querySelector('button');
156 popover
.addEventListener('beforetoggle',(e
) => {
157 if (e
.newState
=== "open")
163 async
function assertState(expectOpen
,expectShow
,expectHide
) {
164 assert_equals(popover
.matches(':open'),expectOpen
,'Popover open state is incorrect');
165 await
new Promise(resolve
=> requestAnimationFrame(resolve
));
166 assert_equals(showCount
,expectShow
,'Show count is incorrect');
167 assert_equals(hideCount
,expectHide
,'Hide count is incorrect');
170 window
.addEventListener('load', () => {
171 promise_test(async () => {
172 showCount
= hideCount
= 0;
173 await
assertState(false,0,0);
174 await
clickOn(button
);
175 await
assertState(true,1,0);
176 popover
.hidePopover();
177 await
assertState(false,1,1);
179 await
assertState(true,2,1);
180 popover
.hidePopover();
181 await
assertState(false,2,2);
182 }, "Clicking a popovertarget button opens a closed popover (also check event counts)");
184 promise_test(async () => {
185 showCount
= hideCount
= 0;
186 await
assertState(false,0,0);
187 await
clickOn(button
);
188 await
assertState(true,1,0);
189 await
clickOn(button
);
190 await
assertState(false,1,1);
191 }, "Clicking a popovertarget button closes an open popover (also check event counts)");