commit 33294a54240daf8cd6d85d0216b0bc77b82ae64f
parent d93437669de7a0dd10e5903fb3a6dd5f6a15c204
Author: Maxx Crawford <mcrawford@mozilla.com>
Date: Wed, 1 Oct 2025 14:03:13 +0000
Bug 1991678 - Add remote data support (images, custom text strings) to FollowSectionButtonHighlight component r=home-newtab-reviewers,nbarrett
Differential Revision: https://phabricator.services.mozilla.com/D266885
Diffstat:
6 files changed, 107 insertions(+), 35 deletions(-)
diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardSections/CardSections.jsx b/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/CardSections/CardSections.jsx
@@ -284,6 +284,7 @@ function CardSection({
position="arrow-inline-start"
dispatch={dispatch}
feature="FEATURE_FOLLOW_SECTION_BUTTON"
+ messageData={messageData}
/>
</MessageWrapper>
)}
diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FollowSectionButtonHighlight.jsx b/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FollowSectionButtonHighlight.jsx
@@ -7,12 +7,13 @@ import React, { useCallback } from "react";
export function FollowSectionButtonHighlight({
arrowPosition,
- position,
- verticalPosition,
dispatch,
- handleDismiss,
- handleBlock,
feature,
+ handleBlock,
+ handleDismiss,
+ messageData,
+ position,
+ verticalPosition,
}) {
const onDismiss = useCallback(() => {
handleDismiss();
@@ -20,7 +21,9 @@ export function FollowSectionButtonHighlight({
}, [handleDismiss, handleBlock]);
return (
- <div className="follow-section-button-highlight">
+ <div
+ className={`follow-section-button-highlight ${messageData.content?.darkModeDismiss ? "is-inverted-dark-dismiss-button" : ""}`}
+ >
<FeatureHighlight
position={position}
arrowPosition={arrowPosition}
@@ -29,21 +32,40 @@ export function FollowSectionButtonHighlight({
dispatch={dispatch}
message={
<div className="follow-section-button-highlight-content">
- <img
- src="chrome://browser/content/asrouter/assets/smiling-fox-icon.svg"
- width="24"
- height="24"
- alt=""
- />
- <div className="follow-section-button-highlight-copy">
- <p
- className="title"
- data-l10n-id="newtab-section-follow-highlight-title"
+ <picture className="follow-section-button-highlight-image">
+ <source
+ srcSet={
+ messageData.content?.darkModeImageURL ||
+ "chrome://newtab/content/data/content/assets/highlights/omc-newtab-follow.svg"
+ }
+ media="(prefers-color-scheme: dark)"
/>
- <p
- className="subtitle"
- data-l10n-id="newtab-section-follow-highlight-subtitle"
+ <source
+ srcSet={
+ messageData.content?.imageURL ||
+ "chrome://newtab/content/data/content/assets/highlights/omc-newtab-follow.svg"
+ }
+ media="(prefers-color-scheme: light)"
/>
+ <img width="320" height="195" alt="" />
+ </picture>
+ <div className="follow-section-button-highlight-copy">
+ {messageData.content?.cardTitle ? (
+ <p className="title">{messageData.content.cardTitle}</p>
+ ) : (
+ <p
+ className="title"
+ data-l10n-id="newtab-section-follow-highlight-title"
+ />
+ )}
+ {messageData.content?.cardMessage ? (
+ <p className="subtitle">{messageData.content.cardMessage}</p>
+ ) : (
+ <p
+ className="subtitle"
+ data-l10n-id="newtab-section-follow-highlight-subtitle"
+ />
+ )}
</div>
</div>
}
diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FollowSectionButtonHighlight.scss b/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FollowSectionButtonHighlight.scss
@@ -2,6 +2,7 @@
.follow-section-button-highlight {
.feature-highlight-modal {
padding: var(--space-large);
+ width: auto;
p {
margin: 0;
@@ -15,6 +16,25 @@
margin-block-start: 0;
margin-inline-end: 0;
}
+
+ // The "Dismiss" button in the top right corner of the highlight needs to be in front of the image
+ > moz-button {
+ position: absolute;
+ inset-inline-end: var(--space-large);
+ inset-block-start: var(--space-large);
+ }
+ }
+
+ // Custom override for dismiss-contrast
+ &.is-inverted-dark-dismiss-button {
+ .feature-highlight-modal {
+ @media (prefers-color-scheme: dark) {
+ // override color so that it is visible on the image
+ >moz-button {
+ --button-icon-fill: var(--color-gray-70);
+ }
+ }
+ }
}
.feature-highlight-modal.arrow-inline-start {
@@ -53,6 +73,7 @@
.follow-section-button-highlight-content {
display: flex;
+ flex-direction: column;
gap: var(--space-medium);
}
diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css
@@ -8426,6 +8426,7 @@ dialog::after {
.follow-section-button-highlight .feature-highlight-modal {
padding: var(--space-large);
+ width: auto;
}
.follow-section-button-highlight .feature-highlight-modal p {
margin: 0;
@@ -8437,6 +8438,16 @@ dialog::after {
margin-block-start: 0;
margin-inline-end: 0;
}
+.follow-section-button-highlight .feature-highlight-modal > moz-button {
+ position: absolute;
+ inset-inline-end: var(--space-large);
+ inset-block-start: var(--space-large);
+}
+@media (prefers-color-scheme: dark) {
+ .follow-section-button-highlight.is-inverted-dark-dismiss-button .feature-highlight-modal > moz-button {
+ --button-icon-fill: var(--color-gray-70);
+ }
+}
.follow-section-button-highlight .feature-highlight-modal.arrow-inline-start {
inset-block-start: calc(100% + var(--space-large));
inset-inline-start: 50%;
@@ -8468,6 +8479,7 @@ dialog::after {
}
.follow-section-button-highlight .follow-section-button-highlight-content {
display: flex;
+ flex-direction: column;
gap: var(--space-medium);
}
.follow-section-button-highlight .follow-section-button-highlight-copy {
diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js
@@ -11462,19 +11462,20 @@ const PersonalizedCard = ({
function FollowSectionButtonHighlight({
arrowPosition,
- position,
- verticalPosition,
dispatch,
- handleDismiss,
+ feature,
handleBlock,
- feature
+ handleDismiss,
+ messageData,
+ position,
+ verticalPosition
}) {
const onDismiss = (0,external_React_namespaceObject.useCallback)(() => {
handleDismiss();
handleBlock();
}, [handleDismiss, handleBlock]);
return /*#__PURE__*/external_React_default().createElement("div", {
- className: "follow-section-button-highlight"
+ className: `follow-section-button-highlight ${messageData.content?.darkModeDismiss ? "is-inverted-dark-dismiss-button" : ""}`
}, /*#__PURE__*/external_React_default().createElement(FeatureHighlight, {
position: position,
arrowPosition: arrowPosition,
@@ -11483,17 +11484,28 @@ function FollowSectionButtonHighlight({
dispatch: dispatch,
message: /*#__PURE__*/external_React_default().createElement("div", {
className: "follow-section-button-highlight-content"
- }, /*#__PURE__*/external_React_default().createElement("img", {
- src: "chrome://browser/content/asrouter/assets/smiling-fox-icon.svg",
- width: "24",
- height: "24",
+ }, /*#__PURE__*/external_React_default().createElement("picture", {
+ className: "follow-section-button-highlight-image"
+ }, /*#__PURE__*/external_React_default().createElement("source", {
+ srcSet: messageData.content?.darkModeImageURL || "chrome://newtab/content/data/content/assets/highlights/omc-newtab-follow.svg",
+ media: "(prefers-color-scheme: dark)"
+ }), /*#__PURE__*/external_React_default().createElement("source", {
+ srcSet: messageData.content?.imageURL || "chrome://newtab/content/data/content/assets/highlights/omc-newtab-follow.svg",
+ media: "(prefers-color-scheme: light)"
+ }), /*#__PURE__*/external_React_default().createElement("img", {
+ width: "320",
+ height: "195",
alt: ""
- }), /*#__PURE__*/external_React_default().createElement("div", {
+ })), /*#__PURE__*/external_React_default().createElement("div", {
className: "follow-section-button-highlight-copy"
- }, /*#__PURE__*/external_React_default().createElement("p", {
+ }, messageData.content?.cardTitle ? /*#__PURE__*/external_React_default().createElement("p", {
+ className: "title"
+ }, messageData.content.cardTitle) : /*#__PURE__*/external_React_default().createElement("p", {
className: "title",
"data-l10n-id": "newtab-section-follow-highlight-title"
- }), /*#__PURE__*/external_React_default().createElement("p", {
+ }), messageData.content?.cardMessage ? /*#__PURE__*/external_React_default().createElement("p", {
+ className: "subtitle"
+ }, messageData.content.cardMessage) : /*#__PURE__*/external_React_default().createElement("p", {
className: "subtitle",
"data-l10n-id": "newtab-section-follow-highlight-subtitle"
}))),
@@ -12203,7 +12215,8 @@ function CardSection({
verticalPosition: "inset-block-center",
position: "arrow-inline-start",
dispatch: dispatch,
- feature: "FEATURE_FOLLOW_SECTION_BUTTON"
+ feature: "FEATURE_FOLLOW_SECTION_BUTTON",
+ messageData: messageData
})), !anySectionsFollowed && sectionPosition === 1 && shouldShowOMCHighlight(messageData, "FollowSectionButtonAltHighlight") && /*#__PURE__*/external_React_default().createElement(MessageWrapper, {
dispatch: dispatch
}, /*#__PURE__*/external_React_default().createElement(FollowSectionButtonHighlight, {
diff --git a/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/FollowSectionButtonHighlight.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/FollowSectionButtonHighlight.test.jsx
@@ -8,23 +8,26 @@ describe("Discovery Stream <FollowSectionButtonHighlight>", () => {
let dispatch;
let handleDismiss;
let handleBlock;
+ let messageData;
beforeEach(() => {
sandbox = sinon.createSandbox();
dispatch = sandbox.stub();
handleDismiss = sandbox.stub();
handleBlock = sandbox.stub();
+ messageData = sandbox.stub();
wrapper = mount(
<FollowSectionButtonHighlight
+ arrowPosition="arrow-inline-start"
dispatch={dispatch}
- handleDismiss={handleDismiss}
+ feature="FEATURE_FOLLOW_SECTION_BUTTON"
handleBlock={handleBlock}
+ handleDismiss={handleDismiss}
isIntersecting={false}
- arrowPosition="arrow-inline-start"
- verticalPosition="inset-block-center"
+ messageData={messageData}
position="inset-inline-end"
- feature="FEATURE_FOLLOW_SECTION_BUTTON"
+ verticalPosition="inset-block-center"
/>
);
});