mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
Bug 1792238 - Create a moz-button-group component r=hjones
Differential Revision: https://phabricator.services.mozilla.com/D163141
This commit is contained in:
parent
eac0c55546
commit
568be762eb
@ -24,7 +24,7 @@ window.MozXULElement = {
|
||||
// This should be browser, locales-preview or toolkit.
|
||||
let [root, ...rest] = name.split("/");
|
||||
let ftlContents;
|
||||
//
|
||||
|
||||
// TODO(mstriemer): These seem like they could be combined but I don't want
|
||||
// to fight with webpack anymore.
|
||||
if (root == "toolkit") {
|
||||
|
@ -92,6 +92,8 @@ toolkit.jar:
|
||||
content/global/elements/message-bar.js (widgets/message-bar.js)
|
||||
content/global/elements/menu.js (widgets/menu.js)
|
||||
content/global/elements/menupopup.js (widgets/menupopup.js)
|
||||
content/global/elements/moz-button-group.css (widgets/moz-button-group/moz-button-group.css)
|
||||
content/global/elements/moz-button-group.mjs (widgets/moz-button-group/moz-button-group.mjs)
|
||||
content/global/elements/moz-input-box.js (widgets/moz-input-box.js)
|
||||
content/global/elements/named-deck.js (widgets/named-deck.js)
|
||||
content/global/elements/notificationbox.js (widgets/notificationbox.js)
|
||||
|
@ -23,6 +23,7 @@ run-if = os == "mac" && os_version != "10.15" # Mac only feature, requires > 10.
|
||||
[test_label_checkbox.xhtml]
|
||||
[test_menubar.xhtml]
|
||||
skip-if = os == 'mac'
|
||||
[test_moz_button_group.html]
|
||||
[test_popupanchor.xhtml]
|
||||
skip-if = os == 'linux' || (verify && (os == 'win')) # Bug 1335894 perma-fail on linux 16.04
|
||||
[test_popupreflows.xhtml]
|
||||
|
156
toolkit/content/tests/widgets/test_moz_button_group.html
Normal file
156
toolkit/content/tests/widgets/test_moz_button_group.html
Normal file
@ -0,0 +1,156 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>moz-button-group tests</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<!-- TODO: Bug 1798404 - in-content/common.css can be removed once we have a better
|
||||
solution for token variables for the new widgets -->
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<script type="module" src="chrome://global/content/elements/moz-button-group.mjs"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<button id="before-button">Before</button>
|
||||
<div id="render"></div>
|
||||
<button id="after-button">After</button>
|
||||
</div>
|
||||
<!-- This is here to ensure the stylesheet is loaded. It gets removed in setup. -->
|
||||
<moz-button-group></moz-button-group>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<script>
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
let html;
|
||||
let render;
|
||||
|
||||
let renderArea = document.getElementById("render");
|
||||
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();
|
||||
|
||||
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})`);
|
||||
|
||||
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");
|
||||
synthesizeKey("VK_TAB");
|
||||
is(document.activeElement, afterButton, "After button is at the end in tab order");
|
||||
}
|
||||
|
||||
|
||||
add_setup(async function setup() {
|
||||
({ html, render} = await import("chrome://global/content/vendor/lit.all.mjs"));
|
||||
document.querySelector("moz-button-group").remove();
|
||||
});
|
||||
|
||||
add_task(async function testButtonOrderingSlot() {
|
||||
render(
|
||||
html`
|
||||
<moz-button-group>
|
||||
<button slot="primary" id="primary-button">Primary</button>
|
||||
<button id="secondary-button">Secondary</button>
|
||||
</moz-button-group>
|
||||
`,
|
||||
renderArea
|
||||
);
|
||||
|
||||
let buttonGroup = document.querySelector("moz-button-group");
|
||||
let primaryButton = document.getElementById("primary-button");
|
||||
let secondaryButton = document.getElementById("secondary-button");
|
||||
|
||||
buttonGroup.platform = "win";
|
||||
await buttonGroup.updateComplete;
|
||||
await checkButtons(primaryButton, secondaryButton);
|
||||
|
||||
buttonGroup.platform = "macosx";
|
||||
await buttonGroup.updateComplete;
|
||||
await checkButtons(secondaryButton, primaryButton);
|
||||
});
|
||||
|
||||
add_task(async function testPrimaryButtonAutoSlotting() {
|
||||
render(
|
||||
html`
|
||||
<moz-button-group>
|
||||
<button class="primary">Primary</button>
|
||||
<button class="secondary">Secondary</button>
|
||||
</moz-button-group>
|
||||
`,
|
||||
renderArea
|
||||
);
|
||||
|
||||
let buttonGroup = document.querySelector("moz-button-group");
|
||||
let primaryButton = buttonGroup.querySelector(".primary");
|
||||
let secondaryButton = buttonGroup.querySelector(".secondary");
|
||||
buttonGroup.platform = "win";
|
||||
await buttonGroup.updateComplete;
|
||||
is(primaryButton.slot, "primary", "primary button was auto-slotted")
|
||||
await checkButtons(primaryButton, secondaryButton);
|
||||
|
||||
buttonGroup.platform = "macosx";
|
||||
await buttonGroup.updateComplete;
|
||||
await checkButtons(secondaryButton, primaryButton);
|
||||
});
|
||||
|
||||
add_task(async function testSubmitButtonAutoSlotting() {
|
||||
render(
|
||||
html`
|
||||
<moz-button-group>
|
||||
<button type="submit">Submit</button>
|
||||
<button class="secondary">Secondary</button>
|
||||
</moz-button-group>
|
||||
`,
|
||||
renderArea
|
||||
);
|
||||
|
||||
let buttonGroup = document.querySelector("moz-button-group");
|
||||
let submitButton = buttonGroup.querySelector("[type=submit]");
|
||||
let secondaryButton = buttonGroup.querySelector(".secondary");
|
||||
buttonGroup.platform = "win";
|
||||
await buttonGroup.updateComplete;
|
||||
is(submitButton.slot, "primary", "submit button was auto-slotted")
|
||||
await checkButtons(submitButton, secondaryButton);
|
||||
|
||||
buttonGroup.platform = "macosx";
|
||||
await buttonGroup.updateComplete;
|
||||
await checkButtons(secondaryButton, submitButton);
|
||||
});
|
||||
|
||||
add_task(async function testAutofocusButtonAutoSlotting() {
|
||||
render(
|
||||
html`
|
||||
<moz-button-group>
|
||||
<button autofocus>First</button>
|
||||
<button class="secondary">Secondary</button>
|
||||
</moz-button-group>
|
||||
`,
|
||||
renderArea
|
||||
);
|
||||
|
||||
let buttonGroup = document.querySelector("moz-button-group");
|
||||
let autofocusButton = buttonGroup.querySelector("[autofocus]");
|
||||
let secondaryButton = buttonGroup.querySelector(".secondary");
|
||||
buttonGroup.platform = "win";
|
||||
await buttonGroup.updateComplete;
|
||||
is(autofocusButton.slot, "primary", "autofocus button was auto-slotted")
|
||||
await checkButtons(autofocusButton, secondaryButton);
|
||||
|
||||
buttonGroup.platform = "macosx";
|
||||
await buttonGroup.updateComplete;
|
||||
await checkButtons(secondaryButton, autofocusButton);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -102,24 +102,17 @@ export class MozLitElement extends LitElement {
|
||||
|
||||
/**
|
||||
* The URL for this component's styles. To make development in Storybook
|
||||
* easier this will use the chrome:// URL when in product (feature detected
|
||||
* by AppConstants existing) and a relative path for Storybook.
|
||||
* easier this will use the chrome:// URL when in product and a relative path
|
||||
* for Storybook.
|
||||
*
|
||||
* LOCAL_NAME should be the kebab-cased name of the element. It is added by
|
||||
* the `./mach addwidget` command.
|
||||
*/
|
||||
static get stylesheetUrl() {
|
||||
if (this.useChromeStylesheet) {
|
||||
return `chrome://global/content/elements/${this.LOCAL_NAME}.css`;
|
||||
if (window.IS_STORYBOOK) {
|
||||
return `./${this.LOCAL_NAME}/${this.LOCAL_NAME}.css`;
|
||||
}
|
||||
return `./${this.LOCAL_NAME}/${this.LOCAL_NAME}.css`;
|
||||
}
|
||||
|
||||
static get useChromeStylesheet() {
|
||||
return (
|
||||
typeof AppConstants != "undefined" ||
|
||||
(typeof Cu != "undefined" && Cu.isInAutomation)
|
||||
);
|
||||
return `chrome://global/content/elements/${this.LOCAL_NAME}.css`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
@ -0,0 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
::slotted(button) {
|
||||
margin: 0 !important;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "../vendor/lit.all.mjs";
|
||||
import { MozLitElement } from "../lit-utils.mjs";
|
||||
|
||||
export const PLATFORM_LINUX = "linux";
|
||||
export const PLATFORM_MACOS = "macosx";
|
||||
export const PLATFORM_WINDOWS = "win";
|
||||
|
||||
export default class MozButtonGroup extends MozLitElement {
|
||||
static queries = {
|
||||
defaultSlotEl: "slot:not([name])",
|
||||
primarySlotEl: "slot[name=primary]",
|
||||
};
|
||||
|
||||
static properties = {
|
||||
platform: { state: true },
|
||||
};
|
||||
|
||||
// Use a relative URL in storybook to get faster reloads on style changes.
|
||||
static stylesheetUrl = window.IS_STORYBOOK
|
||||
? "./moz-button-group/moz-button-group.css"
|
||||
: "chrome://global/content/elements/moz-button-group.css";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#detectPlatform();
|
||||
}
|
||||
|
||||
#detectPlatform() {
|
||||
if (typeof AppConstants !== "undefined") {
|
||||
this.platform = AppConstants.platform;
|
||||
} else if (navigator.platform.includes("Linux")) {
|
||||
this.platform = PLATFORM_LINUX;
|
||||
} else if (navigator.platform.includes("Mac")) {
|
||||
this.platform = PLATFORM_MACOS;
|
||||
} else {
|
||||
this.platform = PLATFORM_WINDOWS;
|
||||
}
|
||||
}
|
||||
|
||||
onSlotchange(e) {
|
||||
for (let child of this.defaultSlotEl.assignedNodes()) {
|
||||
if (!(child instanceof Element)) {
|
||||
// Text nodes won't support classList or getAttribute.
|
||||
continue;
|
||||
}
|
||||
// Bug 1791816: These should check moz-button instead of button.
|
||||
if (
|
||||
child.localName == "button" &&
|
||||
(child.classList.contains("primary") ||
|
||||
child.getAttribute("type") == "submit" ||
|
||||
child.hasAttribute("autofocus"))
|
||||
) {
|
||||
child.slot = "primary";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let slots = [
|
||||
html`
|
||||
<slot @slotchange=${this.onSlotchange}></slot>
|
||||
`,
|
||||
html`
|
||||
<slot name="primary"></slot>
|
||||
`,
|
||||
];
|
||||
if (this.platform == PLATFORM_WINDOWS) {
|
||||
slots = [slots[1], slots[0]];
|
||||
}
|
||||
return html`
|
||||
<link rel="stylesheet" href=${this.constructor.stylesheetUrl} />
|
||||
${slots}
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define("moz-button-group", MozButtonGroup);
|
@ -0,0 +1,48 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { html } from "../vendor/lit.all.mjs";
|
||||
import {
|
||||
PLATFORM_LINUX,
|
||||
PLATFORM_MACOS,
|
||||
PLATFORM_WINDOWS,
|
||||
} from "./moz-button-group.mjs";
|
||||
|
||||
export default {
|
||||
title: "Design System/Experiments/MozButtonGroup",
|
||||
argTypes: {
|
||||
platform: {
|
||||
options: [PLATFORM_LINUX, PLATFORM_MACOS, PLATFORM_WINDOWS],
|
||||
control: { type: "select" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = ({ platform }) => html`
|
||||
<div class="card card-no-hover" style="max-width: 400px">
|
||||
<p>The button group is below. Card for emphasis.</p>
|
||||
<moz-button-group .platform=${platform}>
|
||||
<button class="primary">OK</button>
|
||||
<button>Cancel</button>
|
||||
</moz-button-group>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
// Platform will auto-detected.
|
||||
};
|
||||
|
||||
export const Windows = Template.bind({});
|
||||
Windows.args = {
|
||||
platform: PLATFORM_WINDOWS,
|
||||
};
|
||||
export const Mac = Template.bind({});
|
||||
Mac.args = {
|
||||
platform: PLATFORM_MACOS,
|
||||
};
|
||||
export const Linux = Template.bind({});
|
||||
Linux.args = {
|
||||
platform: PLATFORM_LINUX,
|
||||
};
|
Loading…
Reference in New Issue
Block a user