Bug 1932205 - Configure IAB Banner positions r=home-newtab-reviewers,reemhamz

Differential Revision: https://phabricator.services.mozilla.com/D231752
This commit is contained in:
Reem H
2024-12-13 15:44:04 +00:00
parent 0dfda0c4f4
commit b08ead089d
10 changed files with 523 additions and 251 deletions

View File

@@ -134,6 +134,7 @@ export class DiscoveryStreamAdminUI extends React.PureComponent {
this.refreshTopicSelectionCache.bind(this);
this.toggleTBRFeed = this.toggleTBRFeed.bind(this);
this.handleSectionsToggle = this.handleSectionsToggle.bind(this);
this.toggleIABBanners = this.toggleIABBanners.bind(this);
this.state = {
toggledStories: {},
weatherQuery: "",
@@ -228,6 +229,41 @@ export class DiscoveryStreamAdminUI extends React.PureComponent {
this.props.dispatch(ac.SetPref("weather.query", weatherQuery));
}
toggleIABBanners(e) {
const { pressed, id } = e.target;
const billboardEnabled = this.props.otherPrefs["newtabAdSize.billboard"];
const leaderboardEnabled =
this.props.otherPrefs["newtabAdSize.leaderboard"];
let spocValue;
let spocCount;
if (id === "billboard") {
this.props.dispatch(ac.SetPref("newtabAdSize.billboard", pressed));
if (pressed) {
spocValue = `newtab_spocs, newtab_billboard${leaderboardEnabled ? ", newtab_leaderboard" : ""}`;
spocCount = `6,1${leaderboardEnabled ? ",1" : ""}`;
} else {
spocValue = `newtab_spocs${leaderboardEnabled ? ", newtab_leaderboard" : ""}`;
spocCount = `6${leaderboardEnabled ? ",1" : ""}`;
}
} else if (id === "leaderboard") {
this.props.dispatch(ac.SetPref("newtabAdSize.leaderboard", pressed));
if (pressed) {
spocValue = `newtab_spocs, newtab_leaderboard${billboardEnabled ? ", newtab_billboard" : ""}`;
spocCount = `6,1${billboardEnabled ? ",1" : ""}`;
} else {
spocValue = `newtab_spocs${billboardEnabled ? ", newtab_billboard" : ""}`;
spocCount = `6${billboardEnabled ? ",1" : ""}`;
}
}
this.props.dispatch(
ac.SetPref("discoverystream.placements.spocs", spocValue)
);
this.props.dispatch(
ac.SetPref("discoverystream.placements.spocs.counts", spocCount)
);
}
handleSectionsToggle(e) {
const { pressed } = e.target;
this.props.dispatch(
@@ -485,6 +521,17 @@ export class DiscoveryStreamAdminUI extends React.PureComponent {
.split(",")
.map(s => s.trim())
.filter(item => item);
// Prefs for IAB Banners
const billboardsEnabled = this.props.otherPrefs["newtabAdSize.billboard"];
const leaderboardEnabled =
this.props.otherPrefs["newtabAdSize.leaderboard"];
const spocPlacements =
this.props.otherPrefs["discoverystream.placements.spocs"];
const billboardPressed =
billboardsEnabled && spocPlacements.includes("newtab_billboard");
const leaderboardPressed =
leaderboardEnabled && spocPlacements.includes("newtab_leaderboard");
return (
<div>
<button className="button" onClick={this.restorePrefDefaults}>
@@ -533,6 +580,26 @@ export class DiscoveryStreamAdminUI extends React.PureComponent {
label="Toggle DS Sections"
/>
</div>
{/* Collapsible Sections for experiments for easy on/off */}
<details className="details-section">
<summary>IAB Banner Ad Sizes</summary>
<div className="toggle-wrapper">
<moz-toggle
id="leaderboard"
pressed={leaderboardPressed || null}
onToggle={this.toggleIABBanners}
label="Enable IAB Leaderboard"
/>
</div>
<div className="toggle-wrapper">
<moz-toggle
id="billboard"
pressed={billboardPressed || null}
onToggle={this.toggleIABBanners}
label="Enable IAB Billboard"
/>
</div>
</details>
<table>
<tbody>
{prefToggles.map(pref => (

View File

@@ -124,6 +124,14 @@
width: 200px;
}
.details-section {
margin-block: var(--space-large);
summary {
font-size: var(--font-size-large);
}
}
table {
border-collapse: collapse;
width: 100%;

View File

@@ -8,7 +8,7 @@ import { SafeAnchor } from "../SafeAnchor/SafeAnchor";
import { ImpressionStats } from "../../DiscoveryStreamImpressionStats/ImpressionStats";
import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp }) => {
export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp, row }) => {
const getDimensions = format => {
switch (format) {
case "leaderboard":
@@ -57,8 +57,13 @@ export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp }) => {
);
};
// in the default card grid 1 would come before the 1st row of cards and 9 comes after the last row
// using clamp to make sure its between valid values (1-9)
const clampedRow = Math.max(1, Math.min(9, row));
return (
<aside className={`ad-banner-wrapper ${spoc.format}`}>
<aside className={`ad-banner-wrapper`} style={{ gridRow: clampedRow }}>
<div className={`ad-banner-inner ${spoc.format}`}>
<div className="ad-banner-dismiss">
<button
className="icon icon-dismiss"
@@ -66,7 +71,11 @@ export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp }) => {
data-l10n-id="newtab-toast-dismiss-button"
></button>
</div>
<SafeAnchor className="ad-banner-link" url={spoc.url} title={spoc.title}>
<SafeAnchor
className="ad-banner-link"
url={spoc.url}
title={spoc.title}
>
<ImpressionStats
flightId={spoc.flight_id}
rows={[
@@ -86,7 +95,7 @@ export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp }) => {
<img
src={spoc.raw_image_src}
alt={spoc.alt_text}
loading="lazy"
loading="eager"
width={imgWidth}
height={imgHeight}
/>
@@ -98,6 +107,7 @@ export const AdBanner = ({ spoc, dispatch, firstVisibleTimestamp }) => {
data-l10n-id="newtab-topsite-sponsored"
/>
</div>
</div>
</aside>
);
};

View File

@@ -1,22 +1,74 @@
.ad-banner-wrapper {
--billboard-width: 970px;
--billboard-height: 250px;
--leaderboard-width: 728px;
--leaderboard-height: 90px;
grid-column: 1/-1;
margin: 24px 0;
overflow: hidden;
// allow the ad banner to take up full width
// of screen rather than card-grid width
width: 100vw;
margin-inline-start: 50%;
transform: translate3d(-50%, 0, 0);
.ad-banner-inner {
margin-inline: auto;
.ad-banner-dismiss {
margin: 10px auto;
margin-block: 0 var(--space-small);
margin-inline: 0 var(--space-xxsmall);
text-align: end;
margin-inline-end: 10px;
.icon-dismiss {
background-size: 20px;
background-size: var(--size-item-small);
border: 0;
cursor: pointer;
}
}
&.leaderboard {
max-width: var(--leaderboard-width);
.ad-banner-dismiss {
width: var(--leaderboard-width);
}
.ad-banner-content {
height: var(--leaderboard-height);
width: var(--leaderboard-width);
margin: 0 auto;
}
.ad-banner-sponsored {
margin: 13px auto;
width: var(--leaderboard-width);
}
@media (width <= 758px) {
display: none;
}
}
&.billboard {
max-width: var(--billboard-width);
.ad-banner-content {
height: var(--billboard-height);
width: var(--billboard-width);
margin: 0 auto;
}
.ad-banner-sponsored {
width: var(--billboard-width);
}
@media (width <= 1015px) {
display: none;
}
}
.ad-banner-sponsored {
margin-block: var(--space-small) 0;
span {
text-transform: uppercase;
@@ -24,40 +76,5 @@
color: var(--newtab-contextual-text-secondary-color);
}
}
&.leaderboard {
.ad-banner-dismiss {
width: 728px;
}
.ad-banner-content {
height: 90px;
width: 728px;
margin: 0 auto;
}
.ad-banner-sponsored {
width: 728px;
}
@media (max-width: $break-point-large) {
display: none;
}
}
&.billboard {
.ad-banner-content {
height: 250px;
width: 970px;
margin: 0 auto;
}
.ad-banner-sponsored {
width: 970px;
}
@media (max-width: $break-point-widest) {
display: none;
}
}
}

View File

@@ -27,6 +27,10 @@ const PREF_LIST_FEED_SELECTED_FEED =
"discoverystream.contextualContent.selectedFeed";
const PREF_FAKESPOT_ENABLED =
"discoverystream.contextualContent.fakespot.enabled";
const PREF_BILLBOARD_ENABLED = "newtabAdSize.billboard";
const PREF_LEADERBOARD_ENABLED = "newtabAdSize.leaderboard";
const PREF_LEADERBOARD_POSITION = "newtabAdSize.billboard.position";
const PREF_BILLBOARD_POSITION = "newtabAdSize.billboard.position";
const INTERSECTION_RATIO = 0.5;
const VISIBLE = "visible";
const VISIBILITY_CHANGE_EVENT = "visibilitychange";
@@ -354,6 +358,8 @@ export class _CardGrid extends React.PureComponent {
const spocsStartupCacheEnabled = prefs[PREF_SPOCS_STARTUPCACHE_ENABLED];
const listFeedEnabled = prefs[PREF_LIST_FEED_ENABLED];
const listFeedSelectedFeed = prefs[PREF_LIST_FEED_SELECTED_FEED];
const billboardEnabled = prefs[PREF_BILLBOARD_ENABLED];
const leaderboardEnabled = prefs[PREF_LEADERBOARD_ENABLED];
// filter out recs that should be in ListFeed
const recs = this.props.data.recommendations
.filter(item => !item.feedName)
@@ -365,18 +371,6 @@ export class _CardGrid extends React.PureComponent {
for (let index = 0; index < items; index++) {
const rec = recs[index];
if (rec?.format === "billboard" || rec?.format === "leaderboard") {
cards.push(
<AdBanner
spoc={rec}
key={`dscard-${rec.id}`}
dispatch={this.props.dispatch}
type={this.props.type}
firstVisibleTimestamp={this.props.firstVisibleTimestamp}
/>
);
} else {
cards.push(
topicsLoading ||
!rec ||
@@ -433,7 +427,6 @@ export class _CardGrid extends React.PureComponent {
)
);
}
}
if (widgets?.positions?.length && widgets?.data?.length) {
let positionIndex = 0;
@@ -485,6 +478,38 @@ export class _CardGrid extends React.PureComponent {
}
}
// if a banner ad is enabled and we have any available, place them in the grid
const { spocs } = this.props.DiscoveryStream;
if ((billboardEnabled || leaderboardEnabled) && spocs.data.newtab_spocs) {
const spocTypes = [
billboardEnabled && "billboard",
leaderboardEnabled && "leaderboard",
].filter(Boolean);
// We need to go through the billboards in `newtab_spocs` because they have been normalized
// in DiscoveryStreamFeed on line 1024
const bannerSpocs = spocs.data.newtab_spocs.items.filter(({ format }) =>
spocTypes.includes(format)
);
if (bannerSpocs.length) {
for (const spoc of bannerSpocs) {
const row =
spoc.format === "leaderboard"
? prefs[PREF_LEADERBOARD_POSITION]
: prefs[PREF_BILLBOARD_POSITION];
cards.push(
<AdBanner
spoc={spoc}
key={`dscard-${spoc.id}`}
dispatch={this.props.dispatch}
type={this.props.type}
firstVisibleTimestamp={this.props.firstVisibleTimestamp}
row={row}
/>
);
}
}
}
let moreRecsHeader = "";
// For now this is English only.
if (showRecentSaves || (essentialReadsHeader && editorsPicksHeader)) {

View File

@@ -27,8 +27,11 @@ export const selectLayoutRender = ({ state = {}, prefs = {} }) => {
const results = [...data];
for (let position of spocsPositions) {
const spoc = spocsData[spocIndexPlacementMap[placementName]];
const format = spoc?.format;
// If there are no spocs left, we can stop filling positions.
if (!spoc) {
// Since banner-type ads are placed by row and don't use the normal spoc-position,
// dont combine with content
if (!spoc || format === "billboard" || format === "leaderboard") {
break;
}

View File

@@ -3618,6 +3618,12 @@ main section {
margin-block: var(--space-large);
width: 200px;
}
.discoverystream-admin .details-section {
margin-block: var(--space-large);
}
.discoverystream-admin .details-section summary {
font-size: var(--font-size-large);
}
.discoverystream-admin table {
border-collapse: collapse;
width: 100%;
@@ -7622,54 +7628,69 @@ main section {
}
.ad-banner-wrapper {
--billboard-width: 970px;
--billboard-height: 250px;
--leaderboard-width: 728px;
--leaderboard-height: 90px;
grid-column: 1/-1;
margin: 24px 0;
overflow: hidden;
width: 100vw;
margin-inline-start: 50%;
transform: translate3d(-50%, 0, 0);
}
.ad-banner-wrapper .ad-banner-dismiss {
margin: 10px auto;
.ad-banner-wrapper .ad-banner-inner {
margin-inline: auto;
}
.ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss {
margin-block: 0 var(--space-small);
margin-inline: 0 var(--space-xxsmall);
text-align: end;
margin-inline-end: 10px;
}
.ad-banner-wrapper .ad-banner-dismiss .icon-dismiss {
background-size: 20px;
.ad-banner-wrapper .ad-banner-inner .ad-banner-dismiss .icon-dismiss {
background-size: var(--size-item-small);
border: 0;
cursor: pointer;
}
.ad-banner-wrapper .ad-banner-sponsored {
margin: 13px auto;
.ad-banner-wrapper .ad-banner-inner.leaderboard {
max-width: var(--leaderboard-width);
}
.ad-banner-wrapper .ad-banner-sponsored span {
.ad-banner-wrapper .ad-banner-inner.leaderboard .ad-banner-dismiss {
width: var(--leaderboard-width);
}
.ad-banner-wrapper .ad-banner-inner.leaderboard .ad-banner-content {
height: var(--leaderboard-height);
width: var(--leaderboard-width);
margin: 0 auto;
}
.ad-banner-wrapper .ad-banner-inner.leaderboard .ad-banner-sponsored {
width: var(--leaderboard-width);
}
@media (width <= 758px) {
.ad-banner-wrapper .ad-banner-inner.leaderboard {
display: none;
}
}
.ad-banner-wrapper .ad-banner-inner.billboard {
max-width: var(--billboard-width);
}
.ad-banner-wrapper .ad-banner-inner.billboard .ad-banner-content {
height: var(--billboard-height);
width: var(--billboard-width);
margin: 0 auto;
}
.ad-banner-wrapper .ad-banner-inner.billboard .ad-banner-sponsored {
width: var(--billboard-width);
}
@media (width <= 1015px) {
.ad-banner-wrapper .ad-banner-inner.billboard {
display: none;
}
}
.ad-banner-wrapper .ad-banner-inner .ad-banner-sponsored {
margin-block: var(--space-small) 0;
}
.ad-banner-wrapper .ad-banner-inner .ad-banner-sponsored span {
text-transform: uppercase;
font-size: var(--font-size-small);
color: var(--newtab-contextual-text-secondary-color);
}
.ad-banner-wrapper.leaderboard .ad-banner-dismiss {
width: 728px;
}
.ad-banner-wrapper.leaderboard .ad-banner-content {
height: 90px;
width: 728px;
margin: 0 auto;
}
.ad-banner-wrapper.leaderboard .ad-banner-sponsored {
width: 728px;
}
@media (max-width: 866px) {
.ad-banner-wrapper.leaderboard {
display: none;
}
}
.ad-banner-wrapper.billboard .ad-banner-content {
height: 250px;
width: 970px;
margin: 0 auto;
}
.ad-banner-wrapper.billboard .ad-banner-sponsored {
width: 970px;
}
@media (max-width: 1122px) {
.ad-banner-wrapper.billboard {
display: none;
}
}

View File

@@ -700,6 +700,7 @@ class DiscoveryStreamAdminUI extends (external_React_default()).PureComponent {
this.refreshTopicSelectionCache = this.refreshTopicSelectionCache.bind(this);
this.toggleTBRFeed = this.toggleTBRFeed.bind(this);
this.handleSectionsToggle = this.handleSectionsToggle.bind(this);
this.toggleIABBanners = this.toggleIABBanners.bind(this);
this.state = {
toggledStories: {},
weatherQuery: ""
@@ -774,6 +775,37 @@ class DiscoveryStreamAdminUI extends (external_React_default()).PureComponent {
} = this.state;
this.props.dispatch(actionCreators.SetPref("weather.query", weatherQuery));
}
toggleIABBanners(e) {
const {
pressed,
id
} = e.target;
const billboardEnabled = this.props.otherPrefs["newtabAdSize.billboard"];
const leaderboardEnabled = this.props.otherPrefs["newtabAdSize.leaderboard"];
let spocValue;
let spocCount;
if (id === "billboard") {
this.props.dispatch(actionCreators.SetPref("newtabAdSize.billboard", pressed));
if (pressed) {
spocValue = `newtab_spocs, newtab_billboard${leaderboardEnabled ? ", newtab_leaderboard" : ""}`;
spocCount = `6,1${leaderboardEnabled ? ",1" : ""}`;
} else {
spocValue = `newtab_spocs${leaderboardEnabled ? ", newtab_leaderboard" : ""}`;
spocCount = `6${leaderboardEnabled ? ",1" : ""}`;
}
} else if (id === "leaderboard") {
this.props.dispatch(actionCreators.SetPref("newtabAdSize.leaderboard", pressed));
if (pressed) {
spocValue = `newtab_spocs, newtab_leaderboard${billboardEnabled ? ", newtab_billboard" : ""}`;
spocCount = `6,1${billboardEnabled ? ",1" : ""}`;
} else {
spocValue = `newtab_spocs${billboardEnabled ? ", newtab_billboard" : ""}`;
spocCount = `6${billboardEnabled ? ",1" : ""}`;
}
}
this.props.dispatch(actionCreators.SetPref("discoverystream.placements.spocs", spocValue));
this.props.dispatch(actionCreators.SetPref("discoverystream.placements.spocs.counts", spocCount));
}
handleSectionsToggle(e) {
const {
pressed
@@ -928,6 +960,13 @@ class DiscoveryStreamAdminUI extends (external_React_default()).PureComponent {
const selectedFeed = this.props.otherPrefs["discoverystream.contextualContent.selectedFeed"];
const sectionsEnabled = this.props.otherPrefs["discoverystream.sections.enabled"];
const TBRFeeds = this.props.otherPrefs["discoverystream.contextualContent.feeds"].split(",").map(s => s.trim()).filter(item => item);
// Prefs for IAB Banners
const billboardsEnabled = this.props.otherPrefs["newtabAdSize.billboard"];
const leaderboardEnabled = this.props.otherPrefs["newtabAdSize.leaderboard"];
const spocPlacements = this.props.otherPrefs["discoverystream.placements.spocs"];
const billboardPressed = billboardsEnabled && spocPlacements.includes("newtab_billboard");
const leaderboardPressed = leaderboardEnabled && spocPlacements.includes("newtab_leaderboard");
return /*#__PURE__*/external_React_default().createElement("div", null, /*#__PURE__*/external_React_default().createElement("button", {
className: "button",
onClick: this.restorePrefDefaults
@@ -966,7 +1005,23 @@ class DiscoveryStreamAdminUI extends (external_React_default()).PureComponent {
pressed: sectionsEnabled || null,
onToggle: this.handleSectionsToggle,
label: "Toggle DS Sections"
})), /*#__PURE__*/external_React_default().createElement("table", null, /*#__PURE__*/external_React_default().createElement("tbody", null, prefToggles.map(pref => /*#__PURE__*/external_React_default().createElement(Row, {
})), /*#__PURE__*/external_React_default().createElement("details", {
className: "details-section"
}, /*#__PURE__*/external_React_default().createElement("summary", null, "IAB Banner Ad Sizes"), /*#__PURE__*/external_React_default().createElement("div", {
className: "toggle-wrapper"
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
id: "leaderboard",
pressed: leaderboardPressed || null,
onToggle: this.toggleIABBanners,
label: "Enable IAB Leaderboard"
})), /*#__PURE__*/external_React_default().createElement("div", {
className: "toggle-wrapper"
}, /*#__PURE__*/external_React_default().createElement("moz-toggle", {
id: "billboard",
pressed: billboardPressed || null,
onToggle: this.toggleIABBanners,
label: "Enable IAB Billboard"
}))), /*#__PURE__*/external_React_default().createElement("table", null, /*#__PURE__*/external_React_default().createElement("tbody", null, prefToggles.map(pref => /*#__PURE__*/external_React_default().createElement(Row, {
key: pref
}, /*#__PURE__*/external_React_default().createElement("td", null, /*#__PURE__*/external_React_default().createElement(TogglePrefCheckbox, {
checked: config[pref],
@@ -4110,7 +4165,8 @@ function ListFeed({
const AdBanner = ({
spoc,
dispatch,
firstVisibleTimestamp
firstVisibleTimestamp,
row
}) => {
const getDimensions = format => {
switch (format) {
@@ -4156,8 +4212,17 @@ const AdBanner = ({
}]
}));
};
// in the default card grid 1 would come before the 1st row of cards and 9 comes after the last row
// using clamp to make sure its between valid values (1-9)
const clampedRow = Math.max(1, Math.min(9, row));
return /*#__PURE__*/external_React_default().createElement("aside", {
className: `ad-banner-wrapper ${spoc.format}`
className: `ad-banner-wrapper`,
style: {
gridRow: clampedRow
}
}, /*#__PURE__*/external_React_default().createElement("div", {
className: `ad-banner-inner ${spoc.format}`
}, /*#__PURE__*/external_React_default().createElement("div", {
className: "ad-banner-dismiss"
}, /*#__PURE__*/external_React_default().createElement("button", {
@@ -4185,7 +4250,7 @@ const AdBanner = ({
}, /*#__PURE__*/external_React_default().createElement("img", {
src: spoc.raw_image_src,
alt: spoc.alt_text,
loading: "lazy",
loading: "eager",
width: imgWidth,
height: imgHeight
}))), /*#__PURE__*/external_React_default().createElement("div", {
@@ -4193,7 +4258,7 @@ const AdBanner = ({
}, /*#__PURE__*/external_React_default().createElement("span", {
className: "ad-banner-sponsored-label",
"data-l10n-id": "newtab-topsite-sponsored"
})));
}))));
};
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
/* This Source Code Form is subject to the terms of the Mozilla Public
@@ -4221,6 +4286,10 @@ const PREF_SPOCS_STARTUPCACHE_ENABLED = "discoverystream.spocs.startupCache.enab
const PREF_LIST_FEED_ENABLED = "discoverystream.contextualContent.enabled";
const PREF_LIST_FEED_SELECTED_FEED = "discoverystream.contextualContent.selectedFeed";
const PREF_FAKESPOT_ENABLED = "discoverystream.contextualContent.fakespot.enabled";
const PREF_BILLBOARD_ENABLED = "newtabAdSize.billboard";
const PREF_LEADERBOARD_ENABLED = "newtabAdSize.leaderboard";
const PREF_LEADERBOARD_POSITION = "newtabAdSize.billboard.position";
const PREF_BILLBOARD_POSITION = "newtabAdSize.billboard.position";
const CardGrid_INTERSECTION_RATIO = 0.5;
const CardGrid_VISIBLE = "visible";
const CardGrid_VISIBILITY_CHANGE_EVENT = "visibilitychange";
@@ -4501,6 +4570,8 @@ class _CardGrid extends (external_React_default()).PureComponent {
const spocsStartupCacheEnabled = prefs[PREF_SPOCS_STARTUPCACHE_ENABLED];
const listFeedEnabled = prefs[PREF_LIST_FEED_ENABLED];
const listFeedSelectedFeed = prefs[PREF_LIST_FEED_SELECTED_FEED];
const billboardEnabled = prefs[PREF_BILLBOARD_ENABLED];
const leaderboardEnabled = prefs[PREF_LEADERBOARD_ENABLED];
// filter out recs that should be in ListFeed
const recs = this.props.data.recommendations.filter(item => !item.feedName).slice(0, items);
const cards = [];
@@ -4508,15 +4579,6 @@ class _CardGrid extends (external_React_default()).PureComponent {
let editorsPicksCards = [];
for (let index = 0; index < items; index++) {
const rec = recs[index];
if (rec?.format === "billboard" || rec?.format === "leaderboard") {
cards.push( /*#__PURE__*/external_React_default().createElement(AdBanner, {
spoc: rec,
key: `dscard-${rec.id}`,
dispatch: this.props.dispatch,
type: this.props.type,
firstVisibleTimestamp: this.props.firstVisibleTimestamp
}));
} else {
cards.push(topicsLoading || !rec || rec.placeholder || rec.flight_id && !spocsStartupCacheEnabled && this.props.App.isForStartupCache ? /*#__PURE__*/external_React_default().createElement(PlaceholderDSCard, {
key: `dscard-${index}`
}) : /*#__PURE__*/external_React_default().createElement(DSCard, {
@@ -4564,7 +4626,6 @@ class _CardGrid extends (external_React_default()).PureComponent {
alt_text: rec.alt_text
}));
}
}
if (widgets?.positions?.length && widgets?.data?.length) {
let positionIndex = 0;
const source = "CARDGRID_WIDGET";
@@ -4602,6 +4663,32 @@ class _CardGrid extends (external_React_default()).PureComponent {
cards.splice(2, 1, this.renderListFeed(this.props.data.recommendations, listFeedSelectedFeed));
}
}
// if a banner ad is enabled and we have any available, place them in the grid
const {
spocs
} = this.props.DiscoveryStream;
if ((billboardEnabled || leaderboardEnabled) && spocs.data.newtab_spocs) {
const spocTypes = [billboardEnabled && "billboard", leaderboardEnabled && "leaderboard"].filter(Boolean);
// We need to go through the billboards in `newtab_spocs` because they have been normalized
// in DiscoveryStreamFeed on line 1024
const bannerSpocs = spocs.data.newtab_spocs.items.filter(({
format
}) => spocTypes.includes(format));
if (bannerSpocs.length) {
for (const spoc of bannerSpocs) {
const row = spoc.format === "leaderboard" ? prefs[PREF_LEADERBOARD_POSITION] : prefs[PREF_BILLBOARD_POSITION];
cards.push( /*#__PURE__*/external_React_default().createElement(AdBanner, {
spoc: spoc,
key: `dscard-${spoc.id}`,
dispatch: this.props.dispatch,
type: this.props.type,
firstVisibleTimestamp: this.props.firstVisibleTimestamp,
row: row
}));
}
}
}
let moreRecsHeader = "";
// For now this is English only.
if (showRecentSaves || essentialReadsHeader && editorsPicksHeader) {
@@ -9372,8 +9459,11 @@ const selectLayoutRender = ({ state = {}, prefs = {} }) => {
const results = [...data];
for (let position of spocsPositions) {
const spoc = spocsData[spocIndexPlacementMap[placementName]];
const format = spoc?.format;
// If there are no spocs left, we can stop filling positions.
if (!spoc) {
// Since banner-type ads are placed by row and don't use the normal spoc-position,
// dont combine with content
if (!spoc || format === "billboard" || format === "leaderboard") {
break;
}

View File

@@ -436,6 +436,14 @@ export const PREFS_CONFIG = new Map([
value: false,
},
],
[
"newtabAdSize.leaderboard.position",
{
title:
"CSV string of positions for leaderboard spocs - should corralate to a row in DS grid",
value: "1",
},
],
[
"newtabAdSize.billboard",
{
@@ -443,6 +451,14 @@ export const PREFS_CONFIG = new Map([
value: false,
},
],
[
"newtabAdSize.billboard.position",
{
title:
"number string of positions for billboard spocs - should corralate to a row in DS grid",
value: "1",
},
],
[
"newtabLayouts.variant-a",
{

View File

@@ -858,6 +858,13 @@ newtabAdSizingExperiment:
pref: browser.newtabpage.activity-stream.newtabAdSize.leaderboard
description: >-
Leaderboard ad size and UI.
leaderboard_position:
type: string
setPref:
branch: user
pref: browser.newtabpage.activity-stream.newtabAdSize.leaderboard.position
description: >-
Leaderboard row position.
billboard:
type: boolean
setPref:
@@ -865,6 +872,14 @@ newtabAdSizingExperiment:
pref: browser.newtabpage.activity-stream.newtabAdSize.billboard
description: >-
Billboard ad size and UI.
billboard_position:
type: string
setPref:
branch: user
pref: browser.newtabpage.activity-stream.newtabAdSize.billboard.position
description: >-
Billboard row position.
newtabLayoutExperiment:
description: >-
Change the default layout of new tab by adjusting sizes and spacing of elements.