Skip to content

Instantly share code, notes, and snippets.

@aminomancer
Created April 2, 2024 21:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aminomancer/9ec3623729c1ec160bcc4b71bc38ef3c to your computer and use it in GitHub Desktop.
Save aminomancer/9ec3623729c1ec160bcc4b71bc38ef3c to your computer and use it in GitHub Desktop.
awMenulistChanges
diff --git a/browser/components/aboutwelcome/content-src/aboutwelcome.scss b/browser/components/aboutwelcome/content-src/aboutwelcome.scss
index d96e1052a813b..897fe87c340c8 100644
--- a/browser/components/aboutwelcome/content-src/aboutwelcome.scss
+++ b/browser/components/aboutwelcome/content-src/aboutwelcome.scss
@@ -1732,15 +1732,35 @@ html {
.aboutwelcome-menulist {
width: fit-content;
margin: auto;
+ fill: currentColor;
+ -moz-context-properties: fill;
- .menupopup-arrowscrollbox,
- .fxms-menulist-submenu {
+ .fxms-menulist menupopup {
--panel-border-radius: 4px;
+ --panel-padding: var(--panel-padding-block);
+ --panel-disabled-color: color-mix(in srgb, currentColor 40%, transparent);
shmed@IMPServer /c/mozilla-git/mozilla-unified-2
$ git diff
diff --git a/browser/components/aboutwelcome/content-src/aboutwelcome.scss b/browser/components/aboutwelcome/content-src/aboutwelcome.scss
index d96e1052a813b..897fe87c340c8 100644
--- a/browser/components/aboutwelcome/content-src/aboutwelcome.scss
+++ b/browser/components/aboutwelcome/content-src/aboutwelcome.scss
@@ -1732,15 +1732,35 @@ html {
.aboutwelcome-menulist {
width: fit-content;
margin: auto;
+ fill: currentColor;
+ -moz-context-properties: fill;
- .menupopup-arrowscrollbox,
- .fxms-menulist-submenu {
+ .fxms-menulist menupopup {
--panel-border-radius: 4px;
+ --panel-padding: var(--panel-padding-block);
+ --panel-disabled-color: color-mix(in srgb, currentColor 40%, transparent);
- .menuitem-iconic {
- border-radius: 4px;
- padding: 8px 4px;
- margin: 2px;
+ menuitem {
+ padding-block: 0.5em;
+
+ &[selected='true'] {
+ color: revert;
+ background-color: revert;
+ }
+
+ &[disabled='true'] {
+ color: var(--panel-disabled-color);
+ text-shadow: none;
+ }
+
+ &[_moz-menuactive]:not([disabled='true']) {
+ color: var(--in-content-item-selected-text);
+ background-color: var(--in-content-item-selected);
+ }
+
+ .menu-iconic-left {
+ display: flex;
+ }
}
}
}
diff --git a/browser/components/aboutwelcome/content-src/components/MenuList.jsx b/browser/components/aboutwelcome/content-src/components/MenuList.jsx
index 1256b4a402099..7ab71b9ef1a2b 100644
--- a/browser/components/aboutwelcome/content-src/components/MenuList.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MenuList.jsx
@@ -2,7 +2,7 @@
* 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 React, { useEffect, useRef, useCallback } from "react";
+import React, { useEffect, useRef } from "react";
export const MenuList = props => {
return document.createXULElement ? <MenuListInner {...props} /> : null;
@@ -35,66 +35,30 @@ function translateMenuitem(item, element) {
function addMenuitems(items, popup) {
for (let item of items) {
- switch (item.type) {
- case "separator":
- popup.appendChild(document.createXULElement("menuseparator"));
- break;
- case "menu":
- let menu = document.createXULElement("menu");
- menu.className = "fxms-multi-stage-menu";
- translateMenuitem(item, menu);
- if (item.id) {
- menu.value = item.id;
- }
- if (item.icon) {
- menu.classList.add("menu-iconic");
- menu.setAttribute("image", item.icon);
- }
- popup.appendChild(menu);
- let submenuPopup = document.createXULElement("menupopup");
- menu.appendChild(submenuPopup);
- addMenuitems(item.submenu, submenuPopup);
- break;
- case "action":
- let menuitem = document.createXULElement("menuitem");
- translateMenuitem(item, menuitem);
- menuitem.config = item;
- if (item.id) {
- menuitem.value = item.id;
- }
- if (item.icon) {
- menuitem.classList.add("menuitem-iconic");
- menuitem.setAttribute("image", item.icon);
- }
- popup.appendChild(menuitem);
- break;
+ let menuitem = document.createXULElement("menuitem");
+ translateMenuitem(item, menuitem);
+ menuitem.config = item;
+ if (item.id) {
+ menuitem.value = item.id;
}
+ if (item.icon) {
+ menuitem.classList.add("menuitem-iconic");
+ menuitem.setAttribute("image", item.icon);
+ }
+ popup.appendChild(menuitem);
}
}
-const MenuListInner = ({ content, handleAction }) => {
+const MenuListInner = ({ content }) => {
const ref = useRef(null);
- const onCommand = useCallback(
- event => {
- let { config } = event.target;
- let mockEvent = {
- currentTarget: ref.current,
- source: config.id,
- name: "command",
- action: config.action,
- };
- handleAction(mockEvent);
- },
- [handleAction]
- );
useEffect(() => {
let list = ref.current;
- if (!list || list.querySelector(".fxms-multi-stage-list-submenu")) {
+ if (!list || list.querySelector(".fxms-menulist")) {
return null;
}
let menulist = document.createXULElement("menulist");
+ menulist.className = "fxms-menulist";
let menupopup = document.createXULElement("menupopup");
- menupopup.className = "fxms-menulist-submenu";
menupopup.toggleAttribute("position", "center");
addMenuitems(content.submenu, menupopup);
menulist.appendChild(menupopup);
@@ -110,15 +74,11 @@ const MenuListInner = ({ content, handleAction }) => {
stylesheet.href = "chrome://global/content/widgets.css";
document.head.appendChild(stylesheet);
}
- if (!menupopup.listenersRegistered) {
- menupopup.addEventListener("command", onCommand);
- menupopup.listenersRegistered = true;
- }
return () => {
menupopup?.remove();
stylesheet?.remove();
};
- }, [onCommand]); // eslint-disable-line react-hooks/exhaustive-deps
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
<div className="aboutwelcome-menulist" ref={ref}>
diff --git a/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx b/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsxindex 3deda007ac94c..34ef122d41e75 100644
--- a/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MultiStageAboutWelcome.jsx
@@ -397,6 +397,7 @@ export class WelcomeScreen extends React.PureComponent {
return AboutWelcomeUtils.handleUserAction({ type, data });
}
+ // eslint-disable-next-line max-statements
async handleAction(event) {
let { props } = this;
const value =
@@ -411,10 +412,6 @@ export class WelcomeScreen extends React.PureComponent {
targetContent = { action: event.action };
}
- if (targetContent.type === "menulist" && event.action) {
- this.setPrimaryAction(event.action);
- }
-
if (!(targetContent && targetContent.action)) {
return;
}
@@ -431,6 +428,10 @@ export class WelcomeScreen extends React.PureComponent {
let { action } = targetContent;
action = JSON.parse(JSON.stringify(action));
+ if (action.collectMenuList) {
+ this.setMenuListActions(action);
+ }
+
if (action.collectSelect) {
this.setMultiSelectActions(action);
}
@@ -493,11 +494,39 @@ export class WelcomeScreen extends React.PureComponent {
}
}
- setPrimaryAction(action) {
- let { props } = this;
- // Find the primary button && set the action
- let primaryButton = props.content.primary_button;
- primaryButton.action = action;
+ setMenuListActions(action) {
+ if (action.type !== "MULTI_ACTION") {
+ console.error(
+ "collectMenuList is only supported for MULTI_ACTION type actions"
+ );
+ action.type = "MULTI_ACTION";
+ }
+ if (!Array.isArray(action.data?.actions)) {
+ console.error(
+ "collectMenuList is only supported for MULTI_ACTION type actions with an array of actions"
+ );
+ action.data = { actions: [] };
+ }
+
+ let menulist = document.querySelector(".fxms-menulist");
+ if (menulist) {
+ let { selectedItem } = menulist;
+ if (selectedItem) {
+ let { config } = selectedItem;
+ let selectedAction = config?.action;
+ if (selectedAction) {
+ action.data.actions.push(selectedAction);
+ }
+ if (config?.id) {
+ // Send telemetry with selected menuitem id
+ AboutWelcomeUtils.sendActionTelemetry(
+ this.props.messageId,
+ config?.id,
+ "SELECT_MENULIST_ITEM"
+ );
+ }
+ }
+ }
}
setMultiSelectActions(action) {
diff --git a/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx b/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsxindex dd6be7712f4e9..a19154999e450 100644
--- a/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx
+++ b/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx
@@ -318,10 +318,7 @@ export class ProtonScreen extends React.PureComponent {
/>
) : null}
{content.tiles && content.tiles.type === "menulist" ? (
- <MenuList
- handleAction={this.props.handleAction}
- content={content.tiles.data}
- />
+ <MenuList content={content.tiles.data} />
) : null}
</React.Fragment>
);
diff --git a/browser/components/aboutwelcome/content/aboutwelcome.html b/browser/components/aboutwelcome/content/aboutwelcome.html
index 3a1391bb969ac..eb56c63110fec 100644
--- a/browser/components/aboutwelcome/content/aboutwelcome.html
+++ b/browser/components/aboutwelcome/content/aboutwelcome.html
@@ -42,7 +42,6 @@
<script src="chrome://browser/content/aboutwelcome/aboutwelcome.bundle.js"></script>
<script src="chrome://global/content/elements/named-deck.js" async></script>
<script src="chrome://global/content/elements/panel-list.js" async></script>
- <script src="chrome://global/content/elements/menulist.js" async></script>
<script
src="chrome://browser/content/migration/migration-wizard.mjs"
type="module"
diff --git a/browser/components/asrouter/docs/feature-callout.md b/browser/components/asrouter/docs/feature-callout.md
index 9cd083e5f22d1..ee7f5f789382c 100644
--- a/browser/components/asrouter/docs/feature-callout.md
+++ b/browser/components/asrouter/docs/feature-callout.md
@@ -439,6 +439,12 @@ interface Action {
// all the actions for all the selected checkboxes/radios into this action's
// data.actions array, and perform them in series.
collectSelect?: boolean;
+ // Set to true if this action is for the primary button and you're using the
+ // "menulist" tile. This is what allows the primary button to perform the
+ // actions specified by the item the user selects from the dropdown menu. It
+ // behaves like collectSelect. The action you set this for must be of
+ // MULTI_ACTION type, with an array (which can be empty) in data.actions.
+ collectMenuList?: boolean;
}
// Either an image or a paragraph that supports inline links. Currently requires
diff --git a/browser/components/asrouter/modules/PanelTestProvider.sys.mjs b/browser/components/asrouter/modules/PanelTestProvider.sys.mjs
index 446b769140ba3..2b2a85a24fa67 100644
--- a/browser/components/asrouter/modules/PanelTestProvider.sys.mjs
+++ b/browser/components/asrouter/modules/PanelTestProvider.sys.mjs
@@ -658,9 +658,14 @@ const MESSAGES = () => [
},
primary_button: {
label: {
- raw: "Save",
+ raw: "Do the thing",
+ },
+ action: {
+ type: "MULTI_ACTION",
+ collectMenuList: true,
+ data: { actions: [] },
+ navigate: true,
},
- action: {},
},
secondary_button: {
label: {
@@ -676,22 +681,29 @@ const MESSAGES = () => [
submenu: [
{
type: "action",
- label: { raw: "Item 1" },
+ label: { raw: "Mozilla VPN" },
action: {
type: "OPEN_URL",
data: {
- args: "https://vpn.mozilla.org/",
+ args: "https://www.mozilla.org/products/vpn/",
+ where: "tabshifted",
},
},
id: "item1",
- icon: "chrome://branding/content/icon16.png",
+ icon: "chrome://browser/skin/preferences/vpn-logo.svg",
},
{
type: "action",
- label: { raw: "Item 2" },
- action: { navigate: true },
+ label: { raw: "Firefox Relay" },
+ action: {
+ type: "OPEN_URL",
+ data: {
+ args: "https://relay.firefox.com/",
+ where: "tabshifted",
+ },
+ },
id: "item2",
- icon: "chrome://browser/skin/circle-check-dotted.svg",
+ icon: "chrome://browser/skin/preferences/relay-logo.svg",
},
],
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment