interestfor-invoker-descendants.tentative.html (9845B)
1 <!DOCTYPE html> 2 <meta charset="utf-8" /> 3 <link rel="author" href="mailto:masonf@chromium.org"> 4 <link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> 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-actions.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 <script src="resources/invoker-utils.js"></script> 11 12 <button id=invoker interestfor=target> 13 Button content 14 <span id=inner-span tabindex=0>Inner content</span> 15 </button> 16 <div id=target>Target</div> 17 <button id=otherbutton>Other button</button> 18 <style> 19 [interestfor] { interest-delay: 0s; } 20 :interest-source { background-color: lightgreen;} 21 :interest-target { background-color: lightblue;} 22 </style> 23 24 <script> 25 const invoker = document.getElementById('invoker'); 26 const innerSpan = document.getElementById('inner-span'); 27 const target = document.getElementById('target'); 28 const otherbutton = document.getElementById('otherbutton'); 29 30 async function showInterest(el, method) { 31 if (method === 'focus') { 32 await focusOn(el); 33 } else { 34 assert_equals(method,'hover'); 35 await hoverOver(el); 36 } 37 } 38 39 ['focus','hover'].forEach(method => { 40 promise_test(async function (t) { 41 t.add_cleanup(() => showInterest(otherbutton, method)); 42 const signal = t.get_signal(); 43 let interestCount = 0; 44 let loseInterestCount = 0; 45 target.addEventListener('interest',() => (++interestCount),{signal}); 46 target.addEventListener('loseinterest',() => (++loseInterestCount),{signal}); 47 await showInterest(invoker, method); 48 assert_true(invoker.matches(':interest-source'),'focusing invoker should show interest'); 49 assert_equals(interestCount,1,'One interest event'); 50 interestCount = 0; 51 assert_equals(loseInterestCount,0,'No loseinterest events'); 52 await showInterest(innerSpan, method); 53 assert_true(invoker.matches(':interest-source'),'focusing inner span should keep interest'); 54 assert_equals(interestCount,0,'No extra interest events'); 55 assert_equals(loseInterestCount,0,'No loseinterest events'); 56 await showInterest(invoker, method); 57 assert_true(invoker.matches(':interest-source'),'focusing back to outer button should keep interest'); 58 assert_equals(interestCount,0,'No extra interest events'); 59 assert_equals(loseInterestCount,0,'No loseinterest events'); 60 await showInterest(otherbutton, method); 61 assert_false(invoker.matches(':interest-source'),'focusing outside should lose interest'); 62 assert_equals(interestCount,0,'No extra interest events'); 63 assert_equals(loseInterestCount,1,'Finally got loseinterest event'); 64 },`Moving focus within invoker, ${method}`); 65 }); 66 </script> 67 68 <a id=outer href="#" interestfor="middle">Outer</a> 69 <div id="middle" popover> 70 <a id="middle-link" href="#" interestfor="inner">Middle</a> 71 </div> 72 <div id="inner" popover>Inner <button id="inner-button">btn</button></div> 73 74 <script> 75 const outer = document.getElementById('outer'); 76 const middle = document.getElementById('middle'); 77 const middleLink = document.getElementById('middle-link'); 78 const inner = document.getElementById('inner'); 79 const innerButton = document.getElementById('inner-button'); 80 81 ['focus','hover'].forEach(method => { 82 promise_test(async function (t) { 83 t.add_cleanup(() => showInterest(otherbutton, method)); 84 assert_false(middle.matches(':popover-open'),'middle starts closed'); 85 await showInterest(outer, method); 86 assert_true(outer.matches(':interest-source'),'focusing outer should show interest'); 87 assert_true(middle.matches(':popover-open'),'outer popover open'); 88 89 await showInterest(middleLink, method); 90 assert_true(middleLink.matches(':interest-source'),'focusing middle should show interest'); 91 assert_true(outer.matches(':interest-source'),'outer keeps interest'); 92 assert_true(middle.matches(':popover-open'),'middle popover stays open'); 93 assert_true(inner.matches(':popover-open'),'inner popover opens'); 94 95 await showInterest(innerButton, method); 96 assert_true(outer.matches(':interest-source'),'outer keeps interest'); 97 assert_true(middleLink.matches(':interest-source'),'middle keeps interest'); 98 assert_true(middle.matches(':popover-open'),'middle popover stays open'); 99 assert_true(inner.matches(':popover-open'),'inner popover stays open'); 100 },`Nested invokers, ${method}`); 101 }); 102 </script> 103 104 <a id=outer2-first href="#" interestfor="middle2-inner">Outer first</a> 105 <a id=outer2-second href="#" interestfor="middle2-outer">Outer second</a> 106 <div id="middle2-outer">Middle outer 107 <div id="middle2-inner">Middle inner 108 <a id="middle2-link" href="#" interestfor="inner2">Middle</a> 109 </div> 110 </div> 111 <div id="inner2">Inner <button id="inner2-button">btn</button></div> 112 113 <script> 114 const outer2First = document.getElementById('outer2-first'); 115 const outer2Second = document.getElementById('outer2-second'); 116 const middle2Outer = document.getElementById('middle2-outer'); 117 const middle2Inner = document.getElementById('middle2-inner'); 118 const middle2Link = document.getElementById('middle2-link'); 119 const inner2 = document.getElementById('inner2'); 120 const inner2Button = document.getElementById('inner2-button'); 121 122 ['focus','hover'].forEach(method => { 123 promise_test(async function (t) { 124 t.add_cleanup(() => showInterest(otherbutton, method)); 125 const hideDelayMs = 500; // Long enough to focus a few things without elapsing 126 outer2First.style.interestDelayEnd = `${hideDelayMs}ms`; 127 128 const hoverStart = performance.now(); 129 await showInterest(outer2First, method); 130 assert_true(outer2First.matches(':interest-source'),'should show interest'); 131 assert_false(outer2Second.matches(':interest-source'),'no interest yet in second target'); 132 await showInterest(outer2Second, method); 133 assert_true(outer2Second.matches(':interest-source'),'outer2Second should now have interest'); 134 await showInterest(middle2Link, method); 135 let firstHasInterest = outer2First.matches(':interest-source'); 136 if (performance.now() - hoverStart >= hideDelayMs) { 137 return; // Test is running too slowly 138 } 139 assert_true(firstHasInterest,'outer2First should still have interest (delay)'); 140 assert_true(inner2.matches(':interest-target'),'inner2 should be an interest target'); 141 assert_true(middle2Outer.matches(':interest-target'),'middle2Outer should still be an interest target'); 142 assert_true(outer2Second.matches(':interest-source'),'outer2Second should still have interest'); 143 144 // Focusing the inner target should keep both outer invokers alive. 145 await showInterest(inner2Button, method); 146 assert_true(middle2Outer.matches(':interest-target'),'middle2Outer should still be an interest target (after moving to inner button)'); 147 assert_true(outer2Second.matches(':interest-source'),'outer2Second should still have interest (after moving to inner button)'); 148 assert_true(outer2First.matches(':interest-source'),'outer2First should still have interest'); 149 150 // Now let the time elapse and make sure things are still good. 151 const elapsedMs = performance.now() - hoverStart; 152 await new Promise(resolve => step_timeout(resolve,hideDelayMs - elapsedMs + 10)); 153 assert_true(inner2.matches(':interest-target'),'inner2 should still be an interest target'); 154 assert_true(middle2Outer.matches(':interest-target'),'middle2Outer should still be an interest target'); 155 assert_true(outer2Second.matches(':interest-source'),'outer2Second should still have interest'); 156 assert_true(outer2First.matches(':interest-source'),'outer2First should still have interest'); 157 assert_true(middle2Inner.matches(':interest-target'),'middle2Inner should still be an interest target'); 158 },`Nested invokers with intermediate interest target, ${method}`); 159 }); 160 </script> 161 162 163 <a id=outer3 href="#" interestfor="middle3-inner">Outer</a> 164 <div id="middle3-outer">Middle <button id=middle3-button>outer</button> 165 <div id="middle3-inner">Middle inner 166 <a id="middle3-link" href="#" interestfor="middle3-outer">Middle</a> 167 </div> 168 </div> 169 170 <script> 171 const outer3 = document.getElementById('outer3'); 172 const middle3Outer = document.getElementById('middle3-outer'); 173 const middle3Inner = document.getElementById('middle3-inner'); 174 const middle3Link = document.getElementById('middle3-link'); 175 const middle3Button = document.getElementById('middle3-button'); 176 177 ['focus','hover'].forEach(method => { 178 promise_test(async function (t) { 179 t.add_cleanup(() => showInterest(otherbutton, method)); 180 await showInterest(outer3, method); 181 assert_true(outer3.matches(':interest-source'),'should show interest'); 182 assert_true(middle3Inner.matches(':interest-target'),'middle3Inner should now be an interest target'); 183 await showInterest(middle3Link, method); 184 assert_true(middle3Link.matches(':interest-source'),'middle3Link should now have interest'); 185 assert_true(middle3Outer.matches(':interest-target'),'middle3Outer should now be an interest target'); 186 assert_true(outer3.matches(':interest-source'),'outer3 should still have interest'); 187 assert_true(middle3Inner.matches(':interest-target'),'middle3Inner should still be an interest target'); 188 await showInterest(middle3Button, method); 189 assert_true(middle3Link.matches(':interest-source'),'middle3Link should now have interest (on outer button)'); 190 assert_true(middle3Outer.matches(':interest-target'),'middle3Outer should now be an interest target (on outer button)'); 191 assert_true(outer3.matches(':interest-source'),'outer3 no longer has interest (on outer button)'); 192 assert_true(middle3Inner.matches(':interest-target'),'middle3Inner is no longer an interest target (on outer button)'); 193 },`Nested invokers with circular dependencies, ${method}`); 194 }); 195 </script>