Instantly share code, notes, and snippets.
Created
April 2, 2024 21:47
-
Save aminomancer/9ec3623729c1ec160bcc4b71bc38ef3c to your computer and use it in GitHub Desktop.
awMenulistChanges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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