chromium/third_party/blink/web_tests/fast/scrolling/scroll-element-into-view.html

<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
  ::-webkit-scrollbar {
    width: 10px;
    height: 10px;
  }
  ::-webkit-scrollbar-thumb {
    background: #aaa;
  }
  #grid {
    display:grid;
    grid-template-columns: 310px 310px;
    grid-gap: 30px;
  }
  .spacer {
    width: 2000px;
    height: 2000px;
  }
  .container {
    width: 310px;
    height: 310px;
    position: absolute;
    overflow: scroll;
    background-color: lightgrey;
  }
  .focusable {
    position: absolute;
    background-color: coral;
    left: 400px;
    top: 400px;
    border: 1px solid black;
    box-sizing: border-box;
  }

  .focusable.smaller {
    width: 100px;
    height: 100px;
  }
  .focusable.larger {
    width: 400px;
    height: 400px;
  }

  .bullseye {
    width: 10px;
    height: 10px;
    position: absolute;
    left: calc(50% - 5px);
    top: calc(50% - 5px);
    background-color: red;
  }
</style>
<p>
  Tests that scrollable areas are scrolled to the correct location when an
  element is focused.
</p>
<p>
  Focus should center the element when fully offscreen; avoid scrolling if
  fully onscreen. When partially offscreen, horizontal axis shouldn't scroll
  and vertical axis should align the nearest edges.
</p>
<div id="grid">
  <div id="testSmaller">
    <p>
      Element smaller than scrollport.
      <button onclick="focusSmaller()">Focus</button>
    </p>
    <div class="container">
      <div class="spacer"></div>
      <div class="focusable smaller" tabindex="-1"></div>
    </div>
  </div>

  <div id="testLarger">
    <p>
      Element larger than scrollport.
      <button onclick="focusLarger()">Focus</button>
    </p>
    <div class="container">
      <div class="spacer"></div>
      <div class="focusable larger" tabindex="-1">
        <div class="bullseye"></div>
      </div>
    </div>
  </div>
</div>

<script>
  function focusSmaller() {
    const e = document.querySelector('#testSmaller .focusable');
    e.focus();
    e.blur()
  }
  function focusLarger() {
    const e = document.querySelector('#testLarger .focusable');
    e.focus();
    e.blur();
  }

  function runTest(container, initialScroll, expectedFinalScroll) {
    container.scrollLeft = initialScroll.x;
    container.scrollTop = initialScroll.y;

    const focusable = container.querySelector('.focusable');
    focusable.focus();
    focusable.blur();

    assert_equals(container.scrollLeft, expectedFinalScroll.x, "Expected horizontal scroll.");
    assert_equals(container.scrollTop, expectedFinalScroll.y, "Expected vertical scroll.");
  };

  const smallerContainer = document.querySelector('#testSmaller .container');
  const largerContainer = document.querySelector('#testLarger .container');

  // Smaller fully offscreen element.
  test(runTest.bind(null, smallerContainer, {x: 10, y: 10}, {x: 300, y: 300}),
       "Smaller fully offscreen element is centered when focused from left/top.");
  test(runTest.bind(null, smallerContainer, {x: 500, y: 500}, {x: 300, y: 300}),
       "Smaller fully offscreen element is centered when focused right/bottom.");

  // Smaller fully onscreen element.
  test(runTest.bind(null, smallerContainer, {x: 395, y: 395}, {x: 395, y: 395}),
       "Smaller fully onscreen element at top/left does not scroll when focused.");
  test(runTest.bind(null, smallerContainer, {x: 205, y: 205}, {x: 205, y: 205}),
       "Smaller fully onscreen element at bottom/right does not scroll when focused.");

  // Smaller partially onscreen element.
  test(runTest.bind(null, smallerContainer, {x: 450, y: 450}, {x: 450, y: 400}),
       "Smaller partially onscreen element at top/left aligns only top edges.");
  test(runTest.bind(null, smallerContainer, {x: 150, y: 150}, {x: 150, y: 200}),
       "Smaller partially onscreen element at bottom/right aligns only bottom edges.");

  // Larger fully offscreen element.
  test(runTest.bind(null, largerContainer, {x: 10, y: 10}, {x: 450, y: 450}),
       "Larger fully offscreen element is centered when focused from left/top.");
  test(runTest.bind(null, largerContainer, {x: 800, y: 800}, {x: 450, y: 450}),
       "Larger fully offscreen element is centered when focused right/bottom.");

  // Larger fully onscreen element.
  test(runTest.bind(null, largerContainer, {x: 405, y: 405}, {x: 405, y: 405}),
       "Larger fully onscreen element does not scroll when focused from left/top.");
  test(runTest.bind(null, largerContainer, {x: 495, y: 495}, {x: 495, y: 495}),
       "Larger fully onscreen element does not scroll when focused from right/bottom.");

  // Larger partially onscreen element.
  test(runTest.bind(null, largerContainer, {x: 650, y: 650}, {x: 650, y: 500}),
       "Larger partially onscreen element at top/left aligns only bottom edges.");
  test(runTest.bind(null, largerContainer, {x: 250, y: 250}, {x: 250, y: 400}),
       "Larger partially onscreen element at bottom/right aligns only top edges.");
</script>