blob: 44d72c525cb724b1f30bcd0c4af60e32867ec897 [file] [log] [blame]
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import './index';
import { assert } from 'chai';
import { SelectSk } from './select-sk';
import { setUpElementUnderTest } from '../../../infra-sk/modules/test_util';
describe('select-sk', () => {
const newInstance = setUpElementUnderTest<SelectSk>('select-sk');
interface InstantiationOptions {
disabled: boolean;
itemIds: string[];
selectedItemId: string;
}
const newInstanceWithOpts = async (
opts: Partial<InstantiationOptions>
): Promise<SelectSk> => {
const instance = newInstance((instance: SelectSk) => {
if (opts.disabled) instance.setAttribute('disabled', '');
(opts.itemIds || []).forEach((id) => {
const item = document.createElement('div');
item.id = id;
if (opts.selectedItemId && opts.selectedItemId === id)
item.setAttribute('selected', '');
instance.appendChild(item);
});
});
await Promise.resolve(); // Give the mutation observer a chance to fire.
return instance;
};
describe('selection property', () => {
it('has a default value', () => {
const selectSk = newInstance();
assert.equal(-1, (selectSk as any)._selection);
assert.equal(-1, selectSk.selection);
});
it('changes based on children', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['a', 'b'],
selectedItemId: 'b',
});
assert.equal(1, (selectSk as any)._selection);
assert.equal(1, selectSk.selection);
});
it('can go back to -1', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['a', 'b'],
selectedItemId: 'b',
});
selectSk.selection = -1;
assert.equal(-1, selectSk.selection);
assert.isFalse(selectSk.querySelector('#b')!.hasAttribute('selected'));
});
it('parses strings', async () => {
const selectSk = await newInstanceWithOpts({ itemIds: ['a', 'b'] });
selectSk.selection = '1';
assert.equal(1, +selectSk.selection);
assert.isTrue(selectSk.querySelector('#b')!.hasAttribute('selected'));
});
it('treats null and undefined and out of range as -1', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['a', 'b'],
selectedItemId: 'b',
});
selectSk.selection = null;
assert.equal(-1, selectSk.selection);
assert.isFalse(selectSk.querySelector('#b')!.hasAttribute('selected'));
selectSk.selection = 0;
assert.equal(0, selectSk.selection);
assert.isTrue(selectSk.querySelector('#a')!.hasAttribute('selected'));
selectSk.selection = undefined;
assert.equal(-1, selectSk.selection);
selectSk.selection = 10;
assert.equal(-1, selectSk.selection);
selectSk.selection = -3;
assert.equal(-1, selectSk.selection);
});
it('changes selected attributes on children', async () => {
const selectSk = await newInstanceWithOpts({ itemIds: ['a', 'b'] });
const a = selectSk.querySelector('#a')!;
const b = selectSk.querySelector('#b')!;
selectSk.selection = 0;
assert.equal(0, selectSk.selection);
assert.isTrue(a.hasAttribute('selected'));
assert.isFalse(b.hasAttribute('selected'));
selectSk.selection = 1;
assert.equal(1, selectSk.selection);
assert.isFalse(a.hasAttribute('selected'));
assert.isTrue(b.hasAttribute('selected'));
});
it('stays fixed when disabled', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['a', 'b'],
selectedItemId: 'b',
});
assert.equal(1, (selectSk as any)._selection);
assert.equal(1, selectSk.selection);
assert.equal('0', selectSk.getAttribute('tabindex'));
selectSk.disabled = true;
selectSk.selection = 0;
assert.equal(1, (selectSk as any)._selection);
assert.equal(1, selectSk.selection);
assert.equal(false, selectSk.hasAttribute('tabindex'));
});
it('gets updated when select-sk is re-enabled', async () => {
const selectSk = await newInstanceWithOpts({
disabled: true,
itemIds: ['a', 'b'],
selectedItemId: 'b',
});
assert.equal(-1, (selectSk as any)._selection);
assert.equal(-1, selectSk.selection);
selectSk.disabled = false;
assert.equal(1, (selectSk as any)._selection);
assert.equal(1, selectSk.selection);
assert.isFalse(selectSk.hasAttribute('disabled'));
});
}); // end describe('selected property')
describe('click', () => {
it('changes selection', async () => {
const selectSk = await newInstanceWithOpts({ itemIds: ['a', 'b'] });
assert.equal('listbox', selectSk.getAttribute('role'));
const a = selectSk.querySelector<HTMLDivElement>('#a')!;
const b = selectSk.querySelector<HTMLDivElement>('#b')!;
a.click();
assert.equal(0, selectSk.selection);
assert.isTrue(a.hasAttribute('selected'));
assert.equal('true', a.getAttribute('aria-selected'));
assert.equal('option', a.getAttribute('role'));
assert.isFalse(b.hasAttribute('selected'));
assert.equal('false', b.getAttribute('aria-selected'));
assert.equal('option', b.getAttribute('role'));
b.click();
assert.equal(1, selectSk.selection);
assert.isFalse(a.hasAttribute('selected'));
assert.equal('false', a.getAttribute('aria-selected'));
assert.isTrue(b.hasAttribute('selected'));
assert.equal('true', b.getAttribute('aria-selected'));
});
it('ignores clicks when disabled', async () => {
const selectSk = await newInstanceWithOpts({
disabled: true,
itemIds: ['a', 'b'],
});
const a = selectSk.querySelector<HTMLDivElement>('#a')!;
const b = selectSk.querySelector<HTMLDivElement>('#b')!;
a.click();
assert.equal(-1, selectSk.selection);
assert.isFalse(a.hasAttribute('selected'));
assert.isFalse(b.hasAttribute('selected'));
b.click();
assert.equal(-1, selectSk.selection);
assert.isFalse(a.hasAttribute('selected'));
assert.isFalse(b.hasAttribute('selected'));
});
}); // end describe('click')
describe('inserting new children', () => {
it('updates selection property', async () => {
const selectSk = await newInstanceWithOpts({ itemIds: ['', '', ''] });
assert.equal(-1, selectSk.selection);
let div = document.createElement('div');
div.setAttribute('selected', '');
selectSk.appendChild(div);
div = document.createElement('div');
selectSk.appendChild(div);
// Need to do the check post microtask so the mutation observer gets a
// chance to fire.
await Promise.resolve();
assert.equal(3, selectSk.selection);
});
it('does not check children when disabled', async () => {
const selectSk = await newInstanceWithOpts({
disabled: true,
itemIds: ['', '', ''],
});
assert.equal(-1, selectSk.selection);
let div = document.createElement('div');
div.setAttribute('selected', '');
selectSk.appendChild(div);
div = document.createElement('div');
selectSk.appendChild(div);
// Need to do the check post microtask so the mutation observer gets a
// chance to fire.
await Promise.resolve();
assert.equal(-1, selectSk.selection);
});
}); // end describe('inserting new children')
describe('mutation of child selected attribute', () => {
it('does update selection', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['', 'd1', 'd2'],
selectedItemId: 'd2',
});
assert.equal(2, selectSk.selection);
selectSk.querySelector('#d2')!.removeAttribute('selected');
selectSk.querySelector('#d1')!.setAttribute('selected', '');
// Need to do the check post microtask so the mutation observer gets a
// chance to fire.
await Promise.resolve();
assert.equal(1, selectSk.selection);
});
}); // end describe('mutation of child selected attribute'
describe('keyboard navigation', () => {
it('follows arrow keys', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['', '', 'd2'],
selectedItemId: 'd2',
});
assert.equal(2, selectSk.selection);
(selectSk as any)._onKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowUp' })
);
assert.equal(1, selectSk.selection);
(selectSk as any)._onKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
);
assert.equal(2, selectSk.selection);
(selectSk as any)._onKeyDown(
new KeyboardEvent('keydown', { key: 'Home' })
);
assert.equal(0, selectSk.selection);
(selectSk as any)._onKeyDown(
new KeyboardEvent('keydown', { key: 'End' })
);
assert.equal(2, selectSk.selection);
// Don't wrap around.
(selectSk as any)._onKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
);
assert.equal(2, selectSk.selection);
});
}); // end describe('keyboard navigation')
describe('focus', () => {
it('drops focus when disabled', async () => {
const selectSk = await newInstanceWithOpts({
itemIds: ['', '', 'd2'],
selectedItemId: 'd2',
});
selectSk.focus();
assert.equal(selectSk, document.activeElement);
selectSk.disabled = true;
assert.notEqual(selectSk, document.activeElement);
});
}); // end describe('focus')
});