summaryrefslogtreecommitdiffstats
path: root/devtools/client/aboutdebugging/src/components/sidebar
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /devtools/client/aboutdebugging/src/components/sidebar
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/aboutdebugging/src/components/sidebar')
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/RefreshDevicesButton.js46
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/Sidebar.css43
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js259
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.css29
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.js60
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.css71
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js81
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.css41
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.js216
-rw-r--r--devtools/client/aboutdebugging/src/components/sidebar/moz.build11
10 files changed, 857 insertions, 0 deletions
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/RefreshDevicesButton.js b/devtools/client/aboutdebugging/src/components/sidebar/RefreshDevicesButton.js
new file mode 100644
index 0000000000..78ebb61661
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/RefreshDevicesButton.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
+const Localized = createFactory(FluentReact.Localized);
+
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Actions = require("resource://devtools/client/aboutdebugging/src/actions/index.js");
+
+class RefreshDevicesButton extends PureComponent {
+ static get propTypes() {
+ return {
+ dispatch: PropTypes.func.isRequired,
+ isScanning: PropTypes.bool.isRequired,
+ };
+ }
+
+ refreshDevices() {
+ this.props.dispatch(Actions.scanUSBRuntimes());
+ }
+
+ render() {
+ return Localized(
+ { id: "about-debugging-refresh-usb-devices-button" },
+ dom.button(
+ {
+ className: "default-button qa-refresh-devices-button",
+ disabled: this.props.isScanning,
+ onClick: () => this.refreshDevices(),
+ },
+ "Refresh devices"
+ )
+ );
+ }
+}
+
+module.exports = RefreshDevicesButton;
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.css b/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.css
new file mode 100644
index 0000000000..afad630f6e
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.css
@@ -0,0 +1,43 @@
+/* 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/. */
+
+.sidebar {
+ display: grid;
+ grid-template-rows: auto auto;
+}
+
+.sidebar__label {
+ color: var(--secondary-text-color);
+ display: block;
+ padding: 12px 0;
+ text-align: center;
+ font-size: var(--message-font-size);
+}
+
+.sidebar__adb-status {
+ margin-block-end: calc(var(--base-unit) * 2);
+}
+
+.sidebar__refresh-usb {
+ text-align: center;
+}
+
+.sidebar__footer {
+ align-self: flex-end;
+}
+
+.sidebar__footer__support-help {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ column-gap: calc(var(--base-unit) * 4);
+ height: 100%;
+}
+
+.sidebar__footer__icon {
+ width: calc(var(--base-unit) * 4);
+ height: calc(var(--base-unit) * 4);
+ -moz-context-properties: fill;
+ fill: currentColor;
+}
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js b/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js
new file mode 100644
index 0000000000..3213b8141c
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/Sidebar.js
@@ -0,0 +1,259 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
+const Localized = createFactory(FluentReact.Localized);
+
+const {
+ ICON_LABEL_LEVEL,
+ PAGE_TYPES,
+ RUNTIMES,
+} = require("resource://devtools/client/aboutdebugging/src/constants.js");
+const Types = require("resource://devtools/client/aboutdebugging/src/types/index.js");
+loader.lazyRequireGetter(
+ this,
+ "ADB_ADDON_STATES",
+ "resource://devtools/client/shared/remote-debugging/adb/adb-addon.js",
+ true
+);
+
+const IconLabel = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/shared/IconLabel.js")
+);
+const SidebarItem = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js")
+);
+const SidebarFixedItem = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.js")
+);
+const SidebarRuntimeItem = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.js")
+);
+const RefreshDevicesButton = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/RefreshDevicesButton.js")
+);
+const FIREFOX_ICON =
+ "chrome://devtools/skin/images/aboutdebugging-firefox-logo.svg";
+const CONNECT_ICON = "chrome://devtools/skin/images/settings.svg";
+const GLOBE_ICON =
+ "chrome://devtools/skin/images/aboutdebugging-globe-icon.svg";
+const USB_ICON =
+ "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
+
+class Sidebar extends PureComponent {
+ static get propTypes() {
+ return {
+ adbAddonStatus: Types.adbAddonStatus,
+ className: PropTypes.string,
+ dispatch: PropTypes.func.isRequired,
+ isAdbReady: PropTypes.bool.isRequired,
+ isScanningUsb: PropTypes.bool.isRequired,
+ networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
+ selectedPage: Types.page,
+ selectedRuntimeId: PropTypes.string,
+ usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
+ };
+ }
+
+ renderAdbStatus() {
+ const isUsbEnabled =
+ this.props.isAdbReady &&
+ this.props.adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
+ const localizationId = isUsbEnabled
+ ? "about-debugging-sidebar-usb-enabled"
+ : "about-debugging-sidebar-usb-disabled";
+ return IconLabel(
+ {
+ level: isUsbEnabled ? ICON_LABEL_LEVEL.OK : ICON_LABEL_LEVEL.INFO,
+ },
+ Localized(
+ {
+ id: localizationId,
+ },
+ dom.span(
+ {
+ className: "qa-sidebar-usb-status",
+ },
+ localizationId
+ )
+ )
+ );
+ }
+
+ renderDevicesEmpty() {
+ return SidebarItem(
+ {},
+ Localized(
+ {
+ id: "about-debugging-sidebar-no-devices",
+ },
+ dom.aside(
+ {
+ className: "sidebar__label qa-sidebar-no-devices",
+ },
+ "No devices discovered"
+ )
+ )
+ );
+ }
+
+ renderDevices() {
+ const { networkRuntimes, usbRuntimes } = this.props;
+
+ // render a "no devices" messages when the lists are empty
+ if (!networkRuntimes.length && !usbRuntimes.length) {
+ return this.renderDevicesEmpty();
+ }
+ // render all devices otherwise
+ return [
+ ...this.renderRuntimeItems(GLOBE_ICON, networkRuntimes),
+ ...this.renderRuntimeItems(USB_ICON, usbRuntimes),
+ ];
+ }
+
+ renderRuntimeItems(icon, runtimes) {
+ const { dispatch, selectedPage, selectedRuntimeId } = this.props;
+
+ return runtimes.map(runtime => {
+ const keyId = `${runtime.type}-${runtime.id}`;
+ const runtimeHasDetails = !!runtime.runtimeDetails;
+ const isSelected =
+ selectedPage === PAGE_TYPES.RUNTIME && runtime.id === selectedRuntimeId;
+
+ let name = runtime.name;
+ if (runtime.type === RUNTIMES.USB && runtimeHasDetails) {
+ // Update the name to be same to the runtime page.
+ name = runtime.runtimeDetails.info.name;
+ }
+
+ return SidebarRuntimeItem({
+ deviceName: runtime.extra.deviceName,
+ dispatch,
+ icon,
+ key: keyId,
+ isConnected: runtimeHasDetails,
+ isConnecting: runtime.isConnecting,
+ isConnectionFailed: runtime.isConnectionFailed,
+ isConnectionNotResponding: runtime.isConnectionNotResponding,
+ isConnectionTimeout: runtime.isConnectionTimeout,
+ isSelected,
+ isUnavailable: runtime.isUnavailable,
+ isUnplugged: runtime.isUnplugged,
+ name,
+ runtimeId: runtime.id,
+ });
+ });
+ }
+
+ renderFooter() {
+ const HELP_ICON_SRC = "chrome://global/skin/icons/help.svg";
+ const SUPPORT_URL =
+ "https://firefox-source-docs.mozilla.org/devtools-user/about_colon_debugging/";
+
+ return dom.footer(
+ {
+ className: "sidebar__footer",
+ },
+ dom.ul(
+ {},
+ SidebarItem(
+ {
+ className: "sidebar-item--condensed",
+ to: SUPPORT_URL,
+ },
+ dom.span(
+ {
+ className: "sidebar__footer__support-help",
+ },
+ Localized(
+ {
+ id: "about-debugging-sidebar-support-icon",
+ attrs: {
+ alt: true,
+ },
+ },
+ dom.img({
+ className: "sidebar__footer__icon",
+ src: HELP_ICON_SRC,
+ })
+ ),
+ Localized(
+ {
+ id: "about-debugging-sidebar-support",
+ },
+ dom.span({}, "about-debugging-sidebar-support")
+ )
+ )
+ )
+ )
+ );
+ }
+
+ render() {
+ const { dispatch, selectedPage, selectedRuntimeId, isScanningUsb } =
+ this.props;
+
+ return dom.aside(
+ {
+ className: `sidebar ${this.props.className || ""}`,
+ },
+ dom.ul(
+ {},
+ Localized(
+ { id: "about-debugging-sidebar-setup", attrs: { name: true } },
+ SidebarFixedItem({
+ dispatch,
+ icon: CONNECT_ICON,
+ isSelected: PAGE_TYPES.CONNECT === selectedPage,
+ key: PAGE_TYPES.CONNECT,
+ name: "Setup",
+ to: "/setup",
+ })
+ ),
+ Localized(
+ { id: "about-debugging-sidebar-this-firefox", attrs: { name: true } },
+ SidebarFixedItem({
+ icon: FIREFOX_ICON,
+ isSelected:
+ PAGE_TYPES.RUNTIME === selectedPage &&
+ selectedRuntimeId === RUNTIMES.THIS_FIREFOX,
+ key: RUNTIMES.THIS_FIREFOX,
+ name: "This Firefox",
+ to: `/runtime/${RUNTIMES.THIS_FIREFOX}`,
+ })
+ ),
+ SidebarItem(
+ {
+ className: "sidebar__adb-status",
+ },
+ dom.hr({ className: "separator separator--breathe" }),
+ this.renderAdbStatus()
+ ),
+ this.renderDevices(),
+ SidebarItem(
+ {
+ className: "sidebar-item--breathe sidebar__refresh-usb",
+ key: "refresh-devices",
+ },
+ RefreshDevicesButton({
+ dispatch,
+ isScanning: isScanningUsb,
+ })
+ )
+ ),
+ this.renderFooter()
+ );
+ }
+}
+
+module.exports = Sidebar;
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.css b/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.css
new file mode 100644
index 0000000000..7345b8e80f
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.css
@@ -0,0 +1,29 @@
+/* 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/. */
+
+/*
+ * Layout of a fixed sidebar item
+ *
+ * +--------+----------------+
+ * | Icon | Name |
+ * +--------+----------------+
+ */
+
+.sidebar-fixed-item__container {
+ align-items: center;
+ border-radius: 2px;
+ display: grid;
+ grid-template-columns: 34px 1fr;
+ height: 100%;
+ font-size: var(--body-20-font-size);
+ font-weight: var(--body-20-font-weight);
+}
+
+.sidebar-fixed-item__icon {
+ fill: currentColor;
+ height: 24px;
+ margin-inline-end: 9px;
+ width: 24px;
+ -moz-context-properties: fill;
+}
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.js b/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.js
new file mode 100644
index 0000000000..cf5dbab31a
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarFixedItem.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ PureComponent,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const SidebarItem = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js")
+);
+
+/**
+ * This component displays a fixed item in the Sidebar component.
+ */
+class SidebarFixedItem extends PureComponent {
+ static get propTypes() {
+ return {
+ icon: PropTypes.string.isRequired,
+ isSelected: PropTypes.bool.isRequired,
+ name: PropTypes.string.isRequired,
+ to: PropTypes.string,
+ };
+ }
+
+ render() {
+ const { icon, isSelected, name, to } = this.props;
+
+ return SidebarItem(
+ {
+ className: "sidebar-item--tall",
+ isSelected,
+ to,
+ },
+ dom.div(
+ {
+ className: "sidebar-fixed-item__container",
+ },
+ dom.img({
+ className: "sidebar-fixed-item__icon",
+ src: icon,
+ }),
+ dom.span(
+ {
+ className: "ellipsis-text",
+ title: name,
+ },
+ name
+ )
+ )
+ );
+ }
+}
+
+module.exports = SidebarFixedItem;
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.css b/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.css
new file mode 100644
index 0000000000..3f12964012
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.css
@@ -0,0 +1,71 @@
+/* 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/. */
+
+.sidebar-item {
+ color: var(--sidebar-text-color);
+ border-radius: 2px;
+ padding-inline-end: var(--category-padding);
+ padding-inline-start: var(--category-padding);
+ transition: background-color var(--category-transition-duration);
+ user-select: none;
+}
+
+.sidebar-item--tall {
+ height: var(--category-height);
+}
+
+.sidebar-item--condensed {
+ height: calc(var(--base-unit) * 9);
+}
+
+.sidebar-item__link {
+ display: block;
+ height: 100%;
+}
+
+.sidebar-item__link,
+.sidebar-item__link:hover {
+ color: inherit; /* do not apply usual link colors, but grab this element parent's */
+}
+
+.sidebar-item:not(.sidebar-item--selectable) {
+ color: var(--secondary-text-color);
+}
+
+.sidebar-item--selectable:hover {
+ background-color: var(--sidebar-background-hover);
+}
+
+.sidebar-item--selected {
+ color: var(--sidebar-selected-color);
+}
+
+.sidebar-item--breathe {
+ margin-block-start: calc(6 * var(--base-unit));
+ margin-block-end: calc(2 * var(--base-unit));
+}
+
+@media (prefers-contrast) {
+ /* Color transitions (black <-> white) look bad in high contrast */
+ .sidebar-item {
+ transition: background 0s;
+ }
+
+ .sidebar-item--selected,
+ .sidebar-item--selected:hover {
+ background-color: ButtonText;
+ }
+
+ /* `color: inherit` should not be used in high contrast mode
+ otherwise the link inherits the <a> color from ua.css */
+ .sidebar-item__link,
+ .sidebar-item__link:hover {
+ color: ButtonText;
+ }
+
+ .sidebar-item--selected .sidebar-item__link,
+ .sidebar-item--selected .sidebar-item__link:hover {
+ color: ButtonFace;
+ }
+}
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js b/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js
new file mode 100644
index 0000000000..cd17d8e6bc
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js
@@ -0,0 +1,81 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Link = createFactory(
+ require("resource://devtools/client/shared/vendor/react-router-dom.js").Link
+);
+
+/**
+ * This component is used as a wrapper by items in the sidebar.
+ */
+class SidebarItem extends PureComponent {
+ static get propTypes() {
+ return {
+ children: PropTypes.node.isRequired,
+ className: PropTypes.string,
+ isSelected: PropTypes.bool.isRequired,
+ to: PropTypes.string,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ isSelected: false,
+ };
+ }
+
+ renderContent() {
+ const { children, to } = this.props;
+
+ if (to) {
+ const isExternalUrl = /^http/.test(to);
+
+ return isExternalUrl
+ ? dom.a(
+ {
+ className: "sidebar-item__link undecorated-link",
+ href: to,
+ target: "_blank",
+ },
+ children
+ )
+ : Link(
+ {
+ className: "sidebar-item__link qa-sidebar-link undecorated-link",
+ to,
+ },
+ children
+ );
+ }
+
+ return children;
+ }
+
+ render() {
+ const { className, isSelected, to } = this.props;
+
+ return dom.li(
+ {
+ className:
+ "sidebar-item qa-sidebar-item" +
+ (className ? ` ${className}` : "") +
+ (isSelected
+ ? " sidebar-item--selected qa-sidebar-item-selected"
+ : "") +
+ (to ? " sidebar-item--selectable" : ""),
+ },
+ this.renderContent()
+ );
+ }
+}
+
+module.exports = SidebarItem;
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.css b/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.css
new file mode 100644
index 0000000000..423723a27f
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+/*
+ * Layout of a runtime sidebar item
+ *
+ * +--------+----------------+---------------------------+
+ * | Icon | Runtime name | Connect button |
+ * +--------+----------------+---------------------------+
+ */
+
+.sidebar-runtime-item__container {
+ box-sizing: border-box;
+ height: var(--category-height);
+ align-items: center;
+ display: grid;
+ grid-column-gap: var(--base-unit);
+ grid-template-columns: calc(var(--base-unit) * 6) 1fr auto;
+ font-size: var(--body-20-font-size);
+ font-weight: var(--body-20-font-weight);
+}
+
+.sidebar-runtime-item__icon {
+ fill: currentColor;
+ -moz-context-properties: fill;
+}
+
+.sidebar-runtime-item__runtime {
+ line-height: 1;
+}
+
+.sidebar-runtime-item__runtime__details {
+ font-size: var(--caption-10-font-size);
+ font-weight: var(--caption-10-font-weight);
+ line-height: 1.2;
+}
+
+.sidebar-runtime-item__message:first-of-type {
+ margin-block-start: calc(var(--base-unit) * -1);
+}
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.js b/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.js
new file mode 100644
index 0000000000..a5c5fe6d55
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/SidebarRuntimeItem.js
@@ -0,0 +1,216 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
+const Localized = createFactory(FluentReact.Localized);
+
+const Message = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/shared/Message.js")
+);
+const SidebarItem = createFactory(
+ require("resource://devtools/client/aboutdebugging/src/components/sidebar/SidebarItem.js")
+);
+const Actions = require("resource://devtools/client/aboutdebugging/src/actions/index.js");
+const {
+ MESSAGE_LEVEL,
+} = require("resource://devtools/client/aboutdebugging/src/constants.js");
+
+/**
+ * This component displays a runtime item of the Sidebar component.
+ */
+class SidebarRuntimeItem extends PureComponent {
+ static get propTypes() {
+ return {
+ deviceName: PropTypes.string,
+ dispatch: PropTypes.func.isRequired,
+ // Provided by wrapping the component with FluentReact.withLocalization.
+ getString: PropTypes.func.isRequired,
+ icon: PropTypes.string.isRequired,
+ isConnected: PropTypes.bool.isRequired,
+ isConnecting: PropTypes.bool.isRequired,
+ isConnectionFailed: PropTypes.bool.isRequired,
+ isConnectionNotResponding: PropTypes.bool.isRequired,
+ isConnectionTimeout: PropTypes.bool.isRequired,
+ isSelected: PropTypes.bool.isRequired,
+ isUnavailable: PropTypes.bool.isRequired,
+ isUnplugged: PropTypes.bool.isRequired,
+ name: PropTypes.string.isRequired,
+ runtimeId: PropTypes.string.isRequired,
+ };
+ }
+
+ renderConnectButton() {
+ const { isConnecting } = this.props;
+ const localizationId = isConnecting
+ ? "about-debugging-sidebar-item-connect-button-connecting"
+ : "about-debugging-sidebar-item-connect-button";
+ return Localized(
+ {
+ id: localizationId,
+ },
+ dom.button(
+ {
+ className: "default-button default-button--micro qa-connect-button",
+ disabled: isConnecting,
+ onClick: () => {
+ const { dispatch, runtimeId } = this.props;
+ dispatch(Actions.connectRuntime(runtimeId));
+ },
+ },
+ localizationId
+ )
+ );
+ }
+
+ renderMessage(flag, level, localizationId, className) {
+ if (!flag) {
+ return null;
+ }
+
+ return Message(
+ {
+ level,
+ className: `${className} sidebar-runtime-item__message`,
+ isCloseable: true,
+ },
+ Localized(
+ {
+ id: localizationId,
+ },
+ dom.p({ className: "word-wrap-anywhere" }, localizationId)
+ )
+ );
+ }
+
+ renderName() {
+ const { deviceName, getString, isUnavailable, isUnplugged, name } =
+ this.props;
+
+ let displayName, qaClassName;
+ if (isUnplugged) {
+ displayName = getString("about-debugging-sidebar-runtime-item-unplugged");
+ qaClassName = "qa-runtime-item-unplugged";
+ } else if (isUnavailable) {
+ displayName = getString(
+ "about-debugging-sidebar-runtime-item-waiting-for-browser"
+ );
+ qaClassName = "qa-runtime-item-waiting-for-browser";
+ } else {
+ displayName = name;
+ qaClassName = "qa-runtime-item-standard";
+ }
+
+ const localizationId = deviceName
+ ? "about-debugging-sidebar-runtime-item-name"
+ : "about-debugging-sidebar-runtime-item-name-no-device";
+
+ const className = "ellipsis-text sidebar-runtime-item__runtime";
+
+ function renderWithDevice() {
+ return dom.span(
+ {
+ className,
+ title: localizationId,
+ },
+ deviceName,
+ dom.br({}),
+ dom.span(
+ {
+ className: `sidebar-runtime-item__runtime__details ${qaClassName}`,
+ },
+ displayName
+ )
+ );
+ }
+
+ function renderNoDevice() {
+ return dom.span(
+ {
+ className,
+ title: localizationId,
+ },
+ displayName
+ );
+ }
+
+ return Localized(
+ {
+ id: localizationId,
+ attrs: { title: true },
+ $deviceName: deviceName,
+ $displayName: displayName,
+ },
+ deviceName ? renderWithDevice() : renderNoDevice()
+ );
+ }
+
+ render() {
+ const {
+ getString,
+ icon,
+ isConnected,
+ isConnectionFailed,
+ isConnectionTimeout,
+ isConnectionNotResponding,
+ isSelected,
+ isUnavailable,
+ runtimeId,
+ } = this.props;
+
+ const connectionStatus = isConnected
+ ? getString("aboutdebugging-sidebar-runtime-connection-status-connected")
+ : getString(
+ "aboutdebugging-sidebar-runtime-connection-status-disconnected"
+ );
+
+ return SidebarItem(
+ {
+ isSelected,
+ to: isConnected ? `/runtime/${encodeURIComponent(runtimeId)}` : null,
+ },
+ dom.section(
+ {
+ className: "sidebar-runtime-item__container",
+ },
+ dom.img({
+ className: "sidebar-runtime-item__icon ",
+ src: icon,
+ alt: connectionStatus,
+ title: connectionStatus,
+ }),
+ this.renderName(),
+ !isUnavailable && !isConnected ? this.renderConnectButton() : null
+ ),
+ this.renderMessage(
+ isConnectionFailed,
+ MESSAGE_LEVEL.ERROR,
+ "about-debugging-sidebar-item-connect-button-connection-failed",
+ "qa-connection-error"
+ ),
+ this.renderMessage(
+ isConnectionTimeout,
+ MESSAGE_LEVEL.ERROR,
+ "about-debugging-sidebar-item-connect-button-connection-timeout",
+ "qa-connection-timeout"
+ ),
+ this.renderMessage(
+ isConnectionNotResponding,
+ MESSAGE_LEVEL.WARNING,
+ "about-debugging-sidebar-item-connect-button-connection-not-responding",
+ "qa-connection-not-responding"
+ )
+ );
+ }
+}
+
+module.exports = FluentReact.withLocalization(SidebarRuntimeItem);
diff --git a/devtools/client/aboutdebugging/src/components/sidebar/moz.build b/devtools/client/aboutdebugging/src/components/sidebar/moz.build
new file mode 100644
index 0000000000..081ea2a848
--- /dev/null
+++ b/devtools/client/aboutdebugging/src/components/sidebar/moz.build
@@ -0,0 +1,11 @@
+# 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/.
+
+DevToolsModules(
+ "RefreshDevicesButton.js",
+ "Sidebar.js",
+ "SidebarFixedItem.js",
+ "SidebarItem.js",
+ "SidebarRuntimeItem.js",
+)