Bug 1522752 - Add Snippets header, article sources and bug fixes to Activity Stream r=k88hudson

Differential Revision: https://phabricator.services.mozilla.com/D17599

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ed Lee 2019-01-25 15:37:38 +00:00
parent 7b94b61d51
commit b7463bed23
50 changed files with 1331 additions and 453 deletions

View File

@ -41,7 +41,7 @@
}
}
[lwt-newtab-brighttext] {
[lwt-newtab-brighttext]:not(.force-light-theme) {
.secondary {
background-color: $grey-10-10;

View File

@ -0,0 +1,2 @@
// lifted from https://gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
export const ConditionalWrapper = ({condition, wrap, children}) => (condition ? wrap(children) : children);

View File

@ -23,7 +23,7 @@
text-decoration: underline;
}
[lwt-newtab-brighttext] & {
[lwt-newtab-brighttext]:not(.force-light-theme) & {
font-weight: bold;
}
}

View File

@ -1,4 +1,5 @@
import {Button} from "../../components/Button/Button";
import {ConditionalWrapper} from "../../components/ConditionalWrapper/ConditionalWrapper";
import React from "react";
import {RichText} from "../../components/RichText/RichText";
import {safeURI} from "../../template-utils";
@ -67,22 +68,64 @@ export class SimpleSnippet extends React.PureComponent {
sendClick={props.sendClick} />);
}
wrapSectionHeader(url) {
return function(children) {
return <a href={url}>{children}</a>;
};
}
wrapSnippetContent(children) {
return <div className="innerContentWrapper">{children}</div>;
}
renderSectionHeader() {
const {props} = this;
// an icon and text must be specified to render the section header
if (props.content.section_title_icon && props.content.section_title_text) {
const sectionTitleIcon = safeURI(props.content.section_title_icon);
const sectionTitleURL = props.content.section_title_url;
return (
<div className="section-header">
<h3 className="section-title">
<ConditionalWrapper condition={sectionTitleURL} wrap={this.wrapSectionHeader(sectionTitleURL)}>
<span className="icon icon-small-spacer" style={{backgroundImage: `url("${sectionTitleIcon}")`}} />
<span className="section-title-text">{props.content.section_title_text}</span>
</ConditionalWrapper>
</h3>
</div>
);
}
return null;
}
render() {
const {props} = this;
const sectionHeader = this.renderSectionHeader();
let className = "SimpleSnippet";
if (props.className) {
className += ` ${props.className}`;
}
if (props.content.tall) {
className += " tall";
}
if (sectionHeader) {
className += " has-section-header";
}
return (<SnippetBase {...props} className={className} textStyle={this.props.textStyle}>
<img src={safeURI(props.content.icon) || DEFAULT_ICON_PATH} className="icon" />
<div>
{this.renderTitle()} <p className="body">{this.renderText()}</p>
{this.props.extraContent}
</div>
{<div>{this.renderButton()}</div>}
{sectionHeader}
<ConditionalWrapper condition={sectionHeader} wrap={this.wrapSnippetContent}>
<img src={safeURI(props.content.icon) || DEFAULT_ICON_PATH} className="icon" />
<div>
{this.renderTitle()} <p className="body">{this.renderText()}</p>
{this.props.extraContent}
</div>
{<div>{this.renderButton()}</div>}
</ConditionalWrapper>
</SnippetBase>);
}
}

View File

@ -97,6 +97,20 @@
"description": "Additional parameters for link action, example which specific menu the button should open"
}
}
},
"section_title_icon": {
"type": "string",
"description": "Section title icon. 16x16px. SVG or PNG preferred. section_title_text must also be specified to display."
},
"section_title_text": {
"type": "string",
"description": "Section title text. section_title_icon must also be specified to display."
},
"section_title_url": {
"allOf": [
{"$ref": "#/definitions/link_url"},
{"description": "A url, section_title_text links to this"}
]
}
},
"additionalProperties": false,
@ -105,6 +119,7 @@
"button_action": ["button_label"],
"button_url": ["button_label"],
"button_color": ["button_label"],
"button_background_color": ["button_label"]
"button_background_color": ["button_label"],
"section_title_url": ["section_title_text"]
}
}

View File

@ -1,3 +1,6 @@
$section-header-height: 30px;
$icon-width: 54px; // width of primary icon + margin
.SimpleSnippet {
&.tall {
padding: 27px 0;
@ -88,4 +91,45 @@
.icon {
align-self: flex-start;
}
&.has-section-header .innerWrapper {
// account for section header being 100% width
flex-wrap: wrap;
padding-top: 7px;
}
// wrapper div added if section-header is displayed that allows icon/text/button
// to squish instead of wrapping. this is effectively replicating layout behavior
// when section-header is *not* present.
.innerContentWrapper {
align-items: center;
display: flex;
}
.section-header {
flex: 0 0 100%;
margin-bottom: 10px;
}
.section-title {
// color should match that of 'Recommended by Pocket' and 'Highlights' in newtab page
color: var(--newtab-section-header-text-color);
display: inline-block;
font-size: 13px;
font-weight: bold;
margin: 0;
a {
color: var(--newtab-section-header-text-color);
font-weight: inherit;
text-decoration: none;
}
.icon {
height: 16px;
margin-inline-end: 6px;
margin-top: -2px;
width: 16px;
}
}
}

View File

@ -92,7 +92,7 @@
background: $yellow-50;
padding: 2px 5px;
[lwt-newtab-brighttext] & {
[lwt-newtab-brighttext]:not(.force-light-theme) & {
color: $black;
}
}

View File

@ -136,6 +136,15 @@ export class BaseContent extends React.PureComponent {
this.props.dispatch(ac.UserEvent({event: "OPEN_NEWTAB_PREFS"}));
}
disableDarkTheme() {
// Dark themes are not supported in discovery stream view
// Add force-light-theme class to body tag to disable dark mode. See Bug 1519764
const bodyClassNames = global.document.body.classList;
if (!bodyClassNames.contains("force-light-theme")) {
bodyClassNames.add("force-light-theme");
}
}
render() {
const {props} = this;
const {App} = props;
@ -147,6 +156,10 @@ export class BaseContent extends React.PureComponent {
const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
if (isDiscoveryStream) {
this.disableDarkTheme();
}
const outerClassName = [
"outer-wrapper",
shouldBeFixedToTop && "fixed-to-top",
@ -171,7 +184,10 @@ export class BaseContent extends React.PureComponent {
<ManualMigration />
</div>
}
{isDiscoveryStream ? <DiscoveryStreamBase /> : <Sections />}
{isDiscoveryStream ? (
<ErrorBoundary className="borderless-error">
<DiscoveryStreamBase />
</ErrorBoundary>) : <Sections />}
<PrefsButton onClick={this.openPreferences} />
</div>
<ConfirmDialog />

View File

@ -19,6 +19,11 @@
}
}
.force-light-theme {
--newtab-background-color: #{$grey-10} !important; // sass-lint:disable-line no-important
--newtab-text-primary-color: #{$grey-90} !important; // sass-lint:disable-line no-important
}
main {
margin: auto;
// Offset the snippets container so things at the bottom of the page are still

View File

@ -71,7 +71,7 @@
overflow: hidden;
position: relative;
[lwt-newtab-brighttext] & {
[lwt-newtab-brighttext]:not(.force-light-theme) & {
background-color: $grey-60;
}

View File

@ -113,7 +113,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
});
}
renderComponent(component) {
renderComponent(component, embedWidth) {
let rows;
const {spocs} = this.props.DiscoveryStream;
@ -157,6 +157,8 @@ export class _DiscoveryStreamBase extends React.PureComponent {
return (
<ImpressionStats rows={rows} dispatch={this.props.dispatch} source={component.type}>
<Hero
subComponentType={embedWidth >= 9 ? `cards` : `list`}
feed={component.feed}
title={component.header && component.header.title}
data={component.data}
border={component.properties.border}
@ -173,6 +175,8 @@ export class _DiscoveryStreamBase extends React.PureComponent {
<ImpressionStats rows={rows} dispatch={this.props.dispatch} source={component.type}>
<List
feed={component.feed}
fullWidth={component.properties.full_width}
hasBorders={component.properties.border === "border"}
hasImages={component.properties.has_images}
hasNumbers={component.properties.has_numbers}
items={component.properties.items}
@ -203,7 +207,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
{row.components.map((component, componentIndex) => {
styles[rowIndex] = [...styles[rowIndex] || [], component.styles];
return (<div key={`component-${componentIndex}`}>
{this.renderComponent(component)}
{this.renderComponent(component, row.width)}
</div>);
})}
</div>

View File

@ -31,7 +31,7 @@ export class DSCard extends React.PureComponent {
</div>
<div className="meta">
<header className="title">{this.props.title}</header>
<p className="excerpt">{this.props.excerpt}</p>
{this.props.excerpt && <p className="excerpt">{this.props.excerpt}</p>}
{this.props.context ? (
<p className="context">{this.props.context}</p>
) : (

View File

@ -1,6 +1,8 @@
import {actionCreators as ac} from "common/Actions.jsm";
import {DSCard} from "../DSCard/DSCard.jsx";
import {List} from "../List/List.jsx";
import React from "react";
import {truncateText} from "content-src/lib/truncate-text";
export class Hero extends React.PureComponent {
constructor(props) {
@ -36,7 +38,6 @@ export class Hero extends React.PureComponent {
let [heroRec, ...otherRecs] = data.recommendations.slice(0, this.props.items);
this.heroRec = heroRec;
let truncateText = (text, cap) => `${text.substring(0, cap)}${text.length > cap ? `...` : ``}`;
// Note that `{index + 1}` is necessary below for telemetry since we treat heroRec as index 0.
let cards = otherRecs.map((rec, index) => (
@ -49,10 +50,20 @@ export class Hero extends React.PureComponent {
index={index + 1}
type={this.props.type}
dispatch={this.props.dispatch}
context={truncateText(rec.context || "", 22)}
source={truncateText(`TODO: SOURCE`, 22)} />
context={truncateText(rec.context, 22)}
source={truncateText(rec.domain, 22)} />
));
let list = (
<List
recStartingPoint={1}
feed={this.props.feed}
hasImages={true}
hasBorders={this.props.border === `border`}
items={this.props.items}
type={`Hero`} />
);
return (
<div>
<div className="ds-header">{this.props.title}</div>
@ -67,12 +78,12 @@ export class Hero extends React.PureComponent {
{heroRec.context ? (
<p className="context">{truncateText(heroRec.context, 22)}</p>
) : (
<p>{truncateText(`TODO: SOURCE`, 22)}</p>
<p>{truncateText(heroRec.domain, 22)}</p>
)}
</div>
</a>
<div className="cards">
{ cards }
<div className={`${this.props.subComponentType}`}>
{ this.props.subComponentType === `cards` ? cards : list }
</div>
</div>
</div>

View File

@ -1,8 +1,24 @@
$card-header-in-hero-font-size: 13;
$card-header-in-hero-line-height: 20;
.ds-hero {
.img {
@include image-as-background;
}
header {
font-weight: 600;
}
p {
line-height: 1.538;
}
.ds-list {
border-top: 0;
padding-top: 0;
}
.ds-card {
border: 0;
@ -14,6 +30,17 @@
.meta {
padding: 0;
.title {
// show only 2 lines of copy
@include limit-visibile-lines(2, $card-header-in-hero-line-height, $card-header-in-hero-font-size);
font-size: $card-header-in-hero-font-size * 1px;
line-height: $card-header-in-hero-line-height * 1px;
}
}
.img-wrapper {
margin: 0 0 12px;
}
}
@ -29,6 +56,11 @@
border-top: $border-secondary;
border-bottom: $border-secondary;
@at-root .ds-hero-no-border .wrapper {
border-top: 0;
border-bottom: 0;
}
&:hover .meta header {
color: $blue-60;
}
@ -72,6 +104,17 @@
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 24px;
.img-wrapper {
margin: 0;
grid-column: 2;
grid-row: 1;
}
.meta {
grid-column: 1;
grid-row: 1;
}
.img {
height: 0;
padding-top: 100%; // 1:1 aspect ratio
@ -97,6 +140,7 @@
.img-wrapper {
width: 67%;
margin: 0;
}
.img {
@ -114,6 +158,7 @@
p {
font-size: 15px;
line-height: 1.6;
}
}
}

View File

@ -1,6 +1,7 @@
import {actionCreators as ac} from "common/Actions.jsm";
import {connect} from "react-redux";
import React from "react";
import {truncateText} from "content-src/lib/truncate-text";
/**
* @note exported for testing only
@ -39,6 +40,7 @@ export class ListItem extends React.PureComponent {
{this.props.title}
</b>
</div>
{this.props.excerpt && <div className="ds-list-item-excerpt">{truncateText(this.props.excerpt, 90)}</div>}
<div className="ds-list-item-info">{this.props.domain}</div>
</div>
<div className="ds-list-image" style={{backgroundImage: `url(${this.props.image_src})`}} />
@ -60,25 +62,29 @@ export function _List(props) {
const recs = feed.data.recommendations;
let recMarkup = recs.slice(0, props.items).map((rec, index) => (
let recMarkup = recs.slice(props.recStartingPoint, props.items).map((rec, index) => (
<ListItem {...rec} key={`ds-list-item-${index}`} index={index} type={props.type} dispatch={props.dispatch} />)
);
const listStyles = [
"ds-list",
props.fullWidth ? "ds-list-full-width" : "",
props.hasBorders ? "ds-list-borders" : "",
props.hasImages ? "ds-list-images" : "",
props.hasNumbers ? "ds-list-numbers" : "",
];
return (
<div>
{props.header && props.header.title ? <div className="ds-header">{props.header.title}</div> : null }
<hr className="ds-list-border" />
<ul className={listStyles.join(" ")}>{recMarkup}</ul>
</div>
);
}
_List.defaultProps = {
recStartingPoint: 0, // Index of recommendations to start displaying from
fullWidth: false, // Display items taking up the whole column
hasBorders: false, // Display lines separating each item
hasImages: false, // Display images for each item
hasNumbers: false, // Display numbers for each item
items: 6, // Number of stories to display. TODO: get from endpoint

View File

@ -1,17 +1,44 @@
// Type sizes
$item-font-size: 13;
$item-image-size: 72px;
$item-line-height: 20;
.ds-list-border {
border: 0;
border-top: $border-secondary;
// XXX this is gross, and attaches the bottom-border to the item above.
// Ideally, we'd attach the top-border to the item that needs it.
// Unfortunately the border needs to go _above_ the row gap as currently
// set up, which means that some refactoring will be required to do this.
@mixin bottom-border-except-last-grid-row($columns) {
.ds-list-item:not(:nth-last-child(-n+#{$columns})) {
border-bottom: $border-secondary;
margin-bottom: -1px; // cancel out the pixel we used for the border
padding-top: 1px;
padding-bottom: 2px;
}
}
// Instead of using margin, we need to use these to override stuff that comes
// by default from <hr>.
margin-block-start: 8px;
margin-block-end: 8px;
@mixin set-item-sizes($font-size, $line-height, $image-size) {
.ds-list-item {
// XXX see if we really want absolute units, maybe hoist somewhere central?
font-size: $font-size * 1px;
line-height: $line-height * 1px;
}
.ds-list-item-excerpt {
@include limit-visibile-lines(2, $line-height, $font-size);
}
.ds-list-item-info {
@include limit-visibile-lines(1, $line-height, $font-size);
}
.ds-list-item-title {
@include limit-visibile-lines(2, $line-height, $font-size);
}
.ds-list-image {
min-width: $image-size;
width: $image-size;
}
}
.ds-list {
@ -23,22 +50,35 @@ $item-line-height: 20;
// regression detection?
padding-inline-start: 0;
// "2/3 width layout"
.ds-column-5 &,
.ds-column-6 &,
.ds-column-7 &,
.ds-column-8 & {
grid-template-columns: repeat(2, 1fr);
grid-row-gap: 24px;
&:not(.ds-list-full-width) {
@include set-item-sizes($item-font-size, $item-line-height, $item-image-size);
// "2/3 width layout"
.ds-column-5 &,
.ds-column-6 &,
.ds-column-7 &,
.ds-column-8 & {
grid-template-columns: repeat(2, 1fr);
}
// "Full width layout"
.ds-column-9 &,
.ds-column-10 &,
.ds-column-11 &,
.ds-column-12 & {
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 18px;
}
.ds-list-item-excerpt {
display: none;
}
}
// "Full width layout"
.ds-column-9 &,
.ds-column-10 &,
.ds-column-11 &,
.ds-column-12 & {
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 18px;
&:not(.ds-list-images) {
.ds-list-image {
display: none;
}
}
a {
@ -47,12 +87,6 @@ $item-line-height: 20;
}
}
.ds-list-images {
.ds-list-item .ds-list-image {
display: block;
}
}
.ds-list-numbers {
$counter-whitespace: ($item-line-height - $item-font-size) * 1px;
$counter-size: ($item-font-size) * 2px + $counter-whitespace;
@ -90,22 +124,42 @@ $item-line-height: 20;
}
}
// XXX this is gross, and attaches the bottom-border to the item above.
// Ideally, we'd attach the top-border to the item that needs it.
// Unfortunately the border needs to go _above_ the row gap as currently
// set up, which means that some refactoring will be required to do this.
.ds-list-item:nth-child(-n+3) { // all but the last three items
border-bottom: $border-secondary;
margin-bottom: -1px; // cancel out the pixel we used for the border
.ds-list-borders {
border-top: $border-secondary;
padding-top: $item-line-height * 1px;
padding-bottom: 2px;
&.ds-list-full-width,
.ds-column-1 &,
.ds-column-2 &,
.ds-column-3 &,
.ds-column-4 & {
@include bottom-border-except-last-grid-row(1);
}
&:not(.ds-list-full-width) {
// "2/3 width layout"
.ds-column-5 &,
.ds-column-6 &,
.ds-column-7 &,
.ds-column-8 & {
@include bottom-border-except-last-grid-row(2);
}
// "Full width layout"
.ds-column-9 &,
.ds-column-10 &,
.ds-column-11 &,
.ds-column-12 & {
@include bottom-border-except-last-grid-row(3);
}
}
}
.ds-list-full-width {
@include set-item-sizes(17, 24, $item-image-size * 2);
}
.ds-list-item {
// XXX see if we really want absolute units, maybe hoist somewhere central?
line-height: $item-line-height * 1px;
font-size: $item-font-size * 1px;
// reset some stuff from <li>. Should maybe be hoisted when we have better
// regression detection?
display: block;
@ -120,18 +174,17 @@ $item-line-height: 20;
justify-content: space-between;
}
.ds-list-item-excerpt {
color: var(--newtab-text-secondary-color);
margin-bottom: 8px;
}
.ds-list-item-info {
@include limit-visibile-lines(1, $item-line-height, $item-font-size);
color: $grey-50;
overflow: hidden;
color: var(--newtab-text-secondary-color);
text-overflow: ellipsis;
}
.ds-list-item-title {
@include limit-visibile-lines(2, $item-line-height, $item-font-size);
margin-bottom: 8px;
}
@ -141,15 +194,10 @@ $item-line-height: 20;
}
.ds-list-image {
$image-size: 72px;
@include image-as-background;
display: none;
height: $image-size;
height: $item-image-size;
margin-inline-start: $item-font-size * 1px;
min-height: $image-size;
min-width: $image-size;
width: $image-size;
min-height: $item-image-size;
}
&:hover {

View File

@ -10,6 +10,10 @@
justify-items: center;
line-height: $error-fallback-line-height;
&.borderless-error {
box-shadow: none;
}
a {
color: var(--newtab-text-conditional-color);
text-decoration: underline;

View File

@ -146,7 +146,7 @@ export class _Search extends React.PureComponent {
tabIndex="-1"
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-editable" tabIndex="-1" aria-hidden="true" contentEditable="" onDrop={this.onSearchHandoffDrop} onPaste={this.onSearchHandoffPaste} />
<input type="search" className="fake-editable" tabIndex="-1" aria-hidden="true" onDrop={this.onSearchHandoffDrop} onPaste={this.onSearchHandoffPaste} />
<div className="fake-caret" />
</button>
{/*

View File

@ -181,6 +181,8 @@ $glyph-forward: url('chrome://browser/skin/forward.svg');
.fake-editable {
color: transparent;
height: 100%;
opacity: 0;
position: absolute;
top: 0;
left: 0;

View File

@ -0,0 +1,3 @@
export function truncateText(text = "", cap) {
return text.substring(0, cap).trim() + (text.length > cap ? "…" : "");
}

View File

@ -78,7 +78,7 @@ body {
--newtab-snippets-background-color: #{$white};
--newtab-snippets-hairline-color: transparent;
&[lwt-newtab-brighttext] {
&[lwt-newtab-brighttext]:not(.force-light-theme) {
// General styles
--newtab-background-color: #{$grey-80};
--newtab-border-primary-color: #{$grey-10-80};

View File

@ -4,7 +4,7 @@ $os-infopanel-arrow-height: 10px;
$os-infopanel-arrow-offset-end: 7px;
$os-infopanel-arrow-width: 18px;
[lwt-newtab-brighttext] {
[lwt-newtab-brighttext]:not(.force-light-theme) {
-moz-osx-font-smoothing: grayscale;
}

View File

@ -68,7 +68,7 @@ body {
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
body[lwt-newtab-brighttext]:not(.force-light-theme) {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
--newtab-border-secondary-color: rgba(249, 249, 250, 0.1);
@ -336,6 +336,10 @@ input[type='text'], input[type='search'] {
.outer-wrapper a {
color: var(--newtab-link-primary-color); }
.force-light-theme {
--newtab-background-color: #F9F9FA !important;
--newtab-text-primary-color: #0C0C0D !important; }
main {
margin: auto;
padding-bottom: 68px;
@ -399,6 +403,8 @@ main {
justify-content: center;
justify-items: center;
line-height: 1.5; }
.as-error-fallback.borderless-error {
box-shadow: none; }
.as-error-fallback a {
color: var(--newtab-text-conditional-color);
text-decoration: underline; }
@ -1081,6 +1087,8 @@ main {
caret-color: transparent; }
.search-handoff-button .fake-editable {
color: transparent;
height: 100%;
opacity: 0;
position: absolute;
top: 0;
left: 0;
@ -1343,7 +1351,7 @@ main {
height: 122px;
overflow: hidden;
position: relative; }
[lwt-newtab-brighttext] .card-outer .card-preview-image-outer {
[lwt-newtab-brighttext]:not(.force-light-theme) .card-outer .card-preview-image-outer {
background-color: #4A4A4F; }
.card-outer .card-preview-image-outer::after {
border-bottom: 1px solid var(--newtab-card-hairline-color);
@ -1682,7 +1690,7 @@ main {
.asrouter-admin .message-item.current .message-id span {
background: #FFE900;
padding: 2px 5px; }
[lwt-newtab-brighttext] .asrouter-admin .message-item.current .message-id span {
[lwt-newtab-brighttext]:not(.force-light-theme) .asrouter-admin .message-item.current .message-id span {
color: #000; }
.asrouter-admin .message-item.blocked .message-id,
.asrouter-admin .message-item.blocked .message-summary {
@ -1855,6 +1863,16 @@ main {
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box; }
.ds-hero header {
font-weight: 600; }
.ds-hero p {
line-height: 1.538; }
.ds-hero .ds-list {
border-top: 0;
padding-top: 0; }
.ds-hero .ds-card {
border: 0; }
.ds-hero .ds-card:hover {
@ -1863,6 +1881,13 @@ main {
border-radius: 0; }
.ds-hero .ds-card .meta {
padding: 0; }
.ds-hero .ds-card .meta .title {
max-height: 3.07692em;
overflow: hidden;
font-size: 13px;
line-height: 20px; }
.ds-hero .ds-card .img-wrapper {
margin: 0 0 12px; }
.ds-hero .img-wrapper {
margin: 0 0 12px; }
@ -1873,6 +1898,9 @@ main {
padding: 20px 0;
border-top: 1px solid var(--newtab-border-secondary-color);
border-bottom: 1px solid var(--newtab-border-secondary-color); }
.ds-hero-no-border .wrapper {
border-top: 0;
border-bottom: 0; }
.ds-hero .wrapper:hover .meta header {
color: #0060DF; }
.ds-hero .wrapper:active .meta header {
@ -1897,6 +1925,19 @@ main {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 24px; }
.ds-column-5 .ds-hero .wrapper .img-wrapper,
.ds-column-6 .ds-hero .wrapper .img-wrapper,
.ds-column-7 .ds-hero .wrapper .img-wrapper,
.ds-column-8 .ds-hero .wrapper .img-wrapper {
margin: 0;
grid-column: 2;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .meta,
.ds-column-6 .ds-hero .wrapper .meta,
.ds-column-7 .ds-hero .wrapper .meta,
.ds-column-8 .ds-hero .wrapper .meta {
grid-column: 1;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .img,
.ds-column-6 .ds-hero .wrapper .img,
.ds-column-7 .ds-hero .wrapper .img,
@ -1923,7 +1964,8 @@ main {
.ds-column-10 .ds-hero .wrapper .img-wrapper,
.ds-column-11 .ds-hero .wrapper .img-wrapper,
.ds-column-12 .ds-hero .wrapper .img-wrapper {
width: 67%; }
width: 67%;
margin: 0; }
.ds-column-9 .ds-hero .wrapper .img,
.ds-column-10 .ds-hero .wrapper .img,
.ds-column-11 .ds-hero .wrapper .img,
@ -1945,7 +1987,8 @@ main {
.ds-column-10 .ds-hero .wrapper .meta p,
.ds-column-11 .ds-hero .wrapper .meta p,
.ds-column-12 .ds-hero .wrapper .meta p {
font-size: 15px; }
font-size: 15px;
line-height: 1.6; }
.ds-column-9 .ds-hero .cards,
.ds-column-10 .ds-hero .cards,
@ -1960,36 +2003,44 @@ main {
height: 0;
border-top: 1px solid var(--newtab-border-secondary-color); }
.ds-list-border {
border: 0;
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 1px;
margin-block-start: 8px;
margin-block-end: 8px; }
.ds-list {
display: grid;
grid-row-gap: 24px;
grid-column-gap: 24px;
padding-inline-start: 0; }
.ds-column-5 .ds-list,
.ds-column-6 .ds-list,
.ds-column-7 .ds-list,
.ds-column-8 .ds-list {
grid-template-columns: repeat(2, 1fr);
grid-row-gap: 24px; }
.ds-column-9 .ds-list,
.ds-column-10 .ds-list,
.ds-column-11 .ds-list,
.ds-column-12 .ds-list {
.ds-list:not(.ds-list-full-width) .ds-list-item {
font-size: 13px;
line-height: 20px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-image {
min-width: 72px;
width: 72px; }
.ds-column-5 .ds-list:not(.ds-list-full-width),
.ds-column-6 .ds-list:not(.ds-list-full-width),
.ds-column-7 .ds-list:not(.ds-list-full-width),
.ds-column-8 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(2, 1fr); }
.ds-column-9 .ds-list:not(.ds-list-full-width),
.ds-column-10 .ds-list:not(.ds-list-full-width),
.ds-column-11 .ds-list:not(.ds-list-full-width),
.ds-column-12 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 18px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
display: none; }
.ds-list:not(.ds-list-images) .ds-list-image {
display: none; }
.ds-list a {
color: #0C0C0D; }
.ds-list-images .ds-list-item .ds-list-image {
display: block; }
.ds-list-numbers .ds-list-item {
counter-increment: list; }
@ -2013,14 +2064,53 @@ main {
.ds-list-numbers .ds-list-item-link:active::before {
background-color: #003EAA; }
.ds-list-item:nth-child(-n+3) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-borders {
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 20px; }
.ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-full-width .ds-list-item {
font-size: 17px;
line-height: 24px; }
.ds-list-full-width .ds-list-item-excerpt {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-info {
max-height: 1.41176em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-title {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-image {
min-width: 144px;
width: 144px; }
.ds-list-item {
line-height: 20px;
font-size: 13px;
display: block;
text-align: start; }
.ds-list-item .ds-list-item-link {
@ -2028,15 +2118,13 @@ main {
padding-bottom: 16px;
display: flex;
justify-content: space-between; }
.ds-list-item .ds-list-item-excerpt {
color: var(--newtab-text-secondary-color);
margin-bottom: 8px; }
.ds-list-item .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden;
color: #737373;
overflow: hidden;
color: var(--newtab-text-secondary-color);
text-overflow: ellipsis; }
.ds-list-item .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden;
margin-bottom: 8px; }
.ds-list-item .ds-list-item-text {
display: flex;
@ -2049,12 +2137,9 @@ main {
border-radius: 4px;
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box;
display: none;
height: 72px;
margin-inline-start: 13px;
min-height: 72px;
min-width: 72px;
width: 72px; }
min-height: 72px; }
.ds-list-item:hover .ds-list-item-title {
color: var(--newtab-link-primary-color); }
.ds-list-item:active .ds-list-item-title {
@ -2241,18 +2326,18 @@ main {
.ASRouterButton.secondary:active {
background-color: rgba(12, 12, 13, 0.3); }
[lwt-newtab-brighttext] .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary {
background-color: rgba(249, 249, 250, 0.1); }
[lwt-newtab-brighttext] .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:hover {
background-color: rgba(249, 249, 250, 0.2); }
[lwt-newtab-brighttext] .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:active {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:hover {
background-color: rgba(249, 249, 250, 0.4); }
[lwt-newtab-brighttext] .footer .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:active {
background-color: rgba(249, 249, 250, 0.5); }
.SnippetBaseContainer {
@ -2274,7 +2359,7 @@ main {
color: var(--newtab-link-primary-color); }
.SnippetBaseContainer a:hover {
text-decoration: underline; }
[lwt-newtab-brighttext] .SnippetBaseContainer a {
[lwt-newtab-brighttext]:not(.force-light-theme) .SnippetBaseContainer a {
font-weight: bold; }
.SnippetBaseContainer input[type='checkbox'] {
margin-inline-start: 0; }
@ -2581,6 +2666,34 @@ main {
.SimpleSnippet .icon {
align-self: flex-start; }
.SimpleSnippet.has-section-header .innerWrapper {
flex-wrap: wrap;
padding-top: 7px; }
.SimpleSnippet .innerContentWrapper {
align-items: center;
display: flex; }
.SimpleSnippet .section-header {
flex: 0 0 100%;
margin-bottom: 10px; }
.SimpleSnippet .section-title {
color: var(--newtab-section-header-text-color);
display: inline-block;
font-size: 13px;
font-weight: bold;
margin: 0; }
.SimpleSnippet .section-title a {
color: var(--newtab-section-header-text-color);
font-weight: inherit;
text-decoration: none; }
.SimpleSnippet .section-title .icon {
height: 16px;
margin-inline-end: 6px;
margin-top: -2px;
width: 16px; }
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
@charset "UTF-8";
/* This is the mac variant */
[lwt-newtab-brighttext] {
[lwt-newtab-brighttext]:not(.force-light-theme) {
-moz-osx-font-smoothing: grayscale; }
html {
@ -71,7 +71,7 @@ body {
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
body[lwt-newtab-brighttext]:not(.force-light-theme) {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
--newtab-border-secondary-color: rgba(249, 249, 250, 0.1);
@ -339,6 +339,10 @@ input[type='text'], input[type='search'] {
.outer-wrapper a {
color: var(--newtab-link-primary-color); }
.force-light-theme {
--newtab-background-color: #F9F9FA !important;
--newtab-text-primary-color: #0C0C0D !important; }
main {
margin: auto;
padding-bottom: 68px;
@ -402,6 +406,8 @@ main {
justify-content: center;
justify-items: center;
line-height: 1.5; }
.as-error-fallback.borderless-error {
box-shadow: none; }
.as-error-fallback a {
color: var(--newtab-text-conditional-color);
text-decoration: underline; }
@ -1084,6 +1090,8 @@ main {
caret-color: transparent; }
.search-handoff-button .fake-editable {
color: transparent;
height: 100%;
opacity: 0;
position: absolute;
top: 0;
left: 0;
@ -1346,7 +1354,7 @@ main {
height: 122px;
overflow: hidden;
position: relative; }
[lwt-newtab-brighttext] .card-outer .card-preview-image-outer {
[lwt-newtab-brighttext]:not(.force-light-theme) .card-outer .card-preview-image-outer {
background-color: #4A4A4F; }
.card-outer .card-preview-image-outer::after {
border-bottom: 1px solid var(--newtab-card-hairline-color);
@ -1685,7 +1693,7 @@ main {
.asrouter-admin .message-item.current .message-id span {
background: #FFE900;
padding: 2px 5px; }
[lwt-newtab-brighttext] .asrouter-admin .message-item.current .message-id span {
[lwt-newtab-brighttext]:not(.force-light-theme) .asrouter-admin .message-item.current .message-id span {
color: #000; }
.asrouter-admin .message-item.blocked .message-id,
.asrouter-admin .message-item.blocked .message-summary {
@ -1858,6 +1866,16 @@ main {
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box; }
.ds-hero header {
font-weight: 600; }
.ds-hero p {
line-height: 1.538; }
.ds-hero .ds-list {
border-top: 0;
padding-top: 0; }
.ds-hero .ds-card {
border: 0; }
.ds-hero .ds-card:hover {
@ -1866,6 +1884,13 @@ main {
border-radius: 0; }
.ds-hero .ds-card .meta {
padding: 0; }
.ds-hero .ds-card .meta .title {
max-height: 3.07692em;
overflow: hidden;
font-size: 13px;
line-height: 20px; }
.ds-hero .ds-card .img-wrapper {
margin: 0 0 12px; }
.ds-hero .img-wrapper {
margin: 0 0 12px; }
@ -1876,6 +1901,9 @@ main {
padding: 20px 0;
border-top: 1px solid var(--newtab-border-secondary-color);
border-bottom: 1px solid var(--newtab-border-secondary-color); }
.ds-hero-no-border .wrapper {
border-top: 0;
border-bottom: 0; }
.ds-hero .wrapper:hover .meta header {
color: #0060DF; }
.ds-hero .wrapper:active .meta header {
@ -1900,6 +1928,19 @@ main {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 24px; }
.ds-column-5 .ds-hero .wrapper .img-wrapper,
.ds-column-6 .ds-hero .wrapper .img-wrapper,
.ds-column-7 .ds-hero .wrapper .img-wrapper,
.ds-column-8 .ds-hero .wrapper .img-wrapper {
margin: 0;
grid-column: 2;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .meta,
.ds-column-6 .ds-hero .wrapper .meta,
.ds-column-7 .ds-hero .wrapper .meta,
.ds-column-8 .ds-hero .wrapper .meta {
grid-column: 1;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .img,
.ds-column-6 .ds-hero .wrapper .img,
.ds-column-7 .ds-hero .wrapper .img,
@ -1926,7 +1967,8 @@ main {
.ds-column-10 .ds-hero .wrapper .img-wrapper,
.ds-column-11 .ds-hero .wrapper .img-wrapper,
.ds-column-12 .ds-hero .wrapper .img-wrapper {
width: 67%; }
width: 67%;
margin: 0; }
.ds-column-9 .ds-hero .wrapper .img,
.ds-column-10 .ds-hero .wrapper .img,
.ds-column-11 .ds-hero .wrapper .img,
@ -1948,7 +1990,8 @@ main {
.ds-column-10 .ds-hero .wrapper .meta p,
.ds-column-11 .ds-hero .wrapper .meta p,
.ds-column-12 .ds-hero .wrapper .meta p {
font-size: 15px; }
font-size: 15px;
line-height: 1.6; }
.ds-column-9 .ds-hero .cards,
.ds-column-10 .ds-hero .cards,
@ -1963,36 +2006,44 @@ main {
height: 0;
border-top: 1px solid var(--newtab-border-secondary-color); }
.ds-list-border {
border: 0;
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 1px;
margin-block-start: 8px;
margin-block-end: 8px; }
.ds-list {
display: grid;
grid-row-gap: 24px;
grid-column-gap: 24px;
padding-inline-start: 0; }
.ds-column-5 .ds-list,
.ds-column-6 .ds-list,
.ds-column-7 .ds-list,
.ds-column-8 .ds-list {
grid-template-columns: repeat(2, 1fr);
grid-row-gap: 24px; }
.ds-column-9 .ds-list,
.ds-column-10 .ds-list,
.ds-column-11 .ds-list,
.ds-column-12 .ds-list {
.ds-list:not(.ds-list-full-width) .ds-list-item {
font-size: 13px;
line-height: 20px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-image {
min-width: 72px;
width: 72px; }
.ds-column-5 .ds-list:not(.ds-list-full-width),
.ds-column-6 .ds-list:not(.ds-list-full-width),
.ds-column-7 .ds-list:not(.ds-list-full-width),
.ds-column-8 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(2, 1fr); }
.ds-column-9 .ds-list:not(.ds-list-full-width),
.ds-column-10 .ds-list:not(.ds-list-full-width),
.ds-column-11 .ds-list:not(.ds-list-full-width),
.ds-column-12 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 18px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
display: none; }
.ds-list:not(.ds-list-images) .ds-list-image {
display: none; }
.ds-list a {
color: #0C0C0D; }
.ds-list-images .ds-list-item .ds-list-image {
display: block; }
.ds-list-numbers .ds-list-item {
counter-increment: list; }
@ -2016,14 +2067,53 @@ main {
.ds-list-numbers .ds-list-item-link:active::before {
background-color: #003EAA; }
.ds-list-item:nth-child(-n+3) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-borders {
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 20px; }
.ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-full-width .ds-list-item {
font-size: 17px;
line-height: 24px; }
.ds-list-full-width .ds-list-item-excerpt {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-info {
max-height: 1.41176em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-title {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-image {
min-width: 144px;
width: 144px; }
.ds-list-item {
line-height: 20px;
font-size: 13px;
display: block;
text-align: start; }
.ds-list-item .ds-list-item-link {
@ -2031,15 +2121,13 @@ main {
padding-bottom: 16px;
display: flex;
justify-content: space-between; }
.ds-list-item .ds-list-item-excerpt {
color: var(--newtab-text-secondary-color);
margin-bottom: 8px; }
.ds-list-item .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden;
color: #737373;
overflow: hidden;
color: var(--newtab-text-secondary-color);
text-overflow: ellipsis; }
.ds-list-item .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden;
margin-bottom: 8px; }
.ds-list-item .ds-list-item-text {
display: flex;
@ -2052,12 +2140,9 @@ main {
border-radius: 4px;
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box;
display: none;
height: 72px;
margin-inline-start: 13px;
min-height: 72px;
min-width: 72px;
width: 72px; }
min-height: 72px; }
.ds-list-item:hover .ds-list-item-title {
color: var(--newtab-link-primary-color); }
.ds-list-item:active .ds-list-item-title {
@ -2244,18 +2329,18 @@ main {
.ASRouterButton.secondary:active {
background-color: rgba(12, 12, 13, 0.3); }
[lwt-newtab-brighttext] .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary {
background-color: rgba(249, 249, 250, 0.1); }
[lwt-newtab-brighttext] .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:hover {
background-color: rgba(249, 249, 250, 0.2); }
[lwt-newtab-brighttext] .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:active {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:hover {
background-color: rgba(249, 249, 250, 0.4); }
[lwt-newtab-brighttext] .footer .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:active {
background-color: rgba(249, 249, 250, 0.5); }
.SnippetBaseContainer {
@ -2277,7 +2362,7 @@ main {
color: var(--newtab-link-primary-color); }
.SnippetBaseContainer a:hover {
text-decoration: underline; }
[lwt-newtab-brighttext] .SnippetBaseContainer a {
[lwt-newtab-brighttext]:not(.force-light-theme) .SnippetBaseContainer a {
font-weight: bold; }
.SnippetBaseContainer input[type='checkbox'] {
margin-inline-start: 0; }
@ -2584,6 +2669,34 @@ main {
.SimpleSnippet .icon {
align-self: flex-start; }
.SimpleSnippet.has-section-header .innerWrapper {
flex-wrap: wrap;
padding-top: 7px; }
.SimpleSnippet .innerContentWrapper {
align-items: center;
display: flex; }
.SimpleSnippet .section-header {
flex: 0 0 100%;
margin-bottom: 10px; }
.SimpleSnippet .section-title {
color: var(--newtab-section-header-text-color);
display: inline-block;
font-size: 13px;
font-weight: bold;
margin: 0; }
.SimpleSnippet .section-title a {
color: var(--newtab-section-header-text-color);
font-weight: inherit;
text-decoration: none; }
.SimpleSnippet .section-title .icon {
height: 16px;
margin-inline-end: 6px;
margin-top: -2px;
width: 16px; }
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;

File diff suppressed because one or more lines are too long

View File

@ -68,7 +68,7 @@ body {
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
body[lwt-newtab-brighttext]:not(.force-light-theme) {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
--newtab-border-secondary-color: rgba(249, 249, 250, 0.1);
@ -336,6 +336,10 @@ input[type='text'], input[type='search'] {
.outer-wrapper a {
color: var(--newtab-link-primary-color); }
.force-light-theme {
--newtab-background-color: #F9F9FA !important;
--newtab-text-primary-color: #0C0C0D !important; }
main {
margin: auto;
padding-bottom: 68px;
@ -399,6 +403,8 @@ main {
justify-content: center;
justify-items: center;
line-height: 1.5; }
.as-error-fallback.borderless-error {
box-shadow: none; }
.as-error-fallback a {
color: var(--newtab-text-conditional-color);
text-decoration: underline; }
@ -1081,6 +1087,8 @@ main {
caret-color: transparent; }
.search-handoff-button .fake-editable {
color: transparent;
height: 100%;
opacity: 0;
position: absolute;
top: 0;
left: 0;
@ -1343,7 +1351,7 @@ main {
height: 122px;
overflow: hidden;
position: relative; }
[lwt-newtab-brighttext] .card-outer .card-preview-image-outer {
[lwt-newtab-brighttext]:not(.force-light-theme) .card-outer .card-preview-image-outer {
background-color: #4A4A4F; }
.card-outer .card-preview-image-outer::after {
border-bottom: 1px solid var(--newtab-card-hairline-color);
@ -1682,7 +1690,7 @@ main {
.asrouter-admin .message-item.current .message-id span {
background: #FFE900;
padding: 2px 5px; }
[lwt-newtab-brighttext] .asrouter-admin .message-item.current .message-id span {
[lwt-newtab-brighttext]:not(.force-light-theme) .asrouter-admin .message-item.current .message-id span {
color: #000; }
.asrouter-admin .message-item.blocked .message-id,
.asrouter-admin .message-item.blocked .message-summary {
@ -1855,6 +1863,16 @@ main {
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box; }
.ds-hero header {
font-weight: 600; }
.ds-hero p {
line-height: 1.538; }
.ds-hero .ds-list {
border-top: 0;
padding-top: 0; }
.ds-hero .ds-card {
border: 0; }
.ds-hero .ds-card:hover {
@ -1863,6 +1881,13 @@ main {
border-radius: 0; }
.ds-hero .ds-card .meta {
padding: 0; }
.ds-hero .ds-card .meta .title {
max-height: 3.07692em;
overflow: hidden;
font-size: 13px;
line-height: 20px; }
.ds-hero .ds-card .img-wrapper {
margin: 0 0 12px; }
.ds-hero .img-wrapper {
margin: 0 0 12px; }
@ -1873,6 +1898,9 @@ main {
padding: 20px 0;
border-top: 1px solid var(--newtab-border-secondary-color);
border-bottom: 1px solid var(--newtab-border-secondary-color); }
.ds-hero-no-border .wrapper {
border-top: 0;
border-bottom: 0; }
.ds-hero .wrapper:hover .meta header {
color: #0060DF; }
.ds-hero .wrapper:active .meta header {
@ -1897,6 +1925,19 @@ main {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-column-gap: 24px; }
.ds-column-5 .ds-hero .wrapper .img-wrapper,
.ds-column-6 .ds-hero .wrapper .img-wrapper,
.ds-column-7 .ds-hero .wrapper .img-wrapper,
.ds-column-8 .ds-hero .wrapper .img-wrapper {
margin: 0;
grid-column: 2;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .meta,
.ds-column-6 .ds-hero .wrapper .meta,
.ds-column-7 .ds-hero .wrapper .meta,
.ds-column-8 .ds-hero .wrapper .meta {
grid-column: 1;
grid-row: 1; }
.ds-column-5 .ds-hero .wrapper .img,
.ds-column-6 .ds-hero .wrapper .img,
.ds-column-7 .ds-hero .wrapper .img,
@ -1923,7 +1964,8 @@ main {
.ds-column-10 .ds-hero .wrapper .img-wrapper,
.ds-column-11 .ds-hero .wrapper .img-wrapper,
.ds-column-12 .ds-hero .wrapper .img-wrapper {
width: 67%; }
width: 67%;
margin: 0; }
.ds-column-9 .ds-hero .wrapper .img,
.ds-column-10 .ds-hero .wrapper .img,
.ds-column-11 .ds-hero .wrapper .img,
@ -1945,7 +1987,8 @@ main {
.ds-column-10 .ds-hero .wrapper .meta p,
.ds-column-11 .ds-hero .wrapper .meta p,
.ds-column-12 .ds-hero .wrapper .meta p {
font-size: 15px; }
font-size: 15px;
line-height: 1.6; }
.ds-column-9 .ds-hero .cards,
.ds-column-10 .ds-hero .cards,
@ -1960,36 +2003,44 @@ main {
height: 0;
border-top: 1px solid var(--newtab-border-secondary-color); }
.ds-list-border {
border: 0;
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 1px;
margin-block-start: 8px;
margin-block-end: 8px; }
.ds-list {
display: grid;
grid-row-gap: 24px;
grid-column-gap: 24px;
padding-inline-start: 0; }
.ds-column-5 .ds-list,
.ds-column-6 .ds-list,
.ds-column-7 .ds-list,
.ds-column-8 .ds-list {
grid-template-columns: repeat(2, 1fr);
grid-row-gap: 24px; }
.ds-column-9 .ds-list,
.ds-column-10 .ds-list,
.ds-column-11 .ds-list,
.ds-column-12 .ds-list {
.ds-list:not(.ds-list-full-width) .ds-list-item {
font-size: 13px;
line-height: 20px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden; }
.ds-list:not(.ds-list-full-width) .ds-list-image {
min-width: 72px;
width: 72px; }
.ds-column-5 .ds-list:not(.ds-list-full-width),
.ds-column-6 .ds-list:not(.ds-list-full-width),
.ds-column-7 .ds-list:not(.ds-list-full-width),
.ds-column-8 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(2, 1fr); }
.ds-column-9 .ds-list:not(.ds-list-full-width),
.ds-column-10 .ds-list:not(.ds-list-full-width),
.ds-column-11 .ds-list:not(.ds-list-full-width),
.ds-column-12 .ds-list:not(.ds-list-full-width) {
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 18px; }
.ds-list:not(.ds-list-full-width) .ds-list-item-excerpt {
display: none; }
.ds-list:not(.ds-list-images) .ds-list-image {
display: none; }
.ds-list a {
color: #0C0C0D; }
.ds-list-images .ds-list-item .ds-list-image {
display: block; }
.ds-list-numbers .ds-list-item {
counter-increment: list; }
@ -2013,14 +2064,53 @@ main {
.ds-list-numbers .ds-list-item-link:active::before {
background-color: #003EAA; }
.ds-list-item:nth-child(-n+3) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-borders {
border-top: 1px solid var(--newtab-border-secondary-color);
padding-top: 20px; }
.ds-list-borders.ds-list-full-width .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-1 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-2 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-3 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)),
.ds-column-4 .ds-list-borders .ds-list-item:not(:nth-last-child(-n+1)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-5 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-6 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-7 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)),
.ds-column-8 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+2)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-column-9 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-10 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-11 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)),
.ds-column-12 .ds-list-borders:not(.ds-list-full-width) .ds-list-item:not(:nth-last-child(-n+3)) {
border-bottom: 1px solid var(--newtab-border-secondary-color);
margin-bottom: -1px;
padding-bottom: 2px; }
.ds-list-full-width .ds-list-item {
font-size: 17px;
line-height: 24px; }
.ds-list-full-width .ds-list-item-excerpt {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-info {
max-height: 1.41176em;
overflow: hidden; }
.ds-list-full-width .ds-list-item-title {
max-height: 2.82353em;
overflow: hidden; }
.ds-list-full-width .ds-list-image {
min-width: 144px;
width: 144px; }
.ds-list-item {
line-height: 20px;
font-size: 13px;
display: block;
text-align: start; }
.ds-list-item .ds-list-item-link {
@ -2028,15 +2118,13 @@ main {
padding-bottom: 16px;
display: flex;
justify-content: space-between; }
.ds-list-item .ds-list-item-excerpt {
color: var(--newtab-text-secondary-color);
margin-bottom: 8px; }
.ds-list-item .ds-list-item-info {
max-height: 1.53846em;
overflow: hidden;
color: #737373;
overflow: hidden;
color: var(--newtab-text-secondary-color);
text-overflow: ellipsis; }
.ds-list-item .ds-list-item-title {
max-height: 3.07692em;
overflow: hidden;
margin-bottom: 8px; }
.ds-list-item .ds-list-item-text {
display: flex;
@ -2049,12 +2137,9 @@ main {
border-radius: 4px;
border: 0.5px solid rgba(0, 0, 0, 0.1);
box-sizing: border-box;
display: none;
height: 72px;
margin-inline-start: 13px;
min-height: 72px;
min-width: 72px;
width: 72px; }
min-height: 72px; }
.ds-list-item:hover .ds-list-item-title {
color: var(--newtab-link-primary-color); }
.ds-list-item:active .ds-list-item-title {
@ -2241,18 +2326,18 @@ main {
.ASRouterButton.secondary:active {
background-color: rgba(12, 12, 13, 0.3); }
[lwt-newtab-brighttext] .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary {
background-color: rgba(249, 249, 250, 0.1); }
[lwt-newtab-brighttext] .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:hover {
background-color: rgba(249, 249, 250, 0.2); }
[lwt-newtab-brighttext] .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .secondary:active {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary {
background-color: rgba(249, 249, 250, 0.3); }
[lwt-newtab-brighttext] .footer .secondary:hover {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:hover {
background-color: rgba(249, 249, 250, 0.4); }
[lwt-newtab-brighttext] .footer .secondary:active {
[lwt-newtab-brighttext]:not(.force-light-theme) .footer .secondary:active {
background-color: rgba(249, 249, 250, 0.5); }
.SnippetBaseContainer {
@ -2274,7 +2359,7 @@ main {
color: var(--newtab-link-primary-color); }
.SnippetBaseContainer a:hover {
text-decoration: underline; }
[lwt-newtab-brighttext] .SnippetBaseContainer a {
[lwt-newtab-brighttext]:not(.force-light-theme) .SnippetBaseContainer a {
font-weight: bold; }
.SnippetBaseContainer input[type='checkbox'] {
margin-inline-start: 0; }
@ -2581,6 +2666,34 @@ main {
.SimpleSnippet .icon {
align-self: flex-start; }
.SimpleSnippet.has-section-header .innerWrapper {
flex-wrap: wrap;
padding-top: 7px; }
.SimpleSnippet .innerContentWrapper {
align-items: center;
display: flex; }
.SimpleSnippet .section-header {
flex: 0 0 100%;
margin-bottom: 10px; }
.SimpleSnippet .section-title {
color: var(--newtab-section-header-text-color);
display: inline-block;
font-size: 13px;
font-weight: bold;
margin: 0; }
.SimpleSnippet .section-title a {
color: var(--newtab-section-header-text-color);
font-weight: inherit;
text-decoration: none; }
.SimpleSnippet .section-title .icon {
height: 16px;
margin-inline-end: 6px;
margin-top: -2px;
width: 16px; }
.SubmitFormSnippet {
flex-direction: column;
flex: 1 1 100%;

File diff suppressed because one or more lines are too long

View File

@ -1948,7 +1948,7 @@ module.exports = {"title":"EOYSnippet","description":"Fundraising Snippet","vers
/* 19 */
/***/ (function(module) {
module.exports = {"title":"SimpleSnippet","description":"A simple template with an icon, text, and optional button.","version":"1.1.1","type":"object","definitions":{"plainText":{"description":"Plain text (no HTML allowed)","type":"string"},"richText":{"description":"Text with HTML subset allowed: i, b, u, strong, em, br","type":"string"},"link_url":{"description":"Target for links or buttons","type":"string","format":"uri"}},"properties":{"title":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Snippet title displayed before snippet text"}]},"text":{"allOf":[{"$ref":"#/definitions/richText"},{"description":"Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}]},"icon":{"type":"string","description":"Snippet icon. 64x64px. SVG or PNG preferred."},"title_icon":{"type":"string","description":"Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."},"button_action":{"type":"string","description":"The type of action the button should trigger."},"button_url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"A url, button_label links to this"}]},"button_action_args":{"type":"string","description":"Additional parameters for button action, example which specific menu the button should open"},"button_label":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Text for a button next to main snippet text that links to button_url. Requires button_url."}]},"button_color":{"type":"string","description":"The text color of the button. Valid CSS color."},"button_background_color":{"type":"string","description":"The background color of the button. Valid CSS color."},"block_button_text":{"type":"string","description":"Tooltip text used for dismiss button.","default":"Remove this"},"tall":{"type":"boolean","description":"To be used by fundraising only, increases height to roughly 120px. Defaults to false."},"do_not_autoblock":{"type":"boolean","description":"Used to prevent blocking the snippet after the CTA (link or button) has been clicked"},"links":{"additionalProperties":{"url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"The url where the link points to."}]},"metric":{"type":"string","description":"Custom event name sent with telemetry event."},"args":{"type":"string","description":"Additional parameters for link action, example which specific menu the button should open"}}}},"additionalProperties":false,"required":["text"],"dependencies":{"button_action":["button_label"],"button_url":["button_label"],"button_color":["button_label"],"button_background_color":["button_label"]}};
module.exports = {"title":"SimpleSnippet","description":"A simple template with an icon, text, and optional button.","version":"1.1.1","type":"object","definitions":{"plainText":{"description":"Plain text (no HTML allowed)","type":"string"},"richText":{"description":"Text with HTML subset allowed: i, b, u, strong, em, br","type":"string"},"link_url":{"description":"Target for links or buttons","type":"string","format":"uri"}},"properties":{"title":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Snippet title displayed before snippet text"}]},"text":{"allOf":[{"$ref":"#/definitions/richText"},{"description":"Main body text of snippet. HTML subset allowed: i, b, u, strong, em, br"}]},"icon":{"type":"string","description":"Snippet icon. 64x64px. SVG or PNG preferred."},"title_icon":{"type":"string","description":"Small icon that shows up before the title / text. 16x16px. SVG or PNG preferred. Grayscale."},"button_action":{"type":"string","description":"The type of action the button should trigger."},"button_url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"A url, button_label links to this"}]},"button_action_args":{"type":"string","description":"Additional parameters for button action, example which specific menu the button should open"},"button_label":{"allOf":[{"$ref":"#/definitions/plainText"},{"description":"Text for a button next to main snippet text that links to button_url. Requires button_url."}]},"button_color":{"type":"string","description":"The text color of the button. Valid CSS color."},"button_background_color":{"type":"string","description":"The background color of the button. Valid CSS color."},"block_button_text":{"type":"string","description":"Tooltip text used for dismiss button.","default":"Remove this"},"tall":{"type":"boolean","description":"To be used by fundraising only, increases height to roughly 120px. Defaults to false."},"do_not_autoblock":{"type":"boolean","description":"Used to prevent blocking the snippet after the CTA (link or button) has been clicked"},"links":{"additionalProperties":{"url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"The url where the link points to."}]},"metric":{"type":"string","description":"Custom event name sent with telemetry event."},"args":{"type":"string","description":"Additional parameters for link action, example which specific menu the button should open"}}},"section_title_icon":{"type":"string","description":"Section title icon. 16x16px. SVG or PNG preferred. section_title_text must also be specified to display."},"section_title_text":{"type":"string","description":"Section title text. section_title_icon must also be specified to display."},"section_title_url":{"allOf":[{"$ref":"#/definitions/link_url"},{"description":"A url, section_title_text links to this"}]}},"additionalProperties":false,"required":["text"],"dependencies":{"button_action":["button_label"],"button_url":["button_label"],"button_color":["button_label"],"button_background_color":["button_label"],"section_title_url":["section_title_text"]}};
/***/ }),
/* 20 */
@ -2372,6 +2372,15 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_9___default.a.PureCompo
this.props.dispatch(common_Actions_jsm__WEBPACK_IMPORTED_MODULE_0__["actionCreators"].UserEvent({ event: "OPEN_NEWTAB_PREFS" }));
}
disableDarkTheme() {
// Dark themes are not supported in discovery stream view
// Add force-light-theme class to body tag to disable dark mode. See Bug 1519764
const bodyClassNames = global.document.body.classList;
if (!bodyClassNames.contains("force-light-theme")) {
bodyClassNames.add("force-light-theme");
}
}
render() {
const { props } = this;
const { App } = props;
@ -2383,6 +2392,10 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_9___default.a.PureCompo
const isDiscoveryStream = props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
if (isDiscoveryStream) {
this.disableDarkTheme();
}
const outerClassName = ["outer-wrapper", shouldBeFixedToTop && "fixed-to-top", prefs.showSearch && this.state.fixedSearch && !noSectionsEnabled && "fixed-search", prefs.showSearch && noSectionsEnabled && "only-search"].filter(v => v).join(" ");
return react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(
@ -2411,7 +2424,11 @@ class BaseContent extends react__WEBPACK_IMPORTED_MODULE_9___default.a.PureCompo
{ className: "non-collapsible-section" },
react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_ManualMigration_ManualMigration__WEBPACK_IMPORTED_MODULE_7__["ManualMigration"], null)
),
isDiscoveryStream ? react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_5__["DiscoveryStreamBase"], null) : react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_11__["Sections"], null),
isDiscoveryStream ? react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(
content_src_components_ErrorBoundary_ErrorBoundary__WEBPACK_IMPORTED_MODULE_6__["ErrorBoundary"],
{ className: "borderless-error" },
react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_DiscoveryStreamBase_DiscoveryStreamBase__WEBPACK_IMPORTED_MODULE_5__["DiscoveryStreamBase"], null)
) : react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_Sections_Sections__WEBPACK_IMPORTED_MODULE_11__["Sections"], null),
react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(PrefsButton, { onClick: this.openPreferences })
),
react__WEBPACK_IMPORTED_MODULE_9___default.a.createElement(content_src_components_ConfirmDialog_ConfirmDialog__WEBPACK_IMPORTED_MODULE_3__["ConfirmDialog"], null)
@ -6410,7 +6427,7 @@ class _Search extends react__WEBPACK_IMPORTED_MODULE_4___default.a.PureComponent
{ className: "fake-textbox" },
this.props.intl.formatMessage({ id: "search_web_placeholder" })
),
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", { className: "fake-editable", tabIndex: "-1", "aria-hidden": "true", contentEditable: "", onDrop: this.onSearchHandoffDrop, onPaste: this.onSearchHandoffPaste }),
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("input", { type: "search", className: "fake-editable", tabIndex: "-1", "aria-hidden": "true", onDrop: this.onSearchHandoffDrop, onPaste: this.onSearchHandoffPaste }),
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("div", { className: "fake-caret" })
),
react__WEBPACK_IMPORTED_MODULE_4___default.a.createElement("input", {
@ -7056,7 +7073,7 @@ class DSCard_DSCard extends external_React_default.a.PureComponent {
{ className: "title" },
this.props.title
),
external_React_default.a.createElement(
this.props.excerpt && external_React_default.a.createElement(
"p",
{ className: "excerpt" },
this.props.excerpt
@ -7132,127 +7149,10 @@ CardGrid_CardGrid.defaultProps = {
// EXTERNAL MODULE: external "ReactRedux"
var external_ReactRedux_ = __webpack_require__(24);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
class Hero_Hero extends external_React_default.a.PureComponent {
constructor(props) {
super(props);
this.onLinkClick = this.onLinkClick.bind(this);
}
onLinkClick(event) {
if (this.props.dispatch) {
this.props.dispatch(Actions["actionCreators"].UserEvent({
event: "CLICK",
source: this.props.type.toUpperCase(),
action_position: 0
}));
this.props.dispatch(Actions["actionCreators"].ImpressionStats({
source: this.props.type.toUpperCase(),
click: 0,
tiles: [{ id: this.heroRec.id, pos: 0 }]
}));
}
}
render() {
const { data } = this.props;
// Handle a render before feed has been fetched by displaying nothing
if (!data || !data.recommendations) {
return external_React_default.a.createElement("div", null);
}
let [heroRec, ...otherRecs] = data.recommendations.slice(0, this.props.items);
this.heroRec = heroRec;
let truncateText = (text, cap) => `${text.substring(0, cap)}${text.length > cap ? `...` : ``}`;
// Note that `{index + 1}` is necessary below for telemetry since we treat heroRec as index 0.
let cards = otherRecs.map((rec, index) => external_React_default.a.createElement(DSCard_DSCard, {
key: `dscard-${index}`,
image_src: rec.image_src,
title: truncateText(rec.title, 44),
url: rec.url,
id: rec.id,
index: index + 1,
type: this.props.type,
dispatch: this.props.dispatch,
context: truncateText(rec.context || "", 22),
source: truncateText(`TODO: SOURCE`, 22) }));
return external_React_default.a.createElement(
"div",
null,
external_React_default.a.createElement(
"div",
{ className: "ds-header" },
this.props.title
),
external_React_default.a.createElement(
"div",
{ className: `ds-hero ds-hero-${this.props.border}` },
external_React_default.a.createElement(
"a",
{ href: heroRec.url, className: "wrapper", onClick: this.onLinkClick },
external_React_default.a.createElement(
"div",
{ className: "img-wrapper" },
external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${heroRec.image_src})` } })
),
external_React_default.a.createElement(
"div",
{ className: "meta" },
external_React_default.a.createElement(
"header",
null,
truncateText(heroRec.title, 28)
),
external_React_default.a.createElement(
"p",
null,
truncateText(heroRec.excerpt, 114)
),
heroRec.context ? external_React_default.a.createElement(
"p",
{ className: "context" },
truncateText(heroRec.context, 22)
) : external_React_default.a.createElement(
"p",
null,
truncateText(`TODO: SOURCE`, 22)
)
)
),
external_React_default.a.createElement(
"div",
{ className: "cards" },
cards
)
)
);
}
// CONCATENATED MODULE: ./content-src/lib/truncate-text.js
function truncateText(text = "", cap) {
return text.substring(0, cap).trim() + (text.length > cap ? "…" : "");
}
Hero_Hero.defaultProps = {
data: {},
border: `border`,
items: 1 // Number of stories to display
};
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/HorizontalRule/HorizontalRule.jsx
class HorizontalRule_HorizontalRule extends external_React_default.a.PureComponent {
render() {
return external_React_default.a.createElement("hr", { className: "ds-hr" });
}
}
// EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
var ImpressionStats = __webpack_require__(29);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/List/List.jsx
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
@ -7260,6 +7160,7 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument
/**
* @note exported for testing only
*/
@ -7306,6 +7207,11 @@ class List_ListItem extends external_React_default.a.PureComponent {
this.props.title
)
),
this.props.excerpt && external_React_default.a.createElement(
"div",
{ className: "ds-list-item-excerpt" },
truncateText(this.props.excerpt, 90)
),
external_React_default.a.createElement(
"div",
{ className: "ds-list-item-info" },
@ -7330,9 +7236,9 @@ function _List(props) {
const recs = feed.data.recommendations;
let recMarkup = recs.slice(0, props.items).map((rec, index) => external_React_default.a.createElement(List_ListItem, _extends({}, rec, { key: `ds-list-item-${index}`, index: index, type: props.type, dispatch: props.dispatch })));
let recMarkup = recs.slice(props.recStartingPoint, props.items).map((rec, index) => external_React_default.a.createElement(List_ListItem, _extends({}, rec, { key: `ds-list-item-${index}`, index: index, type: props.type, dispatch: props.dispatch })));
const listStyles = ["ds-list", props.hasImages ? "ds-list-images" : "", props.hasNumbers ? "ds-list-numbers" : ""];
const listStyles = ["ds-list", props.fullWidth ? "ds-list-full-width" : "", props.hasBorders ? "ds-list-borders" : "", props.hasImages ? "ds-list-images" : "", props.hasNumbers ? "ds-list-numbers" : ""];
return external_React_default.a.createElement(
"div",
null,
@ -7341,7 +7247,6 @@ function _List(props) {
{ className: "ds-header" },
props.header.title
) : null,
external_React_default.a.createElement("hr", { className: "ds-list-border" }),
external_React_default.a.createElement(
"ul",
{ className: listStyles.join(" ") },
@ -7351,12 +7256,145 @@ function _List(props) {
}
_List.defaultProps = {
recStartingPoint: 0, // Index of recommendations to start displaying from
fullWidth: false, // Display items taking up the whole column
hasBorders: false, // Display lines separating each item
hasImages: false, // Display images for each item
hasNumbers: false, // Display numbers for each item
items: 6 // Number of stories to display. TODO: get from endpoint
};
const List = Object(external_ReactRedux_["connect"])(state => ({ DiscoveryStream: state.DiscoveryStream }))(_List);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Hero/Hero.jsx
class Hero_Hero extends external_React_default.a.PureComponent {
constructor(props) {
super(props);
this.onLinkClick = this.onLinkClick.bind(this);
}
onLinkClick(event) {
if (this.props.dispatch) {
this.props.dispatch(Actions["actionCreators"].UserEvent({
event: "CLICK",
source: this.props.type.toUpperCase(),
action_position: 0
}));
this.props.dispatch(Actions["actionCreators"].ImpressionStats({
source: this.props.type.toUpperCase(),
click: 0,
tiles: [{ id: this.heroRec.id, pos: 0 }]
}));
}
}
render() {
const { data } = this.props;
// Handle a render before feed has been fetched by displaying nothing
if (!data || !data.recommendations) {
return external_React_default.a.createElement("div", null);
}
let [heroRec, ...otherRecs] = data.recommendations.slice(0, this.props.items);
this.heroRec = heroRec;
// Note that `{index + 1}` is necessary below for telemetry since we treat heroRec as index 0.
let cards = otherRecs.map((rec, index) => external_React_default.a.createElement(DSCard_DSCard, {
key: `dscard-${index}`,
image_src: rec.image_src,
title: truncateText(rec.title, 44),
url: rec.url,
id: rec.id,
index: index + 1,
type: this.props.type,
dispatch: this.props.dispatch,
context: truncateText(rec.context, 22),
source: truncateText(rec.domain, 22) }));
let list = external_React_default.a.createElement(List, {
recStartingPoint: 1,
feed: this.props.feed,
hasImages: true,
hasBorders: this.props.border === `border`,
items: this.props.items,
type: `Hero` });
return external_React_default.a.createElement(
"div",
null,
external_React_default.a.createElement(
"div",
{ className: "ds-header" },
this.props.title
),
external_React_default.a.createElement(
"div",
{ className: `ds-hero ds-hero-${this.props.border}` },
external_React_default.a.createElement(
"a",
{ href: heroRec.url, className: "wrapper", onClick: this.onLinkClick },
external_React_default.a.createElement(
"div",
{ className: "img-wrapper" },
external_React_default.a.createElement("div", { className: "img", style: { backgroundImage: `url(${heroRec.image_src})` } })
),
external_React_default.a.createElement(
"div",
{ className: "meta" },
external_React_default.a.createElement(
"header",
null,
truncateText(heroRec.title, 28)
),
external_React_default.a.createElement(
"p",
null,
truncateText(heroRec.excerpt, 114)
),
heroRec.context ? external_React_default.a.createElement(
"p",
{ className: "context" },
truncateText(heroRec.context, 22)
) : external_React_default.a.createElement(
"p",
null,
truncateText(heroRec.domain, 22)
)
)
),
external_React_default.a.createElement(
"div",
{ className: `${this.props.subComponentType}` },
this.props.subComponentType === `cards` ? cards : list
)
)
);
}
}
Hero_Hero.defaultProps = {
data: {},
border: `border`,
items: 1 // Number of stories to display
};
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/HorizontalRule/HorizontalRule.jsx
class HorizontalRule_HorizontalRule extends external_React_default.a.PureComponent {
render() {
return external_React_default.a.createElement("hr", { className: "ds-hr" });
}
}
// EXTERNAL MODULE: ./content-src/components/DiscoveryStreamImpressionStats/ImpressionStats.jsx
var ImpressionStats = __webpack_require__(29);
// CONCATENATED MODULE: ./content-src/components/DiscoveryStreamComponents/Navigation/Navigation.jsx
@ -7725,7 +7763,7 @@ class DiscoveryStreamBase_DiscoveryStreamBase extends external_React_default.a.P
});
}
renderComponent(component) {
renderComponent(component, embedWidth) {
let rows;
const { spocs } = this.props.DiscoveryStream;
@ -7766,6 +7804,8 @@ class DiscoveryStreamBase_DiscoveryStreamBase extends external_React_default.a.P
ImpressionStats["ImpressionStats"],
{ rows: rows, dispatch: this.props.dispatch, source: component.type },
external_React_default.a.createElement(Hero_Hero, {
subComponentType: embedWidth >= 9 ? `cards` : `list`,
feed: component.feed,
title: component.header && component.header.title,
data: component.data,
border: component.properties.border,
@ -7782,6 +7822,8 @@ class DiscoveryStreamBase_DiscoveryStreamBase extends external_React_default.a.P
{ rows: rows, dispatch: this.props.dispatch, source: component.type },
external_React_default.a.createElement(List, {
feed: component.feed,
fullWidth: component.properties.full_width,
hasBorders: component.properties.border === "border",
hasImages: component.properties.has_images,
hasNumbers: component.properties.has_numbers,
items: component.properties.items,
@ -7821,7 +7863,7 @@ class DiscoveryStreamBase_DiscoveryStreamBase extends external_React_default.a.P
return external_React_default.a.createElement(
"div",
{ key: `component-${componentIndex}` },
this.renderComponent(component)
this.renderComponent(component, row.width)
);
})
)
@ -7881,6 +7923,9 @@ const Button = props => {
props.children
);
};
// CONCATENATED MODULE: ./content-src/asrouter/components/ConditionalWrapper/ConditionalWrapper.jsx
// lifted from https://gist.github.com/kitze/23d82bb9eb0baabfd03a6a720b1d637f
const ConditionalWrapper = ({ condition, wrap, children }) => condition ? wrap(children) : children;
// EXTERNAL MODULE: ./content-src/asrouter/components/RichText/RichText.jsx
var RichText = __webpack_require__(16);
@ -7966,6 +8011,7 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument
const DEFAULT_ICON_PATH = "chrome://branding/content/icon64.png";
class SimpleSnippet_SimpleSnippet extends external_React_default.a.PureComponent {
@ -8035,35 +8081,95 @@ class SimpleSnippet_SimpleSnippet extends external_React_default.a.PureComponent
sendClick: props.sendClick });
}
wrapSectionHeader(url) {
return function (children) {
return external_React_default.a.createElement(
"a",
{ href: url },
children
);
};
}
wrapSnippetContent(children) {
return external_React_default.a.createElement(
"div",
{ className: "innerContentWrapper" },
children
);
}
renderSectionHeader() {
const { props } = this;
// an icon and text must be specified to render the section header
if (props.content.section_title_icon && props.content.section_title_text) {
const sectionTitleIcon = Object(template_utils["safeURI"])(props.content.section_title_icon);
const sectionTitleURL = props.content.section_title_url;
return external_React_default.a.createElement(
"div",
{ className: "section-header" },
external_React_default.a.createElement(
"h3",
{ className: "section-title" },
external_React_default.a.createElement(
ConditionalWrapper,
{ condition: sectionTitleURL, wrap: this.wrapSectionHeader(sectionTitleURL) },
external_React_default.a.createElement("span", { className: "icon icon-small-spacer", style: { backgroundImage: `url("${sectionTitleIcon}")` } }),
external_React_default.a.createElement(
"span",
{ className: "section-title-text" },
props.content.section_title_text
)
)
)
);
}
return null;
}
render() {
const { props } = this;
const sectionHeader = this.renderSectionHeader();
let className = "SimpleSnippet";
if (props.className) {
className += ` ${props.className}`;
}
if (props.content.tall) {
className += " tall";
}
if (sectionHeader) {
className += " has-section-header";
}
return external_React_default.a.createElement(
SnippetBase_SnippetBase,
_extends({}, props, { className: className, textStyle: this.props.textStyle }),
external_React_default.a.createElement("img", { src: Object(template_utils["safeURI"])(props.content.icon) || DEFAULT_ICON_PATH, className: "icon" }),
sectionHeader,
external_React_default.a.createElement(
"div",
null,
this.renderTitle(),
" ",
ConditionalWrapper,
{ condition: sectionHeader, wrap: this.wrapSnippetContent },
external_React_default.a.createElement("img", { src: Object(template_utils["safeURI"])(props.content.icon) || DEFAULT_ICON_PATH, className: "icon" }),
external_React_default.a.createElement(
"p",
{ className: "body" },
this.renderText()
"div",
null,
this.renderTitle(),
" ",
external_React_default.a.createElement(
"p",
{ className: "body" },
this.renderText()
),
this.props.extraContent
),
this.props.extraContent
),
external_React_default.a.createElement(
"div",
null,
this.renderButton()
external_React_default.a.createElement(
"div",
null,
this.renderButton()
)
)
);
}

File diff suppressed because one or more lines are too long

View File

@ -183,7 +183,7 @@ const PREFS_CONFIG = new Map([
} else {
searchShortcuts.push("google");
}
if (["AT", "DE", "FR", "GB", "IT", "JP", "US"].includes(geo)) {
if (["DE", "FR", "GB", "IT", "JP", "US"].includes(geo)) {
searchShortcuts.push("amazon");
}
return searchShortcuts.join(",");
@ -217,6 +217,7 @@ const PREFS_CONFIG = new Map([
title: "Configuration for the new pocket new tab",
value: JSON.stringify({
enabled: false,
show_spocs: true,
// This is currently an exmple layout used for dev purposes.
layout_endpoint: "https://getpocket.com/v3/newtab/layout?version=1&consumer_key=40249-e88c401e1b1f2242d9e441c4&layout_variant=basic",
}),

View File

@ -42,6 +42,12 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
return this._prefCache.config;
}
get showSpocs() {
// showSponsored is generally a use set spoc opt out,
// show_spocs is generally a mozilla set value.
return this.store.getState().Prefs.values.showSponsored && this.config.show_spocs;
}
setupPrefs() {
Services.prefs.addObserver(CONFIG_PREF_NAME, this);
// Send the initial state of the pref on our reducer
@ -157,31 +163,40 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
async loadSpocs() {
const cachedData = await this.cache.get() || {};
let {spocs} = cachedData;
if (!spocs || !(Date.now() - spocs.lastUpdated < SPOCS_FEEDS_UPDATE_TIME)) {
const spocsResponse = await this.fetchSpocs();
if (spocsResponse) {
spocs = {
lastUpdated: Date.now(),
data: spocsResponse,
};
await this.cache.set("spocs", spocs);
} else {
Cu.reportError("No response for spocs_endpoint prop");
// Use old data if we have it, otherwise nothing.
spocs = spocs || {};
let spocs;
if (this.showSpocs) {
spocs = cachedData.spocs;
if (!spocs || !(Date.now() - spocs.lastUpdated < SPOCS_FEEDS_UPDATE_TIME)) {
const spocsResponse = await this.fetchSpocs();
if (spocsResponse) {
spocs = {
lastUpdated: Date.now(),
data: spocsResponse,
};
await this.cache.set("spocs", spocs);
} else {
Cu.reportError("No response for spocs_endpoint prop");
}
}
}
if (spocs) {
this.store.dispatch(ac.BroadcastToContent({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: {
lastUpdated: spocs.lastUpdated,
spocs: spocs.data,
},
}));
}
// Use good data if we have it, otherwise nothing.
// We can have no data if spocs set to off.
// We can have no data if request fails and there is no good cache.
// We want to send an update spocs or not, so client can render something.
spocs = spocs || {
lastUpdated: Date.now(),
data: {},
};
this.store.dispatch(ac.BroadcastToContent({
type: at.DISCOVERY_STREAM_SPOCS_UPDATE,
data: {
lastUpdated: spocs.lastUpdated,
spocs: spocs.data,
},
}));
}
async getComponentFeed(feedUrl) {
@ -275,6 +290,12 @@ this.DiscoveryStreamFeed = class DiscoveryStreamFeed {
// When this feed is shutting down:
this.uninitPrefs();
break;
case at.PREF_CHANGED:
// Check if spocs was disabled. Remove them if they were.
if (action.data.name === "showSponsored") {
await this.loadSpocs();
}
break;
}
}
};

View File

@ -219,6 +219,34 @@ const MESSAGES = () => ([
"test": "takeover",
},
},
{
"id": "SIMPLE_TEST_WITH_SECTION_HEADING",
"template": "simple_snippet",
"content": {
"button_label": "Get one now!",
"button_url": "https://www.mozilla.org/en-US/firefox/accounts",
"icon": TEST_ICON,
"title": "Firefox Account!",
"text": "<syncLink>Sync it, link it, take it with you</syncLink>. All this and more with a Firefox Account.",
"links": {"syncLink": {"url": "https://www.mozilla.org/en-US/firefox/accounts"}},
"block_button_text": "Block",
"section_title_icon": "resource://activity-stream/data/content/assets/glyph-pocket-16.svg",
"section_title_text": "Messages from Mozilla",
},
},
{
"id": "SIMPLE_TEST_WITH_SECTION_HEADING_AND_LINK",
"template": "simple_snippet",
"content": {
"icon": TEST_ICON,
"title": "Firefox Account!",
"text": "Sync it, link it, take it with you. All this and more with a Firefox Account.",
"block_button_text": "Block",
"section_title_icon": "resource://activity-stream/data/content/assets/glyph-pocket-16.svg",
"section_title_text": "Messages from Mozilla (click for info)",
"section_title_url": "https://www.mozilla.org/about",
},
},
]);
const SnippetsTestMessageProvider = {

View File

@ -163,7 +163,7 @@ this.TopSitesFeed = class TopSitesFeed {
const shouldPin = this.store.getState().Prefs.values[SEARCH_SHORTCUTS_SEARCH_ENGINES_PREF]
.split(",")
.map(getSearchProvider)
.filter(s => s);
.filter(s => s && s.shortURL !== this._currentSearchHostname);
// If we've previously inserted all search shortcuts return early
if (shouldPin.every(shortcut => prevInsertedShortcuts.includes(shortcut.shortURL))) {

View File

@ -91,6 +91,11 @@ section_disclaimer_topstories_buttontext=Tamam, başa düşdüm
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=Firefox Ev Məzmunu
prefs_home_description=Firefox Evdə hansı məzmunları görmək istədiyinizi seçin.
prefs_content_discovery_header=Firefox Ev
prefs_content_discovery_description=Firefox Evdəki Məzmun Kəşfi yüksək keyfiyyətli və sizə uyğun internet məqalələrini kəşf etməyinizə imkan verir.
prefs_content_discovery_button=Məzmun Kəşfini Söndür
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
@ -144,7 +149,6 @@ pocket_read_more=Məşhur Mövzular:
# end of the list of popular topic links.
pocket_read_even_more=Daha çox hekayə gör
pocket_more_reccommendations=Daha Çox Tövsiyyələr
pocket_learn_more=Ətraflı Öyrən
pocket_how_it_works=Bu necə işləyir
pocket_cta_button=Pocket əldə edin
pocket_cta_text=Sevdiyiniz məqalələri Pocket-də saxlayın və möhtəşəm yeni yazıları kəşf edin.

View File

@ -91,6 +91,9 @@ section_disclaimer_topstories_buttontext=Ok, ¡ya caché!
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=Contenido de la página de inicio de Firefox
prefs_home_description=Elige qué contenido quieres en tu pantalla de inicio de Firefox.
prefs_content_discovery_header=Inicio de Firefox
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -93,6 +93,7 @@ prefs_home_header=Contenu de la page daccueil de Firefox
prefs_home_description=Choisissez le contenu que vous souhaitez pour la page daccueil de Firefox.
prefs_content_discovery_header=Page daccueil de Firefox
prefs_content_discovery_button=Désactiver la découverte de contenu
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).

View File

@ -91,6 +91,9 @@ section_disclaimer_topstories_buttontext=Oĩma, hesakãma chéve
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=Kuatiarogue retepy Firefox ñepyrũháme
prefs_home_description=Eiporavo mbae retepýpa eipota Firefox mbaerechaha ñepyrũháme.
prefs_content_discovery_header=Firefox kuatiarogue ñepyrũ
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
@ -144,7 +147,6 @@ pocket_read_more=Ñe'ẽmbyrã Ojehayhuvéva:
# end of the list of popular topic links.
pocket_read_even_more=Ahechaseve Mombe'upy
pocket_more_reccommendations=Hetave jeeporã
pocket_learn_more=Kuaave
pocket_how_it_works=Mbaéichapa ombaapo
pocket_cta_button=Eguereko Pocket
pocket_cta_text=Eñongatu umi eipotáva tembiasakue Pocket-pe ha emombarete ne akã ñemoñeẽ haevévape.

View File

@ -22,7 +22,7 @@ type_label_visited=Đã truy cập
type_label_bookmarked=Đã được đánh dấu
type_label_recommended=Xu hướng
type_label_pocket=Đã lưu vào Pocket
type_label_downloaded=Đã tải về
type_label_downloaded=Đã tải xuống
# LOCALIZATION NOTE (menu_action_*): These strings are displayed in a context
# menu and are meant as a call to action for a given page.
@ -91,6 +91,11 @@ section_disclaimer_topstories_buttontext=Ok, đã hiểu
# what is shown for the homepage, new windows, and new tabs.
prefs_home_header=Nội dung trang chủ của Firefox
prefs_home_description=Chọn nội dung mà bạn muốn thêm vào trang chủ của Firefox.
prefs_content_discovery_header=Trang chủ Firefox
prefs_content_discovery_description=Khám phá nội dung trong trang chủ Firefox cho phép bạn khám phá các bài viết chất lượng cao, có liên quan trên web.
prefs_content_discovery_button=Tắt khám phá nội dung
# LOCALIZATION NOTE (prefs_section_rows_option): This is a semi-colon list of
# plural forms used in a drop down of multiple row options (1 row, 2 rows).
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -40,9 +40,9 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_buttontext": "Tamam, başa düşdüm",
"prefs_home_header": "Firefox Ev Məzmunu",
"prefs_home_description": "Firefox Evdə hansı məzmunları görmək istədiyinizi seçin.",
"prefs_content_discovery_header": "Firefox Home",
"prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
"prefs_content_discovery_button": "Turn Off Content Discovery",
"prefs_content_discovery_header": "Firefox Ev",
"prefs_content_discovery_description": "Firefox Evdəki Məzmun Kəşfi yüksək keyfiyyətli və sizə uyğun internet məqalələrini kəşf etməyinizə imkan verir.",
"prefs_content_discovery_button": "Məzmun Kəşfini Söndür",
"prefs_section_rows_option": "{num} sətir;{num} sətir",
"prefs_search_header": "Web Axtarış",
"prefs_topsites_description": "Ən çox ziyarət etdiyiniz saytlar",
@ -110,6 +110,5 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Məxfilik Bildirişi",
"firstrun_continue_to_login": "Davam et",
"firstrun_skip_login": "Bu addımı keç",
"context_menu_title": "Menyunu aç",
"pocket_learn_more": "Ətraflı Öyrən"
"context_menu_title": "Menyunu aç"
};

View File

@ -40,7 +40,7 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_buttontext": "Ok, ¡ya caché!",
"prefs_home_header": "Contenido de la página de inicio de Firefox",
"prefs_home_description": "Elige qué contenido quieres en tu pantalla de inicio de Firefox.",
"prefs_content_discovery_header": "Página de inicio de Firefox",
"prefs_content_discovery_header": "Inicio de Firefox",
"prefs_content_discovery_description": "Content Discovery en la página de inicio de Firefox le permite descubrir artículos relevantes de alta calidad en toda la web.",
"prefs_content_discovery_button": "Desactivar Content Discovery",
"prefs_section_rows_option": "{num} fila;{num} filas",

View File

@ -42,7 +42,7 @@ window.gActivityStreamStrings = {
"prefs_home_description": "Choisissez le contenu que vous souhaitez pour la page daccueil de Firefox.",
"prefs_content_discovery_header": "Page daccueil de Firefox",
"prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
"prefs_content_discovery_button": "Turn Off Content Discovery",
"prefs_content_discovery_button": "Désactiver la découverte de contenu",
"prefs_section_rows_option": "{num} ligne;{num} lignes",
"prefs_search_header": "Recherche web",
"prefs_topsites_description": "Les sites que vous visitez le plus",

View File

@ -40,7 +40,7 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_buttontext": "Oĩma, hesakãma chéve",
"prefs_home_header": "Kuatiarogue retepy Firefox ñepyrũháme",
"prefs_home_description": "Eiporavo mbae retepýpa eipota Firefox mbaerechaha ñepyrũháme.",
"prefs_content_discovery_header": "Firefox Home",
"prefs_content_discovery_header": "Firefox kuatiarogue ñepyrũ",
"prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
"prefs_content_discovery_button": "Turn Off Content Discovery",
"prefs_section_rows_option": "{num} rysýi; {num} rysýi",
@ -110,6 +110,5 @@ window.gActivityStreamStrings = {
"firstrun_privacy_notice": "Ñemigua purureko",
"firstrun_continue_to_login": "Eku'ejey",
"firstrun_skip_login": "Ehejánte kóva",
"context_menu_title": "Eike poravorãme",
"pocket_learn_more": "Kuaave"
"context_menu_title": "Eike poravorãme"
};

View File

@ -10,7 +10,7 @@ window.gActivityStreamStrings = {
"type_label_bookmarked": "Đã được đánh dấu",
"type_label_recommended": "Xu hướng",
"type_label_pocket": "Đã lưu vào Pocket",
"type_label_downloaded": "Đã tải về",
"type_label_downloaded": "Đã tải xuống",
"menu_action_bookmark": "Đánh dấu",
"menu_action_remove_bookmark": "Xóa đánh dấu",
"menu_action_open_new_window": "Mở trong cửa sổ mới",
@ -40,9 +40,9 @@ window.gActivityStreamStrings = {
"section_disclaimer_topstories_buttontext": "Ok, đã hiểu",
"prefs_home_header": "Nội dung trang chủ của Firefox",
"prefs_home_description": "Chọn nội dung mà bạn muốn thêm vào trang chủ của Firefox.",
"prefs_content_discovery_header": "Firefox Home",
"prefs_content_discovery_description": "Content Discovery in Firefox Home allows you to discover high-quality, relevant articles from across the web.",
"prefs_content_discovery_button": "Turn Off Content Discovery",
"prefs_content_discovery_header": "Trang chủ Firefox",
"prefs_content_discovery_description": "Khám phá nội dung trong trang chủ Firefox cho phép bạn khám phá các bài viết chất lượng cao, có liên quan trên web.",
"prefs_content_discovery_button": "Tắt khám phá nội dung",
"prefs_section_rows_option": "{num} hàng",
"prefs_search_header": "Tìm kiếm web",
"prefs_topsites_description": "Những trang bạn truy cập nhiều nhất",

View File

@ -58,8 +58,8 @@ test_newtab({
before: setDefaultTopSites,
test: async function searchTopSites_dismiss() {
const siteSelector = ".search-shortcut";
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(siteSelector).length === 2,
"2 search topsites are loaded by default");
await ContentTaskUtils.waitForCondition(() => content.document.querySelectorAll(siteSelector).length === 1,
"1 search topsites is loaded by default");
const contextMenuItems = content.openContextMenuAndGetOptions(siteSelector);
is(contextMenuItems.length, 2, "Search TopSites should only have Unpin and Dismiss");

View File

@ -126,7 +126,7 @@ test_newtab({
await ContentTaskUtils.waitForCondition(() => content.document.querySelector(".search-shortcut .title.pinned"), "Wait for pinned search topsites");
const searchTopSites = content.document.querySelectorAll(".title.pinned");
ok(searchTopSites.length >= 2, "There should be at least 2 search topsites");
ok(searchTopSites.length >= 1, "There should be at least 2 search topsites");
searchTopSites[0].click();

View File

@ -69,6 +69,38 @@ describe("SimpleSnippet", () => {
assert.calledOnce(wrapper.props().onAction);
assert.calledWithExactly(wrapper.props().onAction, {type: "OPEN_APPLICATIONS_MENU", data: {args: "appMenu"}});
});
it("should not wrap the main content if a section header is not present", () => {
const wrapper = mountAndCheckProps({text: "bar"});
assert.lengthOf(wrapper.find(".innerContentWrapper"), 0);
});
it("should wrap the main content if a section header is present", () => {
const wrapper = mountAndCheckProps({
section_title_icon: "data:image/gif;base64,R0lGODl",
section_title_text: "Messages from Mozilla",
});
assert.lengthOf(wrapper.find(".innerContentWrapper"), 1);
});
it("should render a section header if text and icon are specified", () => {
const wrapper = mountAndCheckProps({
section_title_icon: "data:image/gif;base64,R0lGODl",
section_title_text: "Messages from Mozilla",
});
assert.equal(wrapper.find(".section-title .icon").prop("style").backgroundImage, 'url("data:image/gif;base64,R0lGODl")');
assert.equal(wrapper.find(".section-title-text").text().trim(), "Messages from Mozilla");
// ensure there is no <a> when a section_title_url is not specified
assert.lengthOf(wrapper.find(".section-title a"), 0);
});
it("should render a section header wrapped in an <a> tag if a url is provided", () => {
const wrapper = mountAndCheckProps({
section_title_icon: "data:image/gif;base64,R0lGODl",
section_title_text: "Messages from Mozilla",
section_title_url: "https://www.mozilla.org",
});
assert.equal(wrapper.find(".section-title a").prop("href"), "https://www.mozilla.org");
});
it("should send an OPEN_URL action when button_url is defined and button is clicked", () => {
const wrapper = mountAndCheckProps({
button_label: "Button",

View File

@ -0,0 +1,37 @@
import {truncateText} from "content-src/lib/truncate-text";
describe("truncateText", () => {
it("should accept nothing", () => {
assert.equal(truncateText(), "");
});
it("should give back string with no truncating", () => {
const str = "hello";
assert.equal(truncateText(str), str);
});
it("should give back short string for long cap", () => {
const str = "hello";
assert.equal(truncateText(str, 100), str);
});
it("should give back string for exact cap", () => {
const str = "hello";
assert.equal(truncateText(str, str.length), str);
});
it("should cap off long string with ellipsis", () => {
const str = "hello world";
assert.equal(truncateText(str, 5), "hello…");
});
it("should avoid putting ellipsis after whitespace", () => {
const str = "hello world";
assert.equal(truncateText(str, 10), "hello…");
});
});

View File

@ -18,7 +18,7 @@ describe("DiscoveryStreamFeed", () => {
sandbox = sinon.createSandbox();
configPrefStub = sandbox.stub(global.Services.prefs, "getStringPref")
.withArgs(CONFIG_PREF_NAME)
.returns(JSON.stringify({enabled: false, layout_endpoint: "foo.com"}));
.returns(JSON.stringify({enabled: false, show_spocs: false, layout_endpoint: "foo.com"}));
// Fetch
fetchStub = sandbox.stub(global, "fetch");
@ -38,11 +38,11 @@ describe("DiscoveryStreamFeed", () => {
describe("#observe", () => {
it("should update state.DiscoveryStream.config when the pref changes", async () => {
configPrefStub.returns(JSON.stringify({enabled: true, layout_endpoint: "foo"}));
configPrefStub.returns(JSON.stringify({enabled: true, show_spocs: false, layout_endpoint: "foo"}));
feed.observe(null, null, CONFIG_PREF_NAME);
assert.deepEqual(feed.store.getState().DiscoveryStream.config, {enabled: true, layout_endpoint: "foo"});
assert.deepEqual(feed.store.getState().DiscoveryStream.config, {enabled: true, show_spocs: false, layout_endpoint: "foo"});
});
});
@ -173,6 +173,9 @@ describe("DiscoveryStreamFeed", () => {
});
describe("#loadSpocs", () => {
beforeEach(() => {
Object.defineProperty(feed, "showSpocs", {get: () => true});
});
it("should fetch fresh data if cache is empty", async () => {
sandbox.stub(feed.cache, "get").returns(Promise.resolve());
sandbox.stub(feed, "fetchSpocs").returns(Promise.resolve("data"));
@ -210,6 +213,19 @@ describe("DiscoveryStreamFeed", () => {
});
describe("#fetchSpocs", () => {
beforeEach(() => {
Object.defineProperty(feed, "showSpocs", {get: () => true});
});
it("should return null for fetchSpocs with no spocs_endpoint", async () => {
feed.store.dispatch(ac.BroadcastToContent({
type: at.DISCOVERY_STREAM_SPOCS_ENDPOINT,
data: "",
}));
const result = await feed.fetchSpocs();
assert.isNull(result);
});
it("should return old spocs if fetch failed", async () => {
sandbox.stub(feed.cache, "set").returns(Promise.resolve());
feed.store.dispatch(ac.BroadcastToContent({
@ -241,6 +257,37 @@ describe("DiscoveryStreamFeed", () => {
assert.equal(feed.store.getState().DiscoveryStream.spocs.data, "new data");
});
});
describe("#showSpocs", () => {
it("should return false from showSpocs if user pref showSponsored is false", async () => {
feed.store.getState = () => ({
Prefs: {values: {showSponsored: false}},
});
Object.defineProperty(feed, "config", {get: () => ({show_spocs: true})});
assert.isFalse(feed.showSpocs);
});
it("should return false from showSpocs if DiscoveryStrea pref show_spocs is false", async () => {
feed.store.getState = () => ({
Prefs: {values: {showSponsored: true}},
});
Object.defineProperty(feed, "config", {get: () => ({show_spocs: false})});
assert.isFalse(feed.showSpocs);
});
it("should return true from showSpocs if both prefs are true", async () => {
feed.store.getState = () => ({
Prefs: {values: {showSponsored: true}},
});
Object.defineProperty(feed, "config", {get: () => ({show_spocs: true})});
assert.isTrue(feed.showSpocs);
});
it("should fire loadSpocs is showSponsored pref changes", async () => {
sandbox.stub(feed, "loadSpocs").returns(Promise.resolve());
await feed.onAction({type: at.PREF_CHANGED, data: {name: "showSponsored"}});
assert.calledOnce(feed.loadSpocs);
});
});
describe("#clearCache", () => {
it("should set .layout, .feeds and .spocs to {}", async () => {

View File

@ -1343,6 +1343,12 @@ describe("Top Sites Feed", () => {
assert.deepEqual(fakeNewTabUtils.pinnedLinks.links[6], {url: "https://amazon.com", searchTopSite: true, label: "@amazon"});
});
it("should not pin shortcuts for the current default search engine", async () => {
feed._currentSearchHostname = "google";
await feed._maybeInsertSearchShortcuts(fakeNewTabUtils.pinnedLinks.links);
assert.deepEqual(fakeNewTabUtils.pinnedLinks.links[3], {url: "https://amazon.com", searchTopSite: true, label: "@amazon"});
});
it("should only pin the first shortcut if there's only one available slot", async () => {
fakeNewTabUtils.pinnedLinks.links[3] = {url: ""};
await feed._maybeInsertSearchShortcuts(fakeNewTabUtils.pinnedLinks.links);