chromium/chrome/test/data/webui/cr_elements/iron_list_focus_test.ts

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// clang-format off
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';

import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
// clang-format on

// test-iron-focusable is a native custom element in order to maintain
// compatibility between Polymer v2 and Polymer v3.
class TestElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }

  set text(value) {
    const button = this.shadowRoot!.querySelector('button')!;
    assertTrue(!!button);
    button.textContent = value;
  }

  get text() {
    const button = this.shadowRoot!.querySelector('button');
    assertTrue(!!button);
    return button!.textContent;
  }

  // Pass focus to child in shadowRoot b/c iron-list expects that.
  override focus() {
    const button = this.shadowRoot!.querySelector('button');
    assertTrue(!!button);
    button!.focus();
  }

  connectedCallback() {
    const button = document.createElement('button');
    this.shadowRoot!.appendChild(button);
  }
}
customElements.define('test-iron-focusable', TestElement);

suite('iron-list-focus-test', function() {
  let testDiv: HTMLElement;
  let testIronList: IronListElement;

  setup(function() {
    document.body.innerHTML = getTrustedHTML`
      <div id="testDiv">
        <iron-list>
          <template>
            <test-iron-focusable text="[[item]]" tabindex$='[[tabIndex]]'>
            </test-iron-focusable>
          </template>
        </iron-list>
      </div>`;

    testDiv = document.querySelector('#testDiv')!;
    testIronList = document.querySelector('iron-list')!;

    assertTrue(!!testDiv);
    assertTrue(!!testIronList);

    testIronList.items = Array(15).fill('item').map((v, i) => `${v}${i}`);
    flush();
  });

  test('test focus is NOT preserved', function() {
    const initialFocus =
        testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    initialFocus.focus();
    flush();
    assertEquals('item0', initialFocus.text);
    assertEquals(initialFocus, document.activeElement);

    testIronList.splice('items', 0, 1);  // Remove the item from the list.
    flush();

    const newFocus = testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    assertEquals('item1', newFocus.text);
    assertNotEquals(newFocus, document.activeElement);

    newFocus.focus();
    flush();

    testIronList.splice('items', 5, 1);  // Remove a different item.
    flush();

    const sameFocus =
        testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    assertEquals('item1', sameFocus.text);
    assertNotEquals(sameFocus, document.activeElement);
  });

  test('test focus is preserved', function() {
    testIronList.preserveFocus = true;

    const initialFocus =
        testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    initialFocus.focus();
    flush();
    assertEquals('item0', initialFocus.text);
    assertEquals(initialFocus, document.activeElement);

    testIronList.splice('items', 0, 1);  // Remove the item from the list.
    flush();

    const newFocus = testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    assertEquals('item1', newFocus.text);
    assertEquals(newFocus, document.activeElement);

    testIronList.splice('items', 5, 1);  // Remove a different item.
    flush();

    const sameFocus =
        testIronList.querySelector<TestElement>('[tabindex="0"]')!;
    assertEquals('item1', sameFocus.text);
    assertEquals(sameFocus, document.activeElement);
  });
});