tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

popover-focus-4.html (5268B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8" />
      3 <title>Popover focus behaviors</title>
      4 <meta name="timeout" content="long">
      5 <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
      6 <link rel="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com">
      7 <link rel=help href="https://open-ui.org/components/popover.research.explainer">
      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>
     13 <script src="resources/popover-utils.js"></script>
     14 
     15 <div id=no-focus-candidate>
     16  <button tabindex="0">Toggle popover</button>
     17  <div popover id=no-focus-candidate-p>
     18    Popover with <button tabindex="0">focusable element</button>
     19    <div popover id=no-focus-candidate-p2>Nested popover with <button tabindex="0">focusable element</button></div>
     20  </div>
     21 </div>
     22 <button id="after"></button>
     23 
     24 <script>
     25 async function testNoFocusCandidate() {
     26  const invoker = document.querySelector('#no-focus-candidate>button');
     27  const popover = document.querySelector('#no-focus-candidate>[popover]');
     28  const nestedPopover = document.querySelector('#no-focus-candidate>[popover]>[popover]');
     29  invoker.focus(); // Make sure button is focused.
     30  assert_equals(document.activeElement,invoker);
     31  invoker.click(); // Activate the invoker
     32  assert_true(popover.matches(':popover-open'), 'popover should be invoked by invoker');
     33  assert_equals(document.activeElement,invoker, 'invoker should still be focused');
     34  await sendTab();
     35  assert_equals(document.activeElement,popover.querySelector('button'),'next up is the popover');
     36  await sendEnter(); // Show nested popover
     37  assert_true(nestedPopover.matches(':popover-open'), 'nested popover should be invoked by invoker');
     38  await sendTab();
     39  assert_equals(document.activeElement, nestedPopover.querySelector('button'), 'focus on the nested popover button');
     40  popover.querySelector('button').disabled = true; // Make the invoker no longer a focus candidate.
     41  await sendShiftTab();
     42  assert_equals(document.activeElement, invoker, 'initial invoker should be focused, nested popover invoker is skipped since it is disabled');
     43  nestedPopover.querySelector('button').focus();
     44  await sendTab();
     45  assert_equals(document.activeElement,after,'no more focusable elements after the button');
     46 }
     47 promise_test(async t => {
     48  const invoker = document.querySelector('#no-focus-candidate>button');
     49  const popover = document.querySelector('#no-focus-candidate>[popover]');
     50  const nestedButton = popover.querySelector('button');
     51  const nestedPopover = document.querySelector('#no-focus-candidate>[popover]>[popover]');
     52  invoker.setAttribute('popovertarget', 'no-focus-candidate-p');
     53  nestedButton.setAttribute('popovertarget', 'no-focus-candidate-p2');
     54  t.add_cleanup(() => {
     55    invoker.removeAttribute('popovertarget');
     56    nestedButton.removeAttribute('popovertarget');
     57    nestedButton.disabled = false;
     58    popover.hidePopover();
     59    nestedPopover.hidePopover();
     60  });
     61  await testNoFocusCandidate();
     62 }, "Cases where the next focus candidate isn't in the direct parent scope with popovertarget invocation");
     63 promise_test(async t => {
     64  const invoker = document.querySelector('#no-focus-candidate>button');
     65  const popover = document.querySelector('#no-focus-candidate>[popover]');
     66  const nestedButton = popover.querySelector('button');
     67  const nestedPopover = document.querySelector('#no-focus-candidate>[popover]>[popover]');
     68  invoker.setAttribute('commandfor', 'no-focus-candidate-p');
     69  invoker.setAttribute('command', 'toggle-popover');
     70  nestedButton.setAttribute('commandfor', 'no-focus-candidate-p2');
     71  nestedButton.setAttribute('command', 'toggle-popover');
     72  t.add_cleanup(() => {
     73    invoker.removeAttribute('command');
     74    invoker.removeAttribute('commandfor');
     75    nestedButton.removeAttribute('command');
     76    nestedButton.removeAttribute('commandfor');
     77    nestedButton.disabled = false;
     78    popover.hidePopover();
     79    nestedPopover.hidePopover();
     80  });
     81  await testNoFocusCandidate();
     82 }, "Cases where the next focus candidate isn't in the direct parent scope with command/commandfor invocation");
     83 promise_test(async t => {
     84  const invoker = document.querySelector('#no-focus-candidate>button');
     85  const popover = document.querySelector('#no-focus-candidate>[popover]');
     86  const nestedButton = popover.querySelector('button');
     87  const nestedPopover = document.querySelector('#no-focus-candidate>[popover]>[popover]');
     88  const invokerClick = () => {
     89    popover.togglePopover({ source: invoker });
     90  };
     91  invoker.addEventListener('click', invokerClick);
     92  const nestedButtonClick = () => {
     93    nestedPopover.togglePopover({ source: nestedButton });
     94  };
     95  nestedButton.addEventListener('click', nestedButtonClick);
     96  t.add_cleanup(() => {
     97    invoker.removeEventListener('click', invokerClick);
     98    nestedButton.removeEventListener('click', nestedButtonClick);
     99    nestedButton.disabled = false;
    100    popover.hidePopover();
    101    nestedPopover.hidePopover();
    102  });
    103  await testNoFocusCandidate();
    104 }, "Cases where the next focus candidate isn't in the direct parent scope with imperative invocation");
    105 </script>