зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1767445 - Pocket newtab topics widget r=gvn
Differential Revision: https://phabricator.services.mozilla.com/D146327
This commit is contained in:
Родитель
7c5eeccac8
Коммит
49ba6a6e01
|
@ -1552,6 +1552,7 @@ pref("browser.newtabpage.activity-stream.discoverystream.newSponsoredLabel.enabl
|
|||
pref("browser.newtabpage.activity-stream.discoverystream.essentialReadsHeader.enabled", false);
|
||||
pref("browser.newtabpage.activity-stream.discoverystream.editorsPicksHeader.enabled", false);
|
||||
pref("browser.newtabpage.activity-stream.discoverystream.spoc-positions", "1,5,7,11,18,20");
|
||||
pref("browser.newtabpage.activity-stream.discoverystream.widget-positions", "");
|
||||
|
||||
pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint", "");
|
||||
pref("browser.newtabpage.activity-stream.discoverystream.spocs-endpoint-query", "");
|
||||
|
|
|
@ -207,6 +207,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
|
|||
display_variant={component.properties.display_variant}
|
||||
data={component.data}
|
||||
feed={component.feed}
|
||||
widgets={component.widgets}
|
||||
border={component.properties.border}
|
||||
type={component.type}
|
||||
dispatch={this.props.dispatch}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
LastCardMessage,
|
||||
} from "../DSCard/DSCard.jsx";
|
||||
import { DSEmptyState } from "../DSEmptyState/DSEmptyState.jsx";
|
||||
import { TopicsWidget } from "../TopicsWidget/TopicsWidget.jsx";
|
||||
import { FluentOrText } from "../../FluentOrText/FluentOrText.jsx";
|
||||
import { actionCreators as ac } from "common/Actions.jsm";
|
||||
import React from "react";
|
||||
|
@ -69,6 +70,7 @@ export class CardGrid extends React.PureComponent {
|
|||
readTime,
|
||||
essentialReadsHeader,
|
||||
editorsPicksHeader,
|
||||
widgets,
|
||||
} = this.props;
|
||||
let showLastCardMessage = lastCardMessageEnabled;
|
||||
if (this.showLoadMore) {
|
||||
|
@ -148,6 +150,33 @@ export class CardGrid extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
if (widgets?.positions?.length && widgets?.data?.length) {
|
||||
let positionIndex = 0;
|
||||
|
||||
for (const widget of widgets.data) {
|
||||
let widgetComponent = null;
|
||||
const position = widgets.positions[positionIndex];
|
||||
|
||||
// Stop if we run out of positions to place widgets.
|
||||
if (!position) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (widget?.type) {
|
||||
case "TopicsWidget":
|
||||
widgetComponent = <TopicsWidget />;
|
||||
break;
|
||||
}
|
||||
|
||||
if (widgetComponent) {
|
||||
// We found a widget, so up the position for next try.
|
||||
positionIndex++;
|
||||
// We replace an existing card with the widget.
|
||||
cards.splice(position.index, 1, widgetComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for CSS overrides to default styling (eg: "hero")
|
||||
const variantClass = this.props.display_variant
|
||||
? `ds-card-grid-${this.props.display_variant}`
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/* 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 from "react";
|
||||
|
||||
export function TopicsWidget(props) {
|
||||
return <div className="ds-topics-widget"></div>;
|
||||
}
|
|
@ -167,6 +167,7 @@ input {
|
|||
@import '../components/DiscoveryStreamComponents/DSSignup/DSSignup';
|
||||
@import '../components/DiscoveryStreamComponents/DSPrivacyModal/DSPrivacyModal';
|
||||
@import '../components/DiscoveryStreamComponents/PrivacyLink/PrivacyLink';
|
||||
@import '../components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget';
|
||||
|
||||
// AS Router
|
||||
@import '../asrouter/components/Button/Button';
|
||||
|
|
|
@ -8009,6 +8009,16 @@ class DSEmptyState extends (external_React_default()).PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget.jsx
|
||||
/* 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/. */
|
||||
|
||||
function TopicsWidget(props) {
|
||||
return /*#__PURE__*/external_React_default().createElement("div", {
|
||||
className: "ds-topics-widget"
|
||||
});
|
||||
}
|
||||
;// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/CardGrid/CardGrid.jsx
|
||||
/* 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,
|
||||
|
@ -8018,6 +8028,7 @@ class DSEmptyState extends (external_React_default()).PureComponent {
|
|||
|
||||
|
||||
|
||||
|
||||
class CardGrid extends (external_React_default()).PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -8059,6 +8070,8 @@ class CardGrid extends (external_React_default()).PureComponent {
|
|||
}
|
||||
|
||||
renderCards() {
|
||||
var _widgets$positions, _widgets$data;
|
||||
|
||||
let {
|
||||
items
|
||||
} = this.props;
|
||||
|
@ -8078,7 +8091,8 @@ class CardGrid extends (external_React_default()).PureComponent {
|
|||
descLines,
|
||||
readTime,
|
||||
essentialReadsHeader,
|
||||
editorsPicksHeader
|
||||
editorsPicksHeader,
|
||||
widgets
|
||||
} = this.props;
|
||||
let showLastCardMessage = lastCardMessageEnabled;
|
||||
|
||||
|
@ -8151,6 +8165,32 @@ class CardGrid extends (external_React_default()).PureComponent {
|
|||
cards.splice(cards.length - 1, 1, /*#__PURE__*/external_React_default().createElement(LastCardMessage, {
|
||||
key: `dscard-last-${cards.length - 1}`
|
||||
}));
|
||||
}
|
||||
|
||||
if (widgets !== null && widgets !== void 0 && (_widgets$positions = widgets.positions) !== null && _widgets$positions !== void 0 && _widgets$positions.length && widgets !== null && widgets !== void 0 && (_widgets$data = widgets.data) !== null && _widgets$data !== void 0 && _widgets$data.length) {
|
||||
let positionIndex = 0;
|
||||
|
||||
for (const widget of widgets.data) {
|
||||
let widgetComponent = null;
|
||||
const position = widgets.positions[positionIndex]; // Stop if we run out of positions to place widgets.
|
||||
|
||||
if (!position) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (widget === null || widget === void 0 ? void 0 : widget.type) {
|
||||
case "TopicsWidget":
|
||||
widgetComponent = /*#__PURE__*/external_React_default().createElement(TopicsWidget, null);
|
||||
break;
|
||||
}
|
||||
|
||||
if (widgetComponent) {
|
||||
// We found a widget, so up the position for next try.
|
||||
positionIndex++; // We replace an existing card with the widget.
|
||||
|
||||
cards.splice(position.index, 1, widgetComponent);
|
||||
}
|
||||
}
|
||||
} // Used for CSS overrides to default styling (eg: "hero")
|
||||
|
||||
|
||||
|
@ -13531,6 +13571,7 @@ class _DiscoveryStreamBase extends (external_React_default()).PureComponent {
|
|||
display_variant: component.properties.display_variant,
|
||||
data: component.data,
|
||||
feed: component.feed,
|
||||
widgets: component.widgets,
|
||||
border: component.properties.border,
|
||||
type: component.type,
|
||||
dispatch: this.props.dispatch,
|
||||
|
|
|
@ -418,12 +418,12 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
return urlObject.toString();
|
||||
}
|
||||
|
||||
parseSpocPositions(csvPositions) {
|
||||
let spocPositions;
|
||||
parseGridPositions(csvPositions) {
|
||||
let gridPositions;
|
||||
|
||||
// Only accept parseable non-negative integers
|
||||
try {
|
||||
spocPositions = csvPositions.map(index => {
|
||||
gridPositions = csvPositions.map(index => {
|
||||
let parsedInt = parseInt(index, 10);
|
||||
|
||||
if (!isNaN(parsedInt) && parsedInt >= 0) {
|
||||
|
@ -435,10 +435,10 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
} catch (e) {
|
||||
// Catch spoc positions that are not numbers or negative, and do nothing.
|
||||
// We have hard coded backup positions.
|
||||
spocPositions = undefined;
|
||||
gridPositions = undefined;
|
||||
}
|
||||
|
||||
return spocPositions;
|
||||
return gridPositions;
|
||||
}
|
||||
|
||||
async loadLayout(sendUpdate, isStartup) {
|
||||
|
@ -480,9 +480,15 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
layoutResp = getHardcodedLayout({
|
||||
items,
|
||||
sponsoredCollectionsEnabled,
|
||||
spocPositions: this.parseSpocPositions(
|
||||
spocPositions: this.parseGridPositions(
|
||||
pocketConfig.spocPositions?.split(`,`)
|
||||
),
|
||||
widgetPositions: this.parseGridPositions(
|
||||
pocketConfig.widgetPositions?.split(`,`)
|
||||
),
|
||||
widgetData: [
|
||||
...(this.locale.startsWith("en-") ? [{ type: "TopicsWidget" }] : []),
|
||||
],
|
||||
compactLayout: pocketConfig.compactLayout,
|
||||
hybridLayout: pocketConfig.hybridLayout,
|
||||
hideCardBackground: pocketConfig.hideCardBackground,
|
||||
|
@ -1906,6 +1912,8 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
|
|||
getHardcodedLayout = ({
|
||||
items = 21,
|
||||
spocPositions = [1, 5, 7, 11, 18, 20],
|
||||
widgetPositions = [],
|
||||
widgetData = [],
|
||||
sponsoredCollectionsEnabled = false,
|
||||
compactLayout = false,
|
||||
hybridLayout = false,
|
||||
|
@ -2016,6 +2024,12 @@ getHardcodedLayout = ({
|
|||
editorsPicksHeader,
|
||||
readTime: readTime || compactLayout,
|
||||
},
|
||||
widgets: {
|
||||
positions: widgetPositions.map(position => {
|
||||
return { index: position };
|
||||
}),
|
||||
data: widgetData,
|
||||
},
|
||||
loadMore,
|
||||
lastCardMessageEnabled,
|
||||
pocketButtonEnabled,
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
DSCard,
|
||||
LastCardMessage,
|
||||
} from "content-src/components/DiscoveryStreamComponents/DSCard/DSCard";
|
||||
import { TopicsWidget } from "content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget";
|
||||
import { actionCreators as ac } from "common/Actions.jsm";
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
|
@ -140,4 +141,18 @@ describe("<CardGrid>", () => {
|
|||
loadMoreButton = wrapper.find(".ds-card-grid-load-more-button");
|
||||
assert.ok(!loadMoreButton.exists());
|
||||
});
|
||||
|
||||
it("should create a widget card", () => {
|
||||
wrapper.setProps({
|
||||
widgets: {
|
||||
positions: [{ index: 1 }],
|
||||
data: [{ type: "TopicsWidget" }],
|
||||
},
|
||||
data: {
|
||||
recommendations: [{}, {}, {}],
|
||||
},
|
||||
});
|
||||
|
||||
assert.ok(wrapper.find(TopicsWidget).exists());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { TopicsWidget } from "content-src/components/DiscoveryStreamComponents/TopicsWidget/TopicsWidget";
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
|
||||
describe("Discovery Stream <TopicsWidget>", () => {
|
||||
let sandbox;
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox();
|
||||
wrapper = shallow(<TopicsWidget />);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it("should render", () => {
|
||||
assert.ok(wrapper.exists());
|
||||
assert.ok(wrapper.find(".ds-topics-widget"));
|
||||
});
|
||||
});
|
|
@ -226,15 +226,15 @@ describe("DiscoveryStreamFeed", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#parseSpocPositions", () => {
|
||||
describe("#parseGridPositions", () => {
|
||||
it("should return an equivalent array for an array of non negative integers", async () => {
|
||||
assert.deepEqual(feed.parseSpocPositions([0, 2, 3]), [0, 2, 3]);
|
||||
assert.deepEqual(feed.parseGridPositions([0, 2, 3]), [0, 2, 3]);
|
||||
});
|
||||
it("should return undefined for an array containing negative integers", async () => {
|
||||
assert.equal(feed.parseSpocPositions([-2, 2, 3]), undefined);
|
||||
assert.equal(feed.parseGridPositions([-2, 2, 3]), undefined);
|
||||
});
|
||||
it("should return undefined for an undefined input", async () => {
|
||||
assert.equal(feed.parseSpocPositions(undefined), undefined);
|
||||
assert.equal(feed.parseGridPositions(undefined), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -466,6 +466,31 @@ describe("DiscoveryStreamFeed", () => {
|
|||
const { layout } = feed.store.getState().DiscoveryStream;
|
||||
assert.equal(layout[0].components[2].properties.items, 24);
|
||||
});
|
||||
it("should create a layout with spoc and widget positions", async () => {
|
||||
feed.config.hardcoded_layout = true;
|
||||
feed.store = createStore(combineReducers(reducers), {
|
||||
Prefs: {
|
||||
values: {
|
||||
pocketConfig: {
|
||||
spocPositions: "1, 2",
|
||||
widgetPositions: "3, 4",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await feed.loadLayout(feed.store.dispatch);
|
||||
|
||||
const { layout } = feed.store.getState().DiscoveryStream;
|
||||
assert.deepEqual(layout[0].components[2].spocs.positions, [
|
||||
{ index: 1 },
|
||||
{ index: 2 },
|
||||
]);
|
||||
assert.deepEqual(layout[0].components[2].widgets.positions, [
|
||||
{ index: 3 },
|
||||
{ index: 4 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#updatePlacements", () => {
|
||||
|
|
|
@ -234,6 +234,10 @@ pocketNewtab:
|
|||
type: string
|
||||
fallbackPref: browser.newtabpage.activity-stream.discoverystream.spoc-positions
|
||||
description: CSV string of spoc position indexes on newtab grid
|
||||
widgetPositions:
|
||||
type: string
|
||||
fallbackPref: browser.newtabpage.activity-stream.discoverystream.widget-positions
|
||||
description: CSV string of widget position indexes on newtab grid
|
||||
compactLayout:
|
||||
type: boolean
|
||||
fallbackPref: browser.newtabpage.activity-stream.discoverystream.compactLayout.enabled
|
||||
|
|
Загрузка…
Ссылка в новой задаче