mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 00:35:44 +00:00
Bug 1566202 - Add dismissed thumbnail, autohide whatsnew and bug fixes to New Tab Page r=k88hudson,fluent-reviewers,flod
Differential Revision: https://phabricator.services.mozilla.com/D38120 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
fe8800dbaa
commit
95d2bd35d3
@ -48,7 +48,8 @@ module.exports = {
|
||||
"content-src/asrouter/templates/StartupOverlay/StartupOverlay.jsx",
|
||||
"content-src/components/TopSites/**",
|
||||
"content-src/components/MoreRecommendations/MoreRecommendations.jsx",
|
||||
"content-src/components/CollapsibleSection/CollapsibleSection.jsx"
|
||||
"content-src/components/CollapsibleSection/CollapsibleSection.jsx",
|
||||
"content-src/components/DiscoveryStreamComponents/DSEmptyState/DSEmptyState.jsx"
|
||||
],
|
||||
"rules": {
|
||||
"jsx-a11y/anchor-has-content": 0,
|
||||
|
@ -67,7 +67,7 @@ function templateHTML(options) {
|
||||
}
|
||||
</body>
|
||||
</html>
|
||||
`.trimStart();
|
||||
`.trimLeft();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ export class CardGrid extends React.PureComponent {
|
||||
<PlaceholderDSCard key={`dscard-${index}`} />
|
||||
) : (
|
||||
<DSCard
|
||||
key={`dscard-${index}`}
|
||||
key={`dscard-${rec.id}`}
|
||||
pos={rec.pos}
|
||||
campaignId={rec.campaign_id}
|
||||
image_src={rec.image_src}
|
||||
|
@ -52,13 +52,20 @@ export class DSEmptyState extends React.PureComponent {
|
||||
|
||||
renderButton() {
|
||||
if (this.props.status === "waiting" || this.state.waiting) {
|
||||
return <button className="try-again-button waiting">Loading...</button>;
|
||||
return (
|
||||
<button
|
||||
className="try-again-button waiting"
|
||||
data-l10n-id="newtab-discovery-empty-section-topstories-loading"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button className="try-again-button" onClick={this.onReset}>
|
||||
Try Again
|
||||
</button>
|
||||
<button
|
||||
className="try-again-button"
|
||||
onClick={this.onReset}
|
||||
data-l10n-id="newtab-discovery-empty-section-topstories-try-again-button"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -66,7 +73,7 @@ export class DSEmptyState extends React.PureComponent {
|
||||
if (this.props.status === "waiting" || this.props.status === "failed") {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h2>Oops! We almost loaded this section, but not quite.</h2>
|
||||
<h2 data-l10n-id="newtab-discovery-empty-section-topstories-timed-out" />
|
||||
{this.renderButton()}
|
||||
</React.Fragment>
|
||||
);
|
||||
@ -74,8 +81,8 @@ export class DSEmptyState extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h2>You are caught up!</h2>
|
||||
<p>Check back later for more stories.</p>
|
||||
<h2 data-l10n-id="newtab-discovery-empty-section-topstories-header" />
|
||||
<p data-l10n-id="newtab-discovery-empty-section-topstories-content" />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ export class Hero extends React.PureComponent {
|
||||
) : (
|
||||
<DSCard
|
||||
campaignId={rec.campaign_id}
|
||||
key={`dscard-${index}`}
|
||||
key={`dscard-${rec.id}`}
|
||||
image_src={rec.image_src}
|
||||
raw_image_src={rec.raw_image_src}
|
||||
title={rec.title}
|
||||
@ -87,7 +87,7 @@ export class Hero extends React.PureComponent {
|
||||
heroCard = <PlaceholderDSCard />;
|
||||
} else {
|
||||
heroCard = (
|
||||
<div className="ds-hero-item">
|
||||
<div className="ds-hero-item" key={`dscard-${heroRec.id}`}>
|
||||
<SafeAnchor
|
||||
className="wrapper"
|
||||
dispatch={this.props.dispatch}
|
||||
|
@ -145,7 +145,7 @@ export function _List(props) {
|
||||
<PlaceholderListItem key={`ds-list-item-${index}`} />
|
||||
) : (
|
||||
<ListItem
|
||||
key={`ds-list-item-${index}`}
|
||||
key={`ds-list-item-${rec.id}`}
|
||||
dispatch={props.dispatch}
|
||||
campaignId={rec.campaign_id}
|
||||
domain={rec.domain}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*! THIS FILE IS AUTO-GENERATED: webpack.system-addon.config.js */
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
@ -7577,22 +7578,30 @@ class DSEmptyState_DSEmptyState extends external_React_default.a.PureComponent {
|
||||
renderButton() {
|
||||
if (this.props.status === "waiting" || this.state.waiting) {
|
||||
return external_React_default.a.createElement("button", {
|
||||
className: "try-again-button waiting"
|
||||
}, "Loading...");
|
||||
className: "try-again-button waiting",
|
||||
"data-l10n-id": "newtab-discovery-empty-section-topstories-loading"
|
||||
});
|
||||
}
|
||||
|
||||
return external_React_default.a.createElement("button", {
|
||||
className: "try-again-button",
|
||||
onClick: this.onReset
|
||||
}, "Try Again");
|
||||
onClick: this.onReset,
|
||||
"data-l10n-id": "newtab-discovery-empty-section-topstories-try-again-button"
|
||||
});
|
||||
}
|
||||
|
||||
renderState() {
|
||||
if (this.props.status === "waiting" || this.props.status === "failed") {
|
||||
return external_React_default.a.createElement(external_React_default.a.Fragment, null, external_React_default.a.createElement("h2", null, "Oops! We almost loaded this section, but not quite."), this.renderButton());
|
||||
return external_React_default.a.createElement(external_React_default.a.Fragment, null, external_React_default.a.createElement("h2", {
|
||||
"data-l10n-id": "newtab-discovery-empty-section-topstories-timed-out"
|
||||
}), this.renderButton());
|
||||
}
|
||||
|
||||
return external_React_default.a.createElement(external_React_default.a.Fragment, null, external_React_default.a.createElement("h2", null, "You are caught up!"), external_React_default.a.createElement("p", null, "Check back later for more stories."));
|
||||
return external_React_default.a.createElement(external_React_default.a.Fragment, null, external_React_default.a.createElement("h2", {
|
||||
"data-l10n-id": "newtab-discovery-empty-section-topstories-header"
|
||||
}), external_React_default.a.createElement("p", {
|
||||
"data-l10n-id": "newtab-discovery-empty-section-topstories-content"
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -7621,7 +7630,7 @@ class CardGrid_CardGrid extends external_React_default.a.PureComponent {
|
||||
cards.push(!rec || rec.placeholder ? external_React_default.a.createElement(PlaceholderDSCard, {
|
||||
key: `dscard-${index}`
|
||||
}) : external_React_default.a.createElement(DSCard_DSCard, {
|
||||
key: `dscard-${index}`,
|
||||
key: `dscard-${rec.id}`,
|
||||
pos: rec.pos,
|
||||
campaignId: rec.campaign_id,
|
||||
image_src: rec.image_src,
|
||||
@ -7823,7 +7832,7 @@ function _List(props) {
|
||||
recMarkup.push(!rec || rec.placeholder ? external_React_default.a.createElement(PlaceholderListItem, {
|
||||
key: `ds-list-item-${index}`
|
||||
}) : external_React_default.a.createElement(List_ListItem, {
|
||||
key: `ds-list-item-${index}`,
|
||||
key: `ds-list-item-${rec.id}`,
|
||||
dispatch: props.dispatch,
|
||||
campaignId: rec.campaign_id,
|
||||
domain: rec.domain,
|
||||
@ -7936,7 +7945,7 @@ class Hero_Hero extends external_React_default.a.PureComponent {
|
||||
key: `dscard-${index}`
|
||||
}) : external_React_default.a.createElement(DSCard_DSCard, {
|
||||
campaignId: rec.campaign_id,
|
||||
key: `dscard-${index}`,
|
||||
key: `dscard-${rec.id}`,
|
||||
image_src: rec.image_src,
|
||||
raw_image_src: rec.raw_image_src,
|
||||
title: rec.title,
|
||||
@ -7959,7 +7968,8 @@ class Hero_Hero extends external_React_default.a.PureComponent {
|
||||
heroCard = external_React_default.a.createElement(PlaceholderDSCard, null);
|
||||
} else {
|
||||
heroCard = external_React_default.a.createElement("div", {
|
||||
className: "ds-hero-item"
|
||||
className: "ds-hero-item",
|
||||
key: `dscard-${heroRec.id}`
|
||||
}, external_React_default.a.createElement(SafeAnchor_SafeAnchor, {
|
||||
className: "wrapper",
|
||||
dispatch: this.props.dispatch,
|
||||
|
@ -102,12 +102,14 @@ class _ToolbarBadgeHub {
|
||||
}
|
||||
|
||||
removeToolbarNotification(toolbarButton) {
|
||||
// Remove it from the element that displays the badge
|
||||
toolbarButton
|
||||
.querySelector(".toolbarbutton-badge")
|
||||
.classList.remove("feature-callout");
|
||||
// Remove it from the toolbar icon
|
||||
toolbarButton
|
||||
.querySelector(".toolbarbutton-icon")
|
||||
.classList.add("feature-callout");
|
||||
.classList.remove("feature-callout");
|
||||
toolbarButton.removeAttribute("badged");
|
||||
}
|
||||
|
||||
@ -122,6 +124,8 @@ class _ToolbarBadgeHub {
|
||||
toolbarbutton
|
||||
.querySelector(".toolbarbutton-badge")
|
||||
.classList.add("feature-callout");
|
||||
// This creates the cut-out effect for the icon where the notification
|
||||
// fits in
|
||||
toolbarbutton
|
||||
.querySelector(".toolbarbutton-icon")
|
||||
.classList.add("feature-callout");
|
||||
|
@ -68,6 +68,24 @@ class _ToolbarPanelHub {
|
||||
);
|
||||
}
|
||||
|
||||
// When the panel is hidden we want to run some cleanup
|
||||
_onPanelHidden(win) {
|
||||
const panelContainer = win.document.getElementById(
|
||||
"customizationui-widget-panel"
|
||||
);
|
||||
// When the panel is hidden we want to remove any toolbar buttons that
|
||||
// might have been added as an entry point to the panel
|
||||
const removeToolbarButton = () => {
|
||||
EveryWindow.unregisterCallback(TOOLBAR_BUTTON_ID);
|
||||
};
|
||||
if (!panelContainer) {
|
||||
return;
|
||||
}
|
||||
panelContainer.addEventListener("popuphidden", removeToolbarButton, {
|
||||
once: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Render what's new messages into the panel.
|
||||
async renderMessages(win, doc, containerId) {
|
||||
const messages = (await this._getMessages({
|
||||
@ -97,6 +115,7 @@ class _ToolbarPanelHub {
|
||||
}
|
||||
|
||||
// TODO: TELEMETRY
|
||||
this._onPanelHidden(win);
|
||||
}
|
||||
|
||||
_createMessageElements(win, doc, content, previousDate) {
|
||||
|
@ -148,6 +148,16 @@ newtab-empty-section-highlights = Start browsing, and we’ll show some of the g
|
||||
# $provider (String): Name of the content provider for this section, e.g "Pocket".
|
||||
newtab-empty-section-topstories = You’ve caught up. Check back later for more top stories from { $provider }. Can’t wait? Select a popular topic to find more great stories from around the web.
|
||||
|
||||
|
||||
## Empty Section (Content Discovery Experience). These show when there are no more stories or when some stories fail to load.
|
||||
|
||||
newtab-discovery-empty-section-topstories-header = You are caught up!
|
||||
newtab-discovery-empty-section-topstories-content = Check back later for more stories.
|
||||
newtab-discovery-empty-section-topstories-try-again-button = Try Again
|
||||
newtab-discovery-empty-section-topstories-loading = Loading…
|
||||
# Displays when a layout in a section took too long to fetch articles.
|
||||
newtab-discovery-empty-section-topstories-timed-out = Oops! We almost loaded this section, but not quite.
|
||||
|
||||
## Pocket Content Section.
|
||||
|
||||
# This is shown at the bottom of the trending stories section and precedes a list of links to popular topics.
|
||||
|
541
browser/components/newtab/package-lock.json
generated
541
browser/components/newtab/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,7 @@
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-webpack": "3.0.5",
|
||||
"loader-utils": "1.2.3",
|
||||
"lodash": "4.17.14",
|
||||
"minimist": "1.2.0",
|
||||
"mocha": "6.1.4",
|
||||
"mock-raf": "1.0.1",
|
||||
|
@ -16,18 +16,33 @@ describe("<DSEmptyState>", () => {
|
||||
|
||||
it("should render defaultempty state message", () => {
|
||||
assert.ok(wrapper.find(".empty-state-message").exists());
|
||||
assert.ok(wrapper.find("h2").exists());
|
||||
assert.ok(wrapper.find("p").exists());
|
||||
const header = wrapper.find(
|
||||
"h2[data-l10n-id='newtab-discovery-empty-section-topstories-header']"
|
||||
);
|
||||
const paragraph = wrapper.find(
|
||||
"p[data-l10n-id='newtab-discovery-empty-section-topstories-content']"
|
||||
);
|
||||
|
||||
assert.ok(header.exists());
|
||||
assert.ok(paragraph.exists());
|
||||
});
|
||||
|
||||
it("should render failed state message", () => {
|
||||
wrapper = shallow(<DSEmptyState status="failed" />);
|
||||
assert.ok(wrapper.find("button.try-again-button").exists());
|
||||
const button = wrapper.find(
|
||||
"button[data-l10n-id='newtab-discovery-empty-section-topstories-try-again-button']"
|
||||
);
|
||||
|
||||
assert.ok(button.exists());
|
||||
});
|
||||
|
||||
it("should render waiting state message", () => {
|
||||
wrapper = shallow(<DSEmptyState status="waiting" />);
|
||||
assert.ok(wrapper.find("button.try-again-button.waiting").exists());
|
||||
const button = wrapper.find(
|
||||
"button[data-l10n-id='newtab-discovery-empty-section-topstories-loading']"
|
||||
);
|
||||
|
||||
assert.ok(button.exists());
|
||||
});
|
||||
|
||||
it("should dispatch DISCOVERY_STREAM_RETRY_FEED on failed state button click", () => {
|
||||
|
@ -23,6 +23,10 @@ describe("ToolbarBadgeHub", () => {
|
||||
fxaMessage = msgs.find(({ id }) => id === "FXA_ACCOUNTS_BADGE");
|
||||
whatsnewMessage = msgs.find(({ id }) => id.includes("WHATS_NEW_BADGE_"));
|
||||
fakeElement = {
|
||||
classList: {
|
||||
add: sandbox.stub(),
|
||||
remove: sandbox.stub(),
|
||||
},
|
||||
setAttribute: sandbox.stub(),
|
||||
removeAttribute: sandbox.stub(),
|
||||
querySelector: sandbox.stub(),
|
||||
@ -128,9 +132,9 @@ describe("ToolbarBadgeHub", () => {
|
||||
it("should show a notification", () => {
|
||||
instance.addToolbarNotification(target, fxaMessage);
|
||||
|
||||
assert.calledTwice(fakeElement.setAttribute);
|
||||
assert.calledOnce(fakeElement.setAttribute);
|
||||
assert.calledWithExactly(fakeElement.setAttribute, "badged", true);
|
||||
assert.calledWithExactly(fakeElement.setAttribute, "value", "x");
|
||||
assert.calledWithExactly(fakeElement.classList.add, "feature-callout");
|
||||
});
|
||||
it("should attach a cb on the notification", () => {
|
||||
instance.addToolbarNotification(target, fxaMessage);
|
||||
@ -231,8 +235,10 @@ describe("ToolbarBadgeHub", () => {
|
||||
it("should remove the notification", () => {
|
||||
instance.removeToolbarNotification(fakeElement);
|
||||
|
||||
assert.calledTwice(fakeElement.removeAttribute);
|
||||
assert.calledOnce(fakeElement.removeAttribute);
|
||||
assert.calledWithExactly(fakeElement.removeAttribute, "badged");
|
||||
assert.calledTwice(fakeElement.classList.remove);
|
||||
assert.calledWithExactly(fakeElement.classList.remove, "feature-callout");
|
||||
});
|
||||
});
|
||||
describe("removeAllNotifications", () => {
|
||||
|
@ -22,6 +22,7 @@ describe("ToolbarPanelHub", () => {
|
||||
removeAttribute: sandbox.stub(),
|
||||
querySelector: sandbox.stub().returns(null),
|
||||
appendChild: sandbox.stub(),
|
||||
addEventListener: sandbox.stub(),
|
||||
};
|
||||
fakeDocument = {
|
||||
l10n: {
|
||||
@ -45,6 +46,7 @@ describe("ToolbarPanelHub", () => {
|
||||
},
|
||||
};
|
||||
fakeWindow = {
|
||||
document: fakeDocument,
|
||||
browser: {
|
||||
ownerDocument: fakeDocument,
|
||||
},
|
||||
@ -147,4 +149,44 @@ describe("ToolbarPanelHub", () => {
|
||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||
assert.callCount(instance._createDateElement, uniqueDates.length);
|
||||
});
|
||||
it("should listen for panelhidden and remove the toolbar button", async () => {
|
||||
instance.init({
|
||||
getMessages: sandbox.stub().returns([]),
|
||||
});
|
||||
fakeDocument.getElementById
|
||||
.withArgs("customizationui-widget-panel")
|
||||
.returns(null);
|
||||
|
||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||
|
||||
assert.notCalled(fakeElementById.addEventListener);
|
||||
});
|
||||
it("should listen for panelhidden and remove the toolbar button", async () => {
|
||||
instance.init({
|
||||
getMessages: sandbox.stub().returns([]),
|
||||
});
|
||||
|
||||
await instance.renderMessages(fakeWindow, fakeDocument, "container-id");
|
||||
|
||||
assert.calledOnce(fakeElementById.addEventListener);
|
||||
assert.calledWithExactly(
|
||||
fakeElementById.addEventListener,
|
||||
"popuphidden",
|
||||
sinon.match.func,
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
const [, cb] = fakeElementById.addEventListener.firstCall.args;
|
||||
|
||||
assert.notCalled(everyWindowStub.unregisterCallback);
|
||||
|
||||
cb();
|
||||
|
||||
assert.calledOnce(everyWindowStub.unregisterCallback);
|
||||
assert.calledWithExactly(
|
||||
everyWindowStub.unregisterCallback,
|
||||
"whats-new-menu-button"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -18,7 +18,12 @@ module.exports = (env = {}) => ({
|
||||
},
|
||||
// TODO: switch to eval-source-map for faster builds. Requires CSP changes
|
||||
devtool: env.development ? "inline-source-map" : false,
|
||||
plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
|
||||
plugins: [
|
||||
new webpack.BannerPlugin(
|
||||
`THIS FILE IS AUTO-GENERATED: ${path.basename(__filename)}`
|
||||
),
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
|
@ -148,6 +148,16 @@ newtab-empty-section-highlights = Start browsing, and we’ll show some of the g
|
||||
# $provider (String): Name of the content provider for this section, e.g "Pocket".
|
||||
newtab-empty-section-topstories = You’ve caught up. Check back later for more top stories from { $provider }. Can’t wait? Select a popular topic to find more great stories from around the web.
|
||||
|
||||
|
||||
## Empty Section (Content Discovery Experience). These show when there are no more stories or when some stories fail to load.
|
||||
|
||||
newtab-discovery-empty-section-topstories-header = You are caught up!
|
||||
newtab-discovery-empty-section-topstories-content = Check back later for more stories.
|
||||
newtab-discovery-empty-section-topstories-try-again-button = Try Again
|
||||
newtab-discovery-empty-section-topstories-loading = Loading…
|
||||
# Displays when a layout in a section took too long to fetch articles.
|
||||
newtab-discovery-empty-section-topstories-timed-out = Oops! We almost loaded this section, but not quite.
|
||||
|
||||
## Pocket Content Section.
|
||||
|
||||
# This is shown at the bottom of the trending stories section and precedes a list of links to popular topics.
|
||||
|
Loading…
Reference in New Issue
Block a user