summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/content-src/components/DiscoveryStreamComponents
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/content-src/components/DiscoveryStreamComponents')
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx8
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx76
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss32
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx9
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss3
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx6
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx74
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/SponsoredContentHighlight.jsx34
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FeatureHighlight.scss98
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_SponsoredContentHighlight.scss33
-rw-r--r--browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx9
11 files changed, 317 insertions, 65 deletions
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
index f13e0eb7ed..cf00361df2 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
@@ -31,11 +31,7 @@ export function DSSubHeader({ children }) {
);
}
-export function OnboardingExperience({
- children,
- dispatch,
- windowObj = global,
-}) {
+export function OnboardingExperience({ dispatch, windowObj = global }) {
const [dismissed, setDismissed] = useState(false);
const [maxHeight, setMaxHeight] = useState(null);
const heightElement = useRef(null);
@@ -330,6 +326,7 @@ export class _CardGrid extends React.PureComponent {
onboardingExperience,
ctaButtonSponsors,
ctaButtonVariant,
+ spocMessageVariant,
widgets,
recentSavesEnabled,
hideDescriptions,
@@ -378,6 +375,7 @@ export class _CardGrid extends React.PureComponent {
saveToPocketCard={saveToPocketCard}
ctaButtonSponsors={ctaButtonSponsors}
ctaButtonVariant={ctaButtonVariant}
+ spocMessageVariant={spocMessageVariant}
recommendation_id={rec.recommendation_id}
/>
)
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
index 6aef56fb33..f3e1eab503 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/DSCard.jsx
@@ -85,8 +85,9 @@ export const DefaultMeta = ({
sponsor,
sponsored_by_override,
saveToPocketCard,
- isRecentSave,
ctaButtonVariant,
+ dispatch,
+ spocMessageVariant,
}) => (
<div className="meta">
<div className="info-wrap">
@@ -100,9 +101,7 @@ export const DefaultMeta = ({
sponsored_by_override={sponsored_by_override}
/>
)}
- <header title={title} className="title clamp">
- {title}
- </header>
+ <header className="title clamp">{title}</header>
{excerpt && <p className="excerpt clamp">{excerpt}</p>}
</div>
{!newSponsoredLabel && (
@@ -113,6 +112,8 @@ export const DefaultMeta = ({
sponsored_by_override={sponsored_by_override}
cta_button_variant={ctaButtonVariant}
source={source}
+ dispatch={dispatch}
+ spocMessageVariant={spocMessageVariant}
/>
)}
{/* Sponsored label is normally in the way of any message.
@@ -183,7 +184,7 @@ export class _DSCard extends React.PureComponent {
];
}
- onLinkClick(event) {
+ onLinkClick() {
if (this.props.dispatch) {
this.props.dispatch(
ac.DiscoveryStreamUserEvent({
@@ -223,7 +224,7 @@ export class _DSCard extends React.PureComponent {
}
}
- onSaveClick(event) {
+ onSaveClick() {
if (this.props.dispatch) {
this.props.dispatch(
ac.AlsoToMain({
@@ -408,43 +409,28 @@ export class _DSCard extends React.PureComponent {
};
return (
- <div
+ <article
className={`ds-card ${compactImagesClassName} ${imageGradientClassName} ${titleLinesName} ${descLinesClassName} ${ctaButtonClassName} ${ctaButtonVariantClassName}`}
ref={this.setContextMenuButtonHostRef}
>
+ <div className="img-wrapper">
+ <DSImage
+ extraClassNames="img"
+ source={this.props.image_src}
+ rawSource={this.props.raw_image_src}
+ sizes={this.dsImageSizes}
+ url={this.props.url}
+ title={this.props.title}
+ isRecentSave={isRecentSave}
+ />
+ </div>
<SafeAnchor
className="ds-card-link"
dispatch={this.props.dispatch}
onLinkClick={!this.props.placeholder ? this.onLinkClick : undefined}
url={this.props.url}
+ title={this.props.title}
>
- <div className="img-wrapper">
- <DSImage
- extraClassNames="img"
- source={this.props.image_src}
- rawSource={this.props.raw_image_src}
- sizes={this.dsImageSizes}
- url={this.props.url}
- title={this.props.title}
- isRecentSave={isRecentSave}
- />
- </div>
- {ctaButtonVariant === "variant-b" && (
- <div className="cta-header">Shop Now</div>
- )}
- <DefaultMeta
- source={source}
- title={this.props.title}
- excerpt={excerpt}
- newSponsoredLabel={newSponsoredLabel}
- timeToRead={timeToRead}
- context={this.props.context}
- context_type={this.props.context_type}
- sponsor={this.props.sponsor}
- sponsored_by_override={this.props.sponsored_by_override}
- saveToPocketCard={saveToPocketCard}
- ctaButtonVariant={ctaButtonVariant}
- />
<ImpressionStats
flightId={this.props.flightId}
rows={[
@@ -461,6 +447,24 @@ export class _DSCard extends React.PureComponent {
source={this.props.type}
/>
</SafeAnchor>
+ {ctaButtonVariant === "variant-b" && (
+ <div className="cta-header">Shop Now</div>
+ )}
+ <DefaultMeta
+ source={source}
+ title={this.props.title}
+ excerpt={excerpt}
+ newSponsoredLabel={newSponsoredLabel}
+ timeToRead={timeToRead}
+ context={this.props.context}
+ context_type={this.props.context_type}
+ sponsor={this.props.sponsor}
+ sponsored_by_override={this.props.sponsored_by_override}
+ saveToPocketCard={saveToPocketCard}
+ ctaButtonVariant={ctaButtonVariant}
+ dispatch={this.props.dispatch}
+ spocMessageVariant={this.props.spocMessageVariant}
+ />
{saveToPocketCard && (
<div className="card-stp-button-hover-background">
<div className="card-stp-button-position-wrapper">
@@ -512,7 +516,7 @@ export class _DSCard extends React.PureComponent {
isRecentSave={isRecentSave}
/>
)}
- </div>
+ </article>
);
}
}
@@ -526,4 +530,4 @@ export const DSCard = connect(state => ({
DiscoveryStream: state.DiscoveryStream,
}))(_DSCard);
-export const PlaceholderDSCard = props => <DSCard placeholder={true} />;
+export const PlaceholderDSCard = () => <DSCard placeholder={true} />;
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
index 92afedff26..9004e609df 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -118,6 +118,16 @@ $ds-card-image-gradient-solid: rgba(0, 0, 0, 100%);
transform: scale(1);
}
}
+
+ header {
+ color: var(--newtab-primary-action-background);
+ }
+ }
+
+ &:active {
+ header {
+ color: var(--newtab-primary-element-active-color);
+ }
}
.img {
@@ -131,31 +141,15 @@ $ds-card-image-gradient-solid: rgba(0, 0, 0, 100%);
}
.ds-card-link {
+ position: absolute;
height: 100%;
- display: flex;
- flex-direction: column;
+ width: 100%;
text-decoration: none;
- &:hover {
- header {
- color: var(--newtab-primary-action-background);
- }
- }
-
&:focus {
@include ds-focus;
transition: none;
-
- header {
- color: var(--newtab-primary-action-background);
- }
- }
-
- &:active {
- header {
- color: var(--newtab-primary-element-active-color);
- }
}
}
@@ -255,7 +249,7 @@ $ds-card-image-gradient-solid: rgba(0, 0, 0, 100%);
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
- column-gap: var(--space-small);
+ gap: 0 var(--space-small);
margin-top: 0;
}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx
index 5c7e79685e..6c0641cfc1 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/DSContextFooter.jsx
@@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import { cardContextTypes } from "../../Card/types.js";
+import { SponsoredContentHighlight } from "../FeatureHighlight/SponsoredContentHighlight";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx";
import React from "react";
@@ -82,6 +83,8 @@ export class DSContextFooter extends React.PureComponent {
sponsored_by_override,
cta_button_variant,
source,
+ spocMessageVariant,
+ dispatch,
} = this.props;
const sponsorLabel = SponsorLabel({
@@ -119,6 +122,12 @@ export class DSContextFooter extends React.PureComponent {
return (
<div className="story-footer">
{sponsorLabel}
+ {sponsorLabel && spocMessageVariant === "variant-b" && (
+ <SponsoredContentHighlight
+ dispatch={dispatch}
+ position="inset-block-end inset-inline-start"
+ />
+ )}
{dsMessageLabel}
</div>
);
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss
index c23bb1c661..b28b498682 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss
@@ -2,7 +2,10 @@
color: var(--newtab-text-secondary-color);
inset-inline-start: 0;
margin-top: 12px;
+ display: flex;
+ gap: var(--space-large);
position: relative;
+ pointer-events: none;
.story-sponsored-label span {
display: inline-block;
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx
index f342c9829b..b251fb0401 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal.jsx
@@ -17,7 +17,7 @@ export class DSPrivacyModal extends React.PureComponent {
this.onManageLinkClick = this.onManageLinkClick.bind(this);
}
- onLearnLinkClick(event) {
+ onLearnLinkClick() {
this.props.dispatch(
ac.DiscoveryStreamUserEvent({
event: "CLICK_PRIVACY_INFO",
@@ -26,7 +26,7 @@ export class DSPrivacyModal extends React.PureComponent {
);
}
- onManageLinkClick(event) {
+ onManageLinkClick() {
this.props.dispatch(ac.OnlyToMain({ type: at.SETTINGS_OPEN }));
}
@@ -50,7 +50,7 @@ export class DSPrivacyModal extends React.PureComponent {
className="modal-link modal-link-privacy"
data-l10n-id="newtab-privacy-modal-link"
onClick={this.onLearnLinkClick}
- href="https://help.getpocket.com/article/1142-firefox-new-tab-recommendations-faq"
+ href="https://support.mozilla.org/kb/pocket-recommendations-firefox-new-tab"
/>
<button
className="modal-link modal-link-manage"
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx
new file mode 100644
index 0000000000..792be40ba3
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/FeatureHighlight.jsx
@@ -0,0 +1,74 @@
+/* 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 React, { useState, useCallback, useRef, useEffect } from "react";
+import { actionCreators as ac } from "common/Actions.sys.mjs";
+
+export function FeatureHighlight({
+ message,
+ icon,
+ toggle,
+ position = "top-left",
+ title,
+ ariaLabel,
+ feature = "FEATURE_HIGHLIGHT_DEFAULT",
+ dispatch = () => {},
+ windowObj = global,
+}) {
+ const [opened, setOpened] = useState(false);
+ const ref = useRef(null);
+
+ useEffect(() => {
+ const handleOutsideClick = e => {
+ if (!ref?.current?.contains(e.target)) {
+ setOpened(false);
+ }
+ };
+
+ windowObj.document.addEventListener("click", handleOutsideClick);
+ return () => {
+ windowObj.document.removeEventListener("click", handleOutsideClick);
+ };
+ }, [windowObj]);
+
+ const onToggleClick = useCallback(() => {
+ if (!opened) {
+ dispatch(
+ ac.DiscoveryStreamUserEvent({
+ event: "CLICK",
+ source: "FEATURE_HIGHLIGHT",
+ value: {
+ feature,
+ },
+ })
+ );
+ }
+ setOpened(!opened);
+ }, [dispatch, feature, opened]);
+
+ const openedClassname = opened ? `opened` : `closed`;
+ return (
+ <div ref={ref} className="feature-highlight">
+ <button
+ title={title}
+ aria-haspopup="true"
+ aria-label={ariaLabel}
+ className="toggle-button"
+ onClick={onToggleClick}
+ >
+ {toggle}
+ </button>
+ <div className={`feature-highlight-modal ${position} ${openedClassname}`}>
+ <div className="message-icon">{icon}</div>
+ <p>{message}</p>
+ <button
+ title="Dismiss"
+ aria-label="Close sponsored content more info popup"
+ className="icon icon-dismiss"
+ onClick={() => setOpened(false)}
+ ></button>
+ </div>
+ </div>
+ );
+}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/SponsoredContentHighlight.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/SponsoredContentHighlight.jsx
new file mode 100644
index 0000000000..8fdd03be6d
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/SponsoredContentHighlight.jsx
@@ -0,0 +1,34 @@
+/* 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 { FeatureHighlight } from "./FeatureHighlight";
+import { SafeAnchor } from "../SafeAnchor/SafeAnchor";
+import React from "react";
+
+export function SponsoredContentHighlight({ position, dispatch }) {
+ return (
+ <div className="sponsored-content-highlight">
+ <FeatureHighlight
+ position={position}
+ ariaLabel="Sponsored content supports our mission to build a better web."
+ title="Sponsored content more info"
+ feature="SPONSORED_CONTENT_INFO"
+ dispatch={dispatch}
+ message={
+ <span>
+ Sponsored content supports our mission to build a better web.{" "}
+ <SafeAnchor
+ dispatch={dispatch}
+ url="https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs"
+ >
+ Find out how
+ </SafeAnchor>
+ </span>
+ }
+ icon={<div className="sponsored-message-icon"></div>}
+ toggle={<div className="icon icon-help"></div>}
+ />
+ </div>
+ );
+}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FeatureHighlight.scss b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FeatureHighlight.scss
new file mode 100644
index 0000000000..c0fdd52f58
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_FeatureHighlight.scss
@@ -0,0 +1,98 @@
+.feature-highlight {
+ position: relative;
+ // This is needed because in 1 case this is positioned under a link
+ // and in an element that's not clickable.
+ pointer-events: auto;
+ z-index: 1;
+
+ .feature-highlight-modal {
+ position: absolute;
+ display: flex;
+ opacity: 0;
+ visibility: hidden;
+ cursor: default;
+ justify-content: space-between;
+ border-radius: var(--border-radius-small);
+ background: var(--newtab-background-color-secondary);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 15%);
+ width: 298px;
+ transition: opacity 0.3s, visibility 0.3s;
+
+ .icon-dismiss {
+ flex-shrink: 0;
+ cursor: pointer;
+ background-size: $smaller-icon-size;
+ height: $smaller-icon-size;
+ width: $smaller-icon-size;
+ margin: var(--space-medium);
+ color: var(--icon-color);
+ border: none;
+ }
+
+ .message-icon {
+ margin-block: var(--space-large);
+ margin-inline: var(--space-large) var(--space-medium);
+ }
+
+ &.opened {
+ opacity: 1;
+ visibility: visible;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ height: 24px;
+ width: 24px;
+ background: var(--newtab-background-color-secondary);
+ box-shadow: 4px 4px 6px -2px rgba(0, 0, 0, 15%);
+ }
+
+ &.inset-block-start {
+ inset-block-end: 100%;
+ margin-bottom: var(--space-xlarge);
+
+ &::after {
+ inset-block-end: -12px;
+ transform: rotate(45deg);
+ }
+ }
+
+ &.inset-block-end {
+ inset-block-start: 100%;
+ margin-top: var(--space-xlarge);
+
+ &::after {
+ inset-block-start: -12px;
+ transform: rotate(225deg);
+ }
+ }
+
+ &.inset-inline-start {
+ inset-inline-end: calc(var(--space-xxlarge) * -1);
+
+ &::after {
+ inset-inline-end: calc(var(--space-xxlarge) - 12px);
+ }
+ }
+
+ &.inset-inline-end {
+ inset-inline-start: calc(var(--space-xxlarge) * -1);
+
+ &::after {
+ inset-inline-start: calc(var(--space-xxlarge) - 12px);
+ }
+ }
+
+ p {
+ font-size: var(--font-size-small);
+ font-weight: normal;
+ margin: var(--space-large) 0;
+ }
+ }
+
+ .toggle-button {
+ border: none;
+ padding: 0;
+ }
+}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_SponsoredContentHighlight.scss b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_SponsoredContentHighlight.scss
new file mode 100644
index 0000000000..1dc024b7c0
--- /dev/null
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/FeatureHighlight/_SponsoredContentHighlight.scss
@@ -0,0 +1,33 @@
+.sponsored-content-highlight {
+ float: inline-end;
+
+ .sponsored-message-icon {
+ background-image: url('chrome://activity-stream/content/data/content/assets/sponsor-message-icon.svg');
+ background-size: 18px;
+ height: 18px;
+ width: 18px;
+ }
+
+ .icon-help {
+ cursor: pointer;
+ height: 20px;
+ width: 20px;
+ background-size: 20px;
+ color: var(--icon-color);
+ vertical-align: text-bottom;
+ }
+
+ .feature-highlight-modal {
+ &.inset-inline-start {
+ &::after {
+ margin-inline-end: 11px;
+ }
+ }
+
+ &.inset-inline-end {
+ &::after {
+ margin-inline-start: 9px;
+ }
+ }
+ }
+}
diff --git a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
index cfbc6fe6cb..72ec94e1fe 100644
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SafeAnchor/SafeAnchor.jsx
@@ -55,9 +55,14 @@ export class SafeAnchor extends React.PureComponent {
}
render() {
- const { url, className } = this.props;
+ const { url, className, title } = this.props;
return (
- <a href={this.safeURI(url)} className={className} onClick={this.onClick}>
+ <a
+ href={this.safeURI(url)}
+ title={title}
+ className={className}
+ onClick={this.onClick}
+ >
{this.props.children}
</a>
);