Backed out changeset 5990f24b16ef (bug 1516272) for failing at browser_google_behavior.js on a CLOSED TREE

This commit is contained in:
Gurzau Raul 2019-01-03 01:15:52 +02:00
Родитель a1eb21f649
Коммит 4a500304ba
37 изменённых файлов: 602 добавлений и 1865 удалений

Просмотреть файл

@ -6,8 +6,6 @@ support-files =
searchSuggestionEngine.xml searchSuggestionEngine.xml
POSTSearchEngine.xml POSTSearchEngine.xml
dummy_page.html dummy_page.html
prefs =
browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
[browser_aboutCertError.js] [browser_aboutCertError.js]
[browser_aboutCertError_telemetry.js] [browser_aboutCertError_telemetry.js]

Просмотреть файл

@ -33,20 +33,15 @@ for (const type of [
"AS_ROUTER_TELEMETRY_USER_EVENT", "AS_ROUTER_TELEMETRY_USER_EVENT",
"BLOCK_URL", "BLOCK_URL",
"BOOKMARK_URL", "BOOKMARK_URL",
"CONTENT_LAYOUT",
"COPY_DOWNLOAD_LINK", "COPY_DOWNLOAD_LINK",
"DELETE_BOOKMARK_BY_ID", "DELETE_BOOKMARK_BY_ID",
"DELETE_FROM_POCKET", "DELETE_FROM_POCKET",
"DELETE_HISTORY_URL", "DELETE_HISTORY_URL",
"DIALOG_CANCEL", "DIALOG_CANCEL",
"DIALOG_OPEN", "DIALOG_OPEN",
"DISCOVERY_STREAM_CONFIG_CHANGE",
"DISCOVERY_STREAM_CONFIG_SETUP",
"DISCOVERY_STREAM_LAYOUT_UPDATE",
"DOWNLOAD_CHANGED", "DOWNLOAD_CHANGED",
"FILL_SEARCH_TERM", "FILL_SEARCH_TERM",
"FOCUS_SEARCH",
"HANDOFF_SEARCH_TO_AWESOMEBAR",
"HIDE_SEARCH",
"INIT", "INIT",
"MIGRATION_CANCEL", "MIGRATION_CANCEL",
"MIGRATION_COMPLETED", "MIGRATION_COMPLETED",
@ -96,7 +91,6 @@ for (const type of [
"SET_PREF", "SET_PREF",
"SHOW_DOWNLOAD_FILE", "SHOW_DOWNLOAD_FILE",
"SHOW_FIREFOX_ACCOUNTS", "SHOW_FIREFOX_ACCOUNTS",
"SHOW_SEARCH",
"SKIPPED_SIGNIN", "SKIPPED_SIGNIN",
"SNIPPETS_BLOCKLIST_CLEARED", "SNIPPETS_BLOCKLIST_CLEARED",
"SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_BLOCKLIST_UPDATED",

Просмотреть файл

@ -47,18 +47,7 @@ const INITIAL_STATE = {
pocketCta: {}, pocketCta: {},
waitingForSpoc: true, waitingForSpoc: true,
}, },
// This is the new pocket configurable layout state. Layout: [],
DiscoveryStream: {
// This is a JSON-parsed copy of the discoverystream.config pref value.
config: {enabled: false, layout_endpoint: ""},
layout: [],
},
Search: {
// Pretend the search box is focused after handing off to AwesomeBar.
focus: false,
// Hide the search box after handing off to AwesomeBar and user starts typing.
hide: false,
},
}; };
function App(prevState = INITIAL_STATE.App, action) { function App(prevState = INITIAL_STATE.App, action) {
@ -441,27 +430,10 @@ function Pocket(prevState = INITIAL_STATE.Pocket, action) {
} }
} }
function DiscoveryStream(prevState = INITIAL_STATE.DiscoveryStream, action) { function Layout(prevState = INITIAL_STATE.Layout, action) {
switch (action.type) { switch (action.type) {
case at.DISCOVERY_STREAM_CONFIG_CHANGE: case at.CONTENT_LAYOUT:
// The reason this is a separate action is so it doesn't trigger a listener update on init return action.data;
case at.DISCOVERY_STREAM_CONFIG_SETUP:
return {...prevState, config: action.data || {}};
case at.DISCOVERY_STREAM_LAYOUT_UPDATE:
return {...prevState, layout: action.data || []};
default:
return prevState;
}
}
function Search(prevState = INITIAL_STATE.Search, action) {
switch (action.type) {
case at.HIDE_SEARCH:
return Object.assign({...prevState, hide: true});
case at.FOCUS_SEARCH:
return Object.assign({...prevState, focus: true});
case at.SHOW_SEARCH:
return Object.assign({...prevState, hide: false, focus: false});
default: default:
return prevState; return prevState;
} }
@ -471,23 +443,6 @@ this.INITIAL_STATE = INITIAL_STATE;
this.TOP_SITES_DEFAULT_ROWS = TOP_SITES_DEFAULT_ROWS; this.TOP_SITES_DEFAULT_ROWS = TOP_SITES_DEFAULT_ROWS;
this.TOP_SITES_MAX_SITES_PER_ROW = TOP_SITES_MAX_SITES_PER_ROW; this.TOP_SITES_MAX_SITES_PER_ROW = TOP_SITES_MAX_SITES_PER_ROW;
this.reducers = { this.reducers = {TopSites, App, ASRouter, Snippets, Prefs, Dialog, Sections, Pocket, Layout};
TopSites,
App,
ASRouter,
Snippets,
Prefs,
Dialog,
Sections,
Pocket,
DiscoveryStream,
Search,
};
const EXPORTED_SYMBOLS = [ const EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_ROWS", "TOP_SITES_MAX_SITES_PER_ROW"];
"reducers",
"INITIAL_STATE",
"insertPinned",
"TOP_SITES_DEFAULT_ROWS",
"TOP_SITES_MAX_SITES_PER_ROW",
];

Просмотреть файл

@ -1,10 +1,8 @@
import {ASRouterUtils} from "../../asrouter/asrouter-content"; import {ASRouterUtils} from "../../asrouter/asrouter-content";
import {connect} from "react-redux";
import {ModalOverlay} from "../../asrouter/components/ModalOverlay/ModalOverlay"; import {ModalOverlay} from "../../asrouter/components/ModalOverlay/ModalOverlay";
import React from "react"; import React from "react";
import {SimpleHashRouter} from "./SimpleHashRouter";
export class ASRouterAdminInner extends React.PureComponent { export class ASRouterAdmin extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.onMessage = this.onMessage.bind(this); this.onMessage = this.onMessage.bind(this);
@ -376,23 +374,6 @@ export class ASRouterAdminInner extends React.PureComponent {
ASRouterUtils.sendMessage({type: "FORCE_ATTRIBUTION", data: this.state.attributionParameters}); ASRouterUtils.sendMessage({type: "FORCE_ATTRIBUTION", data: this.state.attributionParameters});
} }
renderPocketStory(story) {
return (<tr className="message-item" key={story.guid}>
<td className="message-id"><span>{story.guid} <br /></span></td>
<td className="message-summary">
<pre>{JSON.stringify(story, null, 2)}</pre>
</td>
</tr>);
}
renderPocketStories() {
const {rows} = this.props.Sections.find(Section => Section.id === "topstories") || {};
return (<table><tbody>
{rows && rows.map(story => this.renderPocketStory(story))}
</tbody></table>);
}
renderAttributionParamers() { renderAttributionParamers() {
return ( return (
<div> <div>
@ -418,45 +399,9 @@ export class ASRouterAdminInner extends React.PureComponent {
</div>); </div>);
} }
getSection() {
const [section] = this.props.location.routes;
switch (section) {
case "targeting":
return (<React.Fragment>
<h2>Targeting Utilities</h2>
<button className="button" onClick={this.expireCache}>Expire Cache</button> (This expires the cache in ASR Targeting for bookmarks and top sites)
{this.renderTargetingParameters()}
{this.renderAttributionParamers()}
</React.Fragment>);
case "pocket":
return (<React.Fragment>
<h2>Pocket</h2>
{this.renderPocketStories()}
</React.Fragment>);
default:
return (<React.Fragment>
<h2>Message Providers <button title="Restore all provider settings that ship with Firefox" className="button" onClick={this.resetPref}>Restorear default prefs</button></h2>
{this.state.providers ? this.renderProviders() : null}
<h2>Messages</h2>
{this.renderMessageFilter()}
{this.renderMessages()}
{this.renderPasteModal()}
</React.Fragment>);
}
}
render() { render() {
return (<div className="asrouter-admin"> return (<div className="asrouter-admin outer-wrapper">
<aside className="sidebar">
<ul>
<li><a href="#devtools">General</a></li>
<li><a href="#devtools-targeting">Targeting</a></li>
<li><a href="#devtools-pocket">Pocket</a></li>
</ul>
</aside>
<main className="main-panel">
<h1>AS Router Admin</h1> <h1>AS Router Admin</h1>
<p className="helpLink"> <p className="helpLink">
<span className="icon icon-small-spacer icon-info" /> <span className="icon icon-small-spacer icon-info" />
{" "} {" "}
@ -464,12 +409,17 @@ export class ASRouterAdminInner extends React.PureComponent {
Need help using these tools? Check out our <a target="blank" href="https://github.com/mozilla/activity-stream/blob/master/content-src/asrouter/docs/debugging-docs.md">documentation</a> Need help using these tools? Check out our <a target="blank" href="https://github.com/mozilla/activity-stream/blob/master/content-src/asrouter/docs/debugging-docs.md">documentation</a>
</span> </span>
</p> </p>
<h2>Targeting Utilities</h2>
<button className="button" onClick={this.expireCache}>Expire Cache</button> (This expires the cache in ASR Targeting for bookmarks and top sites)
<h2>Message Providers <button title="Restore all provider settings that ship with Firefox" className="button" onClick={this.resetPref}>Restore default prefs</button></h2>
{this.getSection()} {this.state.providers ? this.renderProviders() : null}
</main> <h2>Messages</h2>
{this.renderMessageFilter()}
{this.renderMessages()}
{this.renderPasteModal()}
{this.renderTargetingParameters()}
{this.renderAttributionParamers()}
</div>); </div>);
} }
} }
export const _ASRouterAdmin = props => (<SimpleHashRouter><ASRouterAdminInner {...props} /></SimpleHashRouter>);
export const ASRouterAdmin = connect(state => ({Sections: state.Sections}))(_ASRouterAdmin);

Просмотреть файл

@ -2,32 +2,12 @@
.asrouter-admin { .asrouter-admin {
$border-color: var(--newtab-border-secondary-color); $border-color: var(--newtab-border-secondary-color);
$monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace; $monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;
max-width: 996px;
margin: 0 auto; margin: 0 auto;
font-size: 14px; font-size: 14px;
// Reset .outer-wrapper styles
display: flex; display: inherit;
padding: 0 0 92px;
.sidebar {
position: fixed;
width: 240px;
padding: 30px 20px;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li a {
padding: 10px 34px;
display: block;
&:hover {
background: $grey-20;
}
}
}
h1 { h1 {
font-weight: 200; font-weight: 200;

Просмотреть файл

@ -1,31 +0,0 @@
import React from "react";
export class SimpleHashRouter extends React.PureComponent {
constructor(props) {
super(props);
this.onHashChange = this.onHashChange.bind(this);
this.state = {hash: global.location.hash};
}
onHashChange() {
this.setState({hash: global.location.hash});
}
componentWillMount() {
global.addEventListener("hashchange", this.onHashChange);
}
componentWillUnmount() {
global.removeEventListener("hashchange", this.onHashChange);
}
render() {
const [, ...routes] = this.state.hash.replace("#asrouter", "").split("-");
return React.cloneElement(this.props.children, {
location: {
hash: this.state.hash,
routes,
},
});
}
}

Просмотреть файл

@ -3,7 +3,6 @@ import {addLocaleData, injectIntl, IntlProvider} from "react-intl";
import {ASRouterAdmin} from "content-src/components/ASRouterAdmin/ASRouterAdmin"; import {ASRouterAdmin} from "content-src/components/ASRouterAdmin/ASRouterAdmin";
import {ConfirmDialog} from "content-src/components/ConfirmDialog/ConfirmDialog"; import {ConfirmDialog} from "content-src/components/ConfirmDialog/ConfirmDialog";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {DiscoveryStreamBase} from "content-src/components/DiscoveryStreamBase/DiscoveryStreamBase";
import {ErrorBoundary} from "content-src/components/ErrorBoundary/ErrorBoundary"; import {ErrorBoundary} from "content-src/components/ErrorBoundary/ErrorBoundary";
import {ManualMigration} from "content-src/components/ManualMigration/ManualMigration"; import {ManualMigration} from "content-src/components/ManualMigration/ManualMigration";
import {PrerenderData} from "common/PrerenderData.jsm"; import {PrerenderData} from "common/PrerenderData.jsm";
@ -83,11 +82,10 @@ export class _Base extends React.PureComponent {
const prefs = props.Prefs.values; const prefs = props.Prefs.values;
if (prefs["asrouter.devtoolsEnabled"]) { if (prefs["asrouter.devtoolsEnabled"]) {
if (window.location.hash.startsWith("#asrouter") || if (window.location.hash === "#asrouter") {
window.location.hash.startsWith("#devtools")) {
return (<ASRouterAdmin />); return (<ASRouterAdmin />);
} }
console.log("Activity Stream devtools enabled. To access visit %cabout:newtab#devtools", "font-weight: bold"); // eslint-disable-line no-console console.log("ASRouter devtools enabled. To access visit %cabout:newtab#asrouter", "font-weight: bold"); // eslint-disable-line no-console
} }
if (!props.isPrerendered && !initialized) { if (!props.isPrerendered && !initialized) {
@ -140,8 +138,6 @@ export class BaseContent extends React.PureComponent {
const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]); const shouldBeFixedToTop = PrerenderData.arePrefsValid(name => prefs[name]);
const noSectionsEnabled = !prefs["feeds.topsites"] && props.Sections.filter(section => section.enabled).length === 0; const noSectionsEnabled = !prefs["feeds.topsites"] && props.Sections.filter(section => section.enabled).length === 0;
const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
const outerClassName = [ const outerClassName = [
"outer-wrapper", "outer-wrapper",
@ -157,17 +153,17 @@ export class BaseContent extends React.PureComponent {
{prefs.showSearch && {prefs.showSearch &&
<div className="non-collapsible-section"> <div className="non-collapsible-section">
<ErrorBoundary> <ErrorBoundary>
<Search showLogo={noSectionsEnabled} handoffEnabled={searchHandoffEnabled} {...props.Search} /> <Search showLogo={noSectionsEnabled} />
</ErrorBoundary> </ErrorBoundary>
</div> </div>
} }
<div className={`body-wrapper${(initialized ? " on" : "")}`}> <div className={`body-wrapper${(initialized ? " on" : "")}`}>
{!isDiscoveryStream && !prefs.migrationExpired && {!prefs.migrationExpired &&
<div className="non-collapsible-section"> <div className="non-collapsible-section">
<ManualMigration /> <ManualMigration />
</div> </div>
} }
{isDiscoveryStream ? <DiscoveryStreamBase /> : <Sections />} <Sections />
<PrefsButton onClick={this.openPreferences} /> <PrefsButton onClick={this.openPreferences} />
</div> </div>
<ConfirmDialog /> <ConfirmDialog />
@ -177,10 +173,4 @@ export class BaseContent extends React.PureComponent {
} }
} }
export const Base = connect(state => ({ export const Base = connect(state => ({App: state.App, Prefs: state.Prefs, Sections: state.Sections}))(_Base);
App: state.App,
Prefs: state.Prefs,
Sections: state.Sections,
DiscoveryStream: state.DiscoveryStream,
Search: state.Search,
}))(_Base);

Просмотреть файл

@ -1,22 +0,0 @@
import {connect} from "react-redux";
import React from "react";
export class _DiscoveryStreamBase extends React.PureComponent {
render() {
return (
<div className="discovery-stream layout">
{this.props.DiscoveryStream.layout.map((section, sectionIndex) => (
<div key={`section-${sectionIndex}`} className={`column column-${section.width}`}>
{section.components.map((component, componentIndex) => (
<div key={`component-${componentIndex}`}>
<div>{component.type}</div>
</div>
))}
</div>
))}
</div>
);
}
}
export const DiscoveryStreamBase = connect(state => ({DiscoveryStream: state.DiscoveryStream}))(_DiscoveryStreamBase);

Просмотреть файл

@ -1,20 +0,0 @@
.discovery-stream.layout {
$columns: 12;
display: grid;
grid-template-columns: repeat($columns, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
.column {
border: 1px solid $black;
}
@while $columns > 0 {
.column-#{$columns} {
grid-column-start: auto;
grid-column-end: span $columns;
}
$columns: $columns - 1;
}
}

Просмотреть файл

@ -1,8 +1,8 @@
/* globals ContentSearchUIController */ /* globals ContentSearchUIController */
"use strict"; "use strict";
import {actionCreators as ac, actionTypes as at} from "common/Actions.jsm";
import {FormattedMessage, injectIntl} from "react-intl"; import {FormattedMessage, injectIntl} from "react-intl";
import {actionCreators as ac} from "common/Actions.jsm";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {IS_NEWTAB} from "content-src/lib/constants"; import {IS_NEWTAB} from "content-src/lib/constants";
import React from "react"; import React from "react";
@ -10,8 +10,7 @@ import React from "react";
export class _Search extends React.PureComponent { export class _Search extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.onSearchClick = this.onSearchClick.bind(this); this.onClick = this.onClick.bind(this);
this.onSearchHandoffClick = this.onSearchHandoffClick.bind(this);
this.onInputMount = this.onInputMount.bind(this); this.onInputMount = this.onInputMount.bind(this);
} }
@ -22,26 +21,10 @@ export class _Search extends React.PureComponent {
} }
} }
onSearchClick(event) { onClick(event) {
window.gContentSearchController.search(event); window.gContentSearchController.search(event);
} }
onSearchHandoffClick(event) {
// When search hand-off is enabled, we render a big button that is styled to
// look like a search textbox. If the button is clicked with the mouse, we style
// the button as if it was a focused search box and show a fake cursor but
// really focus the awesomebar without the focus styles.
// If the button is clicked from the keyboard, we focus the awesomebar normally.
// This is to minimize confusion with users navigating with the keyboard and
// users using assistive technologoy.
const isKeyboardClick = event.clientX === 0 && event.clientY === 0;
const hiddenFocus = !isKeyboardClick;
this.props.dispatch(ac.OnlyToMain({type: at.HANDOFF_SEARCH_TO_AWESOMEBAR, data: {hiddenFocus}}));
this.props.dispatch({type: at.FOCUS_SEARCH});
// TODO: Send a telemetry ping. BUG 1514732
}
componentWillUnmount() { componentWillUnmount() {
delete window.gContentSearchController; delete window.gContentSearchController;
} }
@ -80,20 +63,13 @@ export class _Search extends React.PureComponent {
* in order to execute searches in various tests * in order to execute searches in various tests
*/ */
render() { render() {
const wrapperClassName = [ return (<div className="search-wrapper">
"search-wrapper",
this.props.hide && "search-hidden",
this.props.focus && "search-active",
].filter(v => v).join(" ");
return (<div className={wrapperClassName}>
{this.props.showLogo && {this.props.showLogo &&
<div className="logo-and-wordmark"> <div className="logo-and-wordmark">
<div className="logo" /> <div className="logo" />
<div className="wordmark" /> <div className="wordmark" />
</div> </div>
} }
{!this.props.handoffEnabled &&
<div className="search-inner-wrapper"> <div className="search-inner-wrapper">
<label htmlFor="newtab-search-text" className="search-label"> <label htmlFor="newtab-search-text" className="search-label">
<span className="sr-only"><FormattedMessage id="search_web_placeholder" /></span> <span className="sr-only"><FormattedMessage id="search_web_placeholder" /></span>
@ -108,32 +84,11 @@ export class _Search extends React.PureComponent {
<button <button
id="searchSubmit" id="searchSubmit"
className="search-button" className="search-button"
onClick={this.onSearchClick} onClick={this.onClick}
title={this.props.intl.formatMessage({id: "search_button"})}> title={this.props.intl.formatMessage({id: "search_button"})}>
<span className="sr-only"><FormattedMessage id="search_button" /></span> <span className="sr-only"><FormattedMessage id="search_button" /></span>
</button> </button>
</div> </div>
}
{this.props.handoffEnabled &&
<div className="search-inner-wrapper">
<button
className="search-handoff-button"
onClick={this.onSearchHandoffClick}
title={this.props.intl.formatMessage({id: "search_web_placeholder"})}>
<div className="fake-textbox">{this.props.intl.formatMessage({id: "search_web_placeholder"})}</div>
<div className="fake-caret" />
<div className="fake-button" />
</button>
{/*
This dummy and hidden input below is so we can load ContentSearchUIController.
Why? It sets --newtab-search-icon for us and it isn't trivial to port over.
*/}
<input
type="search"
style={{display: "none"}}
ref={this.onInputMount} />
</div>
}
</div>); </div>);
} }
} }

Просмотреть файл

@ -1,3 +1,4 @@
.search-wrapper {
$search-height: 48px; $search-height: 48px;
$search-icon-size: 24px; $search-icon-size: 24px;
$search-icon-padding: 12px; $search-icon-padding: 12px;
@ -5,7 +6,6 @@ $search-icon-width: 2 * $search-icon-padding + $search-icon-size -2;
$search-button-width: 48px; $search-button-width: 48px;
$glyph-forward: url('chrome://browser/skin/forward.svg'); $glyph-forward: url('chrome://browser/skin/forward.svg');
.search-wrapper {
padding: 34px 0 64px; padding: 34px 0 64px;
@media (max-height: 700px) { @media (max-height: 700px) {
@ -137,85 +137,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg');
} }
} }
.search-handoff-button {
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center no-repeat;
background-size: $search-icon-size;
border: solid 1px var(--newtab-search-border-color);
border-radius: 3px;
box-shadow: $shadow-secondary, 0 0 0 1px $black-15;
cursor: text;
font-size: 15px;
padding: 0;
padding-inline-end: 48px;
padding-inline-start: 46px;
opacity: 1;
transition: opacity 500ms;
width: 100%;
&:dir(rtl) {
background-position-x: right $search-icon-padding;
}
&:hover {
box-shadow: $shadow-secondary, 0 0 0 1px $black-25;
}
&:focus,
.search-active & {
border: $input-border-active;
box-shadow: var(--newtab-textbox-focus-boxshadow);
}
.search-hidden & {
opacity: 0;
visibility: hidden;
}
.fake-textbox {
opacity: 0.54;
text-align: left;
}
.fake-caret {
animation: caret-animation 1.3s steps(5, start) infinite;
background: var(--newtab-text-primary-color);
display: none;
inset-inline-start: 47px;
height: 17px;
position: absolute;
top: 16px;
width: 1px;
@keyframes caret-animation {
to {
visibility: hidden;
}
}
.search-active & {
display: block;
}
}
.fake-button {
background: $glyph-forward no-repeat center center;
background-size: 16px 16px;
border: 0;
border-radius: 0 $border-radius $border-radius 0;
-moz-context-properties: fill;
fill: var(--newtab-search-icon-color);
height: 100%;
inset-inline-end: 0;
position: absolute;
top: 1px;
width: $search-button-width;
&:dir(rtl) {
transform: scaleX(-1);
}
}
}
@media (min-height: 701px) { @media (min-height: 701px) {
.fixed-search { .fixed-search {
main { main {
@ -251,19 +172,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg');
} }
} }
} }
.search-handoff-button {
background-position-x: $search-icon-padding;
background-size: $search-icon-size;
&:dir(rtl) {
background-position-x: right $search-icon-padding;
}
.fake-caret {
top: 10px;
}
}
} }
} }

Просмотреть файл

@ -306,7 +306,26 @@ export class _Sections extends React.PureComponent {
return sections; return sections;
} }
renderLayout() {
return (
<div className="sections-list layout">
{this.props.Layout.map((section, sectionIndex) => (
<div key={`section-${sectionIndex}`} className={`column column-${section.width}`}>
{section.components.map((component, componentIndex) => (
<div key={`component-${componentIndex}`}>
<div>{component.type}</div>
</div>
))}
</div>
))}
</div>
);
}
render() { render() {
if (this.props.Layout && this.props.Layout.length) {
return this.renderLayout();
}
return ( return (
<div className="sections-list"> <div className="sections-list">
{this.renderSections()} {this.renderSections()}
@ -315,4 +334,4 @@ export class _Sections extends React.PureComponent {
} }
} }
export const Sections = connect(state => ({Sections: state.Sections, Prefs: state.Prefs}))(_Sections); export const Sections = connect(state => ({Sections: state.Sections, Prefs: state.Prefs, Layout: state.Layout}))(_Sections);

Просмотреть файл

@ -1,4 +1,25 @@
.sections-list { .sections-list {
&.layout {
$columns: 12;
display: grid;
grid-template-columns: repeat($columns, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
.column {
border: 1px solid $black;
}
@while $columns > 0 {
.column-#{$columns} {
grid-column-start: auto;
grid-column-end: span $columns;
}
$columns: $columns - 1;
}
}
.section-list { .section-list {
display: grid; display: grid;
grid-gap: $base-gutter; grid-gap: $base-gutter;

Просмотреть файл

@ -143,7 +143,6 @@ input {
@import '../components/ASRouterAdmin/ASRouterAdmin'; @import '../components/ASRouterAdmin/ASRouterAdmin';
@import '../components/PocketLoggedInCta/PocketLoggedInCta'; @import '../components/PocketLoggedInCta/PocketLoggedInCta';
@import '../components/MoreRecommendations/MoreRecommendations'; @import '../components/MoreRecommendations/MoreRecommendations';
@import '../components/DiscoveryStreamBase/DiscoveryStreamBase';
// AS Router // AS Router
@import '../asrouter/components/Button/Button'; @import '../asrouter/components/Button/Button';

Просмотреть файл

@ -846,6 +846,50 @@ main {
opacity: 1; opacity: 1;
transform: translateY(0); } } transform: translateY(0); } }
.sections-list.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.sections-list.layout .column {
border: 1px solid #000; }
.sections-list.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.sections-list.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.sections-list.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.sections-list.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.sections-list.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.sections-list.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.sections-list.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.sections-list.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.sections-list.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.sections-list.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.sections-list.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.sections-list.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.sections-list .section-list { .sections-list .section-list {
display: grid; display: grid;
grid-gap: 32px; grid-gap: 32px;
@ -1048,64 +1092,6 @@ main {
.search-wrapper .search-button:dir(rtl) { .search-wrapper .search-button:dir(rtl) {
transform: scaleX(-1); } transform: scaleX(-1); }
.search-handoff-button {
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
background-size: 24px;
border: solid 1px var(--newtab-search-border-color);
border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
cursor: text;
font-size: 15px;
padding: 0;
padding-inline-end: 48px;
padding-inline-start: 46px;
opacity: 1;
transition: opacity 500ms;
width: 100%; }
.search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.search-handoff-button:hover {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
.search-handoff-button:focus,
.search-active .search-handoff-button {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }
.search-hidden .search-handoff-button {
opacity: 0;
visibility: hidden; }
.search-handoff-button .fake-textbox {
opacity: 0.54;
text-align: left; }
.search-handoff-button .fake-caret {
animation: caret-animation 1.3s steps(5, start) infinite;
background: var(--newtab-text-primary-color);
display: none;
inset-inline-start: 47px;
height: 17px;
position: absolute;
top: 16px;
width: 1px; }
@keyframes caret-animation {
to {
visibility: hidden; } }
.search-active .search-handoff-button .fake-caret {
display: block; }
.search-handoff-button .fake-button {
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
background-size: 16px 16px;
border: 0;
border-radius: 0 3px 3px 0;
-moz-context-properties: fill;
fill: var(--newtab-search-icon-color);
height: 100%;
inset-inline-end: 0;
position: absolute;
top: 1px;
width: 48px; }
.search-handoff-button .fake-button:dir(rtl) {
transform: scaleX(-1); }
@media (min-height: 701px) { @media (min-height: 701px) {
.fixed-search main { .fixed-search main {
padding-top: 146px; } padding-top: 146px; }
@ -1125,14 +1111,7 @@ main {
background-position-x: 16px; background-position-x: 16px;
background-size: 16px; } background-size: 16px; }
.fixed-search .search-wrapper input:dir(rtl) { .fixed-search .search-wrapper input:dir(rtl) {
background-position-x: right 16px; } background-position-x: right 16px; } }
.fixed-search .search-handoff-button {
background-position-x: 12px;
background-size: 24px; }
.fixed-search .search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.fixed-search .search-handoff-button .fake-caret {
top: 10px; } }
.contentSearchSuggestionTable { .contentSearchSuggestionTable {
background-color: var(--newtab-search-dropdown-color); background-color: var(--newtab-search-dropdown-color);
@ -1631,22 +1610,11 @@ main {
display: none; } } display: none; } }
.asrouter-admin { .asrouter-admin {
max-width: 996px;
margin: 0 auto; margin: 0 auto;
font-size: 14px; font-size: 14px;
display: flex; } display: inherit;
.asrouter-admin .sidebar { padding: 0 0 92px; }
position: fixed;
width: 240px;
padding: 30px 20px; }
.asrouter-admin .sidebar ul {
margin: 0;
padding: 0;
list-style: none; }
.asrouter-admin .sidebar li a {
padding: 10px 34px;
display: block; }
.asrouter-admin .sidebar li a:hover {
background: #EDEDF0; }
.asrouter-admin h1 { .asrouter-admin h1 {
font-weight: 200; font-weight: 200;
font-size: 32px; } font-size: 32px; }
@ -1758,50 +1726,6 @@ main {
.more-recommendations:dir(rtl)::after { .more-recommendations:dir(rtl)::after {
transform: scaleX(-1); } transform: scaleX(-1); }
.discovery-stream.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.discovery-stream.layout .column {
border: 1px solid #000; }
.discovery-stream.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.discovery-stream.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.discovery-stream.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.discovery-stream.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.discovery-stream.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.discovery-stream.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.discovery-stream.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.discovery-stream.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.discovery-stream.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.discovery-stream.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.discovery-stream.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.discovery-stream.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.ASRouterButton { .ASRouterButton {
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -849,6 +849,50 @@ main {
opacity: 1; opacity: 1;
transform: translateY(0); } } transform: translateY(0); } }
.sections-list.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.sections-list.layout .column {
border: 1px solid #000; }
.sections-list.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.sections-list.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.sections-list.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.sections-list.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.sections-list.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.sections-list.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.sections-list.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.sections-list.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.sections-list.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.sections-list.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.sections-list.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.sections-list.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.sections-list .section-list { .sections-list .section-list {
display: grid; display: grid;
grid-gap: 32px; grid-gap: 32px;
@ -1051,64 +1095,6 @@ main {
.search-wrapper .search-button:dir(rtl) { .search-wrapper .search-button:dir(rtl) {
transform: scaleX(-1); } transform: scaleX(-1); }
.search-handoff-button {
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
background-size: 24px;
border: solid 1px var(--newtab-search-border-color);
border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
cursor: text;
font-size: 15px;
padding: 0;
padding-inline-end: 48px;
padding-inline-start: 46px;
opacity: 1;
transition: opacity 500ms;
width: 100%; }
.search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.search-handoff-button:hover {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
.search-handoff-button:focus,
.search-active .search-handoff-button {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }
.search-hidden .search-handoff-button {
opacity: 0;
visibility: hidden; }
.search-handoff-button .fake-textbox {
opacity: 0.54;
text-align: left; }
.search-handoff-button .fake-caret {
animation: caret-animation 1.3s steps(5, start) infinite;
background: var(--newtab-text-primary-color);
display: none;
inset-inline-start: 47px;
height: 17px;
position: absolute;
top: 16px;
width: 1px; }
@keyframes caret-animation {
to {
visibility: hidden; } }
.search-active .search-handoff-button .fake-caret {
display: block; }
.search-handoff-button .fake-button {
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
background-size: 16px 16px;
border: 0;
border-radius: 0 3px 3px 0;
-moz-context-properties: fill;
fill: var(--newtab-search-icon-color);
height: 100%;
inset-inline-end: 0;
position: absolute;
top: 1px;
width: 48px; }
.search-handoff-button .fake-button:dir(rtl) {
transform: scaleX(-1); }
@media (min-height: 701px) { @media (min-height: 701px) {
.fixed-search main { .fixed-search main {
padding-top: 146px; } padding-top: 146px; }
@ -1128,14 +1114,7 @@ main {
background-position-x: 16px; background-position-x: 16px;
background-size: 16px; } background-size: 16px; }
.fixed-search .search-wrapper input:dir(rtl) { .fixed-search .search-wrapper input:dir(rtl) {
background-position-x: right 16px; } background-position-x: right 16px; } }
.fixed-search .search-handoff-button {
background-position-x: 12px;
background-size: 24px; }
.fixed-search .search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.fixed-search .search-handoff-button .fake-caret {
top: 10px; } }
.contentSearchSuggestionTable { .contentSearchSuggestionTable {
background-color: var(--newtab-search-dropdown-color); background-color: var(--newtab-search-dropdown-color);
@ -1634,22 +1613,11 @@ main {
display: none; } } display: none; } }
.asrouter-admin { .asrouter-admin {
max-width: 996px;
margin: 0 auto; margin: 0 auto;
font-size: 14px; font-size: 14px;
display: flex; } display: inherit;
.asrouter-admin .sidebar { padding: 0 0 92px; }
position: fixed;
width: 240px;
padding: 30px 20px; }
.asrouter-admin .sidebar ul {
margin: 0;
padding: 0;
list-style: none; }
.asrouter-admin .sidebar li a {
padding: 10px 34px;
display: block; }
.asrouter-admin .sidebar li a:hover {
background: #EDEDF0; }
.asrouter-admin h1 { .asrouter-admin h1 {
font-weight: 200; font-weight: 200;
font-size: 32px; } font-size: 32px; }
@ -1761,50 +1729,6 @@ main {
.more-recommendations:dir(rtl)::after { .more-recommendations:dir(rtl)::after {
transform: scaleX(-1); } transform: scaleX(-1); }
.discovery-stream.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.discovery-stream.layout .column {
border: 1px solid #000; }
.discovery-stream.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.discovery-stream.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.discovery-stream.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.discovery-stream.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.discovery-stream.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.discovery-stream.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.discovery-stream.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.discovery-stream.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.discovery-stream.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.discovery-stream.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.discovery-stream.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.discovery-stream.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.ASRouterButton { .ASRouterButton {
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -846,6 +846,50 @@ main {
opacity: 1; opacity: 1;
transform: translateY(0); } } transform: translateY(0); } }
.sections-list.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.sections-list.layout .column {
border: 1px solid #000; }
.sections-list.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.sections-list.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.sections-list.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.sections-list.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.sections-list.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.sections-list.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.sections-list.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.sections-list.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.sections-list.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.sections-list.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.sections-list.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.sections-list.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.sections-list .section-list { .sections-list .section-list {
display: grid; display: grid;
grid-gap: 32px; grid-gap: 32px;
@ -1048,64 +1092,6 @@ main {
.search-wrapper .search-button:dir(rtl) { .search-wrapper .search-button:dir(rtl) {
transform: scaleX(-1); } transform: scaleX(-1); }
.search-handoff-button {
background: var(--newtab-textbox-background-color) var(--newtab-search-icon) 12px center no-repeat;
background-size: 24px;
border: solid 1px var(--newtab-search-border-color);
border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.15);
cursor: text;
font-size: 15px;
padding: 0;
padding-inline-end: 48px;
padding-inline-start: 46px;
opacity: 1;
transition: opacity 500ms;
width: 100%; }
.search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.search-handoff-button:hover {
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.25); }
.search-handoff-button:focus,
.search-active .search-handoff-button {
border: 1px solid var(--newtab-textbox-focus-color);
box-shadow: var(--newtab-textbox-focus-boxshadow); }
.search-hidden .search-handoff-button {
opacity: 0;
visibility: hidden; }
.search-handoff-button .fake-textbox {
opacity: 0.54;
text-align: left; }
.search-handoff-button .fake-caret {
animation: caret-animation 1.3s steps(5, start) infinite;
background: var(--newtab-text-primary-color);
display: none;
inset-inline-start: 47px;
height: 17px;
position: absolute;
top: 16px;
width: 1px; }
@keyframes caret-animation {
to {
visibility: hidden; } }
.search-active .search-handoff-button .fake-caret {
display: block; }
.search-handoff-button .fake-button {
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
background-size: 16px 16px;
border: 0;
border-radius: 0 3px 3px 0;
-moz-context-properties: fill;
fill: var(--newtab-search-icon-color);
height: 100%;
inset-inline-end: 0;
position: absolute;
top: 1px;
width: 48px; }
.search-handoff-button .fake-button:dir(rtl) {
transform: scaleX(-1); }
@media (min-height: 701px) { @media (min-height: 701px) {
.fixed-search main { .fixed-search main {
padding-top: 146px; } padding-top: 146px; }
@ -1125,14 +1111,7 @@ main {
background-position-x: 16px; background-position-x: 16px;
background-size: 16px; } background-size: 16px; }
.fixed-search .search-wrapper input:dir(rtl) { .fixed-search .search-wrapper input:dir(rtl) {
background-position-x: right 16px; } background-position-x: right 16px; } }
.fixed-search .search-handoff-button {
background-position-x: 12px;
background-size: 24px; }
.fixed-search .search-handoff-button:dir(rtl) {
background-position-x: right 12px; }
.fixed-search .search-handoff-button .fake-caret {
top: 10px; } }
.contentSearchSuggestionTable { .contentSearchSuggestionTable {
background-color: var(--newtab-search-dropdown-color); background-color: var(--newtab-search-dropdown-color);
@ -1631,22 +1610,11 @@ main {
display: none; } } display: none; } }
.asrouter-admin { .asrouter-admin {
max-width: 996px;
margin: 0 auto; margin: 0 auto;
font-size: 14px; font-size: 14px;
display: flex; } display: inherit;
.asrouter-admin .sidebar { padding: 0 0 92px; }
position: fixed;
width: 240px;
padding: 30px 20px; }
.asrouter-admin .sidebar ul {
margin: 0;
padding: 0;
list-style: none; }
.asrouter-admin .sidebar li a {
padding: 10px 34px;
display: block; }
.asrouter-admin .sidebar li a:hover {
background: #EDEDF0; }
.asrouter-admin h1 { .asrouter-admin h1 {
font-weight: 200; font-weight: 200;
font-size: 32px; } font-size: 32px; }
@ -1758,50 +1726,6 @@ main {
.more-recommendations:dir(rtl)::after { .more-recommendations:dir(rtl)::after {
transform: scaleX(-1); } transform: scaleX(-1); }
.discovery-stream.layout {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px; }
.discovery-stream.layout .column {
border: 1px solid #000; }
.discovery-stream.layout .column-12 {
grid-column-start: auto;
grid-column-end: span 12; }
.discovery-stream.layout .column-11 {
grid-column-start: auto;
grid-column-end: span 11; }
.discovery-stream.layout .column-10 {
grid-column-start: auto;
grid-column-end: span 10; }
.discovery-stream.layout .column-9 {
grid-column-start: auto;
grid-column-end: span 9; }
.discovery-stream.layout .column-8 {
grid-column-start: auto;
grid-column-end: span 8; }
.discovery-stream.layout .column-7 {
grid-column-start: auto;
grid-column-end: span 7; }
.discovery-stream.layout .column-6 {
grid-column-start: auto;
grid-column-end: span 6; }
.discovery-stream.layout .column-5 {
grid-column-start: auto;
grid-column-end: span 5; }
.discovery-stream.layout .column-4 {
grid-column-start: auto;
grid-column-end: span 4; }
.discovery-stream.layout .column-3 {
grid-column-start: auto;
grid-column-end: span 3; }
.discovery-stream.layout .column-2 {
grid-column-start: auto;
grid-column-end: span 2; }
.discovery-stream.layout .column-1 {
grid-column-start: auto;
grid-column-end: span 1; }
.ASRouterButton { .ASRouterButton {
font-weight: 600; font-weight: 600;
font-size: 14px; font-size: 14px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -29,7 +29,6 @@ const {TopSitesFeed} = ChromeUtils.import("resource://activity-stream/lib/TopSit
const {TopStoriesFeed} = ChromeUtils.import("resource://activity-stream/lib/TopStoriesFeed.jsm", {}); const {TopStoriesFeed} = ChromeUtils.import("resource://activity-stream/lib/TopStoriesFeed.jsm", {});
const {HighlightsFeed} = ChromeUtils.import("resource://activity-stream/lib/HighlightsFeed.jsm", {}); const {HighlightsFeed} = ChromeUtils.import("resource://activity-stream/lib/HighlightsFeed.jsm", {});
const {ASRouterFeed} = ChromeUtils.import("resource://activity-stream/lib/ASRouterFeed.jsm", {}); const {ASRouterFeed} = ChromeUtils.import("resource://activity-stream/lib/ASRouterFeed.jsm", {});
const {DiscoveryStreamFeed} = ChromeUtils.import("resource://activity-stream/lib/DiscoveryStreamFeed.jsm", {});
const DEFAULT_SITES = new Map([ const DEFAULT_SITES = new Map([
// This first item is the global list fallback for any unexpected geos // This first item is the global list fallback for any unexpected geos
@ -193,10 +192,6 @@ const PREFS_CONFIG = new Map([
title: "A comma-delimited list of search shortcuts that have previously been pinned", title: "A comma-delimited list of search shortcuts that have previously been pinned",
value: "", value: "",
}], }],
["improvesearch.handoffToAwesomebar", {
title: "Should the search box handoff to the Awesomebar?",
value: true,
}],
["asrouter.devtoolsEnabled", { ["asrouter.devtoolsEnabled", {
title: "Are the asrouter devtools enabled?", title: "Are the asrouter devtools enabled?",
value: false, value: false,
@ -215,14 +210,6 @@ const PREFS_CONFIG = new Map([
}), }),
}], }],
// See browser/app/profile/firefox.js for other ASR preferences. They must be defined there to enable roll-outs. // See browser/app/profile/firefox.js for other ASR preferences. They must be defined there to enable roll-outs.
["discoverystream.config", {
title: "Configuration for the new pocket new tab",
value: JSON.stringify({
enabled: false,
// Set this to https://gist.githubusercontent.com/ScottDowne/164995d9535b4203846048bdee29d169/raw/0cf538411e6ee898eb116208d70842c62c8d52f1/spoc.json to test
layout_endpoint: "",
}),
}],
]); ]);
// Array of each feed's FEEDS_CONFIG factory and values to add to PREFS_CONFIG // Array of each feed's FEEDS_CONFIG factory and values to add to PREFS_CONFIG
@ -319,12 +306,6 @@ const FEEDS_DATA = [
title: "Handles AS Router messages, such as snippets and onboaridng", title: "Handles AS Router messages, such as snippets and onboaridng",
value: true, value: true,
}, },
{
name: "discoverystreamfeed",
factory: () => new DiscoveryStreamFeed(),
title: "Handles new pocket ui for the new tab page",
value: true,
},
]; ];
const FEEDS_CONFIG = new Map(); const FEEDS_CONFIG = new Map();

Просмотреть файл

@ -1,150 +0,0 @@
/* 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/. */
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
ChromeUtils.import("resource://gre/modules/Services.jsm");
const {actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
const {PersistentCache} = ChromeUtils.import("resource://activity-stream/lib/PersistentCache.jsm", {});
const CACHE_KEY = "discovery_stream";
const LAYOUT_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
const CONFIG_PREF_NAME = "browser.newtabpage.activity-stream.discoverystream.config";
this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
constructor() {
// Internal state for checking if we've intialized all our data
this.loaded = false;
// Persistent cache for remote endpoint data.
this.cache = new PersistentCache(CACHE_KEY, true);
// Internal in-memory cache for parsing json prefs.
this._prefCache = {};
}
get config() {
if (this._prefCache.config) {
return this._prefCache.config;
}
try {
this._prefCache.config = JSON.parse(Services.prefs.getStringPref(CONFIG_PREF_NAME, ""));
} catch (e) {
// istanbul ignore next
this._prefCache.config = {};
// istanbul ignore next
Cu.reportError(`Could not parse preference. Try resetting ${CONFIG_PREF_NAME} in about:config.`);
}
return this._prefCache.config;
}
setupPrefs() {
Services.prefs.addObserver(CONFIG_PREF_NAME, this);
// Send the initial state of the pref on our reducer
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_CONFIG_SETUP, data: this.config}));
}
uninitPrefs() {
Services.prefs.removeObserver(CONFIG_PREF_NAME, this);
// Reset in-memory cache
this._prefCache = {};
}
observe(aSubject, aTopic, aPrefName) {
if (aPrefName === CONFIG_PREF_NAME) {
this._prefCache.config = null;
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_CONFIG_CHANGE, data: this.config}));
}
}
async fetchLayout() {
const endpoint = this.config.layout_endpoint;
if (!endpoint) {
Cu.reportError("No endpoint configured for pocket, so could not fetch layout");
return null;
}
try {
const response = await fetch(endpoint, {credentials: "omit"});
if (!response.ok) {
// istanbul ignore next
throw new Error(`Stories endpoint returned unexpected status: ${response.status}`);
}
return response.json();
} catch (error) {
// istanbul ignore next
Cu.reportError(`Failed to fetch layout: ${error.message}`);
}
// istanbul ignore next
return null;
}
async loadCachedData() {
const cachedData = await this.cache.get() || {};
let {layout: layoutResponse} = cachedData;
if (!layoutResponse || !(Date.now() - layoutResponse._timestamp > LAYOUT_UPDATE_TIME)) {
layoutResponse = await this.fetchLayout();
if (layoutResponse && layoutResponse.layout) {
layoutResponse._timestamp = Date.now();
await this.cache.set("layout", layoutResponse);
} else {
Cu.reportError("No response for response.layout prop");
}
}
if (layoutResponse && layoutResponse.layout) {
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: layoutResponse.layout}));
}
}
async enable() {
await this.loadCachedData();
this.loaded = true;
}
async disable() {
// Clear cache
await this.cache.set("layout", {});
// Reset reducer
this.store.dispatch(ac.BroadcastToContent({type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: []}));
this.loaded = false;
}
async onPrefChange() {
// Load data from all endpoints if our config.enabled = true.
if (this.config.enabled) {
await this.enable();
}
// Clear state and relevant listeners if config.enabled = false.
if (this.loaded && !this.config.enabled) {
await this.disable();
}
}
async onAction(action) {
switch (action.type) {
case at.INIT:
// During the initialization of Firefox:
// 1. Set-up listeners and initialize the redux state for config;
this.setupPrefs();
// 2. If config.enabled is true, start loading data.
if (this.config.enabled) {
await this.enable();
}
break;
case at.DISCOVERY_STREAM_CONFIG_CHANGE:
// When the config pref changes, load or unload data as needed.
await this.onPrefChange();
break;
case at.UNINIT:
// When this feed is shutting down:
this.uninitPrefs();
break;
}
}
};
const EXPORTED_SYMBOLS = ["DiscoveryStreamFeed"];

Просмотреть файл

@ -283,38 +283,6 @@ class PlacesFeed {
_target.browser.ownerGlobal.gURLBar.search(`${data.label} `); _target.browser.ownerGlobal.gURLBar.search(`${data.label} `);
} }
handoffSearchToAwesomebar({_target, data, meta}) {
const urlBar = _target.browser.ownerGlobal.gURLBar;
if (!data.hiddenFocus) {
// Do a normal focus of awesomebar and reset the in content search (remove fake focus styles).
urlBar.focus();
this.store.dispatch(ac.OnlyToOneContent({type: at.SHOW_SEARCH}, meta.fromTarget));
return;
}
// Focus the awesomebar without the style changes.
urlBar.hiddenFocus();
const onKeydown = () => {
// Once the user starts typing, we want to hide the in content search box
// and show the focus styles on the awesomebar.
this.store.dispatch(ac.OnlyToOneContent({type: at.HIDE_SEARCH}, meta.fromTarget));
urlBar.removeHiddenFocus();
urlBar.removeEventListener("keydown", onKeydown);
};
const onDone = () => {
// When done, let's cleanup everything.
this.store.dispatch(ac.OnlyToOneContent({type: at.SHOW_SEARCH}, meta.fromTarget));
urlBar.removeHiddenFocus();
urlBar.removeEventListener("keydown", onKeydown);
urlBar.removeEventListener("mousedown", onDone);
urlBar.removeEventListener("blur", onDone);
};
urlBar.addEventListener("keydown", onKeydown);
urlBar.addEventListener("mousedown", onDone);
urlBar.addEventListener("blur", onDone);
}
onAction(action) { onAction(action) {
switch (action.type) { switch (action.type) {
case at.INIT: case at.INIT:
@ -355,9 +323,6 @@ class PlacesFeed {
case at.FILL_SEARCH_TERM: case at.FILL_SEARCH_TERM:
this.fillSearchTopSiteTerm(action); this.fillSearchTopSiteTerm(action);
break; break;
case at.HANDOFF_SEARCH_TO_AWESOMEBAR:
this.handoffSearchToAwesomebar(action);
break;
case at.OPEN_LINK: { case at.OPEN_LINK: {
this.openLink(action); this.openLink(action);
break; break;

Просмотреть файл

@ -112,6 +112,13 @@ this.TopStoriesFeed = class TopStoriesFeed {
this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.AlsoToPreloaded(action)); this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.AlsoToPreloaded(action));
} }
maybeDispatchLayoutUpdate(data, shouldBroadcast) {
if (data && data.length) {
const action = {type: at.CONTENT_LAYOUT, data};
this.store.dispatch(shouldBroadcast ? ac.BroadcastToContent(action) : ac.AlsoToPreloaded(action));
}
}
doContentUpdate(shouldBroadcast) { doContentUpdate(shouldBroadcast) {
let updateProps = {}; let updateProps = {};
if (this.stories) { if (this.stories) {
@ -174,6 +181,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
} }
const body = await response.json(); const body = await response.json();
this.maybeDispatchLayoutUpdate(body.layout);
this.updateSettings(body.settings); this.updateSettings(body.settings);
this.stories = this.rotate(this.transform(body.recommendations)); this.stories = this.rotate(this.transform(body.recommendations));
this.cleanUpTopRecImpressionPref(); this.cleanUpTopRecImpressionPref();
@ -194,7 +202,9 @@ this.TopStoriesFeed = class TopStoriesFeed {
async loadCachedData() { async loadCachedData() {
const data = await this.cache.get(); const data = await this.cache.get();
let stories = data.stories && data.stories.recommendations; let stories = data.stories && data.stories.recommendations;
let layout = data.stories && data.stories.layout;
let topics = data.topics && data.topics.topics; let topics = data.topics && data.topics.topics;
this.maybeDispatchLayoutUpdate(layout);
let affinities = data.domainAffinities; let affinities = data.domainAffinities;
if (this.personalized && affinities && affinities.scores) { if (this.personalized && affinities && affinities.scores) {

Просмотреть файл

@ -66,15 +66,5 @@ window.gActivityStreamPrerenderedState = {
"pocketCta": {}, "pocketCta": {},
"waitingForSpoc": true "waitingForSpoc": true
}, },
"DiscoveryStream": { "Layout": []
"config": {
"enabled": false,
"layout_endpoint": ""
},
"layout": []
},
"Search": {
"focus": false,
"hide": false
}
}; };

Просмотреть файл

@ -1,24 +1,9 @@
"use strict"; "use strict";
test_newtab({ test_newtab(function test_render_search() {
async before({pushPrefs}) {
await pushPrefs(["browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", false]);
},
test: function test_render_search() {
let search = content.document.getElementById("newtab-search-text"); let search = content.document.getElementById("newtab-search-text");
ok(search, "Got the search box"); ok(search, "Got the search box");
isnot(search.placeholder, "search_web_placeholder", "Search box is localized"); isnot(search.placeholder, "search_web_placeholder", "Search box is localized");
},
});
test_newtab({
async before({pushPrefs}) {
await pushPrefs(["browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", true]);
},
test: function test_render_search_handoff() {
let search = content.document.querySelector(".search-handoff-button");
ok(search, "Got the search handoff button");
},
}); });
test_newtab(function test_render_topsites() { test_newtab(function test_render_topsites() {

Просмотреть файл

@ -1,5 +1,5 @@
import {INITIAL_STATE, insertPinned, reducers} from "common/Reducers.jsm"; import {INITIAL_STATE, insertPinned, reducers} from "common/Reducers.jsm";
const {TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket, DiscoveryStream, Search} = reducers; const {TopSites, App, Snippets, Prefs, Dialog, Sections, Pocket, Layout} = reducers;
import {actionTypes as at} from "common/Actions.jsm"; import {actionTypes as at} from "common/Actions.jsm";
describe("Reducers", () => { describe("Reducers", () => {
@ -655,35 +655,13 @@ describe("Reducers", () => {
assert.equal(state.pocketCta.useCta, data.use_cta); assert.equal(state.pocketCta.useCta, data.use_cta);
}); });
}); });
describe("DiscoveryStream", () => { describe("Layout", () => {
it("should return INITIAL_STATE by default", () => { it("should return INITIAL_STATE by default", () => {
assert.equal(DiscoveryStream(undefined, {type: "some_action"}), INITIAL_STATE.DiscoveryStream); assert.equal(Layout(undefined, {type: "some_action"}), INITIAL_STATE.Layout);
}); });
it("should set layout data with DISCOVERY_STREAM_LAYOUT_UPDATE", () => { it("should set layout data with layout.type CONTENT_LAYOUT", () => {
const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_LAYOUT_UPDATE, data: ["test"]}); const state = Layout(undefined, {type: at.CONTENT_LAYOUT, data: ["test"]});
assert.equal(state.layout[0], "test"); assert.equal(state[0], "test");
});
it("should set config data with DISCOVERY_STREAM_CONFIG_CHANGE", () => {
const state = DiscoveryStream(undefined, {type: at.DISCOVERY_STREAM_CONFIG_CHANGE, data: {enabled: true}});
assert.deepEqual(state.config, {enabled: true});
});
});
describe("Search", () => {
it("should return INITIAL_STATE by default", () => {
assert.equal(Search(undefined, {type: "some_action"}), INITIAL_STATE.Search);
});
it("should set hide to true on HIDE_SEARCH", () => {
const nextState = Search(undefined, {type: "HIDE_SEARCH"});
assert.propertyVal(nextState, "hide", true);
});
it("should set focus to true on FOCUS_SEARCH", () => {
const nextState = Search(undefined, {type: "FOCUS_SEARCH"});
assert.propertyVal(nextState, "focus", true);
});
it("should set focus and hide to false on SHOW_SEARCH", () => {
const nextState = Search(undefined, {type: "SHOW_SEARCH"});
assert.propertyVal(nextState, "focus", false);
assert.propertyVal(nextState, "hide", false);
}); });
}); });
}); });

Просмотреть файл

@ -1,4 +1,4 @@
import {ASRouterAdminInner} from "content-src/components/ASRouterAdmin/ASRouterAdmin"; import {ASRouterAdmin} from "content-src/components/ASRouterAdmin/ASRouterAdmin";
import {GlobalOverrider} from "test/unit/utils"; import {GlobalOverrider} from "test/unit/utils";
import React from "react"; import React from "react";
import {shallow} from "enzyme"; import {shallow} from "enzyme";
@ -9,7 +9,6 @@ describe("ASRouterAdmin", () => {
let sendMessageStub; let sendMessageStub;
let addListenerStub; let addListenerStub;
let removeListenerStub; let removeListenerStub;
let wrapper;
let FAKE_PROVIDER_PREF = [{ let FAKE_PROVIDER_PREF = [{
enabled: true, enabled: true,
id: "snippets_local_testing", id: "snippets_local_testing",
@ -33,43 +32,37 @@ describe("ASRouterAdmin", () => {
globals.set("RPMSendAsyncMessage", sendMessageStub); globals.set("RPMSendAsyncMessage", sendMessageStub);
globals.set("RPMAddMessageListener", addListenerStub); globals.set("RPMAddMessageListener", addListenerStub);
globals.set("RPMRemoveMessageListener", removeListenerStub); globals.set("RPMRemoveMessageListener", removeListenerStub);
wrapper = shallow(<ASRouterAdminInner location={{routes: [""]}} />);
}); });
afterEach(() => { afterEach(() => {
sandbox.restore(); sandbox.restore();
globals.restore(); globals.restore();
}); });
it("should render ASRouterAdmin component", () => { it("should render ASRouterAdmin component", () => {
const wrapper = shallow(<ASRouterAdmin />);
assert.ok(wrapper.exists()); assert.ok(wrapper.exists());
}); });
it("should send ADMIN_CONNECT_STATE on mount", () => { it("should send ADMIN_CONNECT_STATE on mount", () => {
shallow(<ASRouterAdmin />);
assert.calledOnce(sendMessageStub); assert.calledOnce(sendMessageStub);
assert.propertyVal(sendMessageStub.firstCall.args[1], "type", "ADMIN_CONNECT_STATE"); assert.propertyVal(sendMessageStub.firstCall.args[1], "type", "ADMIN_CONNECT_STATE");
}); });
it("should set a listener on mount", () => { it("should set a listener on mount", () => {
const wrapper = shallow(<ASRouterAdmin />);
assert.calledOnce(addListenerStub); assert.calledOnce(addListenerStub);
assert.calledWithExactly(addListenerStub, sinon.match.string, wrapper.instance().onMessage); assert.calledWithExactly(addListenerStub, sinon.match.string, wrapper.instance().onMessage);
}); });
it("should remove listener on unmount", () => { it("should remove listener on unmount", () => {
const wrapper = shallow(<ASRouterAdmin />);
wrapper.unmount(); wrapper.unmount();
assert.calledOnce(removeListenerStub); assert.calledOnce(removeListenerStub);
}); });
describe("#getSection", () => {
it("should render a message provider section by default", () => {
assert.equal(wrapper.find("h2").at(1).text(), "Messages");
});
it("should render a targeting section for targeting route", () => {
wrapper = shallow(<ASRouterAdminInner location={{routes: ["targeting"]}} />);
assert.equal(wrapper.find("h2").at(0).text(), "Targeting Utilities");
});
it("should render a pocket section for pocket route", () => {
wrapper = shallow(<ASRouterAdminInner location={{routes: ["pocket"]}} Sections={[]} />);
assert.equal(wrapper.find("h2").at(0).text(), "Pocket");
});
});
describe("#render", () => { describe("#render", () => {
let wrapper;
beforeEach(() => { beforeEach(() => {
wrapper = shallow(<ASRouterAdmin />);
wrapper.setState({ wrapper.setState({
providerPrefs: [], providerPrefs: [],
providers: [], providers: [],

Просмотреть файл

@ -5,7 +5,7 @@ import {Search} from "content-src/components/Search/Search";
import {shallow} from "enzyme"; import {shallow} from "enzyme";
describe("<Base>", () => { describe("<Base>", () => {
let DEFAULT_PROPS = {store: {getState: () => {}}, App: {initialized: true}, Prefs: {values: {}}, Sections: [], DiscoveryStream: {config: {enabled: false}}, dispatch: () => {}}; let DEFAULT_PROPS = {store: {getState: () => {}}, App: {initialized: true}, Prefs: {values: {}}, Sections: [], dispatch: () => {}};
it("should render Base component", () => { it("should render Base component", () => {
const wrapper = shallow(<Base {...DEFAULT_PROPS} />); const wrapper = shallow(<Base {...DEFAULT_PROPS} />);
@ -27,7 +27,7 @@ describe("<Base>", () => {
}); });
describe("<BaseContent>", () => { describe("<BaseContent>", () => {
let DEFAULT_PROPS = {store: {getState: () => {}}, App: {initialized: true}, Prefs: {values: {}}, Sections: [], DiscoveryStream: {config: {enabled: false}}, dispatch: () => {}}; let DEFAULT_PROPS = {store: {getState: () => {}}, App: {initialized: true}, Prefs: {values: {}}, Sections: [], dispatch: () => {}};
it("should render an ErrorBoundary with a Search child", () => { it("should render an ErrorBoundary with a Search child", () => {
const searchEnabledProps = const searchEnabledProps =

Просмотреть файл

@ -73,34 +73,4 @@ describe("<Search>", () => {
assert.isUserEventAction(action); assert.isUserEventAction(action);
assert.propertyVal(action.data, "event", "SEARCH"); assert.propertyVal(action.data, "event", "SEARCH");
}); });
describe("Search Hand-off", () => {
it("should render a Search element when hand-off is enabled", () => {
const wrapper = shallowWithIntl(<Search {...DEFAULT_PROPS} handoffEnabled={true} />);
assert.ok(wrapper.exists());
assert.equal(wrapper.find(".search-handoff-button").length, 1);
});
it("should hand-off search when button is clicked with mouse", () => {
const dispatch = sinon.spy();
const wrapper = shallowWithIntl(<Search {...DEFAULT_PROPS} handoffEnabled={true} dispatch={dispatch} />);
wrapper.instance().onSearchHandoffClick({clientX: 101, clientY: 102});
assert.calledWith(dispatch, {
data: {hiddenFocus: true},
meta: {from: "ActivityStream:Content", skipLocal: true, to: "ActivityStream:Main"},
type: "HANDOFF_SEARCH_TO_AWESOMEBAR",
});
assert.calledWith(dispatch, {type: "FOCUS_SEARCH"});
});
it("should hand-off search when button is clicked with keyboard", () => {
const dispatch = sinon.spy();
const wrapper = shallowWithIntl(<Search {...DEFAULT_PROPS} handoffEnabled={true} dispatch={dispatch} />);
wrapper.instance().onSearchHandoffClick({clientX: 0, clientY: 0});
assert.calledWith(dispatch, {
data: {hiddenFocus: false},
meta: {from: "ActivityStream:Content", skipLocal: true, to: "ActivityStream:Main"},
type: "HANDOFF_SEARCH_TO_AWESOMEBAR",
});
assert.calledWith(dispatch, {type: "FOCUS_SEARCH"});
});
});
}); });

Просмотреть файл

@ -1,118 +0,0 @@
import {combineReducers, createStore} from "redux";
import {actionTypes as at} from "common/Actions.jsm";
import {DiscoveryStreamFeed} from "lib/DiscoveryStreamFeed.jsm";
import {reducers} from "common/Reducers.jsm";
const CONFIG_PREF_NAME = "browser.newtabpage.activity-stream.discoverystream.config";
describe("DiscoveryStreamFeed", () => {
let feed;
let sandbox;
let configPrefStub;
let fetchStub;
beforeEach(() => {
// Pref
sandbox = sinon.createSandbox();
configPrefStub = sandbox.stub(global.Services.prefs, "getStringPref")
.withArgs(CONFIG_PREF_NAME)
.returns(JSON.stringify({enabled: false, layout_endpoint: "foo.com"}));
// Fetch
fetchStub = sandbox.stub(global, "fetch");
// Feed
feed = new DiscoveryStreamFeed();
feed.store = createStore(combineReducers(reducers));
});
afterEach(() => {
sandbox.restore();
});
describe("#observe", () => {
it("should update state.DiscoveryStream.config when the pref changes", async () => {
configPrefStub.returns(JSON.stringify({enabled: true, layout_endpoint: "foo"}));
feed.observe(null, null, CONFIG_PREF_NAME);
assert.deepEqual(feed.store.getState().DiscoveryStream.config, {enabled: true, layout_endpoint: "foo"});
});
});
describe("#loadCachedData", () => {
it("should fetch data and populate the cache if it is empty", async () => {
const resp = {layout: ["foo", "bar"]};
const fakeCache = {};
sandbox.stub(feed.cache, "get").returns(Promise.resolve(fakeCache));
sandbox.stub(feed.cache, "set").returns(Promise.resolve());
fetchStub.resolves({ok: true, json: () => Promise.resolve(resp)});
await feed.loadCachedData();
assert.calledOnce(fetchStub);
assert.calledWith(feed.cache.set, "layout", resp);
});
});
describe("#onAction: INIT", () => {
it("should be .loaded=false before initialization", () => {
assert.isFalse(feed.loaded);
});
it("should load data, add pref observer, and set .loaded=true if config.enabled is true", async () => {
configPrefStub.returns(JSON.stringify({enabled: true}));
sandbox.stub(feed, "loadCachedData").returns(Promise.resolve());
sandbox.stub(global.Services.prefs, "addObserver");
await feed.onAction({type: at.INIT});
assert.calledOnce(feed.loadCachedData);
assert.calledWith(global.Services.prefs.addObserver, CONFIG_PREF_NAME, feed);
assert.isTrue(feed.loaded);
});
});
describe("#onAction: DISCOVERY_STREAM_CONFIG_CHANGE", () => {
it("should call this.loadCachedData if config.enabled changes to true ", async () => {
sandbox.stub(feed.cache, "set").returns(Promise.resolve());
// First initialize
await feed.onAction({type: at.INIT});
assert.isFalse(feed.loaded);
// force clear cached pref value
feed._prefCache = {};
configPrefStub.returns(JSON.stringify({enabled: true}));
sandbox.stub(feed, "loadCachedData").returns(Promise.resolve());
await feed.onAction({type: at.DISCOVERY_STREAM_CONFIG_CHANGE});
assert.calledOnce(feed.loadCachedData);
assert.isTrue(feed.loaded);
});
it("should call this.loadCachedData if config.enabled changes to true ", async () => {
sandbox.stub(feed.cache, "set").returns(Promise.resolve());
// force clear cached pref value
feed._prefCache = {};
configPrefStub.returns(JSON.stringify({enabled: true}));
await feed.onAction({type: at.INIT});
assert.isTrue(feed.loaded);
feed._prefCache = {};
configPrefStub.returns(JSON.stringify({enabled: false}));
sandbox.stub(feed, "loadCachedData").returns(Promise.resolve());
await feed.onAction({type: at.DISCOVERY_STREAM_CONFIG_CHANGE});
assert.notCalled(feed.loadCachedData);
assert.isFalse(feed.loaded);
});
});
describe("#onAction: UNINIT", () => {
it("should remove pref listeners", async () => {
sandbox.stub(global.Services.prefs, "removeObserver");
await feed.onAction({type: at.UNINIT});
assert.calledWith(global.Services.prefs.removeObserver, CONFIG_PREF_NAME, feed);
});
});
});

Просмотреть файл

@ -306,95 +306,6 @@ describe("PlacesFeed", () => {
await feed.saveToPocket(action.data.site, action._target.browser); await feed.saveToPocket(action.data.site, action._target.browser);
assert.notCalled(feed.store.dispatch); assert.notCalled(feed.store.dispatch);
}); });
it("should call handoffSearchToAwesomebar on HANDOFF_SEARCH_TO_AWESOMEBAR", () => {
const action = {
type: at.HANDOFF_SEARCH_TO_AWESOMEBAR,
data: {hiddenFocus: false},
meta: {fromTarget: {}},
_target: {browser: {ownerGlobal: {gURLBar: {focus: () => {}}}}},
};
sinon.stub(feed, "handoffSearchToAwesomebar");
feed.onAction(action);
assert.calledWith(feed.handoffSearchToAwesomebar, action);
});
});
describe("handoffSearchToAwesomebar", () => {
let fakeUrlBar;
let listeners;
beforeEach(() => {
fakeUrlBar = {
focus: sinon.spy(),
hiddenFocus: sinon.spy(),
removeHiddenFocus: sinon.spy(),
addEventListener: (ev, cb) => {
listeners[ev] = cb;
},
removeEventListener: sinon.spy(),
};
listeners = {};
});
it("should properly handle hiddenFocus=false", () => {
feed.handoffSearchToAwesomebar({
_target: {browser: {ownerGlobal: {gURLBar: fakeUrlBar}}},
data: {hiddenFocus: false},
meta: {fromTarget: {}},
});
assert.calledOnce(fakeUrlBar.focus);
assert.notCalled(fakeUrlBar.hiddenFocus);
assert.calledOnce(feed.store.dispatch);
assert.calledWith(feed.store.dispatch, {
meta: {
from: "ActivityStream:Main",
skipMain: true,
to: "ActivityStream:Content",
toTarget: {},
},
type: "SHOW_SEARCH",
});
});
it("should properly handle hiddenFocus=true", () => {
feed.handoffSearchToAwesomebar({
_target: {browser: {ownerGlobal: {gURLBar: fakeUrlBar}}},
data: {hiddenFocus: true},
meta: {fromTarget: {}},
});
assert.calledOnce(fakeUrlBar.hiddenFocus);
assert.notCalled(fakeUrlBar.focus);
assert.notCalled(feed.store.dispatch);
// Now call keydown listener.
feed.store.dispatch.resetHistory();
listeners.keydown();
assert.calledOnce(fakeUrlBar.removeHiddenFocus);
assert.calledOnce(feed.store.dispatch);
assert.calledWith(feed.store.dispatch, {
meta: {
from: "ActivityStream:Main",
skipMain: true,
to: "ActivityStream:Content",
toTarget: {},
},
type: "HIDE_SEARCH",
});
// And then call blur listener.
fakeUrlBar.removeHiddenFocus.resetHistory();
feed.store.dispatch.resetHistory();
listeners.blur();
assert.calledOnce(fakeUrlBar.removeHiddenFocus);
assert.calledOnce(feed.store.dispatch);
assert.calledWith(feed.store.dispatch, {
meta: {
from: "ActivityStream:Main",
skipMain: true,
to: "ActivityStream:Content",
toTarget: {},
},
type: "SHOW_SEARCH",
});
});
}); });
describe("#observe", () => { describe("#observe", () => {

Просмотреть файл

@ -1460,4 +1460,40 @@ describe("Top Stories Feed", () => {
assert.calledOnce(instance.uninit); assert.calledOnce(instance.uninit);
assert.calledOnce(instance.init); assert.calledOnce(instance.init);
}); });
describe("#layout", () => {
it("should call maybeDispatchLayoutUpdate from fetchStories", async () => {
instance.stories_endpoint = "stories-endpoint";
let fetchStub = globals.sandbox.stub();
const response = {
"layout": [1, 2],
};
globals.set("fetch", fetchStub);
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
sinon.spy(instance, "maybeDispatchLayoutUpdate");
await instance.fetchStories();
assert.calledOnce(instance.maybeDispatchLayoutUpdate);
assert.calledWith(instance.maybeDispatchLayoutUpdate, [1, 2]);
});
it("should call maybeDispatchLayoutUpdate from loadCachedData", async () => {
sinon.spy(instance, "maybeDispatchLayoutUpdate");
instance.cache.get = () => ({stories: {layout: [2, 3]}});
await instance.loadCachedData();
assert.calledOnce(instance.maybeDispatchLayoutUpdate);
assert.calledWith(instance.maybeDispatchLayoutUpdate, [2, 3]);
});
it("should call dispatch from maybeDispatchLayoutUpdate with available data", () => {
instance.maybeDispatchLayoutUpdate([1, 2]);
assert.calledOnce(instance.store.dispatch);
const [action] = instance.store.dispatch.firstCall.args;
assert.equal(action.type, "CONTENT_LAYOUT");
assert.deepEqual(action.data, [1, 2]);
});
it("should not call dispatch from maybeDispatchLayoutUpdate with no available data", () => {
instance.maybeDispatchLayoutUpdate([]);
assert.notCalled(instance.store.dispatch);
});
});
}); });

Просмотреть файл

@ -1,7 +1,5 @@
[DEFAULT] [DEFAULT]
prefs = prefs = dom.sidebar.enabled=true
browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
dom.sidebar.enabled=true
support-files = support-files =
426329.xml 426329.xml
483086-1.xml 483086-1.xml

Просмотреть файл

@ -1,8 +1,6 @@
[DEFAULT] [DEFAULT]
support-files = support-files =
head.js head.js
prefs =
browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar=false
[browser_BrowserErrorReporter.js] [browser_BrowserErrorReporter.js]
skip-if = (verify && !debug && (os == 'mac' || os == 'win')) skip-if = (verify && !debug && (os == 'mac' || os == 'win'))