1348 lines
45 KiB
HTML
1348 lines
45 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>MozBoxGroup Tests</title>
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
|
<script
|
|
type="module"
|
|
src="chrome://browser/content/preferences/widgets/setting-control.mjs"
|
|
></script>
|
|
<link
|
|
rel="stylesheet"
|
|
href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
|
/>
|
|
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
|
|
<script
|
|
type="module"
|
|
src="chrome://global/content/elements/moz-box-group.mjs"
|
|
></script>
|
|
<script src="lit-test-helpers.js"></script>
|
|
<script>
|
|
let html;
|
|
let MozLitElement;
|
|
let testHelpers = new LitTestHelpers();
|
|
|
|
async function keyboardNavigate(direction, boxGroup) {
|
|
let keyCode = `KEY_Arrow${
|
|
direction.charAt(0).toUpperCase() + direction.slice(1)
|
|
}`;
|
|
synthesizeKey(keyCode);
|
|
await boxGroup.updateComplete;
|
|
}
|
|
|
|
add_setup(async function setup() {
|
|
({ html } = await testHelpers.setupLit());
|
|
({ MozLitElement } =
|
|
await import("chrome://global/content/lit-utils.mjs"));
|
|
let templateFn = () => html`
|
|
<moz-box-group>
|
|
<moz-box-item label="item"></moz-box-item>
|
|
<moz-box-button label="button"></moz-box-button>
|
|
</moz-box-group>
|
|
`;
|
|
testHelpers.setupTests({ templateFn });
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupStyles() {
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate();
|
|
let [boxItem, boxButton] = boxGroup.shadowRoot
|
|
.querySelector("slot:not([name])")
|
|
.assignedElements();
|
|
|
|
const normalizePx = pxVal => Math.round(parseFloat(pxVal));
|
|
|
|
function verifyStyles(el, expectedStyles) {
|
|
let styles = getComputedStyle(el);
|
|
Object.entries(expectedStyles).forEach(([property, value]) => {
|
|
is(
|
|
normalizePx(styles[property]),
|
|
normalizePx(value),
|
|
`${property} is ${value}.`
|
|
);
|
|
});
|
|
}
|
|
|
|
const FIRST_ITEM_STYLES = {
|
|
borderStartEndRadius: "7px",
|
|
borderStartStartRadius: "7px",
|
|
borderEndEndRadius: "0px",
|
|
borderEndStartRadius: "0px",
|
|
borderTopWidth: "0px",
|
|
};
|
|
|
|
const MIDDLE_ITEM_STYLES = {
|
|
borderStartEndRadius: "0px",
|
|
borderStartStartRadius: "0px",
|
|
borderEndEndRadius: "0px",
|
|
borderEndStartRadius: "0px",
|
|
borderTopWidth: "1px",
|
|
};
|
|
|
|
const LAST_ITEM_STYLES = {
|
|
borderStartEndRadius: "0px",
|
|
borderStartStartRadius: "0px",
|
|
borderEndEndRadius: "7px",
|
|
borderEndStartRadius: "7px",
|
|
borderTopWidth: "1px",
|
|
};
|
|
|
|
// Verify that two items use the first and last item styles.
|
|
verifyStyles(boxItem, FIRST_ITEM_STYLES);
|
|
verifyStyles(boxButton, LAST_ITEM_STYLES);
|
|
|
|
// Change the last item and check that styles change accordingly.
|
|
let slotChanged = BrowserTestUtils.waitForEvent(
|
|
boxGroup.shadowRoot,
|
|
"slotchange"
|
|
);
|
|
let secondButton = document.createElement("moz-box-button");
|
|
secondButton.label = "second button";
|
|
boxGroup.append(secondButton);
|
|
await slotChanged;
|
|
|
|
verifyStyles(boxItem, FIRST_ITEM_STYLES);
|
|
verifyStyles(boxButton, MIDDLE_ITEM_STYLES);
|
|
verifyStyles(secondButton, LAST_ITEM_STYLES);
|
|
|
|
// Change the first item and verify that styles change accordingly.
|
|
slotChanged = BrowserTestUtils.waitForEvent(
|
|
boxGroup.shadowRoot,
|
|
"slotchange"
|
|
);
|
|
let secondItem = document.createElement("moz-box-item");
|
|
secondButton.label = "second item";
|
|
boxGroup.prepend(secondItem);
|
|
await slotChanged;
|
|
|
|
verifyStyles(secondItem, FIRST_ITEM_STYLES);
|
|
verifyStyles(boxItem, MIDDLE_ITEM_STYLES);
|
|
verifyStyles(boxButton, MIDDLE_ITEM_STYLES);
|
|
verifyStyles(secondButton, LAST_ITEM_STYLES);
|
|
|
|
// Verify that hiding items doesn't break the box-group styles.
|
|
secondItem.hidden = true;
|
|
await TestUtils.waitForTick();
|
|
|
|
verifyStyles(boxItem, FIRST_ITEM_STYLES);
|
|
verifyStyles(boxButton, MIDDLE_ITEM_STYLES);
|
|
verifyStyles(secondButton, LAST_ITEM_STYLES);
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupList() {
|
|
let listTemplate = html`<moz-box-group type="list">
|
|
<moz-box-item label="header" slot="header"></moz-box-item>
|
|
<moz-box-item label="item"></moz-box-item>
|
|
<moz-box-item label="item 2"></moz-box-item>
|
|
<moz-box-item label="item 3"></moz-box-item>
|
|
<moz-box-button label="button"></moz-box-button>
|
|
<moz-box-item label="footer" slot="footer"></moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(listTemplate);
|
|
let boxEls = boxGroup.querySelectorAll("moz-box-item, moz-box-button");
|
|
const getListItems = () => boxGroup.shadowRoot.querySelectorAll("li");
|
|
|
|
let list = boxGroup.shadowRoot.querySelector("ul");
|
|
ok(list, "Box group renders items in a list element.");
|
|
is(
|
|
list.ariaOrientation,
|
|
"vertical",
|
|
"aria-orientation is set on the list element."
|
|
);
|
|
ok(
|
|
!list.hasAttribute("role"),
|
|
"List has no role override (uses implicit list semantics)."
|
|
);
|
|
|
|
let listItems = getListItems();
|
|
is(
|
|
listItems.length,
|
|
4,
|
|
"Box group renders an li for each slotted moz-box-* element."
|
|
);
|
|
listItems.forEach(li => {
|
|
ok(!li.hasAttribute("role"), "li keeps implicit listitem semantics.");
|
|
});
|
|
boxGroup.listItems.forEach(item => {
|
|
ok(!item.hasAttribute("role"), "moz-box-item has no role override.");
|
|
});
|
|
|
|
let [listHeader] = boxGroup.shadowRoot
|
|
.querySelector('slot[name="header"]')
|
|
.assignedElements();
|
|
is(listHeader, boxEls[0], "List header is the first box element");
|
|
|
|
let [listFooter] = boxGroup.shadowRoot
|
|
.querySelector('slot[name="footer"]')
|
|
.assignedElements();
|
|
is(
|
|
listFooter,
|
|
boxEls[boxEls.length - 1],
|
|
"List footer is the last box element"
|
|
);
|
|
|
|
listItems.forEach((item, i) => {
|
|
let element = item.querySelector("slot").assignedElements()[0];
|
|
is(
|
|
element,
|
|
boxGroup.listItems[i],
|
|
"Each moz-box-* element is wrapped in an li."
|
|
);
|
|
});
|
|
|
|
let slotChanged = BrowserTestUtils.waitForEvent(
|
|
boxGroup.shadowRoot,
|
|
"slotchange"
|
|
);
|
|
let newItem = document.createElement("moz-box-item");
|
|
newItem.label = "create another item.";
|
|
boxGroup.append(newItem);
|
|
await slotChanged;
|
|
|
|
listItems = getListItems();
|
|
is(
|
|
listItems.length,
|
|
5,
|
|
"Box group renders an additional li for the newly slotted element."
|
|
);
|
|
let lastElement = listItems[listItems.length - 1]
|
|
.querySelector("slot")
|
|
.assignedElements()[0];
|
|
is(
|
|
lastElement,
|
|
newItem,
|
|
"The new item is slotted in the last li element."
|
|
);
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupKeyboardInteraction() {
|
|
let keyboardTemplate = html`<moz-button tabindex="0">
|
|
Focus me!
|
|
</moz-button>
|
|
<moz-box-group>
|
|
<moz-box-item label="header" slot="header"></moz-box-item>
|
|
<moz-box-item label="item">
|
|
<moz-button
|
|
class="first-button"
|
|
slot="actions-start"
|
|
iconsrc="chrome://global/skin/icons/more.svg"
|
|
></moz-button>
|
|
<moz-toggle slot="actions"></moz-toggle>
|
|
<moz-button
|
|
class="disabled-button"
|
|
slot="actions"
|
|
disabled
|
|
iconsrc="chrome://global/skin/icons/more.svg"
|
|
></moz-button>
|
|
<img src="chrome://global/skin/icons/more.svg" slot="actions" />
|
|
<moz-button
|
|
class="second-button"
|
|
slot="actions"
|
|
iconsrc="chrome://global/skin/icons/more.svg"
|
|
></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-link label="link"></moz-box-link>
|
|
<moz-box-button label="button"></moz-box-button>
|
|
<moz-box-button label="footer" slot="footer"></moz-box-button>
|
|
</moz-box-group>
|
|
<moz-button tabindex="0">Focus me too!</moz-button>`;
|
|
let {
|
|
children: [beforeButton, boxGroup, afterButton],
|
|
} = await testHelpers.renderTemplate(keyboardTemplate);
|
|
let [, boxItem, boxLink, boxButton, footerButton] =
|
|
boxGroup.querySelectorAll(
|
|
"moz-box-item, moz-box-button, moz-box-link"
|
|
);
|
|
|
|
isnot(document.activeElement, boxGroup, "Box group is not focused.");
|
|
beforeButton.focus();
|
|
|
|
// Verify tab can be used to move through elements in a regular moz-box-group.
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves to the first button action element in moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus moves to the second button action element in the moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves to the first button action element in moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in the moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus skips disabled button and image and moves to the second button action element in the moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxLink,
|
|
"Focus moves to the moz-box-link element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Focus moves to the moz-box-button element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus moves to the footer moz-box-button element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
afterButton,
|
|
"Focus moves out of the group after hitting tab."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus moves back to the moz-box-button element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Focus moves back to the moz-box-button element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxLink,
|
|
"Focus moves back to the moz-box-link element."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus moves back to the second button action element in the moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus skips disabled button and image and moves back to the toggle action element in the moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves back to the first button action element in the moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
beforeButton,
|
|
"Focus moves out of the group."
|
|
);
|
|
|
|
// Verify tab and arrow key behavior in moz-box-group of type="list".
|
|
boxGroup.type = "list";
|
|
await boxGroup.updateComplete;
|
|
await Promise.all(boxGroup.listItems.map(item => item.updateComplete));
|
|
|
|
// Tab from outside lands on the moz-box-item row.
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem,
|
|
"Focus moves to the moz-box-item row."
|
|
);
|
|
|
|
// Tab from the row enters its actions, then steps through them.
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves to the first button action element in moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in moz-box-item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus moves to the second button action element in the moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves to the first button action element in moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus moves to the toggle action element in moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus skips disabled button and image and moves to the second button action element in the moz-box-item."
|
|
);
|
|
|
|
// Shift+Tab from an action steps back through actions, then to the row.
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Shift+Tab from an action goes to the previous action."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Shift+Tab continues to the first action."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxItem,
|
|
"Shift+Tab from the first action returns to the row."
|
|
);
|
|
|
|
// Arrow Down/Up navigate between rows.
|
|
await keyboardNavigate("down", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxLink,
|
|
"Focus moves to the moz-box-link element."
|
|
);
|
|
|
|
await keyboardNavigate("down", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Focus moves to the moz-box-button element."
|
|
);
|
|
|
|
await keyboardNavigate("down", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Down arrow key does not move focus out of the group from the last item."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus moves to the footer button."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
afterButton,
|
|
"Focus moves out of the group."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus moves to the last element in the group."
|
|
);
|
|
|
|
await keyboardNavigate("up", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus stays on the last element in the group."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Focus moves to the last element in the list."
|
|
);
|
|
|
|
await keyboardNavigate("up", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxLink,
|
|
"Focus moves to the moz-box-link element."
|
|
);
|
|
|
|
await keyboardNavigate("up", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem,
|
|
"Arrow up returns to the moz-box-item row."
|
|
);
|
|
|
|
await keyboardNavigate("up", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem,
|
|
"Up arrow key does not move focus out of the group from the first item."
|
|
);
|
|
|
|
// Tab forward exits the group via the footer; non-focused rows are
|
|
// taken out of tab order while focus is inside the group.
|
|
synthesizeKey("KEY_Tab", {}); // → .first-button
|
|
synthesizeKey("KEY_Tab", {}); // → moz-toggle
|
|
synthesizeKey("KEY_Tab", {}); // → .second-button
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Other list items are not tabbable, so focus moves to the footer button."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
afterButton,
|
|
"Focus moves out of the group."
|
|
);
|
|
|
|
// Shift+Tab from outside walks back through the footer and last row
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
footerButton,
|
|
"Focus moves to the last element in the group."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
boxButton,
|
|
"Focus moves to the last element in the list."
|
|
);
|
|
|
|
synthesizeKey("KEY_Tab", { shiftKey: true });
|
|
is(
|
|
document.activeElement,
|
|
beforeButton,
|
|
"Focus moves out of the group"
|
|
);
|
|
|
|
// Validate left/right keyboard navigation between action items for RTL.
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["intl.l10n.pseudo", "bidi"]],
|
|
});
|
|
|
|
synthesizeKey("KEY_Tab", {}); // → moz-box-item
|
|
synthesizeKey("KEY_Tab", {}); // → .first-button
|
|
synthesizeKey("KEY_Tab", {}); // → moz-toggle
|
|
|
|
synthesizeKey("KEY_Tab", {});
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".second-button"),
|
|
"Focus lands on the last action of the moz-box-item."
|
|
);
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus skips disabled button and image and moves to the toggle action element in the moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector(".first-button"),
|
|
"Focus moves to the first button action element in the moz-box-item."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
boxItem.querySelector("moz-toggle"),
|
|
"Focus skips disabled button and image and moves to the toggle action element in the moz-box-item."
|
|
);
|
|
|
|
await SpecialPowers.popPrefEnv();
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupKeydownOnWhitespace() {
|
|
// Test bug 2001746 - ignore keypresses that fire on the ul if whitespace
|
|
// was clicked in moz-box-group
|
|
let listTemplate = html`<moz-box-group type="list">
|
|
<moz-box-item label="item 1"></moz-box-item>
|
|
<moz-box-item label="item 2"></moz-box-item>
|
|
<moz-box-item label="item 3"></moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(listTemplate);
|
|
|
|
let list = boxGroup.shadowRoot.querySelector("ul");
|
|
ok(list, "Box group has a list element.");
|
|
|
|
// Simulate clicking on the list container (whitespace) by focusing it
|
|
list.focus();
|
|
is(
|
|
boxGroup.shadowRoot.activeElement,
|
|
list,
|
|
"The list container can be focused."
|
|
);
|
|
is(
|
|
document.activeElement,
|
|
boxGroup,
|
|
"The moz-box-group is the activeElement."
|
|
);
|
|
|
|
// Press arrow keys - this should not throw an error
|
|
let errorThrown = false;
|
|
try {
|
|
synthesizeKey("KEY_ArrowDown");
|
|
await boxGroup.updateComplete;
|
|
synthesizeKey("KEY_ArrowUp");
|
|
await boxGroup.updateComplete;
|
|
} catch (e) {
|
|
errorThrown = true;
|
|
ok(false, `Unexpected error: ${e.message}`);
|
|
}
|
|
|
|
ok(
|
|
!errorThrown,
|
|
"No error thrown when pressing keys on whitespace in moz-box-group."
|
|
);
|
|
is(
|
|
document.activeElement,
|
|
boxGroup,
|
|
"Focus did not move after arrow key press."
|
|
);
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupReorderable() {
|
|
class TestReorderable extends MozLitElement {
|
|
static properties = { items: { type: Array } };
|
|
constructor() {
|
|
super();
|
|
this.items = [
|
|
html`<moz-box-item label="item 1"></moz-box-item>`,
|
|
html`<moz-box-item label="item 2"></moz-box-item>`,
|
|
html`<moz-box-item label="item 3"></moz-box-item>`,
|
|
html`<moz-box-item label="footer" slot="footer"></moz-box-item>`,
|
|
];
|
|
}
|
|
render() {
|
|
return html`<moz-box-group
|
|
@reorder=${this.onReorder}
|
|
type="reorderable-list"
|
|
>
|
|
${this.items}
|
|
</moz-box-group>`;
|
|
}
|
|
|
|
onReorder(event) {
|
|
const { draggedIndex, targetIndex } = event.detail;
|
|
const [draggedItem] = this.items.splice(draggedIndex, 1);
|
|
this.items.splice(targetIndex, 0, draggedItem);
|
|
this.requestUpdate();
|
|
}
|
|
}
|
|
customElements.define("test-reorderable", TestReorderable);
|
|
let reorderableTemplate = html`<test-reorderable></test-reorderable>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(reorderableTemplate);
|
|
let reorderableWrapper = boxGroup;
|
|
boxGroup = boxGroup.shadowRoot.querySelector("moz-box-group");
|
|
|
|
/**
|
|
Since the DOM nodes are destroyed and recreated, we need to
|
|
get fresh references to the newly created elements. Otherwise,
|
|
we will send a keydown event to an element that is no longer in
|
|
the DOM tree. This will cause the test to timeout.
|
|
*/
|
|
function getNewRefs() {
|
|
let item1 = boxGroup.querySelector("[label='item 1']");
|
|
let item2 = boxGroup.querySelector("[label='item 2']");
|
|
let item3 = boxGroup.querySelector("[label='item 3']");
|
|
let footer = boxGroup.querySelector("[label='footer']");
|
|
return [item1, item2, item3, footer];
|
|
}
|
|
|
|
let [item1, item2, item3, footer] = getNewRefs();
|
|
|
|
async function moveItem(item, direction) {
|
|
if (!item.isDraggable) {
|
|
return;
|
|
}
|
|
let keydown = BrowserTestUtils.waitForEvent(item, "keydown");
|
|
item.focus();
|
|
synthesizeKey(
|
|
`KEY_Arrow${direction.charAt(0).toUpperCase() + direction.slice(1)}`,
|
|
{ shiftKey: true, ctrlKey: true }
|
|
);
|
|
await keydown;
|
|
await boxGroup.updateComplete;
|
|
}
|
|
|
|
ok(
|
|
boxGroup.reorderableList,
|
|
"Box group renders a moz-reorderable-list."
|
|
);
|
|
|
|
let list = boxGroup.shadowRoot.querySelector("ol");
|
|
is(
|
|
list.getAttribute("role"),
|
|
"listbox",
|
|
"Reorderable list has role=listbox."
|
|
);
|
|
boxGroup.shadowRoot.querySelectorAll("li").forEach(li => {
|
|
is(
|
|
li.getAttribute("role"),
|
|
"presentation",
|
|
"Reorderable list li has role=presentation."
|
|
);
|
|
});
|
|
[item1, item2, item3].forEach(item => {
|
|
is(
|
|
item.getAttribute("role"),
|
|
"option",
|
|
`${item.label} has role=option.`
|
|
);
|
|
});
|
|
is(
|
|
footer.getAttribute("role"),
|
|
null,
|
|
"Footer item has no role=option."
|
|
);
|
|
|
|
is(
|
|
item1.handleEl.getAttribute("data-l10n-id"),
|
|
"moz-box-item-reorder-handle-named",
|
|
"Handle uses the named l10n-id when the item has a label."
|
|
);
|
|
is(
|
|
item1.handleEl.getAttribute("data-l10n-args"),
|
|
JSON.stringify({ item: item1.label }),
|
|
"Handle passes the item label via data-l10n-args."
|
|
);
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(2)").label,
|
|
item2.label,
|
|
"item2 starts in the second position."
|
|
);
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(3)").label,
|
|
item3.label,
|
|
"item3 starts after item2 in the third position."
|
|
);
|
|
|
|
/*
|
|
Ensure the moz-box-items get the REORDER_PROP so that the
|
|
reorder event fires as expected.
|
|
**/
|
|
boxGroup.reorderableList.getItems();
|
|
let reordered = BrowserTestUtils.waitForEvent(boxGroup, "reorder");
|
|
await moveItem(item3, "up");
|
|
await reordered;
|
|
await reorderableWrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(2)").label,
|
|
item3.label,
|
|
"item3 has been reordered to the second position."
|
|
);
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(3)").label,
|
|
item2.label,
|
|
"item2 has been reordered to the third position."
|
|
);
|
|
|
|
reordered = BrowserTestUtils.waitForEvent(boxGroup, "reorder");
|
|
await moveItem(item1, "down");
|
|
await reordered;
|
|
await reorderableWrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(1)").label,
|
|
item3.label,
|
|
"item3 has been reordered to the first position."
|
|
);
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(2)").label,
|
|
item1.label,
|
|
"item1 has been reordered to the second position."
|
|
);
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(3)").label,
|
|
item2.label,
|
|
"item2 is still in the third position."
|
|
);
|
|
|
|
/*
|
|
Since item2 was moved when item3 was moved up, item2 no longer
|
|
exists in our DOM. Therefore we need to get a new reference to
|
|
item2 before trying to move it down
|
|
**/
|
|
[item1, item2, item3, footer] = getNewRefs();
|
|
/*
|
|
Item2 is at the bottom of the list, thus trying to move it
|
|
down will never fire a reorder event.
|
|
**/
|
|
await moveItem(item2, "down");
|
|
await reorderableWrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(3)").label,
|
|
item2.label,
|
|
"item2 is still in the third position."
|
|
);
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(4)"),
|
|
footer,
|
|
"footer is in the forth position."
|
|
);
|
|
|
|
/*
|
|
The footer element can't be reordered, so no reorder event
|
|
will fire in this case.
|
|
**/
|
|
await moveItem(footer, "up");
|
|
await reorderableWrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(4)"),
|
|
footer,
|
|
"footer is still in the forth position."
|
|
);
|
|
});
|
|
|
|
/*
|
|
Assert that the tabIndex attribute is removed from all list
|
|
items if the length of the list items changes. Otherwise,
|
|
there's a possibility that a user will not be able to keyboard
|
|
navigate back to the list due to the items having tabindex="-1"
|
|
**/
|
|
add_task(async function testWhenListItemsChange() {
|
|
const removeSelf = event => {
|
|
event.target.closest("moz-box-item").remove();
|
|
};
|
|
let listTemplate = html` <moz-button>Focus me!</moz-button>
|
|
<moz-box-group type="list">
|
|
<moz-box-item label="item 1">
|
|
<moz-button slot="actions" label="item 1 action 1"></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-item label="item 2">
|
|
<moz-button slot="actions" label="item 2 action 1"></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-item label="item 3">
|
|
<moz-button
|
|
slot="actions"
|
|
label="item 3 action 1"
|
|
@click=${removeSelf}
|
|
></moz-button>
|
|
</moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [beforeButton, boxGroup],
|
|
} = await testHelpers.renderTemplate(listTemplate);
|
|
|
|
let list = boxGroup.shadowRoot.querySelector("ul");
|
|
ok(list, "Box group has a list element.");
|
|
|
|
// Focus the button outside of the list so we can easily
|
|
// move focus via the Tab key.
|
|
beforeButton.focus();
|
|
await synthesizeKey("KEY_Tab");
|
|
|
|
// Move through the list until we reach the item that can be
|
|
// removed.
|
|
await keyboardNavigate("down", boxGroup);
|
|
await keyboardNavigate("down", boxGroup);
|
|
await keyboardNavigate("down", boxGroup);
|
|
is(
|
|
document.activeElement.label,
|
|
"item 3",
|
|
"The last item in the box group should be focused."
|
|
);
|
|
await synthesizeKey("KEY_Tab");
|
|
is(
|
|
document.activeElement.label,
|
|
"item 3 action 1",
|
|
"The last action in the box group should be focused."
|
|
);
|
|
|
|
await synthesizeKey("KEY_Enter");
|
|
|
|
await boxGroup.updateComplete;
|
|
await Promise.all(boxGroup.listItems.map(item => item.updateComplete));
|
|
|
|
// Ensure each remaining list item is restored to the focusable
|
|
// tabindex so the user can tab back into the group.
|
|
boxGroup.listItems.forEach(item => {
|
|
is(
|
|
item.getAttribute("tabindex"),
|
|
"0",
|
|
"List item is restored to tabindex=0 after an item is removed"
|
|
);
|
|
});
|
|
|
|
// Ensure that we can tab navigate back to the box group
|
|
beforeButton.focus();
|
|
await synthesizeKey("KEY_Tab");
|
|
|
|
ok(
|
|
boxGroup.contains(document.activeElement),
|
|
"Focus should be able to move back into the box group after a list item is removed"
|
|
);
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupStaticItems() {
|
|
let staticTemplate = html`<moz-box-group type="reorderable-list">
|
|
<moz-box-item label="item 1"></moz-box-item>
|
|
<moz-box-item label="item 2"></moz-box-item>
|
|
<moz-box-item label="static item 1" slot="static"></moz-box-item>
|
|
<moz-box-item label="static item 2" slot="static"></moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(staticTemplate);
|
|
await boxGroup.updateComplete;
|
|
|
|
is(boxGroup.listItems.length, 2, "Box group has 2 regular items.");
|
|
is(boxGroup.staticItems.length, 2, "Box group has 2 static items.");
|
|
|
|
boxGroup.staticItems.forEach((item, i) => {
|
|
ok(
|
|
item.hasAttribute("static"),
|
|
`Static item ${i} has static attribute.`
|
|
);
|
|
is(
|
|
item.slot,
|
|
`static-${i}`,
|
|
`Static item ${i} has slot static-${i}.`
|
|
);
|
|
is(
|
|
item.getAttribute("position"),
|
|
String(2 + i),
|
|
`Static item ${i} positioned after regular items.`
|
|
);
|
|
ok(!item.handleEl, `Static item ${i} has no handle element.`);
|
|
is(
|
|
item.getAttribute("role"),
|
|
"option",
|
|
`Static item ${i} has role=option.`
|
|
);
|
|
});
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupStaticItemsNotReorderable() {
|
|
class TestStaticReorderable extends MozLitElement {
|
|
static properties = { items: { type: Array } };
|
|
constructor() {
|
|
super();
|
|
this.items = [];
|
|
}
|
|
render() {
|
|
return html`<moz-box-group
|
|
@reorder=${this.onReorder}
|
|
type="reorderable-list"
|
|
>
|
|
${this.items}
|
|
</moz-box-group>`;
|
|
}
|
|
|
|
onReorder(event) {
|
|
const { draggedIndex, targetIndex } = event.detail;
|
|
const regularItems = this.items.filter(
|
|
item => !item.values?.[0]?.slot
|
|
);
|
|
const [draggedItem] = regularItems.splice(draggedIndex, 1);
|
|
regularItems.splice(targetIndex, 0, draggedItem);
|
|
const staticItems = this.items.filter(
|
|
item => item.values?.[0]?.slot === "static"
|
|
);
|
|
this.items = [...regularItems, ...staticItems];
|
|
this.requestUpdate();
|
|
}
|
|
}
|
|
customElements.define("test-static-reorderable", TestStaticReorderable);
|
|
|
|
let wrapper = document.createElement("test-static-reorderable");
|
|
wrapper.items = [
|
|
html`<moz-box-item label="item 1"></moz-box-item>`,
|
|
html`<moz-box-item label="item 2"></moz-box-item>`,
|
|
html`<moz-box-item label="static item" slot="static"></moz-box-item>`,
|
|
];
|
|
document.body.appendChild(wrapper);
|
|
await wrapper.updateComplete;
|
|
|
|
let boxGroup = wrapper.shadowRoot.querySelector("moz-box-group");
|
|
await boxGroup.updateComplete;
|
|
|
|
let staticItem = boxGroup.querySelector("[label='static item']");
|
|
ok(!staticItem.handleEl, "Static item has no handle element.");
|
|
ok(
|
|
staticItem.hasAttribute("static"),
|
|
"Static item has static attribute."
|
|
);
|
|
|
|
// Verify only non-static items are in the reorderable list.
|
|
let item1 = boxGroup.querySelector("[label='item 1']");
|
|
let item2 = boxGroup.querySelector("[label='item 2']");
|
|
ok(item1.handleEl, "Regular item 1 has handle element.");
|
|
ok(item2.handleEl, "Regular item 2 has handle element.");
|
|
|
|
boxGroup.reorderableList.getItems();
|
|
item1.focus();
|
|
let reordered = BrowserTestUtils.waitForEvent(boxGroup, "reorder");
|
|
synthesizeKey("KEY_ArrowDown", { shiftKey: true, ctrlKey: true });
|
|
await reordered;
|
|
await wrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(2)").label,
|
|
"item 1",
|
|
"Regular items reordered."
|
|
);
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(3)").label,
|
|
"static item",
|
|
"Static item remains in place after reorder."
|
|
);
|
|
|
|
synthesizeKey("KEY_ArrowDown", { shiftKey: true, ctrlKey: true });
|
|
await wrapper.updateComplete;
|
|
|
|
is(
|
|
boxGroup.querySelector("moz-box-item:nth-of-type(2)").label,
|
|
"item 1",
|
|
"Regular items not reordered when the next item is static."
|
|
);
|
|
|
|
wrapper.remove();
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupStaticItemsKeyboardNavigation() {
|
|
let keyboardTemplate = html`<moz-box-group type="list">
|
|
<moz-box-item label="item 1">
|
|
<moz-button slot="actions" label="action 1"></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-item label="item 2">
|
|
<moz-button slot="actions" label="action 2"></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-item label="static item 1" slot="static">
|
|
<moz-button slot="actions" label="static action 1"></moz-button>
|
|
</moz-box-item>
|
|
<moz-box-item label="static item 2" slot="static">
|
|
<moz-button slot="actions" label="static action 2"></moz-button>
|
|
</moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(keyboardTemplate);
|
|
await boxGroup.updateComplete;
|
|
|
|
let allItems = [...boxGroup.listItems, ...boxGroup.staticItems];
|
|
await Promise.all(allItems.map(item => item.updateComplete));
|
|
|
|
// Focus the first row.
|
|
allItems[0].focus();
|
|
|
|
// Navigate down through all items.
|
|
for (let i = 1; i < allItems.length; i++) {
|
|
await keyboardNavigate("down", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
allItems[i],
|
|
`Down navigates to item ${i}.`
|
|
);
|
|
}
|
|
|
|
// Navigate up through all items.
|
|
for (let i = allItems.length - 2; i >= 0; i--) {
|
|
await keyboardNavigate("up", boxGroup);
|
|
is(document.activeElement, allItems[i], `Up navigates to item ${i}.`);
|
|
}
|
|
});
|
|
|
|
add_task(async function testMozBoxGroupStaticItemsFirstLastStyles() {
|
|
let stylesTemplate = html`<moz-box-group>
|
|
<moz-box-item label="item 1"></moz-box-item>
|
|
<moz-box-item label="item 2"></moz-box-item>
|
|
<moz-box-item label="static item" slot="static"></moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(stylesTemplate);
|
|
await boxGroup.updateComplete;
|
|
|
|
let [item1, item2] = boxGroup.listItems;
|
|
let [staticItem] = boxGroup.staticItems;
|
|
|
|
ok(item1.classList.contains("first"), "First item has 'first' class.");
|
|
ok(!item2.classList.contains("last"), "Middle item no 'last' class.");
|
|
ok(
|
|
staticItem.classList.contains("last"),
|
|
"Static item has 'last' class."
|
|
);
|
|
});
|
|
|
|
add_task(async function testMozBoxItemSettingControlKeyboardNavigation() {
|
|
const mockSetting = {
|
|
value: true,
|
|
on() {},
|
|
off() {},
|
|
userChange() {},
|
|
getControlConfig: c => c,
|
|
controllingExtensionInfo: {},
|
|
visible: true,
|
|
};
|
|
|
|
const disabledSetting = {
|
|
value: false,
|
|
disabled: true,
|
|
on() {},
|
|
off() {},
|
|
userChange() {},
|
|
getControlConfig: c => c,
|
|
controllingExtensionInfo: {},
|
|
visible: true,
|
|
};
|
|
|
|
const settingConfig = {
|
|
id: "test-toggle-setting",
|
|
control: "moz-toggle",
|
|
label: "Test Toggle",
|
|
};
|
|
|
|
const disabledSettingConfig = {
|
|
id: "test-disabled-setting",
|
|
control: "moz-toggle",
|
|
label: "Disabled Toggle",
|
|
};
|
|
|
|
let settingControlTemplate = html`<moz-box-group>
|
|
<moz-box-item label="item with setting control">
|
|
<moz-button
|
|
class="first-button"
|
|
slot="actions-start"
|
|
iconsrc="chrome://global/skin/icons/more.svg"
|
|
></moz-button>
|
|
<setting-control
|
|
slot="actions"
|
|
class="test-setting-control"
|
|
.config=${settingConfig}
|
|
.setting=${mockSetting}
|
|
></setting-control>
|
|
<setting-control
|
|
slot="actions"
|
|
class="disabled-setting-control"
|
|
.config=${disabledSettingConfig}
|
|
.setting=${disabledSetting}
|
|
></setting-control>
|
|
<moz-button
|
|
class="second-button"
|
|
slot="actions"
|
|
iconsrc="chrome://global/skin/icons/more.svg"
|
|
></moz-button>
|
|
</moz-box-item>
|
|
</moz-box-group>`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(settingControlTemplate);
|
|
let boxItem = boxGroup.querySelector("moz-box-item");
|
|
await boxItem.updateComplete;
|
|
|
|
let firstButton = boxItem.querySelector(".first-button");
|
|
let settingControl = boxItem.querySelector(".test-setting-control");
|
|
let disabledSettingControl = boxItem.querySelector(
|
|
".disabled-setting-control"
|
|
);
|
|
let secondButton = boxItem.querySelector(".second-button");
|
|
|
|
await settingControl.updateComplete;
|
|
await disabledSettingControl.updateComplete;
|
|
|
|
// Get the actual toggle element inside the setting-control
|
|
// Note: setting-control delegates focus to its inner toggle element
|
|
let toggle = settingControl.querySelector("moz-toggle");
|
|
ok(toggle, "Toggle element exists inside setting-control.");
|
|
|
|
ok(
|
|
disabledSettingControl.disabled,
|
|
"Disabled setting-control reports disabled."
|
|
);
|
|
|
|
firstButton.focus();
|
|
is(
|
|
document.activeElement,
|
|
firstButton,
|
|
"First button is focused initially."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
ok(
|
|
settingControl.contains(document.activeElement),
|
|
"Focus moves into setting-control after right arrow."
|
|
);
|
|
is(
|
|
document.activeElement,
|
|
toggle,
|
|
"The toggle inside setting-control is the activeElement."
|
|
);
|
|
|
|
await keyboardNavigate("right", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
secondButton,
|
|
"Focus skips disabled setting-control and moves to second button."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
ok(
|
|
settingControl.contains(document.activeElement),
|
|
"Focus moves back into setting-control, skipping disabled one."
|
|
);
|
|
is(
|
|
document.activeElement,
|
|
toggle,
|
|
"The toggle inside setting-control is the activeElement."
|
|
);
|
|
|
|
await keyboardNavigate("left", boxGroup);
|
|
is(
|
|
document.activeElement,
|
|
firstButton,
|
|
"Focus moves back to first button after left arrow."
|
|
);
|
|
});
|
|
|
|
add_task(async function testSlottedPanelListClosesOnScroll() {
|
|
let template = html`
|
|
<moz-box-group style="--box-group-max-height: 80px">
|
|
<moz-box-item label="item 1">
|
|
<moz-select slot="actions">
|
|
<moz-option
|
|
value="1"
|
|
label="One"
|
|
iconSrc="chrome://global/skin/icons/info.svg"
|
|
></moz-option>
|
|
<moz-option
|
|
value="2"
|
|
label="Two"
|
|
iconSrc="chrome://global/skin/icons/warning.svg"
|
|
></moz-option>
|
|
</moz-select>
|
|
</moz-box-item>
|
|
<moz-box-item label="item 2"></moz-box-item>
|
|
<moz-box-item label="item 3"></moz-box-item>
|
|
<moz-box-item label="item 4"></moz-box-item>
|
|
<moz-box-item label="item 5"></moz-box-item>
|
|
</moz-box-group>
|
|
`;
|
|
let {
|
|
children: [boxGroup],
|
|
} = await testHelpers.renderTemplate(template);
|
|
await boxGroup.updateComplete;
|
|
|
|
let select = boxGroup.querySelector("moz-select");
|
|
await select.updateComplete;
|
|
ok(select.usePanelList, "moz-select renders a panel-list.");
|
|
|
|
let scrollContainer =
|
|
boxGroup.shadowRoot.querySelector(".scroll-container");
|
|
ok(
|
|
scrollContainer.scrollHeight > scrollContainer.clientHeight,
|
|
"Scroll container overflows so it can scroll."
|
|
);
|
|
|
|
let selectButton = select.shadowRoot.querySelector(".panel-trigger");
|
|
let shown = BrowserTestUtils.waitForEvent(select.panelList, "shown");
|
|
synthesizeMouseAtCenter(selectButton, {});
|
|
await shown;
|
|
ok(select.panelList.open, "panel-list opened.");
|
|
|
|
let hidden = BrowserTestUtils.waitForEvent(select.panelList, "hidden");
|
|
scrollContainer.scrollTop = 40;
|
|
await hidden;
|
|
ok(
|
|
!select.panelList.open,
|
|
"panel-list closes when the slotted scroll container is scrolled."
|
|
);
|
|
});
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<p id="display"></p>
|
|
<div id="content" style="display: none"></div>
|
|
<pre id="test"></pre>
|
|
</body>
|
|
</html>
|