зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1574334 - Add lazy cards, story engagements and bug fixes to New Tab Page r=pdahiya,fluent-reviewers,flod
Differential Revision: https://phabricator.services.mozilla.com/D42229 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
3aec893f88
Коммит
5ff7943b7d
|
@ -38,6 +38,10 @@
|
||||||
&:active {
|
&:active {
|
||||||
background-color: $grey-90-30;
|
background-color: $grey-90-30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px $blue-50 inset, 0 0 0 1px $blue-50, 0 0 0 4px $blue-50-30;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
# User Actions
|
||||||
|
|
||||||
|
A subset of actions are available to messages via fields like `button_action` for snippets, or `primary_action` for CFRs.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
For snippets, you should add the action type in `button_action` and any additional parameters in `button_action_args. For example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"button_action": "OPEN_ABOUT_PAGE",
|
||||||
|
"button_action_args": "config"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Actions
|
||||||
|
|
||||||
|
### `OPEN_APPLICATIONS_MENU`
|
||||||
|
|
||||||
|
* args: (none)
|
||||||
|
|
||||||
|
Opens the applications menu.
|
||||||
|
|
||||||
|
### `OPEN_PRIVATE_BROWSER_WINDOW`
|
||||||
|
|
||||||
|
* args: (none)
|
||||||
|
|
||||||
|
Opens a new private browsing window.
|
||||||
|
|
||||||
|
|
||||||
|
### `OPEN_URL`
|
||||||
|
|
||||||
|
* args: `string` (a url)
|
||||||
|
|
||||||
|
Opens a given url.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"button_action": "OPEN_URL",
|
||||||
|
"button_action_args": "https://foo.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `OPEN_ABOUT_PAGE`
|
||||||
|
|
||||||
|
* args: `string` (a valid about page without the `about:` prefix)
|
||||||
|
|
||||||
|
Opens a given about page
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"button_action": "OPEN_ABOUT_PAGE",
|
||||||
|
"button_action_args": "config"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `OPEN_PREFERENCES_PAGE`
|
||||||
|
|
||||||
|
* args: `string` (a category accessible via a `#`)
|
||||||
|
|
||||||
|
Opens `about:preferences` with an optional category accessible via a `#` in the URL (e.g. `about:preferences#home`).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"button_action": "OPEN_PREFERENCES_PAGE",
|
||||||
|
"button_action_args": "home"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `SHOW_FIREFOX_ACCOUNTS`
|
||||||
|
|
||||||
|
* args: (none)
|
||||||
|
|
||||||
|
Opens Firefox accounts sign-up page. Encodes some information that the origin was from snippets by default.
|
||||||
|
|
||||||
|
### `PIN_CURRENT_TAB`
|
||||||
|
|
||||||
|
* args: (none)
|
||||||
|
|
||||||
|
Pins the currently focused tab.
|
||||||
|
|
||||||
|
### `ENABLE_FIREFOX_MONITOR`
|
||||||
|
|
||||||
|
* args:
|
||||||
|
```ts
|
||||||
|
{
|
||||||
|
url: string;
|
||||||
|
flowRequestParams: {
|
||||||
|
entrypoint: string;
|
||||||
|
utm_term: string;
|
||||||
|
form_type: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens an oauth flow to enable Firefox Monitor at a given `url` and adds Firefox metrics that user given a set of `flowRequestParams`.
|
||||||
|
|
||||||
|
### `url`
|
||||||
|
|
||||||
|
The URL should start with `https://monitor.firefox.com/oauth/init` and add various metrics tags as search params, including:
|
||||||
|
|
||||||
|
* `utm_source`
|
||||||
|
* `utm_campaign`
|
||||||
|
* `form_type`
|
||||||
|
* `entrypoint`
|
||||||
|
|
||||||
|
You should verify the values of these search params with whoever is doing the data analysis (e.g. Leif Oines).
|
||||||
|
|
||||||
|
### `flowRequestParams`
|
||||||
|
|
||||||
|
These params are used by Firefox to add information specific to that individual user to the final oauth URL. You should include:
|
||||||
|
|
||||||
|
* `entrypoint`
|
||||||
|
* `utm_term`
|
||||||
|
* `form_type`
|
||||||
|
|
||||||
|
The `entrypoint` and `form_type` values should match the encoded values in your `url`.
|
||||||
|
|
||||||
|
You should verify the values with whoever is doing the data analysis (e.g. Leif Oines).
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"button_action": "ENABLE_FIREFOX_MONITOR",
|
||||||
|
"button_action_args": {
|
||||||
|
"url": "https://monitor.firefox.com/oauth/init?utm_source=snippets&utm_campaign=monitor-snippet-test&form_type=email&entrypoint=newtab",
|
||||||
|
"flowRequestParams": {
|
||||||
|
"entrypoint": "snippets",
|
||||||
|
"utm_term": "monitor",
|
||||||
|
"form_type": "email"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
.blockButton {
|
.blockButton {
|
||||||
display: block;
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
// larger inset if discovery stream is enabled.
|
// larger inset if discovery stream is enabled.
|
||||||
.ds-outer-wrapper-breakpoint-override & {
|
.ds-outer-wrapper-breakpoint-override & {
|
||||||
|
@ -105,6 +106,11 @@
|
||||||
inset-inline-end: 20px;
|
inset-inline-end: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px $blue-50 inset, 0 0 0 1px $blue-50, 0 0 0 4px $blue-50-30;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -149,12 +155,17 @@
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
.blockButton {
|
.blockButton {
|
||||||
display: none;
|
display: block;
|
||||||
inset-inline-end: -15%;
|
inset-inline-end: -15%;
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: unset;
|
top: unset;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
inset-inline-end: 2%;
|
inset-inline-end: 2%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ export class CardGrid extends React.PureComponent {
|
||||||
pocket_id={rec.pocket_id}
|
pocket_id={rec.pocket_id}
|
||||||
context_type={rec.context_type}
|
context_type={rec.context_type}
|
||||||
bookmarkGuid={rec.bookmarkGuid}
|
bookmarkGuid={rec.bookmarkGuid}
|
||||||
|
engagement={rec.engagement}
|
||||||
cta={rec.cta}
|
cta={rec.cta}
|
||||||
cta_variant={this.props.cta_variant}
|
cta_variant={this.props.cta_variant}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,6 +17,7 @@ export const DefaultMeta = ({
|
||||||
context,
|
context,
|
||||||
context_type,
|
context_type,
|
||||||
cta,
|
cta,
|
||||||
|
engagement,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
<div className="info-wrap">
|
<div className="info-wrap">
|
||||||
|
@ -29,7 +30,11 @@ export const DefaultMeta = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<DSContextFooter context_type={context_type} context={context} />
|
<DSContextFooter
|
||||||
|
context_type={context_type}
|
||||||
|
context={context}
|
||||||
|
engagement={engagement}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -40,6 +45,7 @@ export const VariantMeta = ({
|
||||||
context,
|
context,
|
||||||
context_type,
|
context_type,
|
||||||
cta,
|
cta,
|
||||||
|
engagement,
|
||||||
sponsor,
|
sponsor,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="meta">
|
<div className="meta">
|
||||||
|
@ -51,9 +57,13 @@ export const VariantMeta = ({
|
||||||
<header className="title clamp">{title}</header>
|
<header className="title clamp">{title}</header>
|
||||||
{excerpt && <p className="excerpt clamp">{excerpt}</p>}
|
{excerpt && <p className="excerpt clamp">{excerpt}</p>}
|
||||||
</div>
|
</div>
|
||||||
{cta && <button className="button cta-button">{cta}</button>}
|
{context && cta && <button className="button cta-button">{cta}</button>}
|
||||||
{!context && (
|
{!context && (
|
||||||
<DSContextFooter context_type={context_type} context={context} />
|
<DSContextFooter
|
||||||
|
context_type={context_type}
|
||||||
|
context={context}
|
||||||
|
engagement={engagement}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -63,6 +73,13 @@ export class DSCard extends React.PureComponent {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.onLinkClick = this.onLinkClick.bind(this);
|
this.onLinkClick = this.onLinkClick.bind(this);
|
||||||
|
this.setPlaceholderRef = element => {
|
||||||
|
this.placholderElement = element;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isSeen: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkClick(event) {
|
onLinkClick(event) {
|
||||||
|
@ -93,9 +110,45 @@ export class DSCard extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSeen(entries) {
|
||||||
|
if (this.state) {
|
||||||
|
const entry = entries.find(e => e.isIntersecting);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
if (this.placholderElement) {
|
||||||
|
this.observer.unobserve(this.placholderElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop observing since element has been seen
|
||||||
|
this.setState({
|
||||||
|
isSeen: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.placholderElement) {
|
||||||
|
this.observer = new IntersectionObserver(this.onSeen.bind(this));
|
||||||
|
this.observer.observe(this.placholderElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// Remove observer on unmount
|
||||||
|
if (this.observer && this.placholderElement) {
|
||||||
|
this.observer.unobserve(this.placholderElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.placeholder || !this.state.isSeen) {
|
||||||
return (
|
return (
|
||||||
<div className={`ds-card${this.props.placeholder ? " placeholder" : ""}`}>
|
<div className="ds-card placeholder" ref={this.setPlaceholderRef} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="ds-card">
|
||||||
<SafeAnchor
|
<SafeAnchor
|
||||||
className="ds-card-link"
|
className="ds-card-link"
|
||||||
dispatch={this.props.dispatch}
|
dispatch={this.props.dispatch}
|
||||||
|
@ -116,6 +169,7 @@ export class DSCard extends React.PureComponent {
|
||||||
excerpt={this.props.excerpt}
|
excerpt={this.props.excerpt}
|
||||||
context={this.props.context}
|
context={this.props.context}
|
||||||
context_type={this.props.context_type}
|
context_type={this.props.context_type}
|
||||||
|
engagement={this.props.engagement}
|
||||||
cta={this.props.cta}
|
cta={this.props.cta}
|
||||||
sponsor={this.props.sponsor}
|
sponsor={this.props.sponsor}
|
||||||
/>
|
/>
|
||||||
|
@ -126,6 +180,7 @@ export class DSCard extends React.PureComponent {
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
excerpt={this.props.excerpt}
|
excerpt={this.props.excerpt}
|
||||||
context={this.props.context}
|
context={this.props.context}
|
||||||
|
engagement={this.props.engagement}
|
||||||
context_type={this.props.context_type}
|
context_type={this.props.context_type}
|
||||||
cta={this.props.cta}
|
cta={this.props.cta}
|
||||||
/>
|
/>
|
||||||
|
@ -145,7 +200,6 @@ export class DSCard extends React.PureComponent {
|
||||||
source={this.props.type}
|
source={this.props.type}
|
||||||
/>
|
/>
|
||||||
</SafeAnchor>
|
</SafeAnchor>
|
||||||
{!this.props.placeholder && (
|
|
||||||
<DSLinkMenu
|
<DSLinkMenu
|
||||||
id={this.props.id}
|
id={this.props.id}
|
||||||
index={this.props.pos}
|
index={this.props.pos}
|
||||||
|
@ -158,7 +212,6 @@ export class DSCard extends React.PureComponent {
|
||||||
shim={this.props.shim}
|
shim={this.props.shim}
|
||||||
bookmarkGuid={this.props.bookmarkGuid}
|
bookmarkGuid={this.props.bookmarkGuid}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,14 +13,7 @@ $excerpt-line-height: 20;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: inset $inner-box-shadow;
|
box-shadow: inset $inner-box-shadow;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
min-height: 300px;
|
||||||
.ds-card-link {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-wrapper {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-wrapper {
|
.img-wrapper {
|
||||||
|
|
|
@ -21,20 +21,24 @@ export const StatusMessage = ({ icon, fluentID }) => (
|
||||||
|
|
||||||
export class DSContextFooter extends React.PureComponent {
|
export class DSContextFooter extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { context, context_type } = this.props;
|
const { context, context_type, engagement } = this.props;
|
||||||
const { icon, fluentID } = cardContextTypes[context_type] || {};
|
const { icon, fluentID } = cardContextTypes[context_type] || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="story-footer">
|
<div className="story-footer">
|
||||||
{context && <p className="story-sponsored-label clamp">{context}</p>}
|
{context && <p className="story-sponsored-label clamp">{context}</p>}
|
||||||
<TransitionGroup component={null}>
|
<TransitionGroup component={null}>
|
||||||
{!context && context_type && (
|
{!context && (context_type || engagement) && (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
key={fluentID}
|
key={fluentID}
|
||||||
timeout={ANIMATION_DURATION}
|
timeout={ANIMATION_DURATION}
|
||||||
classNames="story-animate"
|
classNames="story-animate"
|
||||||
>
|
>
|
||||||
|
{engagement && !context_type ? (
|
||||||
|
<div className="story-view-count">{engagement}</div>
|
||||||
|
) : (
|
||||||
<StatusMessage icon={icon} fluentID={fluentID} />
|
<StatusMessage icon={icon} fluentID={fluentID} />
|
||||||
|
)}
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
)}
|
)}
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
|
@ -8,6 +8,7 @@ $status-dark-green: #7C6;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.story-sponsored-label,
|
.story-sponsored-label,
|
||||||
|
.story-view-count,
|
||||||
.status-message {
|
.status-message {
|
||||||
@include dark-theme-only {
|
@include dark-theme-only {
|
||||||
color: $grey-40;
|
color: $grey-40;
|
||||||
|
|
|
@ -78,6 +78,7 @@ export class Hero extends React.PureComponent {
|
||||||
source={rec.domain}
|
source={rec.domain}
|
||||||
pocket_id={rec.pocket_id}
|
pocket_id={rec.pocket_id}
|
||||||
bookmarkGuid={rec.bookmarkGuid}
|
bookmarkGuid={rec.bookmarkGuid}
|
||||||
|
engagement={rec.engagement}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -112,6 +113,7 @@ export class Hero extends React.PureComponent {
|
||||||
<DSContextFooter
|
<DSContextFooter
|
||||||
context={heroRec.context}
|
context={heroRec.context}
|
||||||
context_type={heroRec.context_type}
|
context_type={heroRec.context_type}
|
||||||
|
engagement={heroRec.engagement}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ImpressionStats
|
<ImpressionStats
|
||||||
|
|
|
@ -45,6 +45,7 @@ $card-header-in-hero-line-height: 20;
|
||||||
.ds-card.placeholder {
|
.ds-card.placeholder {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
|
min-height: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.img-wrapper {
|
.img-wrapper {
|
||||||
|
|
|
@ -81,6 +81,7 @@ export class ListItem extends React.PureComponent {
|
||||||
<DSContextFooter
|
<DSContextFooter
|
||||||
context={this.props.context}
|
context={this.props.context}
|
||||||
context_type={this.props.context_type}
|
context_type={this.props.context_type}
|
||||||
|
engagement={this.props.engagement}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<DSImage
|
<DSImage
|
||||||
|
@ -159,6 +160,7 @@ export function _List(props) {
|
||||||
url={rec.url}
|
url={rec.url}
|
||||||
pocket_id={rec.pocket_id}
|
pocket_id={rec.pocket_id}
|
||||||
bookmarkGuid={rec.bookmarkGuid}
|
bookmarkGuid={rec.bookmarkGuid}
|
||||||
|
engagement={rec.engagement}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -1991,7 +1991,8 @@ main {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-card.placeholder {
|
.ds-hero .ds-card.placeholder {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 20px; }
|
padding-bottom: 20px;
|
||||||
|
min-height: 180px; }
|
||||||
.ds-hero .img-wrapper {
|
.ds-hero .img-wrapper {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-hero-item {
|
.ds-hero .ds-hero-item {
|
||||||
|
@ -2635,11 +2636,8 @@ main {
|
||||||
.ds-card.placeholder {
|
.ds-card.placeholder {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||||
border-radius: 4px; }
|
border-radius: 4px;
|
||||||
.ds-card.placeholder .ds-card-link {
|
min-height: 300px; }
|
||||||
cursor: default; }
|
|
||||||
.ds-card.placeholder .img-wrapper {
|
|
||||||
opacity: 0; }
|
|
||||||
.ds-card .img-wrapper {
|
.ds-card .img-wrapper {
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
.ds-card .img {
|
.ds-card .img {
|
||||||
|
@ -2783,12 +2781,14 @@ main {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
.story-footer .story-sponsored-label,
|
.story-footer .story-sponsored-label,
|
||||||
|
.story-footer .story-view-count,
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: #737373; }
|
color: #737373; }
|
||||||
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
||||||
|
.story-footer .story-view-count, [lwt-newtab-brighttext]
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
color: #B1B1B3; }
|
color: #B1B1B3; }
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
|
@ -3003,6 +3003,8 @@ main {
|
||||||
background-color: rgba(12, 12, 13, 0.2); }
|
background-color: rgba(12, 12, 13, 0.2); }
|
||||||
.ASRouterButton.secondary:active {
|
.ASRouterButton.secondary:active {
|
||||||
background-color: rgba(12, 12, 13, 0.3); }
|
background-color: rgba(12, 12, 13, 0.3); }
|
||||||
|
.ASRouterButton.secondary:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3); }
|
||||||
|
|
||||||
[lwt-newtab-brighttext] .secondary {
|
[lwt-newtab-brighttext] .secondary {
|
||||||
background-color: rgba(249, 249, 250, 0.1); }
|
background-color: rgba(249, 249, 250, 0.1); }
|
||||||
|
@ -3313,7 +3315,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
||||||
background-color: var(--newtab-element-hover-color); }
|
background-color: var(--newtab-element-hover-color); }
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
display: block; }
|
display: block;
|
||||||
|
opacity: 1; }
|
||||||
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
inset-inline-end: -8%; }
|
inset-inline-end: -8%; }
|
||||||
@media (max-width: 865px) {
|
@media (max-width: 865px) {
|
||||||
|
@ -3371,6 +3374,9 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
inset-inline-end: 20px;
|
inset-inline-end: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: 50%; }
|
top: 50%; }
|
||||||
|
.SimpleBelowSearchSnippet .blockButton:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3);
|
||||||
|
border-radius: 2px; }
|
||||||
.SimpleBelowSearchSnippet .title {
|
.SimpleBelowSearchSnippet .title {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
|
@ -3397,11 +3403,14 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
background-color: transparent; }
|
background-color: transparent; }
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
display: none;
|
display: block;
|
||||||
inset-inline-end: -15%;
|
inset-inline-end: -15%;
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: unset; }
|
top: unset; }
|
||||||
|
.SimpleBelowSearchSnippet.withButton .blockButton:focus {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: none; }
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
inset-inline-end: 2%; } }
|
inset-inline-end: 2%; } }
|
||||||
|
|
|
@ -1994,7 +1994,8 @@ main {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-card.placeholder {
|
.ds-hero .ds-card.placeholder {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 20px; }
|
padding-bottom: 20px;
|
||||||
|
min-height: 180px; }
|
||||||
.ds-hero .img-wrapper {
|
.ds-hero .img-wrapper {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-hero-item {
|
.ds-hero .ds-hero-item {
|
||||||
|
@ -2638,11 +2639,8 @@ main {
|
||||||
.ds-card.placeholder {
|
.ds-card.placeholder {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||||
border-radius: 4px; }
|
border-radius: 4px;
|
||||||
.ds-card.placeholder .ds-card-link {
|
min-height: 300px; }
|
||||||
cursor: default; }
|
|
||||||
.ds-card.placeholder .img-wrapper {
|
|
||||||
opacity: 0; }
|
|
||||||
.ds-card .img-wrapper {
|
.ds-card .img-wrapper {
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
.ds-card .img {
|
.ds-card .img {
|
||||||
|
@ -2786,12 +2784,14 @@ main {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
.story-footer .story-sponsored-label,
|
.story-footer .story-sponsored-label,
|
||||||
|
.story-footer .story-view-count,
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: #737373; }
|
color: #737373; }
|
||||||
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
||||||
|
.story-footer .story-view-count, [lwt-newtab-brighttext]
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
color: #B1B1B3; }
|
color: #B1B1B3; }
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
|
@ -3006,6 +3006,8 @@ main {
|
||||||
background-color: rgba(12, 12, 13, 0.2); }
|
background-color: rgba(12, 12, 13, 0.2); }
|
||||||
.ASRouterButton.secondary:active {
|
.ASRouterButton.secondary:active {
|
||||||
background-color: rgba(12, 12, 13, 0.3); }
|
background-color: rgba(12, 12, 13, 0.3); }
|
||||||
|
.ASRouterButton.secondary:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3); }
|
||||||
|
|
||||||
[lwt-newtab-brighttext] .secondary {
|
[lwt-newtab-brighttext] .secondary {
|
||||||
background-color: rgba(249, 249, 250, 0.1); }
|
background-color: rgba(249, 249, 250, 0.1); }
|
||||||
|
@ -3316,7 +3318,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
||||||
background-color: var(--newtab-element-hover-color); }
|
background-color: var(--newtab-element-hover-color); }
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
display: block; }
|
display: block;
|
||||||
|
opacity: 1; }
|
||||||
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
inset-inline-end: -8%; }
|
inset-inline-end: -8%; }
|
||||||
@media (max-width: 865px) {
|
@media (max-width: 865px) {
|
||||||
|
@ -3374,6 +3377,9 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
inset-inline-end: 20px;
|
inset-inline-end: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: 50%; }
|
top: 50%; }
|
||||||
|
.SimpleBelowSearchSnippet .blockButton:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3);
|
||||||
|
border-radius: 2px; }
|
||||||
.SimpleBelowSearchSnippet .title {
|
.SimpleBelowSearchSnippet .title {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
|
@ -3400,11 +3406,14 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
background-color: transparent; }
|
background-color: transparent; }
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
display: none;
|
display: block;
|
||||||
inset-inline-end: -15%;
|
inset-inline-end: -15%;
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: unset; }
|
top: unset; }
|
||||||
|
.SimpleBelowSearchSnippet.withButton .blockButton:focus {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: none; }
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
inset-inline-end: 2%; } }
|
inset-inline-end: 2%; } }
|
||||||
|
|
|
@ -1991,7 +1991,8 @@ main {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-card.placeholder {
|
.ds-hero .ds-card.placeholder {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 20px; }
|
padding-bottom: 20px;
|
||||||
|
min-height: 180px; }
|
||||||
.ds-hero .img-wrapper {
|
.ds-hero .img-wrapper {
|
||||||
margin: 0 0 12px; }
|
margin: 0 0 12px; }
|
||||||
.ds-hero .ds-hero-item {
|
.ds-hero .ds-hero-item {
|
||||||
|
@ -2635,11 +2636,8 @@ main {
|
||||||
.ds-card.placeholder {
|
.ds-card.placeholder {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
box-shadow: inset 0 0 0 1px var(--newtab-inner-box-shadow-color);
|
||||||
border-radius: 4px; }
|
border-radius: 4px;
|
||||||
.ds-card.placeholder .ds-card-link {
|
min-height: 300px; }
|
||||||
cursor: default; }
|
|
||||||
.ds-card.placeholder .img-wrapper {
|
|
||||||
opacity: 0; }
|
|
||||||
.ds-card .img-wrapper {
|
.ds-card .img-wrapper {
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
.ds-card .img {
|
.ds-card .img {
|
||||||
|
@ -2783,12 +2781,14 @@ main {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
.story-footer .story-sponsored-label,
|
.story-footer .story-sponsored-label,
|
||||||
|
.story-footer .story-view-count,
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: #737373; }
|
color: #737373; }
|
||||||
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
[lwt-newtab-brighttext] .story-footer .story-sponsored-label, [lwt-newtab-brighttext]
|
||||||
|
.story-footer .story-view-count, [lwt-newtab-brighttext]
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
color: #B1B1B3; }
|
color: #B1B1B3; }
|
||||||
.story-footer .status-message {
|
.story-footer .status-message {
|
||||||
|
@ -3003,6 +3003,8 @@ main {
|
||||||
background-color: rgba(12, 12, 13, 0.2); }
|
background-color: rgba(12, 12, 13, 0.2); }
|
||||||
.ASRouterButton.secondary:active {
|
.ASRouterButton.secondary:active {
|
||||||
background-color: rgba(12, 12, 13, 0.3); }
|
background-color: rgba(12, 12, 13, 0.3); }
|
||||||
|
.ASRouterButton.secondary:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3); }
|
||||||
|
|
||||||
[lwt-newtab-brighttext] .secondary {
|
[lwt-newtab-brighttext] .secondary {
|
||||||
background-color: rgba(249, 249, 250, 0.1); }
|
background-color: rgba(249, 249, 250, 0.1); }
|
||||||
|
@ -3313,7 +3315,8 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover {
|
||||||
background-color: var(--newtab-element-hover-color); }
|
background-color: var(--newtab-element-hover-color); }
|
||||||
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
display: block; }
|
display: block;
|
||||||
|
opacity: 1; }
|
||||||
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
.ds-outer-wrapper-breakpoint-override .below-search-snippet.withButton .snippet-hover-wrapper:hover .blockButton {
|
||||||
inset-inline-end: -8%; }
|
inset-inline-end: -8%; }
|
||||||
@media (max-width: 865px) {
|
@media (max-width: 865px) {
|
||||||
|
@ -3371,6 +3374,9 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
inset-inline-end: 20px;
|
inset-inline-end: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: 50%; }
|
top: 50%; }
|
||||||
|
.SimpleBelowSearchSnippet .blockButton:focus {
|
||||||
|
box-shadow: 0 0 0 1px #0A84FF inset, 0 0 0 1px #0A84FF, 0 0 0 4px rgba(10, 132, 255, 0.3);
|
||||||
|
border-radius: 2px; }
|
||||||
.SimpleBelowSearchSnippet .title {
|
.SimpleBelowSearchSnippet .title {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
|
@ -3397,11 +3403,14 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
background-color: transparent; }
|
background-color: transparent; }
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
display: none;
|
display: block;
|
||||||
inset-inline-end: -15%;
|
inset-inline-end: -15%;
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: unset; }
|
top: unset; }
|
||||||
|
.SimpleBelowSearchSnippet.withButton .blockButton:focus {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: none; }
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
.SimpleBelowSearchSnippet.withButton .blockButton {
|
.SimpleBelowSearchSnippet.withButton .blockButton {
|
||||||
inset-inline-end: 2%; } }
|
inset-inline-end: 2%; } }
|
||||||
|
|
|
@ -7895,7 +7895,8 @@ class DSContextFooter_DSContextFooter extends external_React_default.a.PureCompo
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
context,
|
context,
|
||||||
context_type
|
context_type,
|
||||||
|
engagement
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
icon,
|
icon,
|
||||||
|
@ -7907,11 +7908,13 @@ class DSContextFooter_DSContextFooter extends external_React_default.a.PureCompo
|
||||||
className: "story-sponsored-label clamp"
|
className: "story-sponsored-label clamp"
|
||||||
}, context), external_React_default.a.createElement(external_ReactTransitionGroup_["TransitionGroup"], {
|
}, context), external_React_default.a.createElement(external_ReactTransitionGroup_["TransitionGroup"], {
|
||||||
component: null
|
component: null
|
||||||
}, !context && context_type && external_React_default.a.createElement(external_ReactTransitionGroup_["CSSTransition"], {
|
}, !context && (context_type || engagement) && external_React_default.a.createElement(external_ReactTransitionGroup_["CSSTransition"], {
|
||||||
key: fluentID,
|
key: fluentID,
|
||||||
timeout: ANIMATION_DURATION,
|
timeout: ANIMATION_DURATION,
|
||||||
classNames: "story-animate"
|
classNames: "story-animate"
|
||||||
}, external_React_default.a.createElement(StatusMessage, {
|
}, engagement && !context_type ? external_React_default.a.createElement("div", {
|
||||||
|
className: "story-view-count"
|
||||||
|
}, engagement) : external_React_default.a.createElement(StatusMessage, {
|
||||||
icon: icon,
|
icon: icon,
|
||||||
fluentID: fluentID
|
fluentID: fluentID
|
||||||
}))));
|
}))));
|
||||||
|
@ -7935,7 +7938,8 @@ const DefaultMeta = ({
|
||||||
excerpt,
|
excerpt,
|
||||||
context,
|
context,
|
||||||
context_type,
|
context_type,
|
||||||
cta
|
cta,
|
||||||
|
engagement
|
||||||
}) => external_React_default.a.createElement("div", {
|
}) => external_React_default.a.createElement("div", {
|
||||||
className: "meta"
|
className: "meta"
|
||||||
}, external_React_default.a.createElement("div", {
|
}, external_React_default.a.createElement("div", {
|
||||||
|
@ -7952,7 +7956,8 @@ const DefaultMeta = ({
|
||||||
tabIndex: "0"
|
tabIndex: "0"
|
||||||
}, cta)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
}, cta)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
||||||
context_type: context_type,
|
context_type: context_type,
|
||||||
context: context
|
context: context,
|
||||||
|
engagement: engagement
|
||||||
}));
|
}));
|
||||||
const VariantMeta = ({
|
const VariantMeta = ({
|
||||||
source,
|
source,
|
||||||
|
@ -7961,6 +7966,7 @@ const VariantMeta = ({
|
||||||
context,
|
context,
|
||||||
context_type,
|
context_type,
|
||||||
cta,
|
cta,
|
||||||
|
engagement,
|
||||||
sponsor
|
sponsor
|
||||||
}) => external_React_default.a.createElement("div", {
|
}) => external_React_default.a.createElement("div", {
|
||||||
className: "meta"
|
className: "meta"
|
||||||
|
@ -7972,16 +7978,25 @@ const VariantMeta = ({
|
||||||
className: "title clamp"
|
className: "title clamp"
|
||||||
}, title), excerpt && external_React_default.a.createElement("p", {
|
}, title), excerpt && external_React_default.a.createElement("p", {
|
||||||
className: "excerpt clamp"
|
className: "excerpt clamp"
|
||||||
}, excerpt)), cta && external_React_default.a.createElement("button", {
|
}, excerpt)), context && cta && external_React_default.a.createElement("button", {
|
||||||
className: "button cta-button"
|
className: "button cta-button"
|
||||||
}, cta), !context && external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
}, cta), !context && external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
||||||
context_type: context_type,
|
context_type: context_type,
|
||||||
context: context
|
context: context,
|
||||||
|
engagement: engagement
|
||||||
}));
|
}));
|
||||||
class DSCard_DSCard extends external_React_default.a.PureComponent {
|
class DSCard_DSCard extends external_React_default.a.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onLinkClick = this.onLinkClick.bind(this);
|
this.onLinkClick = this.onLinkClick.bind(this);
|
||||||
|
|
||||||
|
this.setPlaceholderRef = element => {
|
||||||
|
this.placholderElement = element;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isSeen: false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkClick(event) {
|
onLinkClick(event) {
|
||||||
|
@ -8005,9 +8020,47 @@ class DSCard_DSCard extends external_React_default.a.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSeen(entries) {
|
||||||
|
if (this.state) {
|
||||||
|
const entry = entries.find(e => e.isIntersecting);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
if (this.placholderElement) {
|
||||||
|
this.observer.unobserve(this.placholderElement);
|
||||||
|
} // Stop observing since element has been seen
|
||||||
|
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isSeen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.placholderElement) {
|
||||||
|
this.observer = new IntersectionObserver(this.onSeen.bind(this));
|
||||||
|
this.observer.observe(this.placholderElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// Remove observer on unmount
|
||||||
|
if (this.observer && this.placholderElement) {
|
||||||
|
this.observer.unobserve(this.placholderElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.placeholder || !this.state.isSeen) {
|
||||||
return external_React_default.a.createElement("div", {
|
return external_React_default.a.createElement("div", {
|
||||||
className: `ds-card${this.props.placeholder ? " placeholder" : ""}`
|
className: "ds-card placeholder",
|
||||||
|
ref: this.setPlaceholderRef
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return external_React_default.a.createElement("div", {
|
||||||
|
className: "ds-card"
|
||||||
}, external_React_default.a.createElement(SafeAnchor_SafeAnchor, {
|
}, external_React_default.a.createElement(SafeAnchor_SafeAnchor, {
|
||||||
className: "ds-card-link",
|
className: "ds-card-link",
|
||||||
dispatch: this.props.dispatch,
|
dispatch: this.props.dispatch,
|
||||||
|
@ -8025,6 +8078,7 @@ class DSCard_DSCard extends external_React_default.a.PureComponent {
|
||||||
excerpt: this.props.excerpt,
|
excerpt: this.props.excerpt,
|
||||||
context: this.props.context,
|
context: this.props.context,
|
||||||
context_type: this.props.context_type,
|
context_type: this.props.context_type,
|
||||||
|
engagement: this.props.engagement,
|
||||||
cta: this.props.cta,
|
cta: this.props.cta,
|
||||||
sponsor: this.props.sponsor
|
sponsor: this.props.sponsor
|
||||||
}), !this.props.cta_variant && external_React_default.a.createElement(DefaultMeta, {
|
}), !this.props.cta_variant && external_React_default.a.createElement(DefaultMeta, {
|
||||||
|
@ -8032,6 +8086,7 @@ class DSCard_DSCard extends external_React_default.a.PureComponent {
|
||||||
title: this.props.title,
|
title: this.props.title,
|
||||||
excerpt: this.props.excerpt,
|
excerpt: this.props.excerpt,
|
||||||
context: this.props.context,
|
context: this.props.context,
|
||||||
|
engagement: this.props.engagement,
|
||||||
context_type: this.props.context_type,
|
context_type: this.props.context_type,
|
||||||
cta: this.props.cta
|
cta: this.props.cta
|
||||||
}), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
|
}), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
|
||||||
|
@ -8045,7 +8100,7 @@ class DSCard_DSCard extends external_React_default.a.PureComponent {
|
||||||
}],
|
}],
|
||||||
dispatch: this.props.dispatch,
|
dispatch: this.props.dispatch,
|
||||||
source: this.props.type
|
source: this.props.type
|
||||||
})), !this.props.placeholder && external_React_default.a.createElement(DSLinkMenu_DSLinkMenu, {
|
})), external_React_default.a.createElement(DSLinkMenu_DSLinkMenu, {
|
||||||
id: this.props.id,
|
id: this.props.id,
|
||||||
index: this.props.pos,
|
index: this.props.pos,
|
||||||
dispatch: this.props.dispatch,
|
dispatch: this.props.dispatch,
|
||||||
|
@ -8192,6 +8247,7 @@ class CardGrid_CardGrid extends external_React_default.a.PureComponent {
|
||||||
pocket_id: rec.pocket_id,
|
pocket_id: rec.pocket_id,
|
||||||
context_type: rec.context_type,
|
context_type: rec.context_type,
|
||||||
bookmarkGuid: rec.bookmarkGuid,
|
bookmarkGuid: rec.bookmarkGuid,
|
||||||
|
engagement: rec.engagement,
|
||||||
cta: rec.cta,
|
cta: rec.cta,
|
||||||
cta_variant: this.props.cta_variant
|
cta_variant: this.props.cta_variant
|
||||||
}));
|
}));
|
||||||
|
@ -8336,7 +8392,8 @@ class List_ListItem extends external_React_default.a.PureComponent {
|
||||||
className: "ds-list-item-excerpt clamp"
|
className: "ds-list-item-excerpt clamp"
|
||||||
}, this.props.excerpt)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
}, this.props.excerpt)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
||||||
context: this.props.context,
|
context: this.props.context,
|
||||||
context_type: this.props.context_type
|
context_type: this.props.context_type,
|
||||||
|
engagement: this.props.engagement
|
||||||
})), external_React_default.a.createElement(DSImage_DSImage, {
|
})), external_React_default.a.createElement(DSImage_DSImage, {
|
||||||
extraClassNames: "ds-list-image",
|
extraClassNames: "ds-list-image",
|
||||||
source: this.props.image_src,
|
source: this.props.image_src,
|
||||||
|
@ -8400,7 +8457,8 @@ function _List(props) {
|
||||||
type: props.type,
|
type: props.type,
|
||||||
url: rec.url,
|
url: rec.url,
|
||||||
pocket_id: rec.pocket_id,
|
pocket_id: rec.pocket_id,
|
||||||
bookmarkGuid: rec.bookmarkGuid
|
bookmarkGuid: rec.bookmarkGuid,
|
||||||
|
engagement: rec.engagement
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8513,7 +8571,8 @@ class Hero_Hero extends external_React_default.a.PureComponent {
|
||||||
context_type: rec.context_type,
|
context_type: rec.context_type,
|
||||||
source: rec.domain,
|
source: rec.domain,
|
||||||
pocket_id: rec.pocket_id,
|
pocket_id: rec.pocket_id,
|
||||||
bookmarkGuid: rec.bookmarkGuid
|
bookmarkGuid: rec.bookmarkGuid,
|
||||||
|
engagement: rec.engagement
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8548,7 +8607,8 @@ class Hero_Hero extends external_React_default.a.PureComponent {
|
||||||
className: "excerpt clamp"
|
className: "excerpt clamp"
|
||||||
}, heroRec.excerpt)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
}, heroRec.excerpt)), external_React_default.a.createElement(DSContextFooter_DSContextFooter, {
|
||||||
context: heroRec.context,
|
context: heroRec.context,
|
||||||
context_type: heroRec.context_type
|
context_type: heroRec.context_type,
|
||||||
|
engagement: heroRec.engagement
|
||||||
})), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
|
})), external_React_default.a.createElement(ImpressionStats["ImpressionStats"], {
|
||||||
campaignId: heroRec.campaign_id,
|
campaignId: heroRec.campaign_id,
|
||||||
rows: [{
|
rows: [{
|
||||||
|
|
|
@ -399,10 +399,9 @@ const ONBOARDING_MESSAGES = () => [
|
||||||
id: "PROTECTIONS_PANEL_1",
|
id: "PROTECTIONS_PANEL_1",
|
||||||
template: "protections_panel",
|
template: "protections_panel",
|
||||||
content: {
|
content: {
|
||||||
title: "Browse without being followed",
|
title: { string_id: "cfr-protections-panel-header" },
|
||||||
body:
|
body: { string_id: "cfr-protections-panel-body" },
|
||||||
"Keep your data to yourself. Firefox protects you from many of the most common trackers that follow what you do online.",
|
link_text: { string_id: "cfr-protections-panel-link-text" },
|
||||||
link_text: "Learn more",
|
|
||||||
cta_url: `${Services.urlFormatter.formatURLPref(
|
cta_url: `${Services.urlFormatter.formatURLPref(
|
||||||
"app.support.baseURL"
|
"app.support.baseURL"
|
||||||
)}etp-promotions?as=u&utm_source=inproduct`,
|
)}etp-promotions?as=u&utm_source=inproduct`,
|
||||||
|
|
|
@ -48,6 +48,11 @@ ChromeUtils.defineModuleGetter(
|
||||||
"clearInterval",
|
"clearInterval",
|
||||||
"resource://gre/modules/Timer.jsm"
|
"resource://gre/modules/Timer.jsm"
|
||||||
);
|
);
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"requestIdleCallback",
|
||||||
|
"resource://gre/modules/Timer.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
// Frequency at which to check for new messages
|
// Frequency at which to check for new messages
|
||||||
const SYSTEM_TICK_INTERVAL = 5 * 60 * 1000;
|
const SYSTEM_TICK_INTERVAL = 5 * 60 * 1000;
|
||||||
|
@ -257,17 +262,17 @@ class _ToolbarBadgeHub {
|
||||||
}
|
}
|
||||||
|
|
||||||
registerBadgeToAllWindows(message) {
|
registerBadgeToAllWindows(message) {
|
||||||
// Impression should be added when the badge becomes visible
|
|
||||||
this._addImpression(message);
|
|
||||||
// Send a telemetry ping when adding the notification badge
|
|
||||||
this.sendUserEventTelemetry("IMPRESSION", message);
|
|
||||||
|
|
||||||
if (message.template === "update_action") {
|
if (message.template === "update_action") {
|
||||||
this.executeAction({ ...message.content.action, message_id: message.id });
|
this.executeAction({ ...message.content.action, message_id: message.id });
|
||||||
// No badge to set only an action to execute
|
// No badge to set only an action to execute
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Impression should be added when the badge becomes visible
|
||||||
|
this._addImpression(message);
|
||||||
|
// Send a telemetry ping when adding the notification badge
|
||||||
|
this.sendUserEventTelemetry("IMPRESSION", message);
|
||||||
|
|
||||||
EveryWindow.registerCallback(
|
EveryWindow.registerCallback(
|
||||||
this.id,
|
this.id,
|
||||||
win => {
|
win => {
|
||||||
|
@ -297,7 +302,7 @@ class _ToolbarBadgeHub {
|
||||||
|
|
||||||
if (message.content.delay) {
|
if (message.content.delay) {
|
||||||
this.state.showBadgeTimeoutId = setTimeout(() => {
|
this.state.showBadgeTimeoutId = setTimeout(() => {
|
||||||
this.registerBadgeToAllWindows(message);
|
requestIdleCallback(() => this.registerBadgeToAllWindows(message));
|
||||||
}, message.content.delay);
|
}, message.content.delay);
|
||||||
} else {
|
} else {
|
||||||
this.registerBadgeToAllWindows(message);
|
this.registerBadgeToAllWindows(message);
|
||||||
|
|
|
@ -378,6 +378,8 @@ class _ToolbarPanelHub {
|
||||||
*/
|
*/
|
||||||
async insertProtectionPanelMessage(event) {
|
async insertProtectionPanelMessage(event) {
|
||||||
const win = event.target.ownerGlobal;
|
const win = event.target.ownerGlobal;
|
||||||
|
this.maybeInsertFTL(win);
|
||||||
|
|
||||||
const doc = event.target.ownerDocument;
|
const doc = event.target.ownerDocument;
|
||||||
const container = doc.getElementById("messaging-system-message-container");
|
const container = doc.getElementById("messaging-system-message-container");
|
||||||
const infoButton = doc.getElementById("protections-popup-info-button");
|
const infoButton = doc.getElementById("protections-popup-info-button");
|
||||||
|
|
|
@ -78,6 +78,12 @@ cfr-doorhanger-bookmark-fxa-close-btn-tooltip =
|
||||||
.aria-label = Close button
|
.aria-label = Close button
|
||||||
.title = Close
|
.title = Close
|
||||||
|
|
||||||
|
## Protections panel
|
||||||
|
|
||||||
|
cfr-protections-panel-header = Browse without being followed
|
||||||
|
cfr-protections-panel-body = Keep your data to yourself. { -brand-short-name } protects you from many of the most common trackers that follow what you do online.
|
||||||
|
cfr-protections-panel-link-text = Learn more
|
||||||
|
|
||||||
## What's New toolbar button and panel
|
## What's New toolbar button and panel
|
||||||
|
|
||||||
cfr-whatsnew-button =
|
cfr-whatsnew-button =
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
## avoid breaking quoted text).
|
## avoid breaking quoted text).
|
||||||
|
|
||||||
onboarding-button-label-learn-more = Learn More
|
onboarding-button-label-learn-more = Learn More
|
||||||
onboarding-button-label-try-now = Try It Now
|
|
||||||
onboarding-button-label-get-started = Get Started
|
onboarding-button-label-get-started = Get Started
|
||||||
|
|
||||||
## Welcome modal dialog strings
|
## Welcome modal dialog strings
|
||||||
|
@ -76,21 +75,6 @@ onboarding-benefit-privacy-text = Everything we do honors our Personal Data Prom
|
||||||
## Each message has a title and a description of what the browser feature is.
|
## Each message has a title and a description of what the browser feature is.
|
||||||
## Each message also has an associated button for the user to try the feature.
|
## Each message also has an associated button for the user to try the feature.
|
||||||
## The string for the button is found above, in the UI strings section
|
## The string for the button is found above, in the UI strings section
|
||||||
onboarding-private-browsing-title = Private Browsing
|
|
||||||
onboarding-private-browsing-text = Browse by yourself. Private Browsing with Content Blocking blocks online trackers that follow you around the web.
|
|
||||||
|
|
||||||
onboarding-screenshots-title = Screenshots
|
|
||||||
onboarding-screenshots-text = Take, save and share screenshots - without leaving { -brand-short-name }. Capture a region or an entire page as you browse. Then save to the web for easy access and sharing.
|
|
||||||
|
|
||||||
onboarding-addons-title = Add-ons
|
|
||||||
onboarding-addons-text = Add even more features that make { -brand-short-name } work harder for you. Compare prices, check the weather or express your personality with a custom theme.
|
|
||||||
|
|
||||||
onboarding-ghostery-title = Ghostery
|
|
||||||
onboarding-ghostery-text = Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.
|
|
||||||
|
|
||||||
# Note: "Sync" in this case is a generic verb, as in "to synchronize"
|
|
||||||
onboarding-fxa-title = Sync
|
|
||||||
onboarding-fxa-text = Sign up for a { -fxaccount-brand-name } and sync your bookmarks, passwords, and open tabs everywhere you use { -brand-short-name }.
|
|
||||||
|
|
||||||
onboarding-tracking-protection-title2 = Protection From Tracking
|
onboarding-tracking-protection-title2 = Protection From Tracking
|
||||||
onboarding-tracking-protection-text2 = { -brand-short-name } helps stop websites from tracking you online, making it harder for ads to follow you around the web.
|
onboarding-tracking-protection-text2 = { -brand-short-name } helps stop websites from tracking you online, making it harder for ads to follow you around the web.
|
||||||
|
|
|
@ -16,7 +16,7 @@ const FAKE_TRIPLETS = [
|
||||||
text: { string_id: "onboarding-private-browsing-text" },
|
text: { string_id: "onboarding-private-browsing-text" },
|
||||||
icon: "icon",
|
icon: "icon",
|
||||||
primary_button: {
|
primary_button: {
|
||||||
label: { string_id: "onboarding-button-label-try-now" },
|
label: { string_id: "onboarding-button-label-get-started" },
|
||||||
action: {
|
action: {
|
||||||
type: "OPEN_URL",
|
type: "OPEN_URL",
|
||||||
data: { args: "https://example.com/" },
|
data: { args: "https://example.com/" },
|
||||||
|
|
|
@ -22,7 +22,7 @@ const L10N_CONTENT = {
|
||||||
text: { string_id: "onboarding-private-browsing-text" },
|
text: { string_id: "onboarding-private-browsing-text" },
|
||||||
icon: "icon",
|
icon: "icon",
|
||||||
primary_button: {
|
primary_button: {
|
||||||
label: { string_id: "onboarding-button-label-try-now" },
|
label: { string_id: "onboarding-button-label-get-started" },
|
||||||
action: { type: "SOME_TYPE" },
|
action: { type: "SOME_TYPE" },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const CARDS = [
|
||||||
text: { string_id: "onboarding-private-browsing-text" },
|
text: { string_id: "onboarding-private-browsing-text" },
|
||||||
icon: "icon",
|
icon: "icon",
|
||||||
primary_button: {
|
primary_button: {
|
||||||
label: { string_id: "onboarding-button-label-try-now" },
|
label: { string_id: "onboarding-button-label-get-started" },
|
||||||
action: {
|
action: {
|
||||||
type: "OPEN_URL",
|
type: "OPEN_URL",
|
||||||
data: { args: "https://example.com/" },
|
data: { args: "https://example.com/" },
|
||||||
|
|
|
@ -11,7 +11,7 @@ const CARDS = [
|
||||||
text: { string_id: "onboarding-private-browsing-text" },
|
text: { string_id: "onboarding-private-browsing-text" },
|
||||||
icon: "icon",
|
icon: "icon",
|
||||||
primary_button: {
|
primary_button: {
|
||||||
label: { string_id: "onboarding-button-label-try-now" },
|
label: { string_id: "onboarding-button-label-get-started" },
|
||||||
action: {
|
action: {
|
||||||
type: "OPEN_URL",
|
type: "OPEN_URL",
|
||||||
data: { args: "https://example.com/" },
|
data: { args: "https://example.com/" },
|
||||||
|
|
|
@ -20,6 +20,7 @@ describe("<DSCard>", () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = shallow(<DSCard />);
|
wrapper = shallow(<DSCard />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ describe("<DSCard>", () => {
|
||||||
|
|
||||||
it("should render badges for pocket, bookmark when not a spoc element ", () => {
|
it("should render badges for pocket, bookmark when not a spoc element ", () => {
|
||||||
wrapper = mount(<DSCard context_type="bookmark" />);
|
wrapper = mount(<DSCard context_type="bookmark" />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
const contextFooter = wrapper.find(DSContextFooter);
|
const contextFooter = wrapper.find(DSContextFooter);
|
||||||
|
|
||||||
assert.lengthOf(contextFooter.find(StatusMessage), 1);
|
assert.lengthOf(contextFooter.find(StatusMessage), 1);
|
||||||
|
@ -87,6 +89,7 @@ describe("<DSCard>", () => {
|
||||||
it("should render Sponsored Context for a spoc element", () => {
|
it("should render Sponsored Context for a spoc element", () => {
|
||||||
const context = "Sponsored by Foo";
|
const context = "Sponsored by Foo";
|
||||||
wrapper = mount(<DSCard context_type="bookmark" context={context} />);
|
wrapper = mount(<DSCard context_type="bookmark" context={context} />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
const contextFooter = wrapper.find(DSContextFooter);
|
const contextFooter = wrapper.find(DSContextFooter);
|
||||||
|
|
||||||
assert.lengthOf(contextFooter.find(StatusMessage), 0);
|
assert.lengthOf(contextFooter.find(StatusMessage), 0);
|
||||||
|
@ -99,6 +102,7 @@ describe("<DSCard>", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
dispatch = sandbox.stub();
|
dispatch = sandbox.stub();
|
||||||
wrapper = shallow(<DSCard dispatch={dispatch} />);
|
wrapper = shallow(<DSCard dispatch={dispatch} />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call dispatch with the correct events", () => {
|
it("should call dispatch with the correct events", () => {
|
||||||
|
@ -160,6 +164,7 @@ describe("<DSCard>", () => {
|
||||||
describe("DSCard with CTA", () => {
|
describe("DSCard with CTA", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = mount(<DSCard />);
|
wrapper = mount(<DSCard />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render Default Meta", () => {
|
it("should render Default Meta", () => {
|
||||||
|
@ -178,9 +183,19 @@ describe("<DSCard>", () => {
|
||||||
assert.equal(meta.find(".cta-link").text(), "test");
|
assert.equal(meta.find(".cta-link").text(), "test");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render cta-button when item has cta and cta button variant is true", () => {
|
it("should not render cta-button for non spoc content", () => {
|
||||||
wrapper.setProps({ cta: "test", cta_variant: true });
|
wrapper.setProps({ cta: "test", cta_variant: true });
|
||||||
const meta = wrapper.find(VariantMeta);
|
const meta = wrapper.find(VariantMeta);
|
||||||
|
assert.lengthOf(meta.find(".cta-button"), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render cta-button when item has cta and cta button variant is true and is spoc", () => {
|
||||||
|
wrapper.setProps({
|
||||||
|
cta: "test",
|
||||||
|
cta_variant: true,
|
||||||
|
context: "Sponsored by Foo",
|
||||||
|
});
|
||||||
|
const meta = wrapper.find(VariantMeta);
|
||||||
assert.equal(meta.find(".cta-button").text(), "test");
|
assert.equal(meta.find(".cta-button").text(), "test");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -207,6 +222,44 @@ describe("<DSCard>", () => {
|
||||||
assert.equal(meta.find(".source").text(), "Test · Sponsored");
|
assert.equal(meta.find(".source").text(), "Test · Sponsored");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe("DSCard with Intersection Observer", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = shallow(<DSCard />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render card when seen", () => {
|
||||||
|
let card = wrapper.find("div.ds-card.placeholder");
|
||||||
|
assert.lengthOf(card, 1);
|
||||||
|
|
||||||
|
wrapper.instance().observer = {
|
||||||
|
unobserve: sandbox.stub(),
|
||||||
|
};
|
||||||
|
wrapper.instance().placholderElement = "element";
|
||||||
|
|
||||||
|
wrapper.instance().onSeen([
|
||||||
|
{
|
||||||
|
isIntersecting: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.isTrue(wrapper.instance().state.isSeen);
|
||||||
|
card = wrapper.find("div.ds-card.placeholder");
|
||||||
|
assert.lengthOf(card, 0);
|
||||||
|
assert.lengthOf(wrapper.find(SafeAnchor), 1);
|
||||||
|
assert.calledOnce(wrapper.instance().observer.unobserve);
|
||||||
|
assert.calledWith(wrapper.instance().observer.unobserve, "element");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should setup proper placholder ref for isSeen", () => {
|
||||||
|
wrapper.instance().setPlaceholderRef("element");
|
||||||
|
assert.equal(wrapper.instance().placholderElement, "element");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should setup observer on componentDidMount", () => {
|
||||||
|
wrapper = mount(<DSCard />);
|
||||||
|
assert.isTrue(!!wrapper.instance().observer);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("<PlaceholderDSCard> component", () => {
|
describe("<PlaceholderDSCard> component", () => {
|
||||||
|
@ -221,21 +274,21 @@ describe("<PlaceholderDSCard> component", () => {
|
||||||
|
|
||||||
it("should contain placeholder div", () => {
|
it("should contain placeholder div", () => {
|
||||||
const wrapper = shallow(<DSCard placeholder={true} />);
|
const wrapper = shallow(<DSCard placeholder={true} />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
const card = wrapper.find("div.ds-card.placeholder");
|
const card = wrapper.find("div.ds-card.placeholder");
|
||||||
assert.lengthOf(card, 1);
|
assert.lengthOf(card, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be clickable", () => {
|
it("should not be clickable", () => {
|
||||||
const wrapper = shallow(<DSCard placeholder={true} />);
|
const wrapper = shallow(<DSCard placeholder={true} />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
const anchor = wrapper.find("SafeAnchor.ds-card-link");
|
const anchor = wrapper.find("SafeAnchor.ds-card-link");
|
||||||
assert.lengthOf(anchor, 1);
|
assert.lengthOf(anchor, 0);
|
||||||
|
|
||||||
const linkClick = anchor.prop("onLinkClick");
|
|
||||||
assert.isUndefined(linkClick);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not have context menu", () => {
|
it("should not have context menu", () => {
|
||||||
const wrapper = shallow(<DSCard placeholder={true} />);
|
const wrapper = shallow(<DSCard placeholder={true} />);
|
||||||
|
wrapper.setState({ isSeen: true });
|
||||||
const linkMenu = wrapper.find(DSLinkMenu);
|
const linkMenu = wrapper.find(DSLinkMenu);
|
||||||
assert.lengthOf(linkMenu, 0);
|
assert.lengthOf(linkMenu, 0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,7 @@ describe("<DSContextFooter>", () => {
|
||||||
const bookmarkBadge = "bookmark";
|
const bookmarkBadge = "bookmark";
|
||||||
const removeBookmarkBadge = "removedBookmark";
|
const removeBookmarkBadge = "removedBookmark";
|
||||||
const context = "Sponsored by Babel";
|
const context = "Sponsored by Babel";
|
||||||
|
const engagement = "Popular";
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = mount(<DSContextFooter />);
|
wrapper = mount(<DSContextFooter />);
|
||||||
|
@ -26,19 +27,32 @@ describe("<DSContextFooter>", () => {
|
||||||
assert.isTrue(wrapper.exists());
|
assert.isTrue(wrapper.exists());
|
||||||
assert.isOk(wrapper.find(".story-footer"));
|
assert.isOk(wrapper.find(".story-footer"));
|
||||||
});
|
});
|
||||||
|
it("should render an engagement status if no badge and spoc passed", () => {
|
||||||
|
wrapper = mount(<DSContextFooter engagement={engagement} />);
|
||||||
|
|
||||||
|
const engagementLabel = wrapper.find(".story-view-count");
|
||||||
|
assert.equal(engagementLabel.text(), engagement);
|
||||||
|
});
|
||||||
it("should render a badge if a proper badge prop is passed", () => {
|
it("should render a badge if a proper badge prop is passed", () => {
|
||||||
wrapper = mount(<DSContextFooter context_type={bookmarkBadge} />);
|
wrapper = mount(
|
||||||
|
<DSContextFooter context_type={bookmarkBadge} engagement={engagement} />
|
||||||
|
);
|
||||||
const { fluentID } = cardContextTypes[bookmarkBadge];
|
const { fluentID } = cardContextTypes[bookmarkBadge];
|
||||||
|
|
||||||
|
assert.lengthOf(wrapper.find(".story-view-count"), 0);
|
||||||
const statusLabel = wrapper.find(".story-context-label");
|
const statusLabel = wrapper.find(".story-context-label");
|
||||||
assert.isOk(statusLabel);
|
|
||||||
assert.equal(statusLabel.prop("data-l10n-id"), fluentID);
|
assert.equal(statusLabel.prop("data-l10n-id"), fluentID);
|
||||||
});
|
});
|
||||||
it("should only render a sponsored context if pass a sponsored context", async () => {
|
it("should only render a sponsored context if pass a sponsored context", async () => {
|
||||||
wrapper = mount(
|
wrapper = mount(
|
||||||
<DSContextFooter context_type={bookmarkBadge} context={context} />
|
<DSContextFooter
|
||||||
|
context_type={bookmarkBadge}
|
||||||
|
context={context}
|
||||||
|
engagement={engagement}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert.lengthOf(wrapper.find(".story-view-count"), 0);
|
||||||
assert.lengthOf(wrapper.find(StatusMessage), 0);
|
assert.lengthOf(wrapper.find(StatusMessage), 0);
|
||||||
assert.equal(wrapper.find(".story-sponsored-label").text(), context);
|
assert.equal(wrapper.find(".story-sponsored-label").text(), context);
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,7 @@ describe("ToolbarBadgeHub", () => {
|
||||||
let getStringPrefStub;
|
let getStringPrefStub;
|
||||||
let clearUserPrefStub;
|
let clearUserPrefStub;
|
||||||
let setStringPrefStub;
|
let setStringPrefStub;
|
||||||
|
let requestIdleCallbackStub;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
globals = new GlobalOverrider();
|
globals = new GlobalOverrider();
|
||||||
sandbox = sinon.createSandbox();
|
sandbox = sinon.createSandbox();
|
||||||
|
@ -67,7 +68,9 @@ describe("ToolbarBadgeHub", () => {
|
||||||
getStringPrefStub = sandbox.stub();
|
getStringPrefStub = sandbox.stub();
|
||||||
clearUserPrefStub = sandbox.stub();
|
clearUserPrefStub = sandbox.stub();
|
||||||
setStringPrefStub = sandbox.stub();
|
setStringPrefStub = sandbox.stub();
|
||||||
|
requestIdleCallbackStub = sandbox.stub().callsFake(fn => fn());
|
||||||
globals.set({
|
globals.set({
|
||||||
|
requestIdleCallback: requestIdleCallbackStub,
|
||||||
EveryWindow: everyWindowStub,
|
EveryWindow: everyWindowStub,
|
||||||
PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub },
|
PrivateBrowsingUtils: { isBrowserPrivate: isBrowserPrivateStub },
|
||||||
setTimeout: setTimeoutStub,
|
setTimeout: setTimeoutStub,
|
||||||
|
@ -562,6 +565,8 @@ describe("ToolbarBadgeHub", () => {
|
||||||
instance.registerBadgeToAllWindows,
|
instance.registerBadgeToAllWindows,
|
||||||
msg_with_delay
|
msg_with_delay
|
||||||
);
|
);
|
||||||
|
// Delayed actions should be executed inside requestIdleCallback
|
||||||
|
assert.calledOnce(requestIdleCallbackStub);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe("#sendUserEventTelemetry", () => {
|
describe("#sendUserEventTelemetry", () => {
|
||||||
|
|
|
@ -78,6 +78,12 @@ cfr-doorhanger-bookmark-fxa-close-btn-tooltip =
|
||||||
.aria-label = Close button
|
.aria-label = Close button
|
||||||
.title = Close
|
.title = Close
|
||||||
|
|
||||||
|
## Protections panel
|
||||||
|
|
||||||
|
cfr-protections-panel-header = Browse without being followed
|
||||||
|
cfr-protections-panel-body = Keep your data to yourself. { -brand-short-name } protects you from many of the most common trackers that follow what you do online.
|
||||||
|
cfr-protections-panel-link-text = Learn more
|
||||||
|
|
||||||
## What's New toolbar button and panel
|
## What's New toolbar button and panel
|
||||||
|
|
||||||
cfr-whatsnew-button =
|
cfr-whatsnew-button =
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
## avoid breaking quoted text).
|
## avoid breaking quoted text).
|
||||||
|
|
||||||
onboarding-button-label-learn-more = Learn More
|
onboarding-button-label-learn-more = Learn More
|
||||||
onboarding-button-label-try-now = Try It Now
|
|
||||||
onboarding-button-label-get-started = Get Started
|
onboarding-button-label-get-started = Get Started
|
||||||
|
|
||||||
## Welcome modal dialog strings
|
## Welcome modal dialog strings
|
||||||
|
@ -76,21 +75,6 @@ onboarding-benefit-privacy-text = Everything we do honors our Personal Data Prom
|
||||||
## Each message has a title and a description of what the browser feature is.
|
## Each message has a title and a description of what the browser feature is.
|
||||||
## Each message also has an associated button for the user to try the feature.
|
## Each message also has an associated button for the user to try the feature.
|
||||||
## The string for the button is found above, in the UI strings section
|
## The string for the button is found above, in the UI strings section
|
||||||
onboarding-private-browsing-title = Private Browsing
|
|
||||||
onboarding-private-browsing-text = Browse by yourself. Private Browsing with Content Blocking blocks online trackers that follow you around the web.
|
|
||||||
|
|
||||||
onboarding-screenshots-title = Screenshots
|
|
||||||
onboarding-screenshots-text = Take, save and share screenshots - without leaving { -brand-short-name }. Capture a region or an entire page as you browse. Then save to the web for easy access and sharing.
|
|
||||||
|
|
||||||
onboarding-addons-title = Add-ons
|
|
||||||
onboarding-addons-text = Add even more features that make { -brand-short-name } work harder for you. Compare prices, check the weather or express your personality with a custom theme.
|
|
||||||
|
|
||||||
onboarding-ghostery-title = Ghostery
|
|
||||||
onboarding-ghostery-text = Browse faster, smarter, or safer with extensions like Ghostery, which lets you block annoying ads.
|
|
||||||
|
|
||||||
# Note: "Sync" in this case is a generic verb, as in "to synchronize"
|
|
||||||
onboarding-fxa-title = Sync
|
|
||||||
onboarding-fxa-text = Sign up for a { -fxaccount-brand-name } and sync your bookmarks, passwords, and open tabs everywhere you use { -brand-short-name }.
|
|
||||||
|
|
||||||
onboarding-tracking-protection-title2 = Protection From Tracking
|
onboarding-tracking-protection-title2 = Protection From Tracking
|
||||||
onboarding-tracking-protection-text2 = { -brand-short-name } helps stop websites from tracking you online, making it harder for ads to follow you around the web.
|
onboarding-tracking-protection-text2 = { -brand-short-name } helps stop websites from tracking you online, making it harder for ads to follow you around the web.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче