diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index 262ce8334c75..8ba4411b9c33 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -338,6 +338,10 @@ Tester.prototype = { // Thunderbird "MailMigrator", "SearchIntegration", + // lit + "reactiveElementVersions", + "litHtmlVersions", + "litElementVersions", ]; this.PerTestCoverageUtils.beforeTestSync(); diff --git a/toolkit/content/tests/widgets/test_moz_button_group.html b/toolkit/content/tests/widgets/test_moz_button_group.html index 88418f58c398..73727c195364 100644 --- a/toolkit/content/tests/widgets/test_moz_button_group.html +++ b/toolkit/content/tests/widgets/test_moz_button_group.html @@ -32,22 +32,43 @@ let beforeButton = document.getElementById("before-button"); let afterButton = document.getElementById("after-button"); - async function checkButtons(first, second) { - let firstBounds = first.getBoundingClientRect(); - let secondBounds = second.getBoundingClientRect(); + async function checkButtons(...buttons) { + const checkOrder = (a, b) => { + let firstBounds = a.getBoundingClientRect(); + let secondBounds = b.getBoundingClientRect(); - ok(firstBounds.x < secondBounds.x, `First button comes first`); - let locationDiff = Math.abs(secondBounds.x - firstBounds.x - firstBounds.width - 8); - ok(locationDiff < 1, `Second button is 8px after first (${locationDiff})`); + ok(firstBounds.x < secondBounds.x, `First button comes first`); + let locationDiff = Math.abs(secondBounds.x - firstBounds.x - firstBounds.width - 8); + ok(locationDiff < 1, `Second button is 8px after first (${locationDiff})`); + }; + ok(buttons.length >= 2, "There are at least 2 buttons to check"); + + // Verify tab order is correct. beforeButton.focus(); is(document.activeElement, beforeButton, "Before button is focused"); + synthesizeKey("VK_TAB"); - is(document.activeElement, first, "First button is first in tab order"); - synthesizeKey("VK_TAB"); - is(document.activeElement, second, "Second button is next in tab order"); + is(document.activeElement, buttons[0], "Next button is focused"); + + for (let i = 1; i < buttons.length; i++) { + // Confirm button order in DOM + checkOrder(buttons[i - 1], buttons[i]); + + synthesizeKey("VK_TAB"); + is(document.activeElement, buttons[i], "Next button is focused"); + } + synthesizeKey("VK_TAB"); is(document.activeElement, afterButton, "After button is at the end in tab order"); + + // Verify light DOM order is correct, in case of manual tab management in JS. + let { parentElement } = buttons[0]; + let parentChildren = parentElement.children; + is(parentChildren.length, buttons.length, "Expected number of children"); + for (let i = 0; i < parentChildren.length; i++) { + is(parentChildren[i], buttons[i], `Button ${i} is in correct light DOM spot`); + } } @@ -151,6 +172,65 @@ await buttonGroup.updateComplete; await checkButtons(secondaryButton, autofocusButton); }); + + add_task(async function testDefaultButtonAutoSlotting() { + render( + html` + + + + + `, + renderArea + ); + + let buttonGroup = document.querySelector("moz-button-group"); + let defaultButton = buttonGroup.querySelector("[default]"); + let secondaryButton = buttonGroup.querySelector(".secondary"); + buttonGroup.platform = "win"; + await buttonGroup.updateComplete; + is(defaultButton.slot, "primary", "default button was auto-slotted") + await checkButtons(defaultButton, secondaryButton); + + buttonGroup.platform = "macosx"; + await buttonGroup.updateComplete; + await checkButtons(secondaryButton, defaultButton); + }); + + add_task(async function testInitialButtonLightDomReordering() { + const renderPlatform = platform => render( + html` + + + + + + `, + renderArea + ); + + renderPlatform("win"); + let buttonGroup = document.querySelector("moz-button-group"); + await buttonGroup.updateComplete; + let primaryButton = buttonGroup.querySelector(".primary"); + let defaultButton = buttonGroup.querySelector("[default]"); + let secondaryButton = buttonGroup.querySelector(".secondary"); + + is(primaryButton.slot, "primary", "primary button was auto-slotted"); + is(defaultButton.slot, "primary", "default button was auto-slotted"); + await checkButtons(primaryButton, defaultButton, secondaryButton); + + renderPlatform("macosx"); + buttonGroup = document.querySelector("moz-button-group"); + await buttonGroup.updateComplete; + primaryButton = buttonGroup.querySelector(".primary"); + defaultButton = buttonGroup.querySelector("[default]"); + secondaryButton = buttonGroup.querySelector(".secondary"); + + is(primaryButton.slot, "primary", "primary button was auto-slotted"); + is(defaultButton.slot, "primary", "default button was auto-slotted"); + await checkButtons(secondaryButton, primaryButton, defaultButton); + }); diff --git a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs index 0d98bb94dfb8..1ee0a6b28aa1 100644 --- a/toolkit/content/widgets/moz-button-group/moz-button-group.mjs +++ b/toolkit/content/widgets/moz-button-group/moz-button-group.mjs @@ -60,11 +60,34 @@ export default class MozButtonGroup extends MozLitElement { child.localName == "button" && (child.classList.contains("primary") || child.getAttribute("type") == "submit" || - child.hasAttribute("autofocus")) + child.hasAttribute("autofocus") || + child.hasAttribute("default")) ) { child.slot = "primary"; } } + this.#reorderLightDom(); + } + + #reorderLightDom() { + let primarySlottedChildren = [...this.primarySlotEl.assignedNodes()]; + if (this.platform == PLATFORM_WINDOWS) { + primarySlottedChildren.reverse(); + for (let child of primarySlottedChildren) { + child.parentElement.prepend(child); + } + } else { + for (let child of primarySlottedChildren) { + // Ensure the primary buttons are at the end of the light DOM. + child.parentElement.append(child); + } + } + } + + updated(changedProperties) { + if (changedProperties.has("platform")) { + this.#reorderLightDom(); + } } render() {